diff options
Diffstat (limited to 'src')
403 files changed, 10004 insertions, 6469 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bb88c8ea..d7f68618c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -24,7 +24,7 @@ if (MSVC) | |||
| 24 | # Ensure that projects build with Unicode support. | 24 | # Ensure that projects build with Unicode support. |
| 25 | add_definitions(-DUNICODE -D_UNICODE) | 25 | add_definitions(-DUNICODE -D_UNICODE) |
| 26 | 26 | ||
| 27 | # /W3 - Level 3 warnings | 27 | # /W4 - Level 4 warnings |
| 28 | # /MP - Multi-threaded compilation | 28 | # /MP - Multi-threaded compilation |
| 29 | # /Zi - Output debugging information | 29 | # /Zi - Output debugging information |
| 30 | # /Zm - Specifies the precompiled header memory allocation limit | 30 | # /Zm - Specifies the precompiled header memory allocation limit |
| @@ -61,7 +61,7 @@ if (MSVC) | |||
| 61 | /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers | 61 | /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers |
| 62 | 62 | ||
| 63 | # Warnings | 63 | # Warnings |
| 64 | /W3 | 64 | /W4 |
| 65 | /WX | 65 | /WX |
| 66 | 66 | ||
| 67 | /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled | 67 | /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled |
| @@ -84,12 +84,17 @@ if (MSVC) | |||
| 84 | 84 | ||
| 85 | /wd4100 # 'identifier': unreferenced formal parameter | 85 | /wd4100 # 'identifier': unreferenced formal parameter |
| 86 | /wd4324 # 'struct_name': structure was padded due to __declspec(align()) | 86 | /wd4324 # 'struct_name': structure was padded due to __declspec(align()) |
| 87 | /wd4201 # nonstandard extension used : nameless struct/union | ||
| 88 | /wd4702 # unreachable code (when used with LTO) | ||
| 87 | ) | 89 | ) |
| 88 | 90 | ||
| 89 | if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) | 91 | if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) |
| 90 | # when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format | 92 | # when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format |
| 91 | # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 | 93 | # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 |
| 92 | add_compile_options(/Z7) | 94 | add_compile_options(/Z7) |
| 95 | # Avoid D9025 warning | ||
| 96 | string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") | ||
| 97 | string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") | ||
| 93 | else() | 98 | else() |
| 94 | add_compile_options(/Zi) | 99 | add_compile_options(/Zi) |
| 95 | endif() | 100 | endif() |
| @@ -105,6 +110,8 @@ if (MSVC) | |||
| 105 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) | 110 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) |
| 106 | else() | 111 | else() |
| 107 | add_compile_options( | 112 | add_compile_options( |
| 113 | -fwrapv | ||
| 114 | |||
| 108 | -Werror=all | 115 | -Werror=all |
| 109 | -Werror=extra | 116 | -Werror=extra |
| 110 | -Werror=missing-declarations | 117 | -Werror=missing-declarations |
| @@ -114,19 +121,21 @@ else() | |||
| 114 | -Wno-attributes | 121 | -Wno-attributes |
| 115 | -Wno-invalid-offsetof | 122 | -Wno-invalid-offsetof |
| 116 | -Wno-unused-parameter | 123 | -Wno-unused-parameter |
| 117 | |||
| 118 | $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init> | ||
| 119 | $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field> | ||
| 120 | $<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local> | ||
| 121 | $<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough> | ||
| 122 | $<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits> | ||
| 123 | $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init> | ||
| 124 | $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field> | ||
| 125 | ) | 124 | ) |
| 126 | 125 | ||
| 126 | if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang | ||
| 127 | add_compile_options( | ||
| 128 | -Wno-braced-scalar-init | ||
| 129 | -Wno-unused-private-field | ||
| 130 | -Wno-nullability-completeness | ||
| 131 | -Werror=shadow-uncaptured-local | ||
| 132 | -Werror=implicit-fallthrough | ||
| 133 | -Werror=type-limits | ||
| 134 | ) | ||
| 135 | endif() | ||
| 136 | |||
| 127 | if (ARCHITECTURE_x86_64) | 137 | if (ARCHITECTURE_x86_64) |
| 128 | add_compile_options("-mcx16") | 138 | add_compile_options("-mcx16") |
| 129 | add_compile_options("-fwrapv") | ||
| 130 | endif() | 139 | endif() |
| 131 | 140 | ||
| 132 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) | 141 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) |
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 36e2dac98..832c08e15 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -56,7 +56,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 56 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" | 56 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" |
| 57 | android:theme="@style/Theme.Yuzu.Main" | 57 | android:theme="@style/Theme.Yuzu.Main" |
| 58 | android:launchMode="singleTop" | 58 | android:launchMode="singleTop" |
| 59 | android:screenOrientation="userLandscape" | ||
| 60 | android:supportsPictureInPicture="true" | 59 | android:supportsPictureInPicture="true" |
| 61 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" | 60 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" |
| 62 | android:exported="true"> | 61 | android:exported="true"> |
| @@ -67,6 +66,14 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 67 | <data android:mimeType="application/octet-stream" /> | 66 | <data android:mimeType="application/octet-stream" /> |
| 68 | </intent-filter> | 67 | </intent-filter> |
| 69 | 68 | ||
| 69 | <intent-filter> | ||
| 70 | <action android:name="android.intent.action.VIEW" /> | ||
| 71 | <category android:name="android.intent.category.DEFAULT" /> | ||
| 72 | <data | ||
| 73 | android:mimeType="application/octet-stream" | ||
| 74 | android:scheme="content"/> | ||
| 75 | </intent-filter> | ||
| 76 | |||
| 70 | <meta-data | 77 | <meta-data |
| 71 | android:name="android.nfc.action.TECH_DISCOVERED" | 78 | android:name="android.nfc.action.TECH_DISCOVERED" |
| 72 | android:resource="@xml/nfc_tech_filter" /> | 79 | android:resource="@xml/nfc_tech_filter" /> |
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 9c32e044c..c8706d7a6 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 | |||
| @@ -22,9 +22,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil.exists | |||
| 22 | import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize | 22 | import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize |
| 23 | import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory | 23 | import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory |
| 24 | import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri | 24 | import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri |
| 25 | import org.yuzu.yuzu_emu.utils.Log.error | 25 | import org.yuzu.yuzu_emu.utils.Log |
| 26 | import org.yuzu.yuzu_emu.utils.Log.verbose | ||
| 27 | import org.yuzu.yuzu_emu.utils.Log.warning | ||
| 28 | import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable | 26 | import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable |
| 29 | 27 | ||
| 30 | /** | 28 | /** |
| @@ -219,10 +217,6 @@ object NativeLibrary { | |||
| 219 | 217 | ||
| 220 | external fun reloadSettings() | 218 | external fun reloadSettings() |
| 221 | 219 | ||
| 222 | external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String? | ||
| 223 | |||
| 224 | external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?) | ||
| 225 | |||
| 226 | external fun initGameIni(gameID: String?) | 220 | external fun initGameIni(gameID: String?) |
| 227 | 221 | ||
| 228 | /** | 222 | /** |
| @@ -413,14 +407,17 @@ object NativeLibrary { | |||
| 413 | details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) } | 407 | details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) } |
| 414 | ) | 408 | ) |
| 415 | } | 409 | } |
| 410 | |||
| 416 | CoreError.ErrorSavestate -> { | 411 | CoreError.ErrorSavestate -> { |
| 417 | title = emulationActivity.getString(R.string.save_load_error) | 412 | title = emulationActivity.getString(R.string.save_load_error) |
| 418 | message = details | 413 | message = details |
| 419 | } | 414 | } |
| 415 | |||
| 420 | CoreError.ErrorUnknown -> { | 416 | CoreError.ErrorUnknown -> { |
| 421 | title = emulationActivity.getString(R.string.fatal_error) | 417 | title = emulationActivity.getString(R.string.fatal_error) |
| 422 | message = emulationActivity.getString(R.string.fatal_error_message) | 418 | message = emulationActivity.getString(R.string.fatal_error_message) |
| 423 | } | 419 | } |
| 420 | |||
| 424 | else -> { | 421 | else -> { |
| 425 | return true | 422 | return true |
| 426 | } | 423 | } |
| @@ -454,6 +451,7 @@ object NativeLibrary { | |||
| 454 | captionId = R.string.loader_error_video_core | 451 | captionId = R.string.loader_error_video_core |
| 455 | descriptionId = R.string.loader_error_video_core_description | 452 | descriptionId = R.string.loader_error_video_core_description |
| 456 | } | 453 | } |
| 454 | |||
| 457 | else -> { | 455 | else -> { |
| 458 | captionId = R.string.loader_error_encrypted | 456 | captionId = R.string.loader_error_encrypted |
| 459 | descriptionId = R.string.loader_error_encrypted_roms_description | 457 | descriptionId = R.string.loader_error_encrypted_roms_description |
| @@ -465,7 +463,7 @@ object NativeLibrary { | |||
| 465 | 463 | ||
| 466 | val emulationActivity = sEmulationActivity.get() | 464 | val emulationActivity = sEmulationActivity.get() |
| 467 | if (emulationActivity == null) { | 465 | if (emulationActivity == null) { |
| 468 | warning("[NativeLibrary] EmulationActivity is null, can't exit.") | 466 | Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.") |
| 469 | return | 467 | return |
| 470 | } | 468 | } |
| 471 | 469 | ||
| @@ -490,15 +488,27 @@ object NativeLibrary { | |||
| 490 | } | 488 | } |
| 491 | 489 | ||
| 492 | fun setEmulationActivity(emulationActivity: EmulationActivity?) { | 490 | fun setEmulationActivity(emulationActivity: EmulationActivity?) { |
| 493 | verbose("[NativeLibrary] Registering EmulationActivity.") | 491 | Log.verbose("[NativeLibrary] Registering EmulationActivity.") |
| 494 | sEmulationActivity = WeakReference(emulationActivity) | 492 | sEmulationActivity = WeakReference(emulationActivity) |
| 495 | } | 493 | } |
| 496 | 494 | ||
| 497 | fun clearEmulationActivity() { | 495 | fun clearEmulationActivity() { |
| 498 | verbose("[NativeLibrary] Unregistering EmulationActivity.") | 496 | Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") |
| 499 | sEmulationActivity.clear() | 497 | sEmulationActivity.clear() |
| 500 | } | 498 | } |
| 501 | 499 | ||
| 500 | @Keep | ||
| 501 | @JvmStatic | ||
| 502 | fun onEmulationStarted() { | ||
| 503 | sEmulationActivity.get()!!.onEmulationStarted() | ||
| 504 | } | ||
| 505 | |||
| 506 | @Keep | ||
| 507 | @JvmStatic | ||
| 508 | fun onEmulationStopped(status: Int) { | ||
| 509 | sEmulationActivity.get()!!.onEmulationStopped(status) | ||
| 510 | } | ||
| 511 | |||
| 502 | /** | 512 | /** |
| 503 | * Logs the Yuzu version, Android version and, CPU. | 513 | * Logs the Yuzu version, Android version and, CPU. |
| 504 | */ | 514 | */ |
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 04ab6a220..9561748cb 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 | |||
| @@ -46,7 +46,7 @@ class YuzuApplication : Application() { | |||
| 46 | super.onCreate() | 46 | super.onCreate() |
| 47 | application = this | 47 | application = this |
| 48 | documentsTree = DocumentsTree() | 48 | documentsTree = DocumentsTree() |
| 49 | DirectoryInitialization.start(applicationContext) | 49 | DirectoryInitialization.start() |
| 50 | GpuDriverHelper.initializeDriverParameters(applicationContext) | 50 | GpuDriverHelper.initializeDriverParameters(applicationContext) |
| 51 | NativeLibrary.logDeviceInfo() | 51 | NativeLibrary.logDeviceInfo() |
| 52 | 52 | ||
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 7461fb093..bbd328c71 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 | |||
| @@ -42,7 +42,7 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding | |||
| 42 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | 42 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting |
| 43 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 43 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 44 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 44 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 45 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | 45 | import org.yuzu.yuzu_emu.model.EmulationViewModel |
| 46 | import org.yuzu.yuzu_emu.model.Game | 46 | import org.yuzu.yuzu_emu.model.Game |
| 47 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 47 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| 48 | import org.yuzu.yuzu_emu.utils.ForegroundService | 48 | import org.yuzu.yuzu_emu.utils.ForegroundService |
| @@ -72,18 +72,17 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 72 | private val actionMute = "ACTION_EMULATOR_MUTE" | 72 | private val actionMute = "ACTION_EMULATOR_MUTE" |
| 73 | private val actionUnmute = "ACTION_EMULATOR_UNMUTE" | 73 | private val actionUnmute = "ACTION_EMULATOR_UNMUTE" |
| 74 | 74 | ||
| 75 | private val settingsViewModel: SettingsViewModel by viewModels() | 75 | private val emulationViewModel: EmulationViewModel by viewModels() |
| 76 | 76 | ||
| 77 | override fun onDestroy() { | 77 | override fun onDestroy() { |
| 78 | stopForegroundService(this) | 78 | stopForegroundService(this) |
| 79 | emulationViewModel.clear() | ||
| 79 | super.onDestroy() | 80 | super.onDestroy() |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | override fun onCreate(savedInstanceState: Bundle?) { | 83 | override fun onCreate(savedInstanceState: Bundle?) { |
| 83 | ThemeHelper.setTheme(this) | 84 | ThemeHelper.setTheme(this) |
| 84 | 85 | ||
| 85 | settingsViewModel.settings.loadSettings() | ||
| 86 | |||
| 87 | super.onCreate(savedInstanceState) | 86 | super.onCreate(savedInstanceState) |
| 88 | 87 | ||
| 89 | binding = ActivityEmulationBinding.inflate(layoutInflater) | 88 | binding = ActivityEmulationBinding.inflate(layoutInflater) |
| @@ -91,9 +90,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 91 | 90 | ||
| 92 | val navHostFragment = | 91 | val navHostFragment = |
| 93 | supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment | 92 | supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment |
| 94 | val navController = navHostFragment.navController | 93 | navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras) |
| 95 | navController | ||
| 96 | .setGraph(R.navigation.emulation_navigation, intent.extras) | ||
| 97 | 94 | ||
| 98 | isActivityRecreated = savedInstanceState != null | 95 | isActivityRecreated = savedInstanceState != null |
| 99 | 96 | ||
| @@ -424,6 +421,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 424 | } | 421 | } |
| 425 | } | 422 | } |
| 426 | 423 | ||
| 424 | fun onEmulationStarted() { | ||
| 425 | emulationViewModel.setEmulationStarted(true) | ||
| 426 | } | ||
| 427 | |||
| 428 | fun onEmulationStopped(status: Int) { | ||
| 429 | if (status == 0) { | ||
| 430 | finish() | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 427 | private fun startMotionSensorListener() { | 434 | private fun startMotionSensorListener() { |
| 428 | val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager | 435 | val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager |
| 429 | val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) | 436 | val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) |
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 e91277d35..0013e8512 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 | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.adapters | 4 | package org.yuzu.yuzu_emu.adapters |
| 5 | 5 | ||
| 6 | import android.graphics.Bitmap | 6 | import android.content.Intent |
| 7 | import android.graphics.BitmapFactory | 7 | import android.graphics.drawable.BitmapDrawable |
| 8 | import android.net.Uri | 8 | import android.net.Uri |
| 9 | import android.text.TextUtils | 9 | import android.text.TextUtils |
| 10 | import android.view.LayoutInflater | 10 | import android.view.LayoutInflater |
| @@ -13,25 +13,26 @@ import android.view.ViewGroup | |||
| 13 | import android.widget.ImageView | 13 | import android.widget.ImageView |
| 14 | import android.widget.Toast | 14 | import android.widget.Toast |
| 15 | import androidx.appcompat.app.AppCompatActivity | 15 | import androidx.appcompat.app.AppCompatActivity |
| 16 | import androidx.core.content.pm.ShortcutInfoCompat | ||
| 17 | import androidx.core.content.pm.ShortcutManagerCompat | ||
| 18 | import androidx.core.graphics.drawable.IconCompat | ||
| 16 | import androidx.documentfile.provider.DocumentFile | 19 | import androidx.documentfile.provider.DocumentFile |
| 17 | import androidx.lifecycle.ViewModelProvider | 20 | import androidx.lifecycle.ViewModelProvider |
| 18 | import androidx.lifecycle.lifecycleScope | ||
| 19 | import androidx.navigation.findNavController | 21 | import androidx.navigation.findNavController |
| 20 | import androidx.preference.PreferenceManager | 22 | import androidx.preference.PreferenceManager |
| 21 | import androidx.recyclerview.widget.AsyncDifferConfig | 23 | import androidx.recyclerview.widget.AsyncDifferConfig |
| 22 | import androidx.recyclerview.widget.DiffUtil | 24 | import androidx.recyclerview.widget.DiffUtil |
| 23 | import androidx.recyclerview.widget.ListAdapter | 25 | import androidx.recyclerview.widget.ListAdapter |
| 24 | import androidx.recyclerview.widget.RecyclerView | 26 | import androidx.recyclerview.widget.RecyclerView |
| 25 | import coil.load | ||
| 26 | import kotlinx.coroutines.launch | ||
| 27 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 27 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| 28 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 29 | import org.yuzu.yuzu_emu.R | 28 | import org.yuzu.yuzu_emu.R |
| 30 | import org.yuzu.yuzu_emu.YuzuApplication | 29 | import org.yuzu.yuzu_emu.YuzuApplication |
| 30 | import org.yuzu.yuzu_emu.activities.EmulationActivity | ||
| 31 | import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder | 31 | import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder |
| 32 | import org.yuzu.yuzu_emu.databinding.CardGameBinding | 32 | import org.yuzu.yuzu_emu.databinding.CardGameBinding |
| 33 | import org.yuzu.yuzu_emu.model.Game | 33 | import org.yuzu.yuzu_emu.model.Game |
| 34 | import org.yuzu.yuzu_emu.model.GamesViewModel | 34 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 35 | import org.yuzu.yuzu_emu.utils.GameIconUtils | ||
| 35 | 36 | ||
| 36 | class GameAdapter(private val activity: AppCompatActivity) : | 37 | class GameAdapter(private val activity: AppCompatActivity) : |
| 37 | ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), | 38 | ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), |
| @@ -82,6 +83,21 @@ class GameAdapter(private val activity: AppCompatActivity) : | |||
| 82 | ) | 83 | ) |
| 83 | .apply() | 84 | .apply() |
| 84 | 85 | ||
| 86 | val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { | ||
| 87 | action = Intent.ACTION_VIEW | ||
| 88 | data = Uri.parse(holder.game.path) | ||
| 89 | } | ||
| 90 | val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) | ||
| 91 | .setShortLabel(holder.game.title) | ||
| 92 | .setIcon( | ||
| 93 | IconCompat.createWithBitmap( | ||
| 94 | (holder.binding.imageGameScreen.drawable as BitmapDrawable).bitmap | ||
| 95 | ) | ||
| 96 | ) | ||
| 97 | .setIntent(openIntent) | ||
| 98 | .build() | ||
| 99 | ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) | ||
| 100 | |||
| 85 | val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) | 101 | val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) |
| 86 | view.findNavController().navigate(action) | 102 | view.findNavController().navigate(action) |
| 87 | } | 103 | } |
| @@ -98,12 +114,7 @@ class GameAdapter(private val activity: AppCompatActivity) : | |||
| 98 | this.game = game | 114 | this.game = game |
| 99 | 115 | ||
| 100 | binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP | 116 | binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP |
| 101 | activity.lifecycleScope.launch { | 117 | GameIconUtils.loadGameIcon(game, binding.imageGameScreen) |
| 102 | val bitmap = decodeGameIcon(game.path) | ||
| 103 | binding.imageGameScreen.load(bitmap) { | ||
| 104 | error(R.drawable.default_icon) | ||
| 105 | } | ||
| 106 | } | ||
| 107 | 118 | ||
| 108 | binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") | 119 | binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") |
| 109 | 120 | ||
| @@ -126,14 +137,4 @@ class GameAdapter(private val activity: AppCompatActivity) : | |||
| 126 | return oldItem == newItem | 137 | return oldItem == newItem |
| 127 | } | 138 | } |
| 128 | } | 139 | } |
| 129 | |||
| 130 | private fun decodeGameIcon(uri: String): Bitmap? { | ||
| 131 | val data = NativeLibrary.getIcon(uri) | ||
| 132 | return BitmapFactory.decodeByteArray( | ||
| 133 | data, | ||
| 134 | 0, | ||
| 135 | data.size, | ||
| 136 | BitmapFactory.Options() | ||
| 137 | ) | ||
| 138 | } | ||
| 139 | } | 140 | } |
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 9f859b442..8d87d3bd7 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 | |||
| @@ -45,8 +45,8 @@ class HomeSettingAdapter( | |||
| 45 | holder.option.onClick.invoke() | 45 | holder.option.onClick.invoke() |
| 46 | } else { | 46 | } else { |
| 47 | MessageDialogFragment.newInstance( | 47 | MessageDialogFragment.newInstance( |
| 48 | holder.option.disabledTitleId, | 48 | titleId = holder.option.disabledTitleId, |
| 49 | holder.option.disabledMessageId | 49 | descriptionId = holder.option.disabledMessageId |
| 50 | ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) | 50 | ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) |
| 51 | } | 51 | } |
| 52 | } | 52 | } |
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 a18efef19..6f4b5b13f 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 | |||
| @@ -4,43 +4,43 @@ | |||
| 4 | package org.yuzu.yuzu_emu.disk_shader_cache | 4 | package org.yuzu.yuzu_emu.disk_shader_cache |
| 5 | 5 | ||
| 6 | import androidx.annotation.Keep | 6 | import androidx.annotation.Keep |
| 7 | import androidx.lifecycle.ViewModelProvider | ||
| 7 | import org.yuzu.yuzu_emu.NativeLibrary | 8 | import org.yuzu.yuzu_emu.NativeLibrary |
| 8 | import org.yuzu.yuzu_emu.R | 9 | import org.yuzu.yuzu_emu.R |
| 9 | import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment | 10 | import org.yuzu.yuzu_emu.activities.EmulationActivity |
| 11 | import org.yuzu.yuzu_emu.model.EmulationViewModel | ||
| 12 | import org.yuzu.yuzu_emu.utils.Log | ||
| 10 | 13 | ||
| 11 | @Keep | 14 | @Keep |
| 12 | object DiskShaderCacheProgress { | 15 | object DiskShaderCacheProgress { |
| 13 | val finishLock = Object() | 16 | private lateinit var emulationViewModel: EmulationViewModel |
| 14 | private lateinit var fragment: ShaderProgressDialogFragment | ||
| 15 | 17 | ||
| 16 | private fun prepareDialog() { | 18 | private fun prepareViewModel() { |
| 17 | val emulationActivity = NativeLibrary.sEmulationActivity.get()!! | 19 | emulationViewModel = |
| 18 | emulationActivity.runOnUiThread { | 20 | ViewModelProvider( |
| 19 | fragment = ShaderProgressDialogFragment.newInstance( | 21 | NativeLibrary.sEmulationActivity.get() as EmulationActivity |
| 20 | emulationActivity.getString(R.string.loading), | 22 | )[EmulationViewModel::class.java] |
| 21 | emulationActivity.getString(R.string.preparing_shaders) | ||
| 22 | ) | ||
| 23 | fragment.show( | ||
| 24 | emulationActivity.supportFragmentManager, | ||
| 25 | ShaderProgressDialogFragment.TAG | ||
| 26 | ) | ||
| 27 | } | ||
| 28 | synchronized(finishLock) { finishLock.wait() } | ||
| 29 | } | 23 | } |
| 30 | 24 | ||
| 31 | @JvmStatic | 25 | @JvmStatic |
| 32 | fun loadProgress(stage: Int, progress: Int, max: Int) { | 26 | fun loadProgress(stage: Int, progress: Int, max: Int) { |
| 33 | val emulationActivity = NativeLibrary.sEmulationActivity.get() | 27 | val emulationActivity = NativeLibrary.sEmulationActivity.get() |
| 34 | ?: error("[DiskShaderCacheProgress] EmulationActivity not present") | 28 | if (emulationActivity == null) { |
| 35 | 29 | Log.error("[DiskShaderCacheProgress] EmulationActivity not present") | |
| 36 | when (LoadCallbackStage.values()[stage]) { | 30 | return |
| 37 | LoadCallbackStage.Prepare -> prepareDialog() | 31 | } |
| 38 | LoadCallbackStage.Build -> fragment.onUpdateProgress( | 32 | |
| 39 | emulationActivity.getString(R.string.building_shaders), | 33 | emulationActivity.runOnUiThread { |
| 40 | progress, | 34 | when (LoadCallbackStage.values()[stage]) { |
| 41 | max | 35 | LoadCallbackStage.Prepare -> prepareViewModel() |
| 42 | ) | 36 | LoadCallbackStage.Build -> emulationViewModel.updateProgress( |
| 43 | LoadCallbackStage.Complete -> fragment.dismiss() | 37 | emulationActivity.getString(R.string.building_shaders), |
| 38 | progress, | ||
| 39 | max | ||
| 40 | ) | ||
| 41 | |||
| 42 | LoadCallbackStage.Complete -> {} | ||
| 43 | } | ||
| 44 | } | 44 | } |
| 45 | } | 45 | } |
| 46 | 46 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt deleted file mode 100644 index bf6f0366d..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt +++ /dev/null | |||
| @@ -1,31 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.disk_shader_cache | ||
| 5 | |||
| 6 | import androidx.lifecycle.LiveData | ||
| 7 | import androidx.lifecycle.MutableLiveData | ||
| 8 | import androidx.lifecycle.ViewModel | ||
| 9 | |||
| 10 | class ShaderProgressViewModel : ViewModel() { | ||
| 11 | private val _progress = MutableLiveData(0) | ||
| 12 | val progress: LiveData<Int> get() = _progress | ||
| 13 | |||
| 14 | private val _max = MutableLiveData(0) | ||
| 15 | val max: LiveData<Int> get() = _max | ||
| 16 | |||
| 17 | private val _message = MutableLiveData("") | ||
| 18 | val message: LiveData<String> get() = _message | ||
| 19 | |||
| 20 | fun setProgress(progress: Int) { | ||
| 21 | _progress.postValue(progress) | ||
| 22 | } | ||
| 23 | |||
| 24 | fun setMax(max: Int) { | ||
| 25 | _max.postValue(max) | ||
| 26 | } | ||
| 27 | |||
| 28 | fun setMessage(msg: String) { | ||
| 29 | _message.postValue(msg) | ||
| 30 | } | ||
| 31 | } | ||
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 deleted file mode 100644 index 8a8e0a6e8..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt +++ /dev/null | |||
| @@ -1,103 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.disk_shader_cache.ui | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.os.Bundle | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.View | ||
| 10 | import android.view.ViewGroup | ||
| 11 | import androidx.appcompat.app.AlertDialog | ||
| 12 | import androidx.fragment.app.DialogFragment | ||
| 13 | import androidx.lifecycle.ViewModelProvider | ||
| 14 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 15 | import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding | ||
| 16 | import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress | ||
| 17 | import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel | ||
| 18 | |||
| 19 | class ShaderProgressDialogFragment : DialogFragment() { | ||
| 20 | private var _binding: DialogProgressBarBinding? = null | ||
| 21 | private val binding get() = _binding!! | ||
| 22 | |||
| 23 | private lateinit var alertDialog: AlertDialog | ||
| 24 | |||
| 25 | private lateinit var shaderProgressViewModel: ShaderProgressViewModel | ||
| 26 | |||
| 27 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 28 | _binding = DialogProgressBarBinding.inflate(layoutInflater) | ||
| 29 | shaderProgressViewModel = | ||
| 30 | ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java] | ||
| 31 | |||
| 32 | val title = requireArguments().getString(TITLE) | ||
| 33 | val message = requireArguments().getString(MESSAGE) | ||
| 34 | |||
| 35 | isCancelable = false | ||
| 36 | alertDialog = MaterialAlertDialogBuilder(requireActivity()) | ||
| 37 | .setView(binding.root) | ||
| 38 | .setTitle(title) | ||
| 39 | .setMessage(message) | ||
| 40 | .create() | ||
| 41 | return alertDialog | ||
| 42 | } | ||
| 43 | |||
| 44 | override fun onCreateView( | ||
| 45 | inflater: LayoutInflater, | ||
| 46 | container: ViewGroup?, | ||
| 47 | savedInstanceState: Bundle? | ||
| 48 | ): View { | ||
| 49 | return binding.root | ||
| 50 | } | ||
| 51 | |||
| 52 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 53 | super.onViewCreated(view, savedInstanceState) | ||
| 54 | shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress -> | ||
| 55 | binding.progressBar.progress = progress | ||
| 56 | setUpdateText() | ||
| 57 | } | ||
| 58 | shaderProgressViewModel.max.observe(viewLifecycleOwner) { max -> | ||
| 59 | binding.progressBar.max = max | ||
| 60 | setUpdateText() | ||
| 61 | } | ||
| 62 | shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg -> | ||
| 63 | alertDialog.setMessage(msg) | ||
| 64 | } | ||
| 65 | synchronized(DiskShaderCacheProgress.finishLock) { | ||
| 66 | DiskShaderCacheProgress.finishLock.notifyAll() | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | override fun onDestroyView() { | ||
| 71 | super.onDestroyView() | ||
| 72 | _binding = null | ||
| 73 | } | ||
| 74 | |||
| 75 | fun onUpdateProgress(msg: String, progress: Int, max: Int) { | ||
| 76 | shaderProgressViewModel.setProgress(progress) | ||
| 77 | shaderProgressViewModel.setMax(max) | ||
| 78 | shaderProgressViewModel.setMessage(msg) | ||
| 79 | } | ||
| 80 | |||
| 81 | private fun setUpdateText() { | ||
| 82 | binding.progressText.text = String.format( | ||
| 83 | "%d/%d", | ||
| 84 | shaderProgressViewModel.progress.value, | ||
| 85 | shaderProgressViewModel.max.value | ||
| 86 | ) | ||
| 87 | } | ||
| 88 | |||
| 89 | companion object { | ||
| 90 | const val TAG = "ProgressDialogFragment" | ||
| 91 | const val TITLE = "title" | ||
| 92 | const val MESSAGE = "message" | ||
| 93 | |||
| 94 | fun newInstance(title: String, message: String): ShaderProgressDialogFragment { | ||
| 95 | val frag = ShaderProgressDialogFragment() | ||
| 96 | val args = Bundle() | ||
| 97 | args.putString(TITLE, title) | ||
| 98 | args.putString(MESSAGE, message) | ||
| 99 | frag.arguments = args | ||
| 100 | return frag | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt index a6e9833ee..aeda8d222 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt | |||
| @@ -4,5 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | interface AbstractBooleanSetting : AbstractSetting { | 6 | interface AbstractBooleanSetting : AbstractSetting { |
| 7 | var boolean: Boolean | 7 | val boolean: Boolean |
| 8 | |||
| 9 | fun setBoolean(value: Boolean) | ||
| 8 | } | 10 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt index bd9233d62..606519ad8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import androidx.lifecycle.ViewModel | 6 | interface AbstractByteSetting : AbstractSetting { |
| 7 | val byte: Byte | ||
| 7 | 8 | ||
| 8 | class SettingsViewModel : ViewModel() { | 9 | fun setByte(value: Byte) |
| 9 | val settings = Settings() | ||
| 10 | } | 10 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt index 6fe4bc263..974925eed 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt | |||
| @@ -4,5 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | interface AbstractFloatSetting : AbstractSetting { | 6 | interface AbstractFloatSetting : AbstractSetting { |
| 7 | var float: Float | 7 | val float: Float |
| 8 | |||
| 9 | fun setFloat(value: Float) | ||
| 8 | } | 10 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt index 892b7dcfe..89b285b10 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt | |||
| @@ -4,5 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | interface AbstractIntSetting : AbstractSetting { | 6 | interface AbstractIntSetting : AbstractSetting { |
| 7 | var int: Int | 7 | val int: Int |
| 8 | |||
| 9 | fun setInt(value: Int) | ||
| 8 | } | 10 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt new file mode 100644 index 000000000..4873942db --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | ||
| 5 | |||
| 6 | interface AbstractLongSetting : AbstractSetting { | ||
| 7 | val long: Long | ||
| 8 | |||
| 9 | fun setLong(value: Long) | ||
| 10 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt index 258580209..8b6d29fe5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt | |||
| @@ -3,10 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 6 | interface AbstractSetting { | 8 | interface AbstractSetting { |
| 7 | val key: String? | 9 | val key: String |
| 8 | val section: String? | 10 | val category: Settings.Category |
| 9 | val isRuntimeEditable: Boolean | ||
| 10 | val valueAsString: String | ||
| 11 | val defaultValue: Any | 11 | val defaultValue: Any |
| 12 | val androidDefault: Any? | ||
| 13 | get() = null | ||
| 14 | val valueAsString: String | ||
| 15 | get() = "" | ||
| 16 | |||
| 17 | val isRuntimeModifiable: Boolean | ||
| 18 | get() = NativeConfig.getIsRuntimeModifiable(key) | ||
| 19 | |||
| 20 | val pairedSettingKey: String | ||
| 21 | get() = NativeConfig.getPairedSettingKey(key) | ||
| 22 | |||
| 23 | fun reset() | ||
| 12 | } | 24 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt new file mode 100644 index 000000000..91407ccbb --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | ||
| 5 | |||
| 6 | interface AbstractShortSetting : AbstractSetting { | ||
| 7 | val short: Short | ||
| 8 | |||
| 9 | fun setShort(value: Short) | ||
| 10 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt index 0d02c5997..c8935cc48 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt | |||
| @@ -4,5 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | interface AbstractStringSetting : AbstractSetting { | 6 | interface AbstractStringSetting : AbstractSetting { |
| 7 | var string: String | 7 | val string: String |
| 8 | |||
| 9 | fun setString(value: String) | ||
| 8 | } | 10 | } |
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 d41933766..e0c0538c7 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 | |||
| @@ -3,41 +3,37 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 6 | enum class BooleanSetting( | 8 | enum class BooleanSetting( |
| 7 | override val key: String, | 9 | override val key: String, |
| 8 | override val section: String, | 10 | override val category: Settings.Category, |
| 9 | override val defaultValue: Boolean | 11 | override val androidDefault: Boolean? = null |
| 10 | ) : AbstractBooleanSetting { | 12 | ) : AbstractBooleanSetting { |
| 11 | CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false), | 13 | CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu), |
| 12 | FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true), | 14 | FASTMEM("cpuopt_fastmem", Settings.Category.Cpu), |
| 13 | FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true), | 15 | FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu), |
| 14 | PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), | 16 | RENDERER_USE_SPEED_LIMIT("use_speed_limit", Settings.Category.Core), |
| 15 | USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); | 17 | USE_DOCKED_MODE("use_docked_mode", Settings.Category.System, false), |
| 16 | 18 | RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache", Settings.Category.Renderer), | |
| 17 | override var boolean: Boolean = defaultValue | 19 | RENDERER_FORCE_MAX_CLOCK("force_max_clock", Settings.Category.Renderer), |
| 20 | RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders", Settings.Category.Renderer), | ||
| 21 | RENDERER_REACTIVE_FLUSHING("use_reactive_flushing", Settings.Category.Renderer, false), | ||
| 22 | RENDERER_DEBUG("debug", Settings.Category.Renderer), | ||
| 23 | PICTURE_IN_PICTURE("picture_in_picture", Settings.Category.Android), | ||
| 24 | USE_CUSTOM_RTC("custom_rtc_enabled", Settings.Category.System); | ||
| 25 | |||
| 26 | override val boolean: Boolean | ||
| 27 | get() = NativeConfig.getBoolean(key, false) | ||
| 28 | |||
| 29 | override fun setBoolean(value: Boolean) = NativeConfig.setBoolean(key, value) | ||
| 30 | |||
| 31 | override val defaultValue: Boolean by lazy { | ||
| 32 | androidDefault ?: NativeConfig.getBoolean(key, true) | ||
| 33 | } | ||
| 18 | 34 | ||
| 19 | override val valueAsString: String | 35 | override val valueAsString: String |
| 20 | get() = boolean.toString() | 36 | get() = if (boolean) "1" else "0" |
| 21 | 37 | ||
| 22 | override val isRuntimeEditable: Boolean | 38 | override fun reset() = NativeConfig.setBoolean(key, defaultValue) |
| 23 | get() { | ||
| 24 | for (setting in NOT_RUNTIME_EDITABLE) { | ||
| 25 | if (setting == this) { | ||
| 26 | return false | ||
| 27 | } | ||
| 28 | } | ||
| 29 | return true | ||
| 30 | } | ||
| 31 | |||
| 32 | companion object { | ||
| 33 | private val NOT_RUNTIME_EDITABLE = listOf( | ||
| 34 | PICTURE_IN_PICTURE, | ||
| 35 | USE_CUSTOM_RTC | ||
| 36 | ) | ||
| 37 | |||
| 38 | fun from(key: String): BooleanSetting? = | ||
| 39 | BooleanSetting.values().firstOrNull { it.key == key } | ||
| 40 | |||
| 41 | fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue } | ||
| 42 | } | ||
| 43 | } | 39 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt new file mode 100644 index 000000000..6ec0a765e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | ||
| 5 | |||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 8 | enum class ByteSetting( | ||
| 9 | override val key: String, | ||
| 10 | override val category: Settings.Category | ||
| 11 | ) : AbstractByteSetting { | ||
| 12 | AUDIO_VOLUME("volume", Settings.Category.Audio); | ||
| 13 | |||
| 14 | override val byte: Byte | ||
| 15 | get() = NativeConfig.getByte(key, false) | ||
| 16 | |||
| 17 | override fun setByte(value: Byte) = NativeConfig.setByte(key, value) | ||
| 18 | |||
| 19 | override val defaultValue: Byte by lazy { NativeConfig.getByte(key, true) } | ||
| 20 | |||
| 21 | override val valueAsString: String | ||
| 22 | get() = byte.toString() | ||
| 23 | |||
| 24 | override fun reset() = NativeConfig.setByte(key, defaultValue) | ||
| 25 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt index e5545a916..0181d06f2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt | |||
| @@ -3,34 +3,24 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 6 | enum class FloatSetting( | 8 | enum class FloatSetting( |
| 7 | override val key: String, | 9 | override val key: String, |
| 8 | override val section: String, | 10 | override val category: Settings.Category |
| 9 | override val defaultValue: Float | ||
| 10 | ) : AbstractFloatSetting { | 11 | ) : AbstractFloatSetting { |
| 11 | // No float settings currently exist | 12 | // No float settings currently exist |
| 12 | EMPTY_SETTING("", "", 0f); | 13 | EMPTY_SETTING("", Settings.Category.UiGeneral); |
| 13 | |||
| 14 | override var float: Float = defaultValue | ||
| 15 | 14 | ||
| 16 | override val valueAsString: String | 15 | override val float: Float |
| 17 | get() = float.toString() | 16 | get() = NativeConfig.getFloat(key, false) |
| 18 | 17 | ||
| 19 | override val isRuntimeEditable: Boolean | 18 | override fun setFloat(value: Float) = NativeConfig.setFloat(key, value) |
| 20 | get() { | ||
| 21 | for (setting in NOT_RUNTIME_EDITABLE) { | ||
| 22 | if (setting == this) { | ||
| 23 | return false | ||
| 24 | } | ||
| 25 | } | ||
| 26 | return true | ||
| 27 | } | ||
| 28 | 19 | ||
| 29 | companion object { | 20 | override val defaultValue: Float by lazy { NativeConfig.getFloat(key, true) } |
| 30 | private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>() | ||
| 31 | 21 | ||
| 32 | fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key } | 22 | override val valueAsString: String |
| 23 | get() = float.toString() | ||
| 33 | 24 | ||
| 34 | fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue } | 25 | override fun reset() = NativeConfig.setFloat(key, defaultValue) |
| 35 | } | ||
| 36 | } | 26 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index 4427a7d9d..151362124 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt | |||
| @@ -3,139 +3,37 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 6 | enum class IntSetting( | 8 | enum class IntSetting( |
| 7 | override val key: String, | 9 | override val key: String, |
| 8 | override val section: String, | 10 | override val category: Settings.Category, |
| 9 | override val defaultValue: Int | 11 | override val androidDefault: Int? = null |
| 10 | ) : AbstractIntSetting { | 12 | ) : AbstractIntSetting { |
| 11 | RENDERER_USE_SPEED_LIMIT( | 13 | CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), |
| 12 | "use_speed_limit", | 14 | REGION_INDEX("region_index", Settings.Category.System), |
| 13 | Settings.SECTION_RENDERER, | 15 | LANGUAGE_INDEX("language_index", Settings.Category.System), |
| 14 | 1 | 16 | RENDERER_BACKEND("backend", Settings.Category.Renderer), |
| 15 | ), | 17 | RENDERER_ACCURACY("gpu_accuracy", Settings.Category.Renderer, 0), |
| 16 | USE_DOCKED_MODE( | 18 | RENDERER_RESOLUTION("resolution_setup", Settings.Category.Renderer), |
| 17 | "use_docked_mode", | 19 | RENDERER_VSYNC("use_vsync", Settings.Category.Renderer), |
| 18 | Settings.SECTION_SYSTEM, | 20 | RENDERER_SCALING_FILTER("scaling_filter", Settings.Category.Renderer), |
| 19 | 0 | 21 | RENDERER_ANTI_ALIASING("anti_aliasing", Settings.Category.Renderer), |
| 20 | ), | 22 | RENDERER_SCREEN_LAYOUT("screen_layout", Settings.Category.Android), |
| 21 | RENDERER_USE_DISK_SHADER_CACHE( | 23 | RENDERER_ASPECT_RATIO("aspect_ratio", Settings.Category.Renderer), |
| 22 | "use_disk_shader_cache", | 24 | AUDIO_OUTPUT_ENGINE("output_engine", Settings.Category.Audio); |
| 23 | Settings.SECTION_RENDERER, | 25 | |
| 24 | 1 | 26 | override val int: Int |
| 25 | ), | 27 | get() = NativeConfig.getInt(key, false) |
| 26 | RENDERER_FORCE_MAX_CLOCK( | 28 | |
| 27 | "force_max_clock", | 29 | override fun setInt(value: Int) = NativeConfig.setInt(key, value) |
| 28 | Settings.SECTION_RENDERER, | 30 | |
| 29 | 0 | 31 | override val defaultValue: Int by lazy { |
| 30 | ), | 32 | androidDefault ?: NativeConfig.getInt(key, true) |
| 31 | RENDERER_ASYNCHRONOUS_SHADERS( | 33 | } |
| 32 | "use_asynchronous_shaders", | ||
| 33 | Settings.SECTION_RENDERER, | ||
| 34 | 0 | ||
| 35 | ), | ||
| 36 | RENDERER_REACTIVE_FLUSHING( | ||
| 37 | "use_reactive_flushing", | ||
| 38 | Settings.SECTION_RENDERER, | ||
| 39 | 0 | ||
| 40 | ), | ||
| 41 | RENDERER_DEBUG( | ||
| 42 | "debug", | ||
| 43 | Settings.SECTION_RENDERER, | ||
| 44 | 0 | ||
| 45 | ), | ||
| 46 | RENDERER_SPEED_LIMIT( | ||
| 47 | "speed_limit", | ||
| 48 | Settings.SECTION_RENDERER, | ||
| 49 | 100 | ||
| 50 | ), | ||
| 51 | CPU_ACCURACY( | ||
| 52 | "cpu_accuracy", | ||
| 53 | Settings.SECTION_CPU, | ||
| 54 | 0 | ||
| 55 | ), | ||
| 56 | REGION_INDEX( | ||
| 57 | "region_index", | ||
| 58 | Settings.SECTION_SYSTEM, | ||
| 59 | -1 | ||
| 60 | ), | ||
| 61 | LANGUAGE_INDEX( | ||
| 62 | "language_index", | ||
| 63 | Settings.SECTION_SYSTEM, | ||
| 64 | 1 | ||
| 65 | ), | ||
| 66 | RENDERER_BACKEND( | ||
| 67 | "backend", | ||
| 68 | Settings.SECTION_RENDERER, | ||
| 69 | 1 | ||
| 70 | ), | ||
| 71 | RENDERER_ACCURACY( | ||
| 72 | "gpu_accuracy", | ||
| 73 | Settings.SECTION_RENDERER, | ||
| 74 | 0 | ||
| 75 | ), | ||
| 76 | RENDERER_RESOLUTION( | ||
| 77 | "resolution_setup", | ||
| 78 | Settings.SECTION_RENDERER, | ||
| 79 | 2 | ||
| 80 | ), | ||
| 81 | RENDERER_VSYNC( | ||
| 82 | "use_vsync", | ||
| 83 | Settings.SECTION_RENDERER, | ||
| 84 | 0 | ||
| 85 | ), | ||
| 86 | RENDERER_SCALING_FILTER( | ||
| 87 | "scaling_filter", | ||
| 88 | Settings.SECTION_RENDERER, | ||
| 89 | 1 | ||
| 90 | ), | ||
| 91 | RENDERER_ANTI_ALIASING( | ||
| 92 | "anti_aliasing", | ||
| 93 | Settings.SECTION_RENDERER, | ||
| 94 | 0 | ||
| 95 | ), | ||
| 96 | RENDERER_SCREEN_LAYOUT( | ||
| 97 | "screen_layout", | ||
| 98 | Settings.SECTION_RENDERER, | ||
| 99 | Settings.LayoutOption_MobileLandscape | ||
| 100 | ), | ||
| 101 | RENDERER_ASPECT_RATIO( | ||
| 102 | "aspect_ratio", | ||
| 103 | Settings.SECTION_RENDERER, | ||
| 104 | 0 | ||
| 105 | ), | ||
| 106 | AUDIO_VOLUME( | ||
| 107 | "volume", | ||
| 108 | Settings.SECTION_AUDIO, | ||
| 109 | 100 | ||
| 110 | ); | ||
| 111 | |||
| 112 | override var int: Int = defaultValue | ||
| 113 | 34 | ||
| 114 | override val valueAsString: String | 35 | override val valueAsString: String |
| 115 | get() = int.toString() | 36 | get() = int.toString() |
| 116 | 37 | ||
| 117 | override val isRuntimeEditable: Boolean | 38 | override fun reset() = NativeConfig.setInt(key, defaultValue) |
| 118 | get() { | ||
| 119 | for (setting in NOT_RUNTIME_EDITABLE) { | ||
| 120 | if (setting == this) { | ||
| 121 | return false | ||
| 122 | } | ||
| 123 | } | ||
| 124 | return true | ||
| 125 | } | ||
| 126 | |||
| 127 | companion object { | ||
| 128 | private val NOT_RUNTIME_EDITABLE = listOf( | ||
| 129 | RENDERER_USE_DISK_SHADER_CACHE, | ||
| 130 | RENDERER_ASYNCHRONOUS_SHADERS, | ||
| 131 | RENDERER_DEBUG, | ||
| 132 | RENDERER_BACKEND, | ||
| 133 | RENDERER_RESOLUTION, | ||
| 134 | RENDERER_VSYNC | ||
| 135 | ) | ||
| 136 | |||
| 137 | fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key } | ||
| 138 | |||
| 139 | fun clear() = IntSetting.values().forEach { it.int = it.defaultValue } | ||
| 140 | } | ||
| 141 | } | 39 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt new file mode 100644 index 000000000..c526fc4cf --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | ||
| 5 | |||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 8 | enum class LongSetting( | ||
| 9 | override val key: String, | ||
| 10 | override val category: Settings.Category | ||
| 11 | ) : AbstractLongSetting { | ||
| 12 | CUSTOM_RTC("custom_rtc", Settings.Category.System); | ||
| 13 | |||
| 14 | override val long: Long | ||
| 15 | get() = NativeConfig.getLong(key, false) | ||
| 16 | |||
| 17 | override fun setLong(value: Long) = NativeConfig.setLong(key, value) | ||
| 18 | |||
| 19 | override val defaultValue: Long by lazy { NativeConfig.getLong(key, true) } | ||
| 20 | |||
| 21 | override val valueAsString: String | ||
| 22 | get() = long.toString() | ||
| 23 | |||
| 24 | override fun reset() = NativeConfig.setLong(key, defaultValue) | ||
| 25 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt deleted file mode 100644 index 474f598a9..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt +++ /dev/null | |||
| @@ -1,37 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | ||
| 5 | |||
| 6 | /** | ||
| 7 | * A semantically-related group of Settings objects. These Settings are | ||
| 8 | * internally stored as a HashMap. | ||
| 9 | */ | ||
| 10 | class SettingSection(val name: String) { | ||
| 11 | val settings = HashMap<String, AbstractSetting>() | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Convenience method; inserts a value directly into the backing HashMap. | ||
| 15 | * | ||
| 16 | * @param setting The Setting to be inserted. | ||
| 17 | */ | ||
| 18 | fun putSetting(setting: AbstractSetting) { | ||
| 19 | settings[setting.key!!] = setting | ||
| 20 | } | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Convenience method; gets a value directly from the backing HashMap. | ||
| 24 | * | ||
| 25 | * @param key Used to retrieve the Setting. | ||
| 26 | * @return A Setting object (you should probably cast this before using) | ||
| 27 | */ | ||
| 28 | fun getSetting(key: String): AbstractSetting? { | ||
| 29 | return settings[key] | ||
| 30 | } | ||
| 31 | |||
| 32 | fun mergeSection(settingSection: SettingSection) { | ||
| 33 | for (setting in settingSection.settings.values) { | ||
| 34 | putSetting(setting) | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
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 a6251bafd..0702236e8 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,195 +4,151 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import android.text.TextUtils | 6 | import android.text.TextUtils |
| 7 | import java.util.* | 7 | import android.widget.Toast |
| 8 | import org.yuzu.yuzu_emu.R | 8 | import org.yuzu.yuzu_emu.R |
| 9 | import org.yuzu.yuzu_emu.YuzuApplication | 9 | import org.yuzu.yuzu_emu.YuzuApplication |
| 10 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView | ||
| 11 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 10 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 12 | 11 | ||
| 13 | class Settings { | 12 | object Settings { |
| 14 | private var gameId: String? = null | 13 | private val context get() = YuzuApplication.appContext |
| 15 | 14 | ||
| 16 | var isLoaded = false | 15 | fun saveSettings(gameId: String = "") { |
| 17 | |||
| 18 | /** | ||
| 19 | * A HashMap<String></String>, SettingSection> that constructs a new SettingSection instead of returning null | ||
| 20 | * when getting a key not already in the map | ||
| 21 | */ | ||
| 22 | class SettingsSectionMap : HashMap<String, SettingSection?>() { | ||
| 23 | override operator fun get(key: String): SettingSection? { | ||
| 24 | if (!super.containsKey(key)) { | ||
| 25 | val section = SettingSection(key) | ||
| 26 | super.put(key, section) | ||
| 27 | return section | ||
| 28 | } | ||
| 29 | return super.get(key) | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | var sections: HashMap<String, SettingSection?> = SettingsSectionMap() | ||
| 34 | |||
| 35 | fun getSection(sectionName: String): SettingSection? { | ||
| 36 | return sections[sectionName] | ||
| 37 | } | ||
| 38 | |||
| 39 | val isEmpty: Boolean | ||
| 40 | get() = sections.isEmpty() | ||
| 41 | |||
| 42 | fun loadSettings(view: SettingsActivityView? = null) { | ||
| 43 | sections = SettingsSectionMap() | ||
| 44 | loadYuzuSettings(view) | ||
| 45 | if (!TextUtils.isEmpty(gameId)) { | ||
| 46 | loadCustomGameSettings(gameId!!, view) | ||
| 47 | } | ||
| 48 | isLoaded = true | ||
| 49 | } | ||
| 50 | |||
| 51 | private fun loadYuzuSettings(view: SettingsActivityView?) { | ||
| 52 | for ((fileName) in configFileSectionsMap) { | ||
| 53 | sections.putAll(SettingsFile.readFile(fileName, view)) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) { | ||
| 58 | // Custom game settings | ||
| 59 | mergeSections(SettingsFile.readCustomGameSettings(gameId, view)) | ||
| 60 | } | ||
| 61 | |||
| 62 | private fun mergeSections(updatedSections: HashMap<String, SettingSection?>) { | ||
| 63 | for ((key, updatedSection) in updatedSections) { | ||
| 64 | if (sections.containsKey(key)) { | ||
| 65 | val originalSection = sections[key] | ||
| 66 | originalSection!!.mergeSection(updatedSection!!) | ||
| 67 | } else { | ||
| 68 | sections[key] = updatedSection | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | fun loadSettings(gameId: String, view: SettingsActivityView) { | ||
| 74 | this.gameId = gameId | ||
| 75 | loadSettings(view) | ||
| 76 | } | ||
| 77 | |||
| 78 | fun saveSettings(view: SettingsActivityView) { | ||
| 79 | if (TextUtils.isEmpty(gameId)) { | 16 | if (TextUtils.isEmpty(gameId)) { |
| 80 | view.showToastMessage( | 17 | Toast.makeText( |
| 81 | YuzuApplication.appContext.getString(R.string.ini_saved), | 18 | context, |
| 82 | false | 19 | context.getString(R.string.ini_saved), |
| 83 | ) | 20 | Toast.LENGTH_SHORT |
| 84 | 21 | ).show() | |
| 85 | for ((fileName, sectionNames) in configFileSectionsMap) { | 22 | SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) |
| 86 | val iniSections = TreeMap<String, SettingSection>() | ||
| 87 | for (section in sectionNames) { | ||
| 88 | iniSections[section] = sections[section]!! | ||
| 89 | } | ||
| 90 | |||
| 91 | SettingsFile.saveFile(fileName, iniSections, view) | ||
| 92 | } | ||
| 93 | } else { | 23 | } else { |
| 94 | // Custom game settings | 24 | // TODO: Save custom game settings |
| 95 | view.showToastMessage( | 25 | Toast.makeText( |
| 96 | YuzuApplication.appContext.getString(R.string.gameid_saved, gameId), | 26 | context, |
| 97 | false | 27 | context.getString(R.string.gameid_saved, gameId), |
| 98 | ) | 28 | Toast.LENGTH_SHORT |
| 99 | 29 | ).show() | |
| 100 | SettingsFile.saveCustomGameSettings(gameId, sections) | ||
| 101 | } | 30 | } |
| 102 | } | 31 | } |
| 103 | 32 | ||
| 104 | companion object { | 33 | enum class Category { |
| 105 | const val SECTION_GENERAL = "General" | 34 | Android, |
| 106 | const val SECTION_SYSTEM = "System" | 35 | Audio, |
| 107 | const val SECTION_RENDERER = "Renderer" | 36 | Core, |
| 108 | const val SECTION_AUDIO = "Audio" | 37 | Cpu, |
| 109 | const val SECTION_CPU = "Cpu" | 38 | CpuDebug, |
| 110 | const val SECTION_THEME = "Theme" | 39 | CpuUnsafe, |
| 111 | const val SECTION_DEBUG = "Debug" | 40 | Renderer, |
| 112 | 41 | RendererAdvanced, | |
| 113 | const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" | 42 | RendererDebug, |
| 114 | 43 | System, | |
| 115 | const val PREF_OVERLAY_VERSION = "OverlayVersion" | 44 | SystemAudio, |
| 116 | const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" | 45 | DataStorage, |
| 117 | const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" | 46 | Debugging, |
| 118 | const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" | 47 | DebuggingGraphics, |
| 119 | val overlayLayoutPrefs = listOf( | 48 | Miscellaneous, |
| 120 | PREF_LANDSCAPE_OVERLAY_VERSION, | 49 | Network, |
| 121 | PREF_PORTRAIT_OVERLAY_VERSION, | 50 | WebService, |
| 122 | PREF_FOLDABLE_OVERLAY_VERSION | 51 | AddOns, |
| 123 | ) | 52 | Controls, |
| 124 | 53 | Ui, | |
| 125 | const val PREF_CONTROL_SCALE = "controlScale" | 54 | UiGeneral, |
| 126 | const val PREF_CONTROL_OPACITY = "controlOpacity" | 55 | UiLayout, |
| 127 | const val PREF_TOUCH_ENABLED = "isTouchEnabled" | 56 | UiGameList, |
| 128 | const val PREF_BUTTON_A = "buttonToggle0" | 57 | Screenshots, |
| 129 | const val PREF_BUTTON_B = "buttonToggle1" | 58 | Shortcuts, |
| 130 | const val PREF_BUTTON_X = "buttonToggle2" | 59 | Multiplayer, |
| 131 | const val PREF_BUTTON_Y = "buttonToggle3" | 60 | Services, |
| 132 | const val PREF_BUTTON_L = "buttonToggle4" | 61 | Paths, |
| 133 | const val PREF_BUTTON_R = "buttonToggle5" | 62 | MaxEnum |
| 134 | const val PREF_BUTTON_ZL = "buttonToggle6" | ||
| 135 | const val PREF_BUTTON_ZR = "buttonToggle7" | ||
| 136 | const val PREF_BUTTON_PLUS = "buttonToggle8" | ||
| 137 | const val PREF_BUTTON_MINUS = "buttonToggle9" | ||
| 138 | const val PREF_BUTTON_DPAD = "buttonToggle10" | ||
| 139 | const val PREF_STICK_L = "buttonToggle11" | ||
| 140 | const val PREF_STICK_R = "buttonToggle12" | ||
| 141 | const val PREF_BUTTON_STICK_L = "buttonToggle13" | ||
| 142 | const val PREF_BUTTON_STICK_R = "buttonToggle14" | ||
| 143 | const val PREF_BUTTON_HOME = "buttonToggle15" | ||
| 144 | const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" | ||
| 145 | |||
| 146 | const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" | ||
| 147 | const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" | ||
| 148 | const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" | ||
| 149 | const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" | ||
| 150 | const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" | ||
| 151 | |||
| 152 | const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" | ||
| 153 | const val PREF_THEME = "Theme" | ||
| 154 | const val PREF_THEME_MODE = "ThemeMode" | ||
| 155 | const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds" | ||
| 156 | |||
| 157 | private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() | ||
| 158 | |||
| 159 | val overlayPreferences = listOf( | ||
| 160 | PREF_OVERLAY_VERSION, | ||
| 161 | PREF_CONTROL_SCALE, | ||
| 162 | PREF_CONTROL_OPACITY, | ||
| 163 | PREF_TOUCH_ENABLED, | ||
| 164 | PREF_BUTTON_A, | ||
| 165 | PREF_BUTTON_B, | ||
| 166 | PREF_BUTTON_X, | ||
| 167 | PREF_BUTTON_Y, | ||
| 168 | PREF_BUTTON_L, | ||
| 169 | PREF_BUTTON_R, | ||
| 170 | PREF_BUTTON_ZL, | ||
| 171 | PREF_BUTTON_ZR, | ||
| 172 | PREF_BUTTON_PLUS, | ||
| 173 | PREF_BUTTON_MINUS, | ||
| 174 | PREF_BUTTON_DPAD, | ||
| 175 | PREF_STICK_L, | ||
| 176 | PREF_STICK_R, | ||
| 177 | PREF_BUTTON_HOME, | ||
| 178 | PREF_BUTTON_SCREENSHOT, | ||
| 179 | PREF_BUTTON_STICK_L, | ||
| 180 | PREF_BUTTON_STICK_R | ||
| 181 | ) | ||
| 182 | |||
| 183 | const val LayoutOption_Unspecified = 0 | ||
| 184 | const val LayoutOption_MobilePortrait = 4 | ||
| 185 | const val LayoutOption_MobileLandscape = 5 | ||
| 186 | |||
| 187 | init { | ||
| 188 | configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] = | ||
| 189 | listOf( | ||
| 190 | SECTION_GENERAL, | ||
| 191 | SECTION_SYSTEM, | ||
| 192 | SECTION_RENDERER, | ||
| 193 | SECTION_AUDIO, | ||
| 194 | SECTION_CPU | ||
| 195 | ) | ||
| 196 | } | ||
| 197 | } | 63 | } |
| 64 | |||
| 65 | val settingsList = listOf<AbstractSetting>( | ||
| 66 | *BooleanSetting.values(), | ||
| 67 | *ByteSetting.values(), | ||
| 68 | *ShortSetting.values(), | ||
| 69 | *IntSetting.values(), | ||
| 70 | *FloatSetting.values(), | ||
| 71 | *LongSetting.values(), | ||
| 72 | *StringSetting.values() | ||
| 73 | ) | ||
| 74 | |||
| 75 | const val SECTION_GENERAL = "General" | ||
| 76 | const val SECTION_SYSTEM = "System" | ||
| 77 | const val SECTION_RENDERER = "Renderer" | ||
| 78 | const val SECTION_AUDIO = "Audio" | ||
| 79 | const val SECTION_CPU = "Cpu" | ||
| 80 | const val SECTION_THEME = "Theme" | ||
| 81 | const val SECTION_DEBUG = "Debug" | ||
| 82 | |||
| 83 | const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" | ||
| 84 | |||
| 85 | const val PREF_OVERLAY_VERSION = "OverlayVersion" | ||
| 86 | const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" | ||
| 87 | const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" | ||
| 88 | const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" | ||
| 89 | val overlayLayoutPrefs = listOf( | ||
| 90 | PREF_LANDSCAPE_OVERLAY_VERSION, | ||
| 91 | PREF_PORTRAIT_OVERLAY_VERSION, | ||
| 92 | PREF_FOLDABLE_OVERLAY_VERSION | ||
| 93 | ) | ||
| 94 | |||
| 95 | const val PREF_CONTROL_SCALE = "controlScale" | ||
| 96 | const val PREF_CONTROL_OPACITY = "controlOpacity" | ||
| 97 | const val PREF_TOUCH_ENABLED = "isTouchEnabled" | ||
| 98 | const val PREF_BUTTON_A = "buttonToggle0" | ||
| 99 | const val PREF_BUTTON_B = "buttonToggle1" | ||
| 100 | const val PREF_BUTTON_X = "buttonToggle2" | ||
| 101 | const val PREF_BUTTON_Y = "buttonToggle3" | ||
| 102 | const val PREF_BUTTON_L = "buttonToggle4" | ||
| 103 | const val PREF_BUTTON_R = "buttonToggle5" | ||
| 104 | const val PREF_BUTTON_ZL = "buttonToggle6" | ||
| 105 | const val PREF_BUTTON_ZR = "buttonToggle7" | ||
| 106 | const val PREF_BUTTON_PLUS = "buttonToggle8" | ||
| 107 | const val PREF_BUTTON_MINUS = "buttonToggle9" | ||
| 108 | const val PREF_BUTTON_DPAD = "buttonToggle10" | ||
| 109 | const val PREF_STICK_L = "buttonToggle11" | ||
| 110 | const val PREF_STICK_R = "buttonToggle12" | ||
| 111 | const val PREF_BUTTON_STICK_L = "buttonToggle13" | ||
| 112 | const val PREF_BUTTON_STICK_R = "buttonToggle14" | ||
| 113 | const val PREF_BUTTON_HOME = "buttonToggle15" | ||
| 114 | const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" | ||
| 115 | |||
| 116 | const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" | ||
| 117 | const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" | ||
| 118 | const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" | ||
| 119 | const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" | ||
| 120 | const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" | ||
| 121 | |||
| 122 | const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" | ||
| 123 | const val PREF_THEME = "Theme" | ||
| 124 | const val PREF_THEME_MODE = "ThemeMode" | ||
| 125 | const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds" | ||
| 126 | |||
| 127 | val overlayPreferences = listOf( | ||
| 128 | PREF_OVERLAY_VERSION, | ||
| 129 | PREF_CONTROL_SCALE, | ||
| 130 | PREF_CONTROL_OPACITY, | ||
| 131 | PREF_TOUCH_ENABLED, | ||
| 132 | PREF_BUTTON_A, | ||
| 133 | PREF_BUTTON_B, | ||
| 134 | PREF_BUTTON_X, | ||
| 135 | PREF_BUTTON_Y, | ||
| 136 | PREF_BUTTON_L, | ||
| 137 | PREF_BUTTON_R, | ||
| 138 | PREF_BUTTON_ZL, | ||
| 139 | PREF_BUTTON_ZR, | ||
| 140 | PREF_BUTTON_PLUS, | ||
| 141 | PREF_BUTTON_MINUS, | ||
| 142 | PREF_BUTTON_DPAD, | ||
| 143 | PREF_STICK_L, | ||
| 144 | PREF_STICK_R, | ||
| 145 | PREF_BUTTON_HOME, | ||
| 146 | PREF_BUTTON_SCREENSHOT, | ||
| 147 | PREF_BUTTON_STICK_L, | ||
| 148 | PREF_BUTTON_STICK_R | ||
| 149 | ) | ||
| 150 | |||
| 151 | const val LayoutOption_Unspecified = 0 | ||
| 152 | const val LayoutOption_MobilePortrait = 4 | ||
| 153 | const val LayoutOption_MobileLandscape = 5 | ||
| 198 | } | 154 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt new file mode 100644 index 000000000..c9a0c664c --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | ||
| 5 | |||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 8 | enum class ShortSetting( | ||
| 9 | override val key: String, | ||
| 10 | override val category: Settings.Category | ||
| 11 | ) : AbstractShortSetting { | ||
| 12 | RENDERER_SPEED_LIMIT("speed_limit", Settings.Category.Core); | ||
| 13 | |||
| 14 | override val short: Short | ||
| 15 | get() = NativeConfig.getShort(key, false) | ||
| 16 | |||
| 17 | override fun setShort(value: Short) = NativeConfig.setShort(key, value) | ||
| 18 | |||
| 19 | override val defaultValue: Short by lazy { NativeConfig.getShort(key, true) } | ||
| 20 | |||
| 21 | override val valueAsString: String | ||
| 22 | get() = short.toString() | ||
| 23 | |||
| 24 | override fun reset() = NativeConfig.setShort(key, defaultValue) | ||
| 25 | } | ||
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 6621289fd..9bb3e66d4 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 | |||
| @@ -3,36 +3,24 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 7 | |||
| 6 | enum class StringSetting( | 8 | enum class StringSetting( |
| 7 | override val key: String, | 9 | override val key: String, |
| 8 | override val section: String, | 10 | override val category: Settings.Category |
| 9 | override val defaultValue: String | ||
| 10 | ) : AbstractStringSetting { | 11 | ) : AbstractStringSetting { |
| 11 | AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"), | 12 | // No string settings currently exist |
| 12 | CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); | 13 | EMPTY_SETTING("", Settings.Category.UiGeneral); |
| 14 | |||
| 15 | override val string: String | ||
| 16 | get() = NativeConfig.getString(key, false) | ||
| 17 | |||
| 18 | override fun setString(value: String) = NativeConfig.setString(key, value) | ||
| 13 | 19 | ||
| 14 | override var string: String = defaultValue | 20 | override val defaultValue: String by lazy { NativeConfig.getString(key, true) } |
| 15 | 21 | ||
| 16 | override val valueAsString: String | 22 | override val valueAsString: String |
| 17 | get() = string | 23 | get() = string |
| 18 | 24 | ||
| 19 | override val isRuntimeEditable: Boolean | 25 | override fun reset() = NativeConfig.setString(key, defaultValue) |
| 20 | get() { | ||
| 21 | for (setting in NOT_RUNTIME_EDITABLE) { | ||
| 22 | if (setting == this) { | ||
| 23 | return false | ||
| 24 | } | ||
| 25 | } | ||
| 26 | return true | ||
| 27 | } | ||
| 28 | |||
| 29 | companion object { | ||
| 30 | private val NOT_RUNTIME_EDITABLE = listOf( | ||
| 31 | CUSTOM_RTC | ||
| 32 | ) | ||
| 33 | |||
| 34 | fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key } | ||
| 35 | |||
| 36 | fun clear() = StringSetting.values().forEach { it.string = it.defaultValue } | ||
| 37 | } | ||
| 38 | } | 26 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt index bc0bf7788..8bc164197 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt | |||
| @@ -3,29 +3,16 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting |
| 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting | ||
| 8 | 7 | ||
| 9 | class DateTimeSetting( | 8 | class DateTimeSetting( |
| 10 | setting: AbstractSetting?, | 9 | private val longSetting: AbstractLongSetting, |
| 11 | titleId: Int, | 10 | titleId: Int, |
| 12 | descriptionId: Int, | 11 | descriptionId: Int |
| 13 | val key: String? = null, | 12 | ) : SettingsItem(longSetting, titleId, descriptionId) { |
| 14 | private val defaultValue: String? = null | ||
| 15 | ) : SettingsItem(setting, titleId, descriptionId) { | ||
| 16 | override val type = TYPE_DATETIME_SETTING | 13 | override val type = TYPE_DATETIME_SETTING |
| 17 | 14 | ||
| 18 | val value: String | 15 | var value: Long |
| 19 | get() = if (setting != null) { | 16 | get() = longSetting.long |
| 20 | val setting = setting as AbstractStringSetting | 17 | set(value) = (setting as AbstractLongSetting).setLong(value) |
| 21 | setting.string | ||
| 22 | } else { | ||
| 23 | defaultValue!! | ||
| 24 | } | ||
| 25 | |||
| 26 | fun setSelectedValue(datetime: String): AbstractStringSetting { | ||
| 27 | val stringSetting = setting as AbstractStringSetting | ||
| 28 | stringSetting.string = datetime | ||
| 29 | return stringSetting | ||
| 30 | } | ||
| 31 | } | 18 | } |
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 a67001311..d31ce1c31 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 | |||
| @@ -5,6 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view | |||
| 5 | 5 | ||
| 6 | class HeaderSetting( | 6 | class HeaderSetting( |
| 7 | titleId: Int | 7 | titleId: Int |
| 8 | ) : SettingsItem(null, titleId, 0) { | 8 | ) : SettingsItem(emptySetting, titleId, 0) { |
| 9 | override val type = TYPE_HEADER | 9 | override val type = TYPE_HEADER |
| 10 | } | 10 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt index caaab50d8..522cc49df 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt | |||
| @@ -8,6 +8,6 @@ class RunnableSetting( | |||
| 8 | descriptionId: Int, | 8 | descriptionId: Int, |
| 9 | val isRuntimeRunnable: Boolean, | 9 | val isRuntimeRunnable: Boolean, |
| 10 | val runnable: () -> Unit | 10 | val runnable: () -> Unit |
| 11 | ) : SettingsItem(null, titleId, descriptionId) { | 11 | ) : SettingsItem(emptySetting, titleId, descriptionId) { |
| 12 | override val type = TYPE_RUNNABLE | 12 | override val type = TYPE_RUNNABLE |
| 13 | } | 13 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 07520849e..b3b3fc209 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt | |||
| @@ -4,7 +4,15 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.NativeLibrary | 6 | import org.yuzu.yuzu_emu.NativeLibrary |
| 7 | import org.yuzu.yuzu_emu.R | ||
| 8 | import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting | ||
| 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | 9 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting |
| 10 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | ||
| 11 | import org.yuzu.yuzu_emu.features.settings.model.ByteSetting | ||
| 12 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | ||
| 13 | import org.yuzu.yuzu_emu.features.settings.model.LongSetting | ||
| 14 | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||
| 15 | import org.yuzu.yuzu_emu.features.settings.model.ShortSetting | ||
| 8 | 16 | ||
| 9 | /** | 17 | /** |
| 10 | * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. | 18 | * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. |
| @@ -14,7 +22,7 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | |||
| 14 | * file.) | 22 | * file.) |
| 15 | */ | 23 | */ |
| 16 | abstract class SettingsItem( | 24 | abstract class SettingsItem( |
| 17 | var setting: AbstractSetting?, | 25 | val setting: AbstractSetting, |
| 18 | val nameId: Int, | 26 | val nameId: Int, |
| 19 | val descriptionId: Int | 27 | val descriptionId: Int |
| 20 | ) { | 28 | ) { |
| @@ -23,7 +31,7 @@ abstract class SettingsItem( | |||
| 23 | val isEditable: Boolean | 31 | val isEditable: Boolean |
| 24 | get() { | 32 | get() { |
| 25 | if (!NativeLibrary.isRunning()) return true | 33 | if (!NativeLibrary.isRunning()) return true |
| 26 | return setting?.isRuntimeEditable ?: false | 34 | return setting.isRuntimeModifiable |
| 27 | } | 35 | } |
| 28 | 36 | ||
| 29 | companion object { | 37 | companion object { |
| @@ -35,5 +43,240 @@ abstract class SettingsItem( | |||
| 35 | const val TYPE_STRING_SINGLE_CHOICE = 5 | 43 | const val TYPE_STRING_SINGLE_CHOICE = 5 |
| 36 | const val TYPE_DATETIME_SETTING = 6 | 44 | const val TYPE_DATETIME_SETTING = 6 |
| 37 | const val TYPE_RUNNABLE = 7 | 45 | const val TYPE_RUNNABLE = 7 |
| 46 | |||
| 47 | const val FASTMEM_COMBINED = "fastmem_combined" | ||
| 48 | |||
| 49 | val emptySetting = object : AbstractSetting { | ||
| 50 | override val key: String = "" | ||
| 51 | override val category: Settings.Category = Settings.Category.Ui | ||
| 52 | override val defaultValue: Any = false | ||
| 53 | override fun reset() {} | ||
| 54 | } | ||
| 55 | |||
| 56 | // Extension for putting SettingsItems into a hashmap without repeating yourself | ||
| 57 | fun HashMap<String, SettingsItem>.put(item: SettingsItem) { | ||
| 58 | put(item.setting.key, item) | ||
| 59 | } | ||
| 60 | |||
| 61 | // List of all general | ||
| 62 | val settingsItems = HashMap<String, SettingsItem>().apply { | ||
| 63 | put( | ||
| 64 | SwitchSetting( | ||
| 65 | BooleanSetting.RENDERER_USE_SPEED_LIMIT, | ||
| 66 | R.string.frame_limit_enable, | ||
| 67 | R.string.frame_limit_enable_description | ||
| 68 | ) | ||
| 69 | ) | ||
| 70 | put( | ||
| 71 | SliderSetting( | ||
| 72 | ShortSetting.RENDERER_SPEED_LIMIT, | ||
| 73 | R.string.frame_limit_slider, | ||
| 74 | R.string.frame_limit_slider_description, | ||
| 75 | 1, | ||
| 76 | 200, | ||
| 77 | "%" | ||
| 78 | ) | ||
| 79 | ) | ||
| 80 | put( | ||
| 81 | SingleChoiceSetting( | ||
| 82 | IntSetting.CPU_ACCURACY, | ||
| 83 | R.string.cpu_accuracy, | ||
| 84 | 0, | ||
| 85 | R.array.cpuAccuracyNames, | ||
| 86 | R.array.cpuAccuracyValues | ||
| 87 | ) | ||
| 88 | ) | ||
| 89 | put( | ||
| 90 | SwitchSetting( | ||
| 91 | BooleanSetting.PICTURE_IN_PICTURE, | ||
| 92 | R.string.picture_in_picture, | ||
| 93 | R.string.picture_in_picture_description | ||
| 94 | ) | ||
| 95 | ) | ||
| 96 | put( | ||
| 97 | SwitchSetting( | ||
| 98 | BooleanSetting.USE_DOCKED_MODE, | ||
| 99 | R.string.use_docked_mode, | ||
| 100 | R.string.use_docked_mode_description | ||
| 101 | ) | ||
| 102 | ) | ||
| 103 | put( | ||
| 104 | SingleChoiceSetting( | ||
| 105 | IntSetting.REGION_INDEX, | ||
| 106 | R.string.emulated_region, | ||
| 107 | 0, | ||
| 108 | R.array.regionNames, | ||
| 109 | R.array.regionValues | ||
| 110 | ) | ||
| 111 | ) | ||
| 112 | put( | ||
| 113 | SingleChoiceSetting( | ||
| 114 | IntSetting.LANGUAGE_INDEX, | ||
| 115 | R.string.emulated_language, | ||
| 116 | 0, | ||
| 117 | R.array.languageNames, | ||
| 118 | R.array.languageValues | ||
| 119 | ) | ||
| 120 | ) | ||
| 121 | put( | ||
| 122 | SwitchSetting( | ||
| 123 | BooleanSetting.USE_CUSTOM_RTC, | ||
| 124 | R.string.use_custom_rtc, | ||
| 125 | R.string.use_custom_rtc_description | ||
| 126 | ) | ||
| 127 | ) | ||
| 128 | put(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0)) | ||
| 129 | put( | ||
| 130 | SingleChoiceSetting( | ||
| 131 | IntSetting.RENDERER_ACCURACY, | ||
| 132 | R.string.renderer_accuracy, | ||
| 133 | 0, | ||
| 134 | R.array.rendererAccuracyNames, | ||
| 135 | R.array.rendererAccuracyValues | ||
| 136 | ) | ||
| 137 | ) | ||
| 138 | put( | ||
| 139 | SingleChoiceSetting( | ||
| 140 | IntSetting.RENDERER_RESOLUTION, | ||
| 141 | R.string.renderer_resolution, | ||
| 142 | 0, | ||
| 143 | R.array.rendererResolutionNames, | ||
| 144 | R.array.rendererResolutionValues | ||
| 145 | ) | ||
| 146 | ) | ||
| 147 | put( | ||
| 148 | SingleChoiceSetting( | ||
| 149 | IntSetting.RENDERER_VSYNC, | ||
| 150 | R.string.renderer_vsync, | ||
| 151 | 0, | ||
| 152 | R.array.rendererVSyncNames, | ||
| 153 | R.array.rendererVSyncValues | ||
| 154 | ) | ||
| 155 | ) | ||
| 156 | put( | ||
| 157 | SingleChoiceSetting( | ||
| 158 | IntSetting.RENDERER_SCALING_FILTER, | ||
| 159 | R.string.renderer_scaling_filter, | ||
| 160 | 0, | ||
| 161 | R.array.rendererScalingFilterNames, | ||
| 162 | R.array.rendererScalingFilterValues | ||
| 163 | ) | ||
| 164 | ) | ||
| 165 | put( | ||
| 166 | SingleChoiceSetting( | ||
| 167 | IntSetting.RENDERER_ANTI_ALIASING, | ||
| 168 | R.string.renderer_anti_aliasing, | ||
| 169 | 0, | ||
| 170 | R.array.rendererAntiAliasingNames, | ||
| 171 | R.array.rendererAntiAliasingValues | ||
| 172 | ) | ||
| 173 | ) | ||
| 174 | put( | ||
| 175 | SingleChoiceSetting( | ||
| 176 | IntSetting.RENDERER_SCREEN_LAYOUT, | ||
| 177 | R.string.renderer_screen_layout, | ||
| 178 | 0, | ||
| 179 | R.array.rendererScreenLayoutNames, | ||
| 180 | R.array.rendererScreenLayoutValues | ||
| 181 | ) | ||
| 182 | ) | ||
| 183 | put( | ||
| 184 | SingleChoiceSetting( | ||
| 185 | IntSetting.RENDERER_ASPECT_RATIO, | ||
| 186 | R.string.renderer_aspect_ratio, | ||
| 187 | 0, | ||
| 188 | R.array.rendererAspectRatioNames, | ||
| 189 | R.array.rendererAspectRatioValues | ||
| 190 | ) | ||
| 191 | ) | ||
| 192 | put( | ||
| 193 | SwitchSetting( | ||
| 194 | BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE, | ||
| 195 | R.string.use_disk_shader_cache, | ||
| 196 | R.string.use_disk_shader_cache_description | ||
| 197 | ) | ||
| 198 | ) | ||
| 199 | put( | ||
| 200 | SwitchSetting( | ||
| 201 | BooleanSetting.RENDERER_FORCE_MAX_CLOCK, | ||
| 202 | R.string.renderer_force_max_clock, | ||
| 203 | R.string.renderer_force_max_clock_description | ||
| 204 | ) | ||
| 205 | ) | ||
| 206 | put( | ||
| 207 | SwitchSetting( | ||
| 208 | BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS, | ||
| 209 | R.string.renderer_asynchronous_shaders, | ||
| 210 | R.string.renderer_asynchronous_shaders_description | ||
| 211 | ) | ||
| 212 | ) | ||
| 213 | put( | ||
| 214 | SwitchSetting( | ||
| 215 | BooleanSetting.RENDERER_REACTIVE_FLUSHING, | ||
| 216 | R.string.renderer_reactive_flushing, | ||
| 217 | R.string.renderer_reactive_flushing_description | ||
| 218 | ) | ||
| 219 | ) | ||
| 220 | put( | ||
| 221 | SingleChoiceSetting( | ||
| 222 | IntSetting.AUDIO_OUTPUT_ENGINE, | ||
| 223 | R.string.audio_output_engine, | ||
| 224 | 0, | ||
| 225 | R.array.outputEngineEntries, | ||
| 226 | R.array.outputEngineValues | ||
| 227 | ) | ||
| 228 | ) | ||
| 229 | put( | ||
| 230 | SliderSetting( | ||
| 231 | ByteSetting.AUDIO_VOLUME, | ||
| 232 | R.string.audio_volume, | ||
| 233 | R.string.audio_volume_description, | ||
| 234 | 0, | ||
| 235 | 100, | ||
| 236 | "%" | ||
| 237 | ) | ||
| 238 | ) | ||
| 239 | put( | ||
| 240 | SingleChoiceSetting( | ||
| 241 | IntSetting.RENDERER_BACKEND, | ||
| 242 | R.string.renderer_api, | ||
| 243 | 0, | ||
| 244 | R.array.rendererApiNames, | ||
| 245 | R.array.rendererApiValues | ||
| 246 | ) | ||
| 247 | ) | ||
| 248 | put( | ||
| 249 | SwitchSetting( | ||
| 250 | BooleanSetting.RENDERER_DEBUG, | ||
| 251 | R.string.renderer_debug, | ||
| 252 | R.string.renderer_debug_description | ||
| 253 | ) | ||
| 254 | ) | ||
| 255 | put( | ||
| 256 | SwitchSetting( | ||
| 257 | BooleanSetting.CPU_DEBUG_MODE, | ||
| 258 | R.string.cpu_debug_mode, | ||
| 259 | R.string.cpu_debug_mode_description | ||
| 260 | ) | ||
| 261 | ) | ||
| 262 | |||
| 263 | val fastmem = object : AbstractBooleanSetting { | ||
| 264 | override val boolean: Boolean | ||
| 265 | get() = | ||
| 266 | BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean | ||
| 267 | |||
| 268 | override fun setBoolean(value: Boolean) { | ||
| 269 | BooleanSetting.FASTMEM.setBoolean(value) | ||
| 270 | BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value) | ||
| 271 | } | ||
| 272 | |||
| 273 | override val key: String = FASTMEM_COMBINED | ||
| 274 | override val category = Settings.Category.Cpu | ||
| 275 | override val isRuntimeModifiable: Boolean = false | ||
| 276 | override val defaultValue: Boolean = true | ||
| 277 | override fun reset() = setBoolean(defaultValue) | ||
| 278 | } | ||
| 279 | put(SwitchSetting(fastmem, R.string.fastmem, 0)) | ||
| 280 | } | ||
| 38 | } | 281 | } |
| 39 | } | 282 | } |
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 7306ec458..705527a73 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,36 +4,27 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting | 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting |
| 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | ||
| 7 | 8 | ||
| 8 | class SingleChoiceSetting( | 9 | class SingleChoiceSetting( |
| 9 | setting: AbstractIntSetting?, | 10 | setting: AbstractSetting, |
| 10 | titleId: Int, | 11 | titleId: Int, |
| 11 | descriptionId: Int, | 12 | descriptionId: Int, |
| 12 | val choicesId: Int, | 13 | val choicesId: Int, |
| 13 | val valuesId: Int, | 14 | val valuesId: Int |
| 14 | val key: String? = null, | ||
| 15 | val defaultValue: Int? = null | ||
| 16 | ) : SettingsItem(setting, titleId, descriptionId) { | 15 | ) : SettingsItem(setting, titleId, descriptionId) { |
| 17 | override val type = TYPE_SINGLE_CHOICE | 16 | override val type = TYPE_SINGLE_CHOICE |
| 18 | 17 | ||
| 19 | val selectedValue: Int | 18 | var selectedValue: Int |
| 20 | get() = if (setting != null) { | 19 | get() { |
| 21 | val setting = setting as AbstractIntSetting | 20 | return when (setting) { |
| 22 | setting.int | 21 | is AbstractIntSetting -> setting.int |
| 23 | } else { | 22 | else -> -1 |
| 24 | defaultValue!! | 23 | } |
| 24 | } | ||
| 25 | set(value) { | ||
| 26 | when (setting) { | ||
| 27 | is AbstractIntSetting -> setting.setInt(value) | ||
| 28 | } | ||
| 25 | } | 29 | } |
| 26 | |||
| 27 | /** | ||
| 28 | * Write a value to the backing int. If that int was previously null, | ||
| 29 | * initializes a new one and returns it, so it can be added to the Hashmap. | ||
| 30 | * | ||
| 31 | * @param selection New value of the int. | ||
| 32 | * @return the existing setting with the new value applied. | ||
| 33 | */ | ||
| 34 | fun setSelectedValue(selection: Int): AbstractIntSetting { | ||
| 35 | val intSetting = setting as AbstractIntSetting | ||
| 36 | intSetting.int = selection | ||
| 37 | return intSetting | ||
| 38 | } | ||
| 39 | } | 30 | } |
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 92d0167ae..c3b5df02c 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,60 +3,39 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import kotlin.math.roundToInt | 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting |
| 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting | 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting |
| 8 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting | 8 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting |
| 9 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | 9 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting |
| 10 | import org.yuzu.yuzu_emu.utils.Log | 10 | import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting |
| 11 | import kotlin.math.roundToInt | ||
| 11 | 12 | ||
| 12 | class SliderSetting( | 13 | class SliderSetting( |
| 13 | setting: AbstractSetting?, | 14 | setting: AbstractSetting, |
| 14 | titleId: Int, | 15 | titleId: Int, |
| 15 | descriptionId: Int, | 16 | descriptionId: Int, |
| 16 | val min: Int, | 17 | val min: Int, |
| 17 | val max: Int, | 18 | val max: Int, |
| 18 | val units: String, | 19 | val units: String |
| 19 | val key: String? = null, | ||
| 20 | val defaultValue: Int? = null | ||
| 21 | ) : SettingsItem(setting, titleId, descriptionId) { | 20 | ) : SettingsItem(setting, titleId, descriptionId) { |
| 22 | override val type = TYPE_SLIDER | 21 | override val type = TYPE_SLIDER |
| 23 | 22 | ||
| 24 | val selectedValue: Int | 23 | var selectedValue: Int |
| 25 | get() { | 24 | get() { |
| 26 | val setting = setting ?: return defaultValue!! | ||
| 27 | return when (setting) { | 25 | return when (setting) { |
| 26 | is AbstractByteSetting -> setting.byte.toInt() | ||
| 27 | is AbstractShortSetting -> setting.short.toInt() | ||
| 28 | is AbstractIntSetting -> setting.int | 28 | is AbstractIntSetting -> setting.int |
| 29 | is AbstractFloatSetting -> setting.float.roundToInt() | 29 | is AbstractFloatSetting -> setting.float.roundToInt() |
| 30 | else -> { | 30 | else -> -1 |
| 31 | Log.error("[SliderSetting] Error casting setting type.") | 31 | } |
| 32 | -1 | 32 | } |
| 33 | } | 33 | set(value) { |
| 34 | when (setting) { | ||
| 35 | is AbstractByteSetting -> setting.setByte(value.toByte()) | ||
| 36 | is AbstractShortSetting -> setting.setShort(value.toShort()) | ||
| 37 | is AbstractIntSetting -> setting.setInt(value) | ||
| 38 | is AbstractFloatSetting -> setting.setFloat(value.toFloat()) | ||
| 34 | } | 39 | } |
| 35 | } | 40 | } |
| 36 | |||
| 37 | /** | ||
| 38 | * Write a value to the backing int. If that int was previously null, | ||
| 39 | * initializes a new one and returns it, so it can be added to the Hashmap. | ||
| 40 | * | ||
| 41 | * @param selection New value of the int. | ||
| 42 | * @return the existing setting with the new value applied. | ||
| 43 | */ | ||
| 44 | fun setSelectedValue(selection: Int): AbstractIntSetting { | ||
| 45 | val intSetting = setting as AbstractIntSetting | ||
| 46 | intSetting.int = selection | ||
| 47 | return intSetting | ||
| 48 | } | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Write a value to the backing float. If that float was previously null, | ||
| 52 | * initializes a new one and returns it, so it can be added to the Hashmap. | ||
| 53 | * | ||
| 54 | * @param selection New value of the float. | ||
| 55 | * @return the existing setting with the new value applied. | ||
| 56 | */ | ||
| 57 | fun setSelectedValue(selection: Float): AbstractFloatSetting { | ||
| 58 | val floatSetting = setting as AbstractFloatSetting | ||
| 59 | floatSetting.float = selection | ||
| 60 | return floatSetting | ||
| 61 | } | ||
| 62 | } | 41 | } |
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 3b6731dcd..871dab4f3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt | |||
| @@ -3,57 +3,31 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | ||
| 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting | 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting |
| 8 | 7 | ||
| 9 | class StringSingleChoiceSetting( | 8 | class StringSingleChoiceSetting( |
| 10 | setting: AbstractSetting?, | 9 | private val stringSetting: AbstractStringSetting, |
| 11 | titleId: Int, | 10 | titleId: Int, |
| 12 | descriptionId: Int, | 11 | descriptionId: Int, |
| 13 | val choices: Array<String>, | 12 | val choices: Array<String>, |
| 14 | val values: Array<String>?, | 13 | val values: Array<String> |
| 15 | val key: String? = null, | 14 | ) : SettingsItem(stringSetting, titleId, descriptionId) { |
| 16 | private val defaultValue: String? = null | ||
| 17 | ) : SettingsItem(setting, titleId, descriptionId) { | ||
| 18 | override val type = TYPE_STRING_SINGLE_CHOICE | 15 | override val type = TYPE_STRING_SINGLE_CHOICE |
| 19 | 16 | ||
| 20 | fun getValueAt(index: Int): String? { | 17 | fun getValueAt(index: Int): String = |
| 21 | if (values == null) return null | 18 | if (index >= 0 && index < values.size) values[index] else "" |
| 22 | return if (index >= 0 && index < values.size) { | 19 | |
| 23 | values[index] | 20 | var selectedValue: String |
| 24 | } else { | 21 | get() = stringSetting.string |
| 25 | "" | 22 | set(value) = stringSetting.setString(value) |
| 26 | } | ||
| 27 | } | ||
| 28 | 23 | ||
| 29 | val selectedValue: String | ||
| 30 | get() = if (setting != null) { | ||
| 31 | val setting = setting as AbstractStringSetting | ||
| 32 | setting.string | ||
| 33 | } else { | ||
| 34 | defaultValue!! | ||
| 35 | } | ||
| 36 | val selectValueIndex: Int | 24 | val selectValueIndex: Int |
| 37 | get() { | 25 | get() { |
| 38 | val selectedValue = selectedValue | 26 | for (i in values.indices) { |
| 39 | for (i in values!!.indices) { | ||
| 40 | if (values[i] == selectedValue) { | 27 | if (values[i] == selectedValue) { |
| 41 | return i | 28 | return i |
| 42 | } | 29 | } |
| 43 | } | 30 | } |
| 44 | return -1 | 31 | return -1 |
| 45 | } | 32 | } |
| 46 | |||
| 47 | /** | ||
| 48 | * Write a value to the backing int. If that int was previously null, | ||
| 49 | * initializes a new one and returns it, so it can be added to the Hashmap. | ||
| 50 | * | ||
| 51 | * @param selection New value of the int. | ||
| 52 | * @return the existing setting with the new value applied. | ||
| 53 | */ | ||
| 54 | fun setSelectedValue(selection: String): AbstractStringSetting { | ||
| 55 | val stringSetting = setting as AbstractStringSetting | ||
| 56 | stringSetting.string = selection | ||
| 57 | return stringSetting | ||
| 58 | } | ||
| 59 | } | 33 | } |
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 8a9d13a92..91c273964 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 | |||
| @@ -7,6 +7,6 @@ class SubmenuSetting( | |||
| 7 | titleId: Int, | 7 | titleId: Int, |
| 8 | descriptionId: Int, | 8 | descriptionId: Int, |
| 9 | val menuKey: String | 9 | val menuKey: String |
| 10 | ) : SettingsItem(null, titleId, descriptionId) { | 10 | ) : SettingsItem(emptySetting, titleId, descriptionId) { |
| 11 | override val type = TYPE_SUBMENU | 11 | override val type = TYPE_SUBMENU |
| 12 | } | 12 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt index 90b198718..416967e64 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt | |||
| @@ -10,53 +10,22 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | |||
| 10 | class SwitchSetting( | 10 | class SwitchSetting( |
| 11 | setting: AbstractSetting, | 11 | setting: AbstractSetting, |
| 12 | titleId: Int, | 12 | titleId: Int, |
| 13 | descriptionId: Int, | 13 | descriptionId: Int |
| 14 | val key: String? = null, | ||
| 15 | val defaultValue: Any? = null | ||
| 16 | ) : SettingsItem(setting, titleId, descriptionId) { | 14 | ) : SettingsItem(setting, titleId, descriptionId) { |
| 17 | override val type = TYPE_SWITCH | 15 | override val type = TYPE_SWITCH |
| 18 | 16 | ||
| 19 | val isChecked: Boolean | 17 | var checked: Boolean |
| 20 | get() { | 18 | get() { |
| 21 | if (setting == null) { | 19 | return when (setting) { |
| 22 | return defaultValue as Boolean | 20 | is AbstractIntSetting -> setting.int == 1 |
| 21 | is AbstractBooleanSetting -> setting.boolean | ||
| 22 | else -> false | ||
| 23 | } | 23 | } |
| 24 | |||
| 25 | // Try integer setting | ||
| 26 | try { | ||
| 27 | val setting = setting as AbstractIntSetting | ||
| 28 | return setting.int == 1 | ||
| 29 | } catch (_: ClassCastException) { | ||
| 30 | } | ||
| 31 | |||
| 32 | // Try boolean setting | ||
| 33 | try { | ||
| 34 | val setting = setting as AbstractBooleanSetting | ||
| 35 | return setting.boolean | ||
| 36 | } catch (_: ClassCastException) { | ||
| 37 | } | ||
| 38 | return defaultValue as Boolean | ||
| 39 | } | 24 | } |
| 40 | 25 | set(value) { | |
| 41 | /** | 26 | when (setting) { |
| 42 | * Write a value to the backing boolean. If that boolean was previously null, | 27 | is AbstractIntSetting -> setting.setInt(if (value) 1 else 0) |
| 43 | * initializes a new one and returns it, so it can be added to the Hashmap. | 28 | is AbstractBooleanSetting -> setting.setBoolean(value) |
| 44 | * | 29 | } |
| 45 | * @param checked Pretty self explanatory. | ||
| 46 | * @return the existing setting with the new value applied. | ||
| 47 | */ | ||
| 48 | fun setChecked(checked: Boolean): AbstractSetting { | ||
| 49 | // Try integer setting | ||
| 50 | try { | ||
| 51 | val setting = setting as AbstractIntSetting | ||
| 52 | setting.int = if (checked) 1 else 0 | ||
| 53 | return setting | ||
| 54 | } catch (_: ClassCastException) { | ||
| 55 | } | 30 | } |
| 56 | |||
| 57 | // Try boolean setting | ||
| 58 | val setting = setting as AbstractBooleanSetting | ||
| 59 | setting.boolean = checked | ||
| 60 | return setting | ||
| 61 | } | ||
| 62 | } | 31 | } |
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 a5af5a7ae..908c01265 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 | |||
| @@ -3,42 +3,34 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | 4 | package org.yuzu.yuzu_emu.features.settings.ui |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 7 | import android.content.Intent | ||
| 8 | import android.os.Bundle | 6 | import android.os.Bundle |
| 9 | import android.view.Menu | ||
| 10 | import android.view.View | 7 | import android.view.View |
| 11 | import android.view.ViewGroup.MarginLayoutParams | 8 | import android.view.ViewGroup.MarginLayoutParams |
| 12 | import android.widget.Toast | 9 | import android.widget.Toast |
| 13 | import androidx.activity.OnBackPressedCallback | 10 | import androidx.activity.OnBackPressedCallback |
| 14 | import androidx.activity.result.ActivityResultLauncher | ||
| 15 | import androidx.activity.viewModels | 11 | import androidx.activity.viewModels |
| 16 | import androidx.appcompat.app.AppCompatActivity | 12 | import androidx.appcompat.app.AppCompatActivity |
| 17 | import androidx.core.view.ViewCompat | 13 | import androidx.core.view.ViewCompat |
| 18 | import androidx.core.view.WindowCompat | 14 | import androidx.core.view.WindowCompat |
| 19 | import androidx.core.view.WindowInsetsCompat | 15 | import androidx.core.view.WindowInsetsCompat |
| 20 | import androidx.core.view.updatePadding | 16 | import androidx.navigation.fragment.NavHostFragment |
| 17 | import androidx.navigation.navArgs | ||
| 21 | import com.google.android.material.color.MaterialColors | 18 | import com.google.android.material.color.MaterialColors |
| 22 | import java.io.IOException | 19 | import java.io.IOException |
| 23 | import org.yuzu.yuzu_emu.R | 20 | import org.yuzu.yuzu_emu.R |
| 24 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding | 21 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding |
| 25 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | ||
| 26 | import org.yuzu.yuzu_emu.features.settings.model.FloatSetting | ||
| 27 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | ||
| 28 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 22 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 29 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | ||
| 30 | import org.yuzu.yuzu_emu.features.settings.model.StringSetting | ||
| 31 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 23 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 24 | import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment | ||
| 25 | import org.yuzu.yuzu_emu.model.SettingsViewModel | ||
| 32 | import org.yuzu.yuzu_emu.utils.* | 26 | import org.yuzu.yuzu_emu.utils.* |
| 33 | 27 | ||
| 34 | class SettingsActivity : AppCompatActivity(), SettingsActivityView { | 28 | class SettingsActivity : AppCompatActivity() { |
| 35 | private val presenter = SettingsActivityPresenter(this) | ||
| 36 | |||
| 37 | private lateinit var binding: ActivitySettingsBinding | 29 | private lateinit var binding: ActivitySettingsBinding |
| 38 | 30 | ||
| 39 | private val settingsViewModel: SettingsViewModel by viewModels() | 31 | private val args by navArgs<SettingsActivityArgs>() |
| 40 | 32 | ||
| 41 | override val settings: Settings get() = settingsViewModel.settings | 33 | private val settingsViewModel: SettingsViewModel by viewModels() |
| 42 | 34 | ||
| 43 | override fun onCreate(savedInstanceState: Bundle?) { | 35 | override fun onCreate(savedInstanceState: Bundle?) { |
| 44 | ThemeHelper.setTheme(this) | 36 | ThemeHelper.setTheme(this) |
| @@ -48,16 +40,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { | |||
| 48 | binding = ActivitySettingsBinding.inflate(layoutInflater) | 40 | binding = ActivitySettingsBinding.inflate(layoutInflater) |
| 49 | setContentView(binding.root) | 41 | setContentView(binding.root) |
| 50 | 42 | ||
| 51 | WindowCompat.setDecorFitsSystemWindows(window, false) | 43 | settingsViewModel.game = args.game |
| 52 | 44 | ||
| 53 | val launcher = intent | 45 | val navHostFragment = |
| 54 | val gameID = launcher.getStringExtra(ARG_GAME_ID) | 46 | supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment |
| 55 | val menuTag = launcher.getStringExtra(ARG_MENU_TAG) | 47 | navHostFragment.navController.setGraph(R.navigation.settings_navigation, intent.extras) |
| 56 | presenter.onCreate(savedInstanceState, menuTag!!, gameID!!) | ||
| 57 | 48 | ||
| 58 | // Show "Back" button in the action bar for navigation | 49 | WindowCompat.setDecorFitsSystemWindows(window, false) |
| 59 | setSupportActionBar(binding.toolbarSettings) | 50 | |
| 60 | supportActionBar!!.setDisplayHomeAsUpEnabled(true) | 51 | if (savedInstanceState != null) { |
| 52 | settingsViewModel.shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE) | ||
| 53 | } | ||
| 61 | 54 | ||
| 62 | if (InsetsHelper.getSystemGestureType(applicationContext) != | 55 | if (InsetsHelper.getSystemGestureType(applicationContext) != |
| 63 | InsetsHelper.GESTURE_NAVIGATION | 56 | InsetsHelper.GESTURE_NAVIGATION |
| @@ -73,6 +66,28 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { | |||
| 73 | ) | 66 | ) |
| 74 | } | 67 | } |
| 75 | 68 | ||
| 69 | settingsViewModel.shouldRecreate.observe(this) { | ||
| 70 | if (it) { | ||
| 71 | settingsViewModel.setShouldRecreate(false) | ||
| 72 | recreate() | ||
| 73 | } | ||
| 74 | } | ||
| 75 | settingsViewModel.shouldNavigateBack.observe(this) { | ||
| 76 | if (it) { | ||
| 77 | settingsViewModel.setShouldNavigateBack(false) | ||
| 78 | navigateBack() | ||
| 79 | } | ||
| 80 | } | ||
| 81 | settingsViewModel.shouldShowResetSettingsDialog.observe(this) { | ||
| 82 | if (it) { | ||
| 83 | settingsViewModel.setShouldShowResetSettingsDialog(false) | ||
| 84 | ResetSettingsDialogFragment().show( | ||
| 85 | supportFragmentManager, | ||
| 86 | ResetSettingsDialogFragment.TAG | ||
| 87 | ) | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 76 | onBackPressedDispatcher.addCallback( | 91 | onBackPressedDispatcher.addCallback( |
| 77 | this, | 92 | this, |
| 78 | object : OnBackPressedCallback(true) { | 93 | object : OnBackPressedCallback(true) { |
| @@ -83,34 +98,28 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { | |||
| 83 | setInsets() | 98 | setInsets() |
| 84 | } | 99 | } |
| 85 | 100 | ||
| 86 | override fun onSupportNavigateUp(): Boolean { | 101 | fun navigateBack() { |
| 87 | navigateBack() | 102 | val navHostFragment = |
| 88 | return true | 103 | supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment |
| 89 | } | 104 | if (navHostFragment.childFragmentManager.backStackEntryCount > 0) { |
| 90 | 105 | navHostFragment.navController.popBackStack() | |
| 91 | private fun navigateBack() { | ||
| 92 | if (supportFragmentManager.backStackEntryCount > 0) { | ||
| 93 | supportFragmentManager.popBackStack() | ||
| 94 | } else { | 106 | } else { |
| 95 | finish() | 107 | finish() |
| 96 | } | 108 | } |
| 97 | } | 109 | } |
| 98 | 110 | ||
| 99 | override fun onCreateOptionsMenu(menu: Menu): Boolean { | ||
| 100 | val inflater = menuInflater | ||
| 101 | inflater.inflate(R.menu.menu_settings, menu) | ||
| 102 | return true | ||
| 103 | } | ||
| 104 | |||
| 105 | override fun onSaveInstanceState(outState: Bundle) { | 111 | override fun onSaveInstanceState(outState: Bundle) { |
| 106 | // Critical: If super method is not called, rotations will be busted. | 112 | // Critical: If super method is not called, rotations will be busted. |
| 107 | super.onSaveInstanceState(outState) | 113 | super.onSaveInstanceState(outState) |
| 108 | presenter.saveState(outState) | 114 | outState.putBoolean(KEY_SHOULD_SAVE, settingsViewModel.shouldSave) |
| 109 | } | 115 | } |
| 110 | 116 | ||
| 111 | override fun onStart() { | 117 | override fun onStart() { |
| 112 | super.onStart() | 118 | super.onStart() |
| 113 | presenter.onStart() | 119 | // TODO: Load custom settings contextually |
| 120 | if (!DirectoryInitialization.areDirectoriesReady) { | ||
| 121 | DirectoryInitialization.start() | ||
| 122 | } | ||
| 114 | } | 123 | } |
| 115 | 124 | ||
| 116 | /** | 125 | /** |
| @@ -120,143 +129,51 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { | |||
| 120 | */ | 129 | */ |
| 121 | override fun onStop() { | 130 | override fun onStop() { |
| 122 | super.onStop() | 131 | super.onStop() |
| 123 | presenter.onStop(isFinishing) | 132 | if (isFinishing && settingsViewModel.shouldSave) { |
| 124 | } | 133 | Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") |
| 125 | 134 | Settings.saveSettings() | |
| 126 | override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) { | ||
| 127 | if (!addToStack && settingsFragment != null) { | ||
| 128 | return | ||
| 129 | } | ||
| 130 | |||
| 131 | val transaction = supportFragmentManager.beginTransaction() | ||
| 132 | if (addToStack) { | ||
| 133 | if (areSystemAnimationsEnabled()) { | ||
| 134 | transaction.setCustomAnimations( | ||
| 135 | R.anim.anim_settings_fragment_in, | ||
| 136 | R.anim.anim_settings_fragment_out, | ||
| 137 | 0, | ||
| 138 | R.anim.anim_pop_settings_fragment_out | ||
| 139 | ) | ||
| 140 | } | ||
| 141 | transaction.addToBackStack(null) | ||
| 142 | } | 135 | } |
| 143 | transaction.replace( | ||
| 144 | R.id.frame_content, | ||
| 145 | SettingsFragment.newInstance(menuTag, gameId), | ||
| 146 | FRAGMENT_TAG | ||
| 147 | ) | ||
| 148 | transaction.commit() | ||
| 149 | } | 136 | } |
| 150 | 137 | ||
| 151 | private fun areSystemAnimationsEnabled(): Boolean { | 138 | override fun onDestroy() { |
| 152 | val duration = android.provider.Settings.Global.getFloat( | 139 | settingsViewModel.clear() |
| 153 | contentResolver, | 140 | super.onDestroy() |
| 154 | android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, | ||
| 155 | 1f | ||
| 156 | ) | ||
| 157 | val transition = android.provider.Settings.Global.getFloat( | ||
| 158 | contentResolver, | ||
| 159 | android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, | ||
| 160 | 1f | ||
| 161 | ) | ||
| 162 | return duration != 0f && transition != 0f | ||
| 163 | } | ||
| 164 | |||
| 165 | override fun onSettingsFileLoaded() { | ||
| 166 | val fragment: SettingsFragmentView? = settingsFragment | ||
| 167 | fragment?.loadSettingsList() | ||
| 168 | } | ||
| 169 | |||
| 170 | override fun onSettingsFileNotFound() { | ||
| 171 | val fragment: SettingsFragmentView? = settingsFragment | ||
| 172 | fragment?.loadSettingsList() | ||
| 173 | } | ||
| 174 | |||
| 175 | override fun showToastMessage(message: String, is_long: Boolean) { | ||
| 176 | Toast.makeText( | ||
| 177 | this, | ||
| 178 | message, | ||
| 179 | if (is_long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT | ||
| 180 | ).show() | ||
| 181 | } | ||
| 182 | |||
| 183 | override fun onSettingChanged() { | ||
| 184 | presenter.onSettingChanged() | ||
| 185 | } | 141 | } |
| 186 | 142 | ||
| 187 | fun onSettingsReset() { | 143 | fun onSettingsReset() { |
| 188 | // Prevents saving to a non-existent settings file | 144 | // Prevents saving to a non-existent settings file |
| 189 | presenter.onSettingsReset() | 145 | settingsViewModel.shouldSave = false |
| 190 | |||
| 191 | // Reset the static memory representation of each setting | ||
| 192 | BooleanSetting.clear() | ||
| 193 | FloatSetting.clear() | ||
| 194 | IntSetting.clear() | ||
| 195 | StringSetting.clear() | ||
| 196 | 146 | ||
| 197 | // Delete settings file because the user may have changed values that do not exist in the UI | 147 | // Delete settings file because the user may have changed values that do not exist in the UI |
| 198 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) | 148 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) |
| 199 | if (!settingsFile.delete()) { | 149 | if (!settingsFile.delete()) { |
| 200 | throw IOException("Failed to delete $settingsFile") | 150 | throw IOException("Failed to delete $settingsFile") |
| 201 | } | 151 | } |
| 152 | Settings.settingsList.forEach { it.reset() } | ||
| 202 | 153 | ||
| 203 | showToastMessage(getString(R.string.settings_reset), true) | 154 | Toast.makeText( |
| 155 | applicationContext, | ||
| 156 | getString(R.string.settings_reset), | ||
| 157 | Toast.LENGTH_LONG | ||
| 158 | ).show() | ||
| 204 | finish() | 159 | finish() |
| 205 | } | 160 | } |
| 206 | 161 | ||
| 207 | fun setToolbarTitle(title: String) { | ||
| 208 | binding.toolbarSettingsLayout.title = title | ||
| 209 | } | ||
| 210 | |||
| 211 | private val settingsFragment: SettingsFragment? | ||
| 212 | get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment? | ||
| 213 | |||
| 214 | private fun setInsets() { | 162 | private fun setInsets() { |
| 215 | ViewCompat.setOnApplyWindowInsetsListener( | 163 | ViewCompat.setOnApplyWindowInsetsListener( |
| 216 | binding.frameContent | 164 | binding.navigationBarShade |
| 217 | ) { view: View, windowInsets: WindowInsetsCompat -> | 165 | ) { view: View, windowInsets: WindowInsetsCompat -> |
| 218 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) | 166 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) |
| 219 | val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) | ||
| 220 | view.updatePadding( | ||
| 221 | left = barInsets.left + cutoutInsets.left, | ||
| 222 | right = barInsets.right + cutoutInsets.right | ||
| 223 | ) | ||
| 224 | 167 | ||
| 225 | val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams | 168 | val mlpShade = view.layoutParams as MarginLayoutParams |
| 226 | mlpAppBar.leftMargin = barInsets.left + cutoutInsets.left | ||
| 227 | mlpAppBar.rightMargin = barInsets.right + cutoutInsets.right | ||
| 228 | binding.appbarSettings.layoutParams = mlpAppBar | ||
| 229 | |||
| 230 | val mlpShade = binding.navigationBarShade.layoutParams as MarginLayoutParams | ||
| 231 | mlpShade.height = barInsets.bottom | 169 | mlpShade.height = barInsets.bottom |
| 232 | binding.navigationBarShade.layoutParams = mlpShade | 170 | view.layoutParams = mlpShade |
| 233 | 171 | ||
| 234 | windowInsets | 172 | windowInsets |
| 235 | } | 173 | } |
| 236 | } | 174 | } |
| 237 | 175 | ||
| 238 | companion object { | 176 | companion object { |
| 239 | private const val ARG_MENU_TAG = "menu_tag" | 177 | private const val KEY_SHOULD_SAVE = "should_save" |
| 240 | private const val ARG_GAME_ID = "game_id" | ||
| 241 | private const val FRAGMENT_TAG = "settings" | ||
| 242 | |||
| 243 | fun launch(context: Context, menuTag: String?, gameId: String?) { | ||
| 244 | val settings = Intent(context, SettingsActivity::class.java) | ||
| 245 | settings.putExtra(ARG_MENU_TAG, menuTag) | ||
| 246 | settings.putExtra(ARG_GAME_ID, gameId) | ||
| 247 | context.startActivity(settings) | ||
| 248 | } | ||
| 249 | |||
| 250 | fun launch( | ||
| 251 | context: Context, | ||
| 252 | launcher: ActivityResultLauncher<Intent>, | ||
| 253 | menuTag: String?, | ||
| 254 | gameId: String? | ||
| 255 | ) { | ||
| 256 | val settings = Intent(context, SettingsActivity::class.java) | ||
| 257 | settings.putExtra(ARG_MENU_TAG, menuTag) | ||
| 258 | settings.putExtra(ARG_GAME_ID, gameId) | ||
| 259 | launcher.launch(settings) | ||
| 260 | } | ||
| 261 | } | 178 | } |
| 262 | } | 179 | } |
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 deleted file mode 100644 index 93e677b21..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt +++ /dev/null | |||
| @@ -1,90 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | ||
| 5 | |||
| 6 | import android.content.Context | ||
| 7 | import android.os.Bundle | ||
| 8 | import android.text.TextUtils | ||
| 9 | import java.io.File | ||
| 10 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 11 | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||
| 12 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||
| 13 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | ||
| 14 | import org.yuzu.yuzu_emu.utils.Log | ||
| 15 | |||
| 16 | class SettingsActivityPresenter(private val activityView: SettingsActivityView) { | ||
| 17 | val settings: Settings get() = activityView.settings | ||
| 18 | |||
| 19 | private var shouldSave = false | ||
| 20 | private lateinit var menuTag: String | ||
| 21 | private lateinit var gameId: String | ||
| 22 | |||
| 23 | fun onCreate(savedInstanceState: Bundle?, menuTag: String, gameId: String) { | ||
| 24 | this.menuTag = menuTag | ||
| 25 | this.gameId = gameId | ||
| 26 | if (savedInstanceState != null) { | ||
| 27 | shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | fun onStart() { | ||
| 32 | prepareDirectoriesIfNeeded() | ||
| 33 | } | ||
| 34 | |||
| 35 | private fun loadSettingsUI() { | ||
| 36 | if (!settings.isLoaded) { | ||
| 37 | if (!TextUtils.isEmpty(gameId)) { | ||
| 38 | settings.loadSettings(gameId, activityView) | ||
| 39 | } else { | ||
| 40 | settings.loadSettings(activityView) | ||
| 41 | } | ||
| 42 | } | ||
| 43 | activityView.showSettingsFragment(menuTag, false, gameId) | ||
| 44 | activityView.onSettingsFileLoaded() | ||
| 45 | } | ||
| 46 | |||
| 47 | private fun prepareDirectoriesIfNeeded() { | ||
| 48 | val configFile = | ||
| 49 | File( | ||
| 50 | "${DirectoryInitialization.userDirectory}/config/" + | ||
| 51 | "${SettingsFile.FILE_NAME_CONFIG}.ini" | ||
| 52 | ) | ||
| 53 | if (!configFile.exists()) { | ||
| 54 | Log.error( | ||
| 55 | "${DirectoryInitialization.userDirectory}/config/" + | ||
| 56 | "${SettingsFile.FILE_NAME_CONFIG}.ini" | ||
| 57 | ) | ||
| 58 | Log.error("yuzu config file could not be found!") | ||
| 59 | } | ||
| 60 | |||
| 61 | if (!DirectoryInitialization.areDirectoriesReady) { | ||
| 62 | DirectoryInitialization.start(activityView as Context) | ||
| 63 | } | ||
| 64 | loadSettingsUI() | ||
| 65 | } | ||
| 66 | |||
| 67 | fun onStop(finishing: Boolean) { | ||
| 68 | if (finishing && shouldSave) { | ||
| 69 | Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") | ||
| 70 | settings.saveSettings(activityView) | ||
| 71 | } | ||
| 72 | NativeLibrary.reloadSettings() | ||
| 73 | } | ||
| 74 | |||
| 75 | fun onSettingChanged() { | ||
| 76 | shouldSave = true | ||
| 77 | } | ||
| 78 | |||
| 79 | fun onSettingsReset() { | ||
| 80 | shouldSave = false | ||
| 81 | } | ||
| 82 | |||
| 83 | fun saveState(outState: Bundle) { | ||
| 84 | outState.putBoolean(KEY_SHOULD_SAVE, shouldSave) | ||
| 85 | } | ||
| 86 | |||
| 87 | companion object { | ||
| 88 | private const val KEY_SHOULD_SAVE = "should_save" | ||
| 89 | } | ||
| 90 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt deleted file mode 100644 index c186fc388..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt +++ /dev/null | |||
| @@ -1,57 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | ||
| 5 | |||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||
| 7 | |||
| 8 | /** | ||
| 9 | * Abstraction for the Activity that manages SettingsFragments. | ||
| 10 | */ | ||
| 11 | interface SettingsActivityView { | ||
| 12 | /** | ||
| 13 | * Show a new SettingsFragment. | ||
| 14 | * | ||
| 15 | * @param menuTag Identifier for the settings group that should be displayed. | ||
| 16 | * @param addToStack Whether or not this fragment should replace a previous one. | ||
| 17 | */ | ||
| 18 | fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Called by a contained Fragment to get access to the Setting HashMap | ||
| 22 | * loaded from disk, so that each Fragment doesn't need to perform its own | ||
| 23 | * read operation. | ||
| 24 | * | ||
| 25 | * @return A HashMap of Settings. | ||
| 26 | */ | ||
| 27 | val settings: Settings | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Called when a load operation completes. | ||
| 31 | */ | ||
| 32 | fun onSettingsFileLoaded() | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Called when a load operation fails. | ||
| 36 | */ | ||
| 37 | fun onSettingsFileNotFound() | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Display a popup text message on screen. | ||
| 41 | * | ||
| 42 | * @param message The contents of the onscreen message. | ||
| 43 | * @param is_long Whether this should be a long Toast or short one. | ||
| 44 | */ | ||
| 45 | fun showToastMessage(message: String, is_long: Boolean) | ||
| 46 | |||
| 47 | /** | ||
| 48 | * End the activity. | ||
| 49 | */ | ||
| 50 | fun finish() | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Called by a containing Fragment to tell the Activity that a setting was changed; | ||
| 54 | * unless this has been called, the Activity will not save to disk. | ||
| 55 | */ | ||
| 56 | fun onSettingChanged() | ||
| 57 | } | ||
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 9711e2c51..a7a029fc1 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 | |||
| @@ -4,51 +4,54 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | 4 | package org.yuzu.yuzu_emu.features.settings.ui |
| 5 | 5 | ||
| 6 | import android.content.Context | 6 | import android.content.Context |
| 7 | import android.content.DialogInterface | ||
| 8 | import android.icu.util.Calendar | 7 | import android.icu.util.Calendar |
| 9 | import android.icu.util.TimeZone | 8 | import android.icu.util.TimeZone |
| 10 | import android.text.format.DateFormat | 9 | import android.text.format.DateFormat |
| 11 | import android.view.LayoutInflater | 10 | import android.view.LayoutInflater |
| 12 | import android.view.ViewGroup | 11 | import android.view.ViewGroup |
| 13 | import android.widget.TextView | 12 | import androidx.fragment.app.Fragment |
| 14 | import androidx.appcompat.app.AlertDialog | 13 | import androidx.lifecycle.Lifecycle |
| 15 | import androidx.appcompat.app.AppCompatActivity | 14 | import androidx.lifecycle.ViewModelProvider |
| 16 | import androidx.recyclerview.widget.RecyclerView | 15 | import androidx.lifecycle.lifecycleScope |
| 16 | import androidx.lifecycle.repeatOnLifecycle | ||
| 17 | import androidx.navigation.findNavController | ||
| 18 | import androidx.recyclerview.widget.AsyncDifferConfig | ||
| 19 | import androidx.recyclerview.widget.DiffUtil | ||
| 20 | import androidx.recyclerview.widget.ListAdapter | ||
| 17 | import com.google.android.material.datepicker.MaterialDatePicker | 21 | import com.google.android.material.datepicker.MaterialDatePicker |
| 18 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 19 | import com.google.android.material.slider.Slider | ||
| 20 | import com.google.android.material.timepicker.MaterialTimePicker | 22 | import com.google.android.material.timepicker.MaterialTimePicker |
| 21 | import com.google.android.material.timepicker.TimeFormat | 23 | import com.google.android.material.timepicker.TimeFormat |
| 24 | import kotlinx.coroutines.launch | ||
| 22 | import org.yuzu.yuzu_emu.R | 25 | import org.yuzu.yuzu_emu.R |
| 23 | import org.yuzu.yuzu_emu.databinding.DialogSliderBinding | 26 | import org.yuzu.yuzu_emu.SettingsNavigationDirections |
| 24 | import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding | 27 | import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding |
| 25 | import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding | 28 | import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding |
| 26 | import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding | 29 | import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding |
| 27 | import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting | ||
| 28 | import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting | ||
| 29 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting | ||
| 30 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | ||
| 31 | import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting | ||
| 32 | import org.yuzu.yuzu_emu.features.settings.model.FloatSetting | ||
| 33 | import org.yuzu.yuzu_emu.features.settings.model.view.* | 30 | import org.yuzu.yuzu_emu.features.settings.model.view.* |
| 34 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* | 31 | import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* |
| 32 | import org.yuzu.yuzu_emu.fragments.SettingsDialogFragment | ||
| 33 | import org.yuzu.yuzu_emu.model.SettingsViewModel | ||
| 35 | 34 | ||
| 36 | class SettingsAdapter( | 35 | class SettingsAdapter( |
| 37 | private val fragmentView: SettingsFragmentView, | 36 | private val fragment: Fragment, |
| 38 | private val context: Context | 37 | private val context: Context |
| 39 | ) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener { | 38 | ) : ListAdapter<SettingsItem, SettingViewHolder>( |
| 40 | private var settings: ArrayList<SettingsItem>? = null | 39 | AsyncDifferConfig.Builder(DiffCallback()).build() |
| 41 | private var clickedItem: SettingsItem? = null | 40 | ) { |
| 42 | private var clickedPosition: Int | 41 | private val settingsViewModel: SettingsViewModel |
| 43 | private var dialog: AlertDialog? = null | 42 | get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java] |
| 44 | private var sliderProgress = 0 | ||
| 45 | private var textSliderValue: TextView? = null | ||
| 46 | |||
| 47 | private var defaultCancelListener = | ||
| 48 | DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() } | ||
| 49 | 43 | ||
| 50 | init { | 44 | init { |
| 51 | clickedPosition = -1 | 45 | fragment.viewLifecycleOwner.lifecycleScope.launch { |
| 46 | fragment.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
| 47 | settingsViewModel.adapterItemChanged.collect { | ||
| 48 | if (it != -1) { | ||
| 49 | notifyItemChanged(it) | ||
| 50 | settingsViewModel.setAdapterItemChanged(-1) | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { | 57 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { |
| @@ -90,67 +93,41 @@ class SettingsAdapter( | |||
| 90 | } | 93 | } |
| 91 | 94 | ||
| 92 | override fun onBindViewHolder(holder: SettingViewHolder, position: Int) { | 95 | override fun onBindViewHolder(holder: SettingViewHolder, position: Int) { |
| 93 | holder.bind(getItem(position)) | 96 | holder.bind(currentList[position]) |
| 94 | } | 97 | } |
| 95 | 98 | ||
| 96 | private fun getItem(position: Int): SettingsItem { | 99 | override fun getItemCount(): Int = currentList.size |
| 97 | return settings!![position] | ||
| 98 | } | ||
| 99 | |||
| 100 | override fun getItemCount(): Int { | ||
| 101 | return if (settings != null) { | ||
| 102 | settings!!.size | ||
| 103 | } else { | ||
| 104 | 0 | ||
| 105 | } | ||
| 106 | } | ||
| 107 | 100 | ||
| 108 | override fun getItemViewType(position: Int): Int { | 101 | override fun getItemViewType(position: Int): Int { |
| 109 | return getItem(position).type | 102 | return currentList[position].type |
| 110 | } | ||
| 111 | |||
| 112 | fun setSettingsList(settings: ArrayList<SettingsItem>?) { | ||
| 113 | this.settings = settings | ||
| 114 | notifyDataSetChanged() | ||
| 115 | } | ||
| 116 | |||
| 117 | fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) { | ||
| 118 | val setting = item.setChecked(checked) | ||
| 119 | fragmentView.putSetting(setting) | ||
| 120 | fragmentView.onSettingChanged() | ||
| 121 | } | 103 | } |
| 122 | 104 | ||
| 123 | private fun onSingleChoiceClick(item: SingleChoiceSetting) { | 105 | fun onBooleanClick(item: SwitchSetting, checked: Boolean) { |
| 124 | clickedItem = item | 106 | item.checked = checked |
| 125 | val value = getSelectionForSingleChoiceValue(item) | 107 | settingsViewModel.setShouldReloadSettingsList(true) |
| 126 | dialog = MaterialAlertDialogBuilder(context) | 108 | settingsViewModel.shouldSave = true |
| 127 | .setTitle(item.nameId) | ||
| 128 | .setSingleChoiceItems(item.choicesId, value, this) | ||
| 129 | .show() | ||
| 130 | } | 109 | } |
| 131 | 110 | ||
| 132 | fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { | 111 | fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { |
| 133 | clickedPosition = position | 112 | SettingsDialogFragment.newInstance( |
| 134 | onSingleChoiceClick(item) | 113 | settingsViewModel, |
| 135 | } | 114 | item, |
| 136 | 115 | SettingsItem.TYPE_SINGLE_CHOICE, | |
| 137 | private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) { | 116 | position |
| 138 | clickedItem = item | 117 | ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) |
| 139 | dialog = MaterialAlertDialogBuilder(context) | ||
| 140 | .setTitle(item.nameId) | ||
| 141 | .setSingleChoiceItems(item.choices, item.selectValueIndex, this) | ||
| 142 | .show() | ||
| 143 | } | 118 | } |
| 144 | 119 | ||
| 145 | fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) { | 120 | fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) { |
| 146 | clickedPosition = position | 121 | SettingsDialogFragment.newInstance( |
| 147 | onStringSingleChoiceClick(item) | 122 | settingsViewModel, |
| 123 | item, | ||
| 124 | SettingsItem.TYPE_STRING_SINGLE_CHOICE, | ||
| 125 | position | ||
| 126 | ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) | ||
| 148 | } | 127 | } |
| 149 | 128 | ||
| 150 | fun onDateTimeClick(item: DateTimeSetting, position: Int) { | 129 | fun onDateTimeClick(item: DateTimeSetting, position: Int) { |
| 151 | clickedItem = item | 130 | val storedTime = item.value * 1000 |
| 152 | clickedPosition = position | ||
| 153 | val storedTime = java.lang.Long.decode(item.value) * 1000 | ||
| 154 | 131 | ||
| 155 | // Helper to extract hour and minute from epoch time | 132 | // Helper to extract hour and minute from epoch time |
| 156 | val calendar: Calendar = Calendar.getInstance() | 133 | val calendar: Calendar = Calendar.getInstance() |
| @@ -158,7 +135,7 @@ class SettingsAdapter( | |||
| 158 | calendar.timeZone = TimeZone.getTimeZone("UTC") | 135 | calendar.timeZone = TimeZone.getTimeZone("UTC") |
| 159 | 136 | ||
| 160 | var timeFormat: Int = TimeFormat.CLOCK_12H | 137 | var timeFormat: Int = TimeFormat.CLOCK_12H |
| 161 | if (DateFormat.is24HourFormat(fragmentView.activityView as AppCompatActivity)) { | 138 | if (DateFormat.is24HourFormat(context)) { |
| 162 | timeFormat = TimeFormat.CLOCK_24H | 139 | timeFormat = TimeFormat.CLOCK_24H |
| 163 | } | 140 | } |
| 164 | 141 | ||
| @@ -175,7 +152,7 @@ class SettingsAdapter( | |||
| 175 | 152 | ||
| 176 | datePicker.addOnPositiveButtonClickListener { | 153 | datePicker.addOnPositiveButtonClickListener { |
| 177 | timePicker.show( | 154 | timePicker.show( |
| 178 | (fragmentView.activityView as AppCompatActivity).supportFragmentManager, | 155 | fragment.childFragmentManager, |
| 179 | "TimePicker" | 156 | "TimePicker" |
| 180 | ) | 157 | ) |
| 181 | } | 158 | } |
| @@ -183,160 +160,50 @@ class SettingsAdapter( | |||
| 183 | var epochTime: Long = datePicker.selection!! / 1000 | 160 | var epochTime: Long = datePicker.selection!! / 1000 |
| 184 | epochTime += timePicker.hour.toLong() * 60 * 60 | 161 | epochTime += timePicker.hour.toLong() * 60 * 60 |
| 185 | epochTime += timePicker.minute.toLong() * 60 | 162 | epochTime += timePicker.minute.toLong() * 60 |
| 186 | val rtcString = epochTime.toString() | 163 | if (item.value != epochTime) { |
| 187 | if (item.value != rtcString) { | 164 | settingsViewModel.shouldSave = true |
| 188 | fragmentView.onSettingChanged() | 165 | notifyItemChanged(position) |
| 166 | item.value = epochTime | ||
| 189 | } | 167 | } |
| 190 | notifyItemChanged(clickedPosition) | ||
| 191 | val setting = item.setSelectedValue(rtcString) | ||
| 192 | fragmentView.putSetting(setting) | ||
| 193 | clickedItem = null | ||
| 194 | } | 168 | } |
| 195 | datePicker.show( | 169 | datePicker.show( |
| 196 | (fragmentView.activityView as AppCompatActivity).supportFragmentManager, | 170 | fragment.childFragmentManager, |
| 197 | "DatePicker" | 171 | "DatePicker" |
| 198 | ) | 172 | ) |
| 199 | } | 173 | } |
| 200 | 174 | ||
| 201 | fun onSliderClick(item: SliderSetting, position: Int) { | 175 | fun onSliderClick(item: SliderSetting, position: Int) { |
| 202 | clickedItem = item | 176 | SettingsDialogFragment.newInstance( |
| 203 | clickedPosition = position | 177 | settingsViewModel, |
| 204 | sliderProgress = item.selectedValue | 178 | item, |
| 205 | 179 | SettingsItem.TYPE_SLIDER, | |
| 206 | val inflater = LayoutInflater.from(context) | 180 | position |
| 207 | val sliderBinding = DialogSliderBinding.inflate(inflater) | 181 | ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) |
| 208 | |||
| 209 | textSliderValue = sliderBinding.textValue | ||
| 210 | textSliderValue!!.text = String.format( | ||
| 211 | context.getString(R.string.value_with_units), | ||
| 212 | sliderProgress.toString(), | ||
| 213 | item.units | ||
| 214 | ) | ||
| 215 | |||
| 216 | sliderBinding.slider.apply { | ||
| 217 | valueFrom = item.min.toFloat() | ||
| 218 | valueTo = item.max.toFloat() | ||
| 219 | value = sliderProgress.toFloat() | ||
| 220 | addOnChangeListener { _: Slider, value: Float, _: Boolean -> | ||
| 221 | sliderProgress = value.toInt() | ||
| 222 | textSliderValue!!.text = String.format( | ||
| 223 | context.getString(R.string.value_with_units), | ||
| 224 | sliderProgress.toString(), | ||
| 225 | item.units | ||
| 226 | ) | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | dialog = MaterialAlertDialogBuilder(context) | ||
| 231 | .setTitle(item.nameId) | ||
| 232 | .setView(sliderBinding.root) | ||
| 233 | .setPositiveButton(android.R.string.ok, this) | ||
| 234 | .setNegativeButton(android.R.string.cancel, defaultCancelListener) | ||
| 235 | .show() | ||
| 236 | } | 182 | } |
| 237 | 183 | ||
| 238 | fun onSubmenuClick(item: SubmenuSetting) { | 184 | fun onSubmenuClick(item: SubmenuSetting) { |
| 239 | fragmentView.loadSubMenu(item.menuKey) | 185 | val action = SettingsNavigationDirections.actionGlobalSettingsFragment(item.menuKey, null) |
| 186 | fragment.view?.findNavController()?.navigate(action) | ||
| 240 | } | 187 | } |
| 241 | 188 | ||
| 242 | override fun onClick(dialog: DialogInterface, which: Int) { | 189 | fun onLongClick(item: SettingsItem, position: Int): Boolean { |
| 243 | when (clickedItem) { | 190 | SettingsDialogFragment.newInstance( |
| 244 | is SingleChoiceSetting -> { | 191 | settingsViewModel, |
| 245 | val scSetting = clickedItem as SingleChoiceSetting | 192 | item, |
| 246 | val value = getValueForSingleChoiceSelection(scSetting, which) | 193 | SettingsDialogFragment.TYPE_RESET_SETTING, |
| 247 | if (scSetting.selectedValue != value) { | 194 | position |
| 248 | fragmentView.onSettingChanged() | 195 | ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) |
| 249 | } | ||
| 250 | |||
| 251 | // Get the backing Setting, which may be null (if for example it was missing from the file) | ||
| 252 | val setting = scSetting.setSelectedValue(value) | ||
| 253 | fragmentView.putSetting(setting) | ||
| 254 | closeDialog() | ||
| 255 | } | ||
| 256 | |||
| 257 | is StringSingleChoiceSetting -> { | ||
| 258 | val scSetting = clickedItem as StringSingleChoiceSetting | ||
| 259 | val value = scSetting.getValueAt(which) | ||
| 260 | if (scSetting.selectedValue != value) fragmentView.onSettingChanged() | ||
| 261 | val setting = scSetting.setSelectedValue(value!!) | ||
| 262 | fragmentView.putSetting(setting) | ||
| 263 | closeDialog() | ||
| 264 | } | ||
| 265 | |||
| 266 | is SliderSetting -> { | ||
| 267 | val sliderSetting = clickedItem as SliderSetting | ||
| 268 | if (sliderSetting.selectedValue != sliderProgress) { | ||
| 269 | fragmentView.onSettingChanged() | ||
| 270 | } | ||
| 271 | if (sliderSetting.setting is FloatSetting) { | ||
| 272 | val value = sliderProgress.toFloat() | ||
| 273 | val setting = sliderSetting.setSelectedValue(value) | ||
| 274 | fragmentView.putSetting(setting) | ||
| 275 | } else { | ||
| 276 | val setting = sliderSetting.setSelectedValue(sliderProgress) | ||
| 277 | fragmentView.putSetting(setting) | ||
| 278 | } | ||
| 279 | closeDialog() | ||
| 280 | } | ||
| 281 | } | ||
| 282 | clickedItem = null | ||
| 283 | sliderProgress = -1 | ||
| 284 | } | ||
| 285 | |||
| 286 | fun onLongClick(setting: AbstractSetting, position: Int): Boolean { | ||
| 287 | MaterialAlertDialogBuilder(context) | ||
| 288 | .setMessage(R.string.reset_setting_confirmation) | ||
| 289 | .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> | ||
| 290 | when (setting) { | ||
| 291 | is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean | ||
| 292 | is AbstractFloatSetting -> setting.float = setting.defaultValue as Float | ||
| 293 | is AbstractIntSetting -> setting.int = setting.defaultValue as Int | ||
| 294 | is AbstractStringSetting -> setting.string = setting.defaultValue as String | ||
| 295 | } | ||
| 296 | notifyItemChanged(position) | ||
| 297 | fragmentView.onSettingChanged() | ||
| 298 | } | ||
| 299 | .setNegativeButton(android.R.string.cancel, null) | ||
| 300 | .show() | ||
| 301 | 196 | ||
| 302 | return true | 197 | return true |
| 303 | } | 198 | } |
| 304 | 199 | ||
| 305 | fun closeDialog() { | 200 | private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() { |
| 306 | if (dialog != null) { | 201 | override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean { |
| 307 | if (clickedPosition != -1) { | 202 | return oldItem.setting.key == newItem.setting.key |
| 308 | notifyItemChanged(clickedPosition) | ||
| 309 | clickedPosition = -1 | ||
| 310 | } | ||
| 311 | dialog!!.dismiss() | ||
| 312 | dialog = null | ||
| 313 | } | 203 | } |
| 314 | } | ||
| 315 | 204 | ||
| 316 | private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { | 205 | override fun areContentsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean { |
| 317 | val valuesId = item.valuesId | 206 | return oldItem.setting.key == newItem.setting.key |
| 318 | return if (valuesId > 0) { | ||
| 319 | val valuesArray = context.resources.getIntArray(valuesId) | ||
| 320 | valuesArray[which] | ||
| 321 | } else { | ||
| 322 | which | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { | ||
| 327 | val value = item.selectedValue | ||
| 328 | val valuesId = item.valuesId | ||
| 329 | if (valuesId > 0) { | ||
| 330 | val valuesArray = context.resources.getIntArray(valuesId) | ||
| 331 | for (index in valuesArray.indices) { | ||
| 332 | val current = valuesArray[index] | ||
| 333 | if (current == value) { | ||
| 334 | return index | ||
| 335 | } | ||
| 336 | } | ||
| 337 | } else { | ||
| 338 | return value | ||
| 339 | } | 207 | } |
| 340 | return -1 | ||
| 341 | } | 208 | } |
| 342 | } | 209 | } |
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 70a74c4dd..bc319714c 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 | |||
| @@ -3,40 +3,43 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | 4 | package org.yuzu.yuzu_emu.features.settings.ui |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 7 | import android.os.Bundle | 6 | import android.os.Bundle |
| 8 | import android.view.LayoutInflater | 7 | import android.view.LayoutInflater |
| 9 | import android.view.View | 8 | import android.view.View |
| 10 | import android.view.ViewGroup | 9 | import android.view.ViewGroup |
| 10 | import android.view.ViewGroup.MarginLayoutParams | ||
| 11 | import androidx.core.view.ViewCompat | 11 | import androidx.core.view.ViewCompat |
| 12 | import androidx.core.view.WindowInsetsCompat | 12 | import androidx.core.view.WindowInsetsCompat |
| 13 | import androidx.core.view.updatePadding | 13 | import androidx.core.view.updatePadding |
| 14 | import androidx.fragment.app.Fragment | 14 | import androidx.fragment.app.Fragment |
| 15 | import androidx.fragment.app.activityViewModels | ||
| 16 | import androidx.navigation.findNavController | ||
| 17 | import androidx.navigation.fragment.navArgs | ||
| 15 | import androidx.recyclerview.widget.LinearLayoutManager | 18 | import androidx.recyclerview.widget.LinearLayoutManager |
| 16 | import com.google.android.material.divider.MaterialDividerItemDecoration | 19 | import com.google.android.material.divider.MaterialDividerItemDecoration |
| 20 | import com.google.android.material.transition.MaterialSharedAxis | ||
| 21 | import org.yuzu.yuzu_emu.R | ||
| 17 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding | 22 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding |
| 18 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | 23 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 19 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | 24 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| 20 | 25 | ||
| 21 | class SettingsFragment : Fragment(), SettingsFragmentView { | 26 | class SettingsFragment : Fragment() { |
| 22 | override var activityView: SettingsActivityView? = null | 27 | private lateinit var presenter: SettingsFragmentPresenter |
| 23 | |||
| 24 | private val fragmentPresenter = SettingsFragmentPresenter(this) | ||
| 25 | private var settingsAdapter: SettingsAdapter? = null | 28 | private var settingsAdapter: SettingsAdapter? = null |
| 26 | 29 | ||
| 27 | private var _binding: FragmentSettingsBinding? = null | 30 | private var _binding: FragmentSettingsBinding? = null |
| 28 | private val binding get() = _binding!! | 31 | private val binding get() = _binding!! |
| 29 | 32 | ||
| 30 | override fun onAttach(context: Context) { | 33 | private val args by navArgs<SettingsFragmentArgs>() |
| 31 | super.onAttach(context) | 34 | |
| 32 | activityView = requireActivity() as SettingsActivityView | 35 | private val settingsViewModel: SettingsViewModel by activityViewModels() |
| 33 | } | ||
| 34 | 36 | ||
| 35 | override fun onCreate(savedInstanceState: Bundle?) { | 37 | override fun onCreate(savedInstanceState: Bundle?) { |
| 36 | super.onCreate(savedInstanceState) | 38 | super.onCreate(savedInstanceState) |
| 37 | val menuTag = requireArguments().getString(ARGUMENT_MENU_TAG) | 39 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) |
| 38 | val gameId = requireArguments().getString(ARGUMENT_GAME_ID) | 40 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) |
| 39 | fragmentPresenter.onCreate(menuTag!!, gameId!!) | 41 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) |
| 42 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 40 | } | 43 | } |
| 41 | 44 | ||
| 42 | override fun onCreateView( | 45 | override fun onCreateView( |
| @@ -49,7 +52,14 @@ class SettingsFragment : Fragment(), SettingsFragmentView { | |||
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | 54 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
| 52 | settingsAdapter = SettingsAdapter(this, requireActivity()) | 55 | settingsAdapter = SettingsAdapter(this, requireContext()) |
| 56 | presenter = SettingsFragmentPresenter( | ||
| 57 | settingsViewModel, | ||
| 58 | settingsAdapter!!, | ||
| 59 | args.menuTag, | ||
| 60 | args.game?.gameId ?: "" | ||
| 61 | ) | ||
| 62 | |||
| 53 | val dividerDecoration = MaterialDividerItemDecoration( | 63 | val dividerDecoration = MaterialDividerItemDecoration( |
| 54 | requireContext(), | 64 | requireContext(), |
| 55 | LinearLayoutManager.VERTICAL | 65 | LinearLayoutManager.VERTICAL |
| @@ -57,71 +67,86 @@ class SettingsFragment : Fragment(), SettingsFragmentView { | |||
| 57 | dividerDecoration.isLastItemDecorated = false | 67 | dividerDecoration.isLastItemDecorated = false |
| 58 | binding.listSettings.apply { | 68 | binding.listSettings.apply { |
| 59 | adapter = settingsAdapter | 69 | adapter = settingsAdapter |
| 60 | layoutManager = LinearLayoutManager(activity) | 70 | layoutManager = LinearLayoutManager(requireContext()) |
| 61 | addItemDecoration(dividerDecoration) | 71 | addItemDecoration(dividerDecoration) |
| 62 | } | 72 | } |
| 63 | fragmentPresenter.onViewCreated() | ||
| 64 | 73 | ||
| 65 | setInsets() | 74 | binding.toolbarSettings.setNavigationOnClickListener { |
| 66 | } | 75 | settingsViewModel.setShouldNavigateBack(true) |
| 76 | } | ||
| 67 | 77 | ||
| 68 | override fun onDetach() { | 78 | settingsViewModel.toolbarTitle.observe(viewLifecycleOwner) { |
| 69 | super.onDetach() | 79 | if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it |
| 70 | activityView = null | ||
| 71 | if (settingsAdapter != null) { | ||
| 72 | settingsAdapter!!.closeDialog() | ||
| 73 | } | 80 | } |
| 74 | } | ||
| 75 | 81 | ||
| 76 | override fun showSettingsList(settingsList: ArrayList<SettingsItem>) { | 82 | settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { |
| 77 | settingsAdapter!!.setSettingsList(settingsList) | 83 | if (it) { |
| 78 | } | 84 | settingsViewModel.setShouldReloadSettingsList(false) |
| 85 | presenter.loadSettingsList() | ||
| 86 | } | ||
| 87 | } | ||
| 79 | 88 | ||
| 80 | override fun loadSettingsList() { | 89 | settingsViewModel.isUsingSearch.observe(viewLifecycleOwner) { |
| 81 | fragmentPresenter.loadSettingsList() | 90 | if (it) { |
| 82 | } | 91 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) |
| 92 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 93 | } else { | ||
| 94 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 95 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 96 | } | ||
| 97 | } | ||
| 83 | 98 | ||
| 84 | override fun loadSubMenu(menuKey: String) { | 99 | if (args.menuTag == SettingsFile.FILE_NAME_CONFIG) { |
| 85 | activityView!!.showSettingsFragment( | 100 | binding.toolbarSettings.inflateMenu(R.menu.menu_settings) |
| 86 | menuKey, | 101 | binding.toolbarSettings.setOnMenuItemClickListener { |
| 87 | true, | 102 | when (it.itemId) { |
| 88 | requireArguments().getString(ARGUMENT_GAME_ID)!! | 103 | R.id.action_search -> { |
| 89 | ) | 104 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) |
| 90 | } | 105 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) |
| 106 | view.findNavController() | ||
| 107 | .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) | ||
| 108 | true | ||
| 109 | } | ||
| 110 | |||
| 111 | else -> false | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 91 | 115 | ||
| 92 | override fun showToastMessage(message: String?, is_long: Boolean) { | 116 | presenter.onViewCreated() |
| 93 | activityView!!.showToastMessage(message!!, is_long) | ||
| 94 | } | ||
| 95 | 117 | ||
| 96 | override fun putSetting(setting: AbstractSetting) { | 118 | setInsets() |
| 97 | fragmentPresenter.putSetting(setting) | ||
| 98 | } | 119 | } |
| 99 | 120 | ||
| 100 | override fun onSettingChanged() { | 121 | override fun onResume() { |
| 101 | activityView!!.onSettingChanged() | 122 | super.onResume() |
| 123 | settingsViewModel.setIsUsingSearch(false) | ||
| 102 | } | 124 | } |
| 103 | 125 | ||
| 104 | private fun setInsets() { | 126 | private fun setInsets() { |
| 105 | ViewCompat.setOnApplyWindowInsetsListener( | 127 | ViewCompat.setOnApplyWindowInsetsListener( |
| 106 | binding.listSettings | 128 | binding.root |
| 107 | ) { view: View, windowInsets: WindowInsetsCompat -> | 129 | ) { _: View, windowInsets: WindowInsetsCompat -> |
| 108 | val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) | 130 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) |
| 109 | view.updatePadding(bottom = insets.bottom) | 131 | val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) |
| 132 | |||
| 133 | val leftInsets = barInsets.left + cutoutInsets.left | ||
| 134 | val rightInsets = barInsets.right + cutoutInsets.right | ||
| 135 | |||
| 136 | val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) | ||
| 137 | val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams | ||
| 138 | mlpSettingsList.leftMargin = sideMargin + leftInsets | ||
| 139 | mlpSettingsList.rightMargin = sideMargin + rightInsets | ||
| 140 | binding.listSettings.layoutParams = mlpSettingsList | ||
| 141 | binding.listSettings.updatePadding( | ||
| 142 | bottom = barInsets.bottom | ||
| 143 | ) | ||
| 144 | |||
| 145 | val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams | ||
| 146 | mlpAppBar.leftMargin = leftInsets | ||
| 147 | mlpAppBar.rightMargin = rightInsets | ||
| 148 | binding.appbarSettings.layoutParams = mlpAppBar | ||
| 110 | windowInsets | 149 | windowInsets |
| 111 | } | 150 | } |
| 112 | } | 151 | } |
| 113 | |||
| 114 | companion object { | ||
| 115 | private const val ARGUMENT_MENU_TAG = "menu_tag" | ||
| 116 | private const val ARGUMENT_GAME_ID = "game_id" | ||
| 117 | |||
| 118 | fun newInstance(menuTag: String?, gameId: String?): Fragment { | ||
| 119 | val fragment = SettingsFragment() | ||
| 120 | val arguments = Bundle() | ||
| 121 | arguments.putString(ARGUMENT_MENU_TAG, menuTag) | ||
| 122 | arguments.putString(ARGUMENT_GAME_ID, gameId) | ||
| 123 | fragment.arguments = arguments | ||
| 124 | return fragment | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | 152 | } |
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 59c1d9d54..22a529b1b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt | |||
| @@ -3,63 +3,66 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | 4 | package org.yuzu.yuzu_emu.features.settings.ui |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 6 | import android.content.SharedPreferences | 7 | import android.content.SharedPreferences |
| 7 | import android.os.Build | 8 | import android.os.Build |
| 8 | import android.text.TextUtils | 9 | import android.text.TextUtils |
| 10 | import android.widget.Toast | ||
| 9 | import androidx.preference.PreferenceManager | 11 | import androidx.preference.PreferenceManager |
| 10 | import org.yuzu.yuzu_emu.R | 12 | import org.yuzu.yuzu_emu.R |
| 11 | import org.yuzu.yuzu_emu.YuzuApplication | 13 | import org.yuzu.yuzu_emu.YuzuApplication |
| 12 | import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting | 14 | import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting |
| 13 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting | 15 | import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting |
| 14 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | ||
| 15 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | 16 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting |
| 17 | import org.yuzu.yuzu_emu.features.settings.model.ByteSetting | ||
| 16 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 18 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 19 | import org.yuzu.yuzu_emu.features.settings.model.LongSetting | ||
| 17 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 20 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 18 | import org.yuzu.yuzu_emu.features.settings.model.StringSetting | 21 | import org.yuzu.yuzu_emu.features.settings.model.ShortSetting |
| 19 | import org.yuzu.yuzu_emu.features.settings.model.view.* | 22 | import org.yuzu.yuzu_emu.features.settings.model.view.* |
| 20 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 23 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 21 | import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment | 24 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| 22 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 25 | import org.yuzu.yuzu_emu.utils.NativeConfig |
| 23 | 26 | ||
| 24 | class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) { | 27 | class SettingsFragmentPresenter( |
| 25 | private var menuTag: String? = null | 28 | private val settingsViewModel: SettingsViewModel, |
| 26 | private lateinit var gameId: String | 29 | private val adapter: SettingsAdapter, |
| 27 | private var settingsList: ArrayList<SettingsItem>? = null | 30 | private var menuTag: String, |
| 31 | private var gameId: String | ||
| 32 | ) { | ||
| 33 | private var settingsList = ArrayList<SettingsItem>() | ||
| 28 | 34 | ||
| 29 | private val settingsActivity get() = fragmentView.activityView as SettingsActivity | 35 | private val preferences: SharedPreferences |
| 30 | private val settings get() = fragmentView.activityView!!.settings | 36 | get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 31 | 37 | ||
| 32 | private lateinit var preferences: SharedPreferences | 38 | private val context: Context get() = YuzuApplication.appContext |
| 33 | 39 | ||
| 34 | fun onCreate(menuTag: String, gameId: String) { | 40 | // Extension for populating settings list based on paired settings |
| 35 | this.gameId = gameId | 41 | fun ArrayList<SettingsItem>.add(key: String) { |
| 36 | this.menuTag = menuTag | 42 | val item = SettingsItem.settingsItems[key]!! |
| 43 | val pairedSettingKey = item.setting.pairedSettingKey | ||
| 44 | if (pairedSettingKey.isNotEmpty()) { | ||
| 45 | val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false) | ||
| 46 | if (!pairedSettingValue) return | ||
| 47 | } | ||
| 48 | add(item) | ||
| 37 | } | 49 | } |
| 38 | 50 | ||
| 39 | fun onViewCreated() { | 51 | fun onViewCreated() { |
| 40 | preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||
| 41 | loadSettingsList() | 52 | loadSettingsList() |
| 42 | } | 53 | } |
| 43 | 54 | ||
| 44 | fun putSetting(setting: AbstractSetting) { | ||
| 45 | if (setting.section == null || setting.key == null) { | ||
| 46 | return | ||
| 47 | } | ||
| 48 | |||
| 49 | val section = settings.getSection(setting.section!!)!! | ||
| 50 | if (section.getSetting(setting.key!!) == null) { | ||
| 51 | section.putSetting(setting) | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | fun loadSettingsList() { | 55 | fun loadSettingsList() { |
| 56 | if (!TextUtils.isEmpty(gameId)) { | 56 | if (!TextUtils.isEmpty(gameId)) { |
| 57 | settingsActivity.setToolbarTitle("Game Settings: $gameId") | 57 | settingsViewModel.setToolbarTitle( |
| 58 | context.getString( | ||
| 59 | R.string.advanced_settings_game, | ||
| 60 | gameId | ||
| 61 | ) | ||
| 62 | ) | ||
| 58 | } | 63 | } |
| 64 | |||
| 59 | val sl = ArrayList<SettingsItem>() | 65 | val sl = ArrayList<SettingsItem>() |
| 60 | if (menuTag == null) { | ||
| 61 | return | ||
| 62 | } | ||
| 63 | when (menuTag) { | 66 | when (menuTag) { |
| 64 | SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl) | 67 | SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl) |
| 65 | Settings.SECTION_GENERAL -> addGeneralSettings(sl) | 68 | Settings.SECTION_GENERAL -> addGeneralSettings(sl) |
| @@ -69,335 +72,104 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 69 | Settings.SECTION_THEME -> addThemeSettings(sl) | 72 | Settings.SECTION_THEME -> addThemeSettings(sl) |
| 70 | Settings.SECTION_DEBUG -> addDebugSettings(sl) | 73 | Settings.SECTION_DEBUG -> addDebugSettings(sl) |
| 71 | else -> { | 74 | else -> { |
| 72 | fragmentView.showToastMessage("Unimplemented menu", false) | 75 | val context = YuzuApplication.appContext |
| 76 | Toast.makeText( | ||
| 77 | context, | ||
| 78 | context.getString(R.string.unimplemented_menu), | ||
| 79 | Toast.LENGTH_SHORT | ||
| 80 | ).show() | ||
| 73 | return | 81 | return |
| 74 | } | 82 | } |
| 75 | } | 83 | } |
| 76 | settingsList = sl | 84 | settingsList = sl |
| 77 | fragmentView.showSettingsList(settingsList!!) | 85 | adapter.submitList(settingsList) |
| 78 | } | 86 | } |
| 79 | 87 | ||
| 80 | private fun addConfigSettings(sl: ArrayList<SettingsItem>) { | 88 | private fun addConfigSettings(sl: ArrayList<SettingsItem>) { |
| 81 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.advanced_settings)) | 89 | settingsViewModel.setToolbarTitle(context.getString(R.string.advanced_settings)) |
| 82 | sl.apply { | 90 | sl.apply { |
| 83 | add( | 91 | add(SubmenuSetting(R.string.preferences_general, 0, Settings.SECTION_GENERAL)) |
| 84 | SubmenuSetting( | 92 | add(SubmenuSetting(R.string.preferences_system, 0, Settings.SECTION_SYSTEM)) |
| 85 | R.string.preferences_general, | 93 | add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.SECTION_RENDERER)) |
| 86 | 0, | 94 | add(SubmenuSetting(R.string.preferences_audio, 0, Settings.SECTION_AUDIO)) |
| 87 | Settings.SECTION_GENERAL | 95 | add(SubmenuSetting(R.string.preferences_debug, 0, Settings.SECTION_DEBUG)) |
| 88 | ) | 96 | add( |
| 89 | ) | 97 | RunnableSetting(R.string.reset_to_default, 0, false) { |
| 90 | add( | 98 | settingsViewModel.setShouldShowResetSettingsDialog(true) |
| 91 | SubmenuSetting( | ||
| 92 | R.string.preferences_system, | ||
| 93 | 0, | ||
| 94 | Settings.SECTION_SYSTEM | ||
| 95 | ) | ||
| 96 | ) | ||
| 97 | add( | ||
| 98 | SubmenuSetting( | ||
| 99 | R.string.preferences_graphics, | ||
| 100 | 0, | ||
| 101 | Settings.SECTION_RENDERER | ||
| 102 | ) | ||
| 103 | ) | ||
| 104 | add( | ||
| 105 | SubmenuSetting( | ||
| 106 | R.string.preferences_audio, | ||
| 107 | 0, | ||
| 108 | Settings.SECTION_AUDIO | ||
| 109 | ) | ||
| 110 | ) | ||
| 111 | add( | ||
| 112 | SubmenuSetting( | ||
| 113 | R.string.preferences_debug, | ||
| 114 | 0, | ||
| 115 | Settings.SECTION_DEBUG | ||
| 116 | ) | ||
| 117 | ) | ||
| 118 | add( | ||
| 119 | RunnableSetting( | ||
| 120 | R.string.reset_to_default, | ||
| 121 | 0, | ||
| 122 | false | ||
| 123 | ) { | ||
| 124 | ResetSettingsDialogFragment().show( | ||
| 125 | settingsActivity.supportFragmentManager, | ||
| 126 | ResetSettingsDialogFragment.TAG | ||
| 127 | ) | ||
| 128 | } | 99 | } |
| 129 | ) | 100 | ) |
| 130 | } | 101 | } |
| 131 | } | 102 | } |
| 132 | 103 | ||
| 133 | private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { | 104 | private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { |
| 134 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_general)) | 105 | settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_general)) |
| 135 | sl.apply { | 106 | sl.apply { |
| 136 | add( | 107 | add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) |
| 137 | SwitchSetting( | 108 | add(ShortSetting.RENDERER_SPEED_LIMIT.key) |
| 138 | IntSetting.RENDERER_USE_SPEED_LIMIT, | 109 | add(IntSetting.CPU_ACCURACY.key) |
| 139 | R.string.frame_limit_enable, | 110 | add(BooleanSetting.PICTURE_IN_PICTURE.key) |
| 140 | R.string.frame_limit_enable_description, | ||
| 141 | IntSetting.RENDERER_USE_SPEED_LIMIT.key, | ||
| 142 | IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue | ||
| 143 | ) | ||
| 144 | ) | ||
| 145 | add( | ||
| 146 | SliderSetting( | ||
| 147 | IntSetting.RENDERER_SPEED_LIMIT, | ||
| 148 | R.string.frame_limit_slider, | ||
| 149 | R.string.frame_limit_slider_description, | ||
| 150 | 1, | ||
| 151 | 200, | ||
| 152 | "%", | ||
| 153 | IntSetting.RENDERER_SPEED_LIMIT.key, | ||
| 154 | IntSetting.RENDERER_SPEED_LIMIT.defaultValue | ||
| 155 | ) | ||
| 156 | ) | ||
| 157 | add( | ||
| 158 | SingleChoiceSetting( | ||
| 159 | IntSetting.CPU_ACCURACY, | ||
| 160 | R.string.cpu_accuracy, | ||
| 161 | 0, | ||
| 162 | R.array.cpuAccuracyNames, | ||
| 163 | R.array.cpuAccuracyValues, | ||
| 164 | IntSetting.CPU_ACCURACY.key, | ||
| 165 | IntSetting.CPU_ACCURACY.defaultValue | ||
| 166 | ) | ||
| 167 | ) | ||
| 168 | add( | ||
| 169 | SwitchSetting( | ||
| 170 | BooleanSetting.PICTURE_IN_PICTURE, | ||
| 171 | R.string.picture_in_picture, | ||
| 172 | R.string.picture_in_picture_description, | ||
| 173 | BooleanSetting.PICTURE_IN_PICTURE.key, | ||
| 174 | BooleanSetting.PICTURE_IN_PICTURE.defaultValue | ||
| 175 | ) | ||
| 176 | ) | ||
| 177 | } | 111 | } |
| 178 | } | 112 | } |
| 179 | 113 | ||
| 180 | private fun addSystemSettings(sl: ArrayList<SettingsItem>) { | 114 | private fun addSystemSettings(sl: ArrayList<SettingsItem>) { |
| 181 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_system)) | 115 | settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_system)) |
| 182 | sl.apply { | 116 | sl.apply { |
| 183 | add( | 117 | add(BooleanSetting.USE_DOCKED_MODE.key) |
| 184 | SwitchSetting( | 118 | add(IntSetting.REGION_INDEX.key) |
| 185 | IntSetting.USE_DOCKED_MODE, | 119 | add(IntSetting.LANGUAGE_INDEX.key) |
| 186 | R.string.use_docked_mode, | 120 | add(BooleanSetting.USE_CUSTOM_RTC.key) |
| 187 | R.string.use_docked_mode_description, | 121 | add(LongSetting.CUSTOM_RTC.key) |
| 188 | IntSetting.USE_DOCKED_MODE.key, | ||
| 189 | IntSetting.USE_DOCKED_MODE.defaultValue | ||
| 190 | ) | ||
| 191 | ) | ||
| 192 | add( | ||
| 193 | SingleChoiceSetting( | ||
| 194 | IntSetting.REGION_INDEX, | ||
| 195 | R.string.emulated_region, | ||
| 196 | 0, | ||
| 197 | R.array.regionNames, | ||
| 198 | R.array.regionValues, | ||
| 199 | IntSetting.REGION_INDEX.key, | ||
| 200 | IntSetting.REGION_INDEX.defaultValue | ||
| 201 | ) | ||
| 202 | ) | ||
| 203 | add( | ||
| 204 | SingleChoiceSetting( | ||
| 205 | IntSetting.LANGUAGE_INDEX, | ||
| 206 | R.string.emulated_language, | ||
| 207 | 0, | ||
| 208 | R.array.languageNames, | ||
| 209 | R.array.languageValues, | ||
| 210 | IntSetting.LANGUAGE_INDEX.key, | ||
| 211 | IntSetting.LANGUAGE_INDEX.defaultValue | ||
| 212 | ) | ||
| 213 | ) | ||
| 214 | add( | ||
| 215 | SwitchSetting( | ||
| 216 | BooleanSetting.USE_CUSTOM_RTC, | ||
| 217 | R.string.use_custom_rtc, | ||
| 218 | R.string.use_custom_rtc_description, | ||
| 219 | BooleanSetting.USE_CUSTOM_RTC.key, | ||
| 220 | BooleanSetting.USE_CUSTOM_RTC.defaultValue | ||
| 221 | ) | ||
| 222 | ) | ||
| 223 | add( | ||
| 224 | DateTimeSetting( | ||
| 225 | StringSetting.CUSTOM_RTC, | ||
| 226 | R.string.set_custom_rtc, | ||
| 227 | 0, | ||
| 228 | StringSetting.CUSTOM_RTC.key, | ||
| 229 | StringSetting.CUSTOM_RTC.defaultValue | ||
| 230 | ) | ||
| 231 | ) | ||
| 232 | } | 122 | } |
| 233 | } | 123 | } |
| 234 | 124 | ||
| 235 | private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) { | 125 | private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) { |
| 236 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics)) | 126 | settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_graphics)) |
| 237 | sl.apply { | 127 | sl.apply { |
| 238 | add( | 128 | add(IntSetting.RENDERER_ACCURACY.key) |
| 239 | SingleChoiceSetting( | 129 | add(IntSetting.RENDERER_RESOLUTION.key) |
| 240 | IntSetting.RENDERER_ACCURACY, | 130 | add(IntSetting.RENDERER_VSYNC.key) |
| 241 | R.string.renderer_accuracy, | 131 | add(IntSetting.RENDERER_SCALING_FILTER.key) |
| 242 | 0, | 132 | add(IntSetting.RENDERER_ANTI_ALIASING.key) |
| 243 | R.array.rendererAccuracyNames, | 133 | add(IntSetting.RENDERER_SCREEN_LAYOUT.key) |
| 244 | R.array.rendererAccuracyValues, | 134 | add(IntSetting.RENDERER_ASPECT_RATIO.key) |
| 245 | IntSetting.RENDERER_ACCURACY.key, | 135 | add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) |
| 246 | IntSetting.RENDERER_ACCURACY.defaultValue | 136 | add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) |
| 247 | ) | 137 | add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) |
| 248 | ) | 138 | add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) |
| 249 | add( | ||
| 250 | SingleChoiceSetting( | ||
| 251 | IntSetting.RENDERER_RESOLUTION, | ||
| 252 | R.string.renderer_resolution, | ||
| 253 | 0, | ||
| 254 | R.array.rendererResolutionNames, | ||
| 255 | R.array.rendererResolutionValues, | ||
| 256 | IntSetting.RENDERER_RESOLUTION.key, | ||
| 257 | IntSetting.RENDERER_RESOLUTION.defaultValue | ||
| 258 | ) | ||
| 259 | ) | ||
| 260 | add( | ||
| 261 | SingleChoiceSetting( | ||
| 262 | IntSetting.RENDERER_VSYNC, | ||
| 263 | R.string.renderer_vsync, | ||
| 264 | 0, | ||
| 265 | R.array.rendererVSyncNames, | ||
| 266 | R.array.rendererVSyncValues, | ||
| 267 | IntSetting.RENDERER_VSYNC.key, | ||
| 268 | IntSetting.RENDERER_VSYNC.defaultValue | ||
| 269 | ) | ||
| 270 | ) | ||
| 271 | add( | ||
| 272 | SingleChoiceSetting( | ||
| 273 | IntSetting.RENDERER_SCALING_FILTER, | ||
| 274 | R.string.renderer_scaling_filter, | ||
| 275 | 0, | ||
| 276 | R.array.rendererScalingFilterNames, | ||
| 277 | R.array.rendererScalingFilterValues, | ||
| 278 | IntSetting.RENDERER_SCALING_FILTER.key, | ||
| 279 | IntSetting.RENDERER_SCALING_FILTER.defaultValue | ||
| 280 | ) | ||
| 281 | ) | ||
| 282 | add( | ||
| 283 | SingleChoiceSetting( | ||
| 284 | IntSetting.RENDERER_ANTI_ALIASING, | ||
| 285 | R.string.renderer_anti_aliasing, | ||
| 286 | 0, | ||
| 287 | R.array.rendererAntiAliasingNames, | ||
| 288 | R.array.rendererAntiAliasingValues, | ||
| 289 | IntSetting.RENDERER_ANTI_ALIASING.key, | ||
| 290 | IntSetting.RENDERER_ANTI_ALIASING.defaultValue | ||
| 291 | ) | ||
| 292 | ) | ||
| 293 | add( | ||
| 294 | SingleChoiceSetting( | ||
| 295 | IntSetting.RENDERER_SCREEN_LAYOUT, | ||
| 296 | R.string.renderer_screen_layout, | ||
| 297 | 0, | ||
| 298 | R.array.rendererScreenLayoutNames, | ||
| 299 | R.array.rendererScreenLayoutValues, | ||
| 300 | IntSetting.RENDERER_SCREEN_LAYOUT.key, | ||
| 301 | IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue | ||
| 302 | ) | ||
| 303 | ) | ||
| 304 | add( | ||
| 305 | SingleChoiceSetting( | ||
| 306 | IntSetting.RENDERER_ASPECT_RATIO, | ||
| 307 | R.string.renderer_aspect_ratio, | ||
| 308 | 0, | ||
| 309 | R.array.rendererAspectRatioNames, | ||
| 310 | R.array.rendererAspectRatioValues, | ||
| 311 | IntSetting.RENDERER_ASPECT_RATIO.key, | ||
| 312 | IntSetting.RENDERER_ASPECT_RATIO.defaultValue | ||
| 313 | ) | ||
| 314 | ) | ||
| 315 | add( | ||
| 316 | SwitchSetting( | ||
| 317 | IntSetting.RENDERER_USE_DISK_SHADER_CACHE, | ||
| 318 | R.string.use_disk_shader_cache, | ||
| 319 | R.string.use_disk_shader_cache_description, | ||
| 320 | IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key, | ||
| 321 | IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue | ||
| 322 | ) | ||
| 323 | ) | ||
| 324 | add( | ||
| 325 | SwitchSetting( | ||
| 326 | IntSetting.RENDERER_FORCE_MAX_CLOCK, | ||
| 327 | R.string.renderer_force_max_clock, | ||
| 328 | R.string.renderer_force_max_clock_description, | ||
| 329 | IntSetting.RENDERER_FORCE_MAX_CLOCK.key, | ||
| 330 | IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue | ||
| 331 | ) | ||
| 332 | ) | ||
| 333 | add( | ||
| 334 | SwitchSetting( | ||
| 335 | IntSetting.RENDERER_ASYNCHRONOUS_SHADERS, | ||
| 336 | R.string.renderer_asynchronous_shaders, | ||
| 337 | R.string.renderer_asynchronous_shaders_description, | ||
| 338 | IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key, | ||
| 339 | IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue | ||
| 340 | ) | ||
| 341 | ) | ||
| 342 | add( | ||
| 343 | SwitchSetting( | ||
| 344 | IntSetting.RENDERER_REACTIVE_FLUSHING, | ||
| 345 | R.string.renderer_reactive_flushing, | ||
| 346 | R.string.renderer_reactive_flushing_description, | ||
| 347 | IntSetting.RENDERER_REACTIVE_FLUSHING.key, | ||
| 348 | IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue | ||
| 349 | ) | ||
| 350 | ) | ||
| 351 | } | 139 | } |
| 352 | } | 140 | } |
| 353 | 141 | ||
| 354 | private fun addAudioSettings(sl: ArrayList<SettingsItem>) { | 142 | private fun addAudioSettings(sl: ArrayList<SettingsItem>) { |
| 355 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) | 143 | settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_audio)) |
| 356 | sl.apply { | 144 | sl.apply { |
| 357 | add( | 145 | add(IntSetting.AUDIO_OUTPUT_ENGINE.key) |
| 358 | StringSingleChoiceSetting( | 146 | add(ByteSetting.AUDIO_VOLUME.key) |
| 359 | StringSetting.AUDIO_OUTPUT_ENGINE, | ||
| 360 | R.string.audio_output_engine, | ||
| 361 | 0, | ||
| 362 | settingsActivity.resources.getStringArray(R.array.outputEngineEntries), | ||
| 363 | settingsActivity.resources.getStringArray(R.array.outputEngineValues), | ||
| 364 | StringSetting.AUDIO_OUTPUT_ENGINE.key, | ||
| 365 | StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue | ||
| 366 | ) | ||
| 367 | ) | ||
| 368 | add( | ||
| 369 | SliderSetting( | ||
| 370 | IntSetting.AUDIO_VOLUME, | ||
| 371 | R.string.audio_volume, | ||
| 372 | R.string.audio_volume_description, | ||
| 373 | 0, | ||
| 374 | 100, | ||
| 375 | "%", | ||
| 376 | IntSetting.AUDIO_VOLUME.key, | ||
| 377 | IntSetting.AUDIO_VOLUME.defaultValue | ||
| 378 | ) | ||
| 379 | ) | ||
| 380 | } | 147 | } |
| 381 | } | 148 | } |
| 382 | 149 | ||
| 383 | private fun addThemeSettings(sl: ArrayList<SettingsItem>) { | 150 | private fun addThemeSettings(sl: ArrayList<SettingsItem>) { |
| 384 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme)) | 151 | settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_theme)) |
| 385 | sl.apply { | 152 | sl.apply { |
| 386 | val theme: AbstractIntSetting = object : AbstractIntSetting { | 153 | val theme: AbstractIntSetting = object : AbstractIntSetting { |
| 387 | override var int: Int | 154 | override val int: Int |
| 388 | get() = preferences.getInt(Settings.PREF_THEME, 0) | 155 | get() = preferences.getInt(Settings.PREF_THEME, 0) |
| 389 | set(value) { | 156 | |
| 390 | preferences.edit() | 157 | override fun setInt(value: Int) { |
| 391 | .putInt(Settings.PREF_THEME, value) | 158 | preferences.edit() |
| 392 | .apply() | 159 | .putInt(Settings.PREF_THEME, value) |
| 393 | settingsActivity.recreate() | 160 | .apply() |
| 394 | } | 161 | settingsViewModel.setShouldRecreate(true) |
| 395 | override val key: String? = null | 162 | } |
| 396 | override val section: String? = null | 163 | |
| 397 | override val isRuntimeEditable: Boolean = false | 164 | override val key: String = Settings.PREF_THEME |
| 398 | override val valueAsString: String | 165 | override val category = Settings.Category.UiGeneral |
| 399 | get() = preferences.getInt(Settings.PREF_THEME, 0).toString() | 166 | override val isRuntimeModifiable: Boolean = false |
| 400 | override val defaultValue: Any = 0 | 167 | override val defaultValue: Int = 0 |
| 168 | override fun reset() { | ||
| 169 | preferences.edit() | ||
| 170 | .putInt(Settings.PREF_THEME, defaultValue) | ||
| 171 | .apply() | ||
| 172 | } | ||
| 401 | } | 173 | } |
| 402 | 174 | ||
| 403 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | 175 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
| @@ -423,20 +195,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 423 | } | 195 | } |
| 424 | 196 | ||
| 425 | val themeMode: AbstractIntSetting = object : AbstractIntSetting { | 197 | val themeMode: AbstractIntSetting = object : AbstractIntSetting { |
| 426 | override var int: Int | 198 | override val int: Int |
| 427 | get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) | 199 | get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) |
| 428 | set(value) { | 200 | |
| 429 | preferences.edit() | 201 | override fun setInt(value: Int) { |
| 430 | .putInt(Settings.PREF_THEME_MODE, value) | 202 | preferences.edit() |
| 431 | .apply() | 203 | .putInt(Settings.PREF_THEME_MODE, value) |
| 432 | ThemeHelper.setThemeMode(settingsActivity) | 204 | .apply() |
| 433 | } | 205 | settingsViewModel.setShouldRecreate(true) |
| 434 | override val key: String? = null | 206 | } |
| 435 | override val section: String? = null | 207 | |
| 436 | override val isRuntimeEditable: Boolean = false | 208 | override val key: String = Settings.PREF_THEME_MODE |
| 437 | override val valueAsString: String | 209 | override val category = Settings.Category.UiGeneral |
| 438 | get() = preferences.getInt(Settings.PREF_THEME_MODE, -1).toString() | 210 | override val isRuntimeModifiable: Boolean = false |
| 439 | override val defaultValue: Any = -1 | 211 | override val defaultValue: Int = -1 |
| 212 | override fun reset() { | ||
| 213 | preferences.edit() | ||
| 214 | .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue) | ||
| 215 | .apply() | ||
| 216 | settingsViewModel.setShouldRecreate(true) | ||
| 217 | } | ||
| 440 | } | 218 | } |
| 441 | 219 | ||
| 442 | add( | 220 | add( |
| @@ -450,21 +228,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 450 | ) | 228 | ) |
| 451 | 229 | ||
| 452 | val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { | 230 | val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { |
| 453 | override var boolean: Boolean | 231 | override val boolean: Boolean |
| 454 | get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) | ||
| 455 | set(value) { | ||
| 456 | preferences.edit() | ||
| 457 | .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value) | ||
| 458 | .apply() | ||
| 459 | settingsActivity.recreate() | ||
| 460 | } | ||
| 461 | override val key: String? = null | ||
| 462 | override val section: String? = null | ||
| 463 | override val isRuntimeEditable: Boolean = false | ||
| 464 | override val valueAsString: String | ||
| 465 | get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) | 232 | get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) |
| 466 | .toString() | 233 | |
| 467 | override val defaultValue: Any = false | 234 | override fun setBoolean(value: Boolean) { |
| 235 | preferences.edit() | ||
| 236 | .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value) | ||
| 237 | .apply() | ||
| 238 | settingsViewModel.setShouldRecreate(true) | ||
| 239 | } | ||
| 240 | |||
| 241 | override val key: String = Settings.PREF_BLACK_BACKGROUNDS | ||
| 242 | override val category = Settings.Category.UiGeneral | ||
| 243 | override val isRuntimeModifiable: Boolean = false | ||
| 244 | override val defaultValue: Boolean = false | ||
| 245 | override fun reset() { | ||
| 246 | preferences.edit() | ||
| 247 | .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue) | ||
| 248 | .apply() | ||
| 249 | settingsViewModel.setShouldRecreate(true) | ||
| 250 | } | ||
| 468 | } | 251 | } |
| 469 | 252 | ||
| 470 | add( | 253 | add( |
| @@ -478,62 +261,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 478 | } | 261 | } |
| 479 | 262 | ||
| 480 | private fun addDebugSettings(sl: ArrayList<SettingsItem>) { | 263 | private fun addDebugSettings(sl: ArrayList<SettingsItem>) { |
| 481 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) | 264 | settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_debug)) |
| 482 | sl.apply { | 265 | sl.apply { |
| 483 | add(HeaderSetting(R.string.gpu)) | 266 | add(HeaderSetting(R.string.gpu)) |
| 484 | add( | 267 | add(IntSetting.RENDERER_BACKEND.key) |
| 485 | SingleChoiceSetting( | 268 | add(BooleanSetting.RENDERER_DEBUG.key) |
| 486 | IntSetting.RENDERER_BACKEND, | ||
| 487 | R.string.renderer_api, | ||
| 488 | 0, | ||
| 489 | R.array.rendererApiNames, | ||
| 490 | R.array.rendererApiValues, | ||
| 491 | IntSetting.RENDERER_BACKEND.key, | ||
| 492 | IntSetting.RENDERER_BACKEND.defaultValue | ||
| 493 | ) | ||
| 494 | ) | ||
| 495 | add( | ||
| 496 | SwitchSetting( | ||
| 497 | IntSetting.RENDERER_DEBUG, | ||
| 498 | R.string.renderer_debug, | ||
| 499 | R.string.renderer_debug_description, | ||
| 500 | IntSetting.RENDERER_DEBUG.key, | ||
| 501 | IntSetting.RENDERER_DEBUG.defaultValue | ||
| 502 | ) | ||
| 503 | ) | ||
| 504 | 269 | ||
| 505 | add(HeaderSetting(R.string.cpu)) | 270 | add(HeaderSetting(R.string.cpu)) |
| 506 | add( | 271 | add(BooleanSetting.CPU_DEBUG_MODE.key) |
| 507 | SwitchSetting( | 272 | add(SettingsItem.FASTMEM_COMBINED) |
| 508 | BooleanSetting.CPU_DEBUG_MODE, | ||
| 509 | R.string.cpu_debug_mode, | ||
| 510 | R.string.cpu_debug_mode_description, | ||
| 511 | BooleanSetting.CPU_DEBUG_MODE.key, | ||
| 512 | BooleanSetting.CPU_DEBUG_MODE.defaultValue | ||
| 513 | ) | ||
| 514 | ) | ||
| 515 | |||
| 516 | val fastmem = object : AbstractBooleanSetting { | ||
| 517 | override var boolean: Boolean | ||
| 518 | get() = | ||
| 519 | BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean | ||
| 520 | set(value) { | ||
| 521 | BooleanSetting.FASTMEM.boolean = value | ||
| 522 | BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value | ||
| 523 | } | ||
| 524 | override val key: String? = null | ||
| 525 | override val section: String = Settings.SECTION_CPU | ||
| 526 | override val isRuntimeEditable: Boolean = false | ||
| 527 | override val valueAsString: String = "" | ||
| 528 | override val defaultValue: Any = true | ||
| 529 | } | ||
| 530 | add( | ||
| 531 | SwitchSetting( | ||
| 532 | fastmem, | ||
| 533 | R.string.fastmem, | ||
| 534 | 0 | ||
| 535 | ) | ||
| 536 | ) | ||
| 537 | } | 273 | } |
| 538 | } | 274 | } |
| 539 | } | 275 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt deleted file mode 100644 index 1ebe35eaa..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt +++ /dev/null | |||
| @@ -1,58 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | ||
| 5 | |||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | ||
| 7 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Abstraction for a screen showing a list of settings. Instances of | ||
| 11 | * this type of view will each display a layer of the setting hierarchy. | ||
| 12 | */ | ||
| 13 | interface SettingsFragmentView { | ||
| 14 | /** | ||
| 15 | * Pass an ArrayList to the View so that it can be displayed on screen. | ||
| 16 | * | ||
| 17 | * @param settingsList The result of converting the HashMap to an ArrayList | ||
| 18 | */ | ||
| 19 | fun showSettingsList(settingsList: ArrayList<SettingsItem>) | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Instructs the Fragment to load the settings screen. | ||
| 23 | */ | ||
| 24 | fun loadSettingsList() | ||
| 25 | |||
| 26 | /** | ||
| 27 | * @return The Fragment's containing activity. | ||
| 28 | */ | ||
| 29 | val activityView: SettingsActivityView? | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Tell the Fragment to tell the containing Activity to show a new | ||
| 33 | * Fragment containing a submenu of settings. | ||
| 34 | * | ||
| 35 | * @param menuKey Identifier for the settings group that should be shown. | ||
| 36 | */ | ||
| 37 | fun loadSubMenu(menuKey: String) | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Tell the Fragment to tell the containing activity to display a toast message. | ||
| 41 | * | ||
| 42 | * @param message Text to be shown in the Toast | ||
| 43 | * @param is_long Whether this should be a long Toast or short one. | ||
| 44 | */ | ||
| 45 | fun showToastMessage(message: String?, is_long: Boolean) | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Have the fragment add a setting to the HashMap. | ||
| 49 | * | ||
| 50 | * @param setting The (possibly previously missing) new setting. | ||
| 51 | */ | ||
| 52 | fun putSetting(setting: AbstractSetting) | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Have the fragment tell the containing Activity that a setting was modified. | ||
| 56 | */ | ||
| 57 | fun onSettingChanged() | ||
| 58 | } | ||
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 79572fc06..525f013f8 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 | |||
| @@ -29,7 +29,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | binding.textSettingValue.visibility = View.VISIBLE | 31 | binding.textSettingValue.visibility = View.VISIBLE |
| 32 | val epochTime = setting.value.toLong() | 32 | val epochTime = setting.value |
| 33 | val instant = Instant.ofEpochMilli(epochTime * 1000) | 33 | val instant = Instant.ofEpochMilli(epochTime * 1000) |
| 34 | val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) | 34 | val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) |
| 35 | val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) | 35 | val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) |
| @@ -46,7 +46,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA | |||
| 46 | 46 | ||
| 47 | override fun onLongClick(clicked: View): Boolean { | 47 | override fun onLongClick(clicked: View): Boolean { |
| 48 | if (setting.isEditable) { | 48 | if (setting.isEditable) { |
| 49 | return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) | 49 | return adapter.onLongClick(setting, bindingAdapterPosition) |
| 50 | } | 50 | } |
| 51 | return false | 51 | return false |
| 52 | } | 52 | } |
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 b42d955aa..80d1b22c1 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 | |||
| @@ -35,7 +35,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti | |||
| 35 | } | 35 | } |
| 36 | } | 36 | } |
| 37 | } else if (item is StringSingleChoiceSetting) { | 37 | } else if (item is StringSingleChoiceSetting) { |
| 38 | for (i in item.values!!.indices) { | 38 | for (i in item.values.indices) { |
| 39 | if (item.values[i] == item.selectedValue) { | 39 | if (item.values[i] == item.selectedValue) { |
| 40 | binding.textSettingValue.text = item.choices[i] | 40 | binding.textSettingValue.text = item.choices[i] |
| 41 | break | 41 | break |
| @@ -66,7 +66,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti | |||
| 66 | 66 | ||
| 67 | override fun onLongClick(clicked: View): Boolean { | 67 | override fun onLongClick(clicked: View): Boolean { |
| 68 | if (setting.isEditable) { | 68 | if (setting.isEditable) { |
| 69 | return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) | 69 | return adapter.onLongClick(setting, bindingAdapterPosition) |
| 70 | } | 70 | } |
| 71 | return false | 71 | return false |
| 72 | } | 72 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt index a23b5d109..b83c90100 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt | |||
| @@ -41,7 +41,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda | |||
| 41 | 41 | ||
| 42 | override fun onLongClick(clicked: View): Boolean { | 42 | override fun onLongClick(clicked: View): Boolean { |
| 43 | if (setting.isEditable) { | 43 | if (setting.isEditable) { |
| 44 | return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) | 44 | return adapter.onLongClick(setting, bindingAdapterPosition) |
| 45 | } | 45 | } |
| 46 | return false | 46 | return false |
| 47 | } | 47 | } |
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 ef34bf5f4..57fdeaa20 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 | |||
| @@ -25,10 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter | |||
| 25 | binding.textSettingDescription.text = "" | 25 | binding.textSettingDescription.text = "" |
| 26 | binding.textSettingDescription.visibility = View.GONE | 26 | binding.textSettingDescription.visibility = View.GONE |
| 27 | } | 27 | } |
| 28 | |||
| 29 | binding.switchWidget.setOnCheckedChangeListener(null) | ||
| 30 | binding.switchWidget.isChecked = setting.checked | ||
| 28 | binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> | 31 | binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> |
| 29 | adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) | 32 | adapter.onBooleanClick(item, binding.switchWidget.isChecked) |
| 30 | } | 33 | } |
| 31 | binding.switchWidget.isChecked = setting.isChecked | ||
| 32 | 34 | ||
| 33 | setStyle(setting.isEditable, binding) | 35 | setStyle(setting.isEditable, binding) |
| 34 | } | 36 | } |
| @@ -41,7 +43,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter | |||
| 41 | 43 | ||
| 42 | override fun onLongClick(clicked: View): Boolean { | 44 | override fun onLongClick(clicked: View): Boolean { |
| 43 | if (setting.isEditable) { | 45 | if (setting.isEditable) { |
| 44 | return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) | 46 | return adapter.onLongClick(setting, bindingAdapterPosition) |
| 45 | } | 47 | } |
| 46 | return false | 48 | return false |
| 47 | } | 49 | } |
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 70a52df5d..2b04d666a 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,18 +3,15 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.utils | 4 | package org.yuzu.yuzu_emu.features.settings.utils |
| 5 | 5 | ||
| 6 | import android.widget.Toast | ||
| 6 | import java.io.* | 7 | import java.io.* |
| 7 | import java.util.* | ||
| 8 | import org.ini4j.Wini | 8 | import org.ini4j.Wini |
| 9 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 10 | import org.yuzu.yuzu_emu.R | 9 | import org.yuzu.yuzu_emu.R |
| 11 | import org.yuzu.yuzu_emu.YuzuApplication | 10 | import org.yuzu.yuzu_emu.YuzuApplication |
| 12 | import org.yuzu.yuzu_emu.features.settings.model.* | 11 | import org.yuzu.yuzu_emu.features.settings.model.* |
| 13 | import org.yuzu.yuzu_emu.features.settings.model.Settings.SettingsSectionMap | ||
| 14 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView | ||
| 15 | import org.yuzu.yuzu_emu.utils.BiMap | ||
| 16 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 12 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 17 | import org.yuzu.yuzu_emu.utils.Log | 13 | import org.yuzu.yuzu_emu.utils.Log |
| 14 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 18 | 15 | ||
| 19 | /** | 16 | /** |
| 20 | * Contains static methods for interacting with .ini files in which settings are stored. | 17 | * Contains static methods for interacting with .ini files in which settings are stored. |
| @@ -22,243 +19,41 @@ import org.yuzu.yuzu_emu.utils.Log | |||
| 22 | object SettingsFile { | 19 | object SettingsFile { |
| 23 | const val FILE_NAME_CONFIG = "config" | 20 | const val FILE_NAME_CONFIG = "config" |
| 24 | 21 | ||
| 25 | private var sectionsMap = BiMap<String?, String?>() | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves | ||
| 29 | * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it | ||
| 30 | * failed. | ||
| 31 | * | ||
| 32 | * @param ini The ini file to load the settings from | ||
| 33 | * @param isCustomGame | ||
| 34 | * @param view The current view. | ||
| 35 | * @return An Observable that emits a HashMap of the file's contents, then completes. | ||
| 36 | */ | ||
| 37 | private fun readFile( | ||
| 38 | ini: File?, | ||
| 39 | isCustomGame: Boolean, | ||
| 40 | view: SettingsActivityView? = null | ||
| 41 | ): HashMap<String, SettingSection?> { | ||
| 42 | val sections: HashMap<String, SettingSection?> = SettingsSectionMap() | ||
| 43 | var reader: BufferedReader? = null | ||
| 44 | try { | ||
| 45 | reader = BufferedReader(FileReader(ini)) | ||
| 46 | var current: SettingSection? = null | ||
| 47 | var line: String? | ||
| 48 | while (reader.readLine().also { line = it } != null) { | ||
| 49 | if (line!!.startsWith("[") && line!!.endsWith("]")) { | ||
| 50 | current = sectionFromLine(line!!, isCustomGame) | ||
| 51 | sections[current.name] = current | ||
| 52 | } else if (current != null) { | ||
| 53 | val setting = settingFromLine(line!!) | ||
| 54 | if (setting != null) { | ||
| 55 | current.putSetting(setting) | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } catch (e: FileNotFoundException) { | ||
| 60 | Log.error("[SettingsFile] File not found: " + e.message) | ||
| 61 | view?.onSettingsFileNotFound() | ||
| 62 | } catch (e: IOException) { | ||
| 63 | Log.error("[SettingsFile] Error reading from: " + e.message) | ||
| 64 | view?.onSettingsFileNotFound() | ||
| 65 | } finally { | ||
| 66 | if (reader != null) { | ||
| 67 | try { | ||
| 68 | reader.close() | ||
| 69 | } catch (e: IOException) { | ||
| 70 | Log.error("[SettingsFile] Error closing: " + e.message) | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | return sections | ||
| 75 | } | ||
| 76 | |||
| 77 | fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> { | ||
| 78 | return readFile(getSettingsFile(fileName), false, view) | ||
| 79 | } | ||
| 80 | |||
| 81 | fun readFile(fileName: String): HashMap<String, SettingSection?> = | ||
| 82 | readFile(getSettingsFile(fileName), false) | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves | ||
| 86 | * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it | ||
| 87 | * failed. | ||
| 88 | * | ||
| 89 | * @param gameId the id of the game to load it's settings. | ||
| 90 | * @param view The current view. | ||
| 91 | */ | ||
| 92 | fun readCustomGameSettings( | ||
| 93 | gameId: String, | ||
| 94 | view: SettingsActivityView? | ||
| 95 | ): HashMap<String, SettingSection?> { | ||
| 96 | return readFile(getCustomGameSettingsFile(gameId), true, view) | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | 22 | /** |
| 100 | * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error | 23 | * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error |
| 101 | * telling why it failed. | 24 | * telling why it failed. |
| 102 | * | 25 | * |
| 103 | * @param fileName The target filename without a path or extension. | 26 | * @param fileName The target filename without a path or extension. |
| 104 | * @param sections The HashMap containing the Settings we want to serialize. | ||
| 105 | * @param view The current view. | ||
| 106 | */ | 27 | */ |
| 107 | fun saveFile( | 28 | fun saveFile(fileName: String) { |
| 108 | fileName: String, | ||
| 109 | sections: TreeMap<String, SettingSection>, | ||
| 110 | view: SettingsActivityView | ||
| 111 | ) { | ||
| 112 | val ini = getSettingsFile(fileName) | 29 | val ini = getSettingsFile(fileName) |
| 113 | try { | 30 | try { |
| 114 | val writer = Wini(ini) | 31 | val wini = Wini(ini) |
| 115 | val keySet: Set<String> = sections.keys | 32 | for (specificCategory in Settings.Category.values()) { |
| 116 | for (key in keySet) { | 33 | val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal) |
| 117 | val section = sections[key] | 34 | for (setting in Settings.settingsList) { |
| 118 | writeSection(writer, section!!) | 35 | if (setting.key!!.isEmpty()) continue |
| 36 | |||
| 37 | val settingCategoryHeader = | ||
| 38 | NativeConfig.getConfigHeader(setting.category.ordinal) | ||
| 39 | val iniSetting: String? = wini.get(categoryHeader, setting.key) | ||
| 40 | if (iniSetting != null || settingCategoryHeader == categoryHeader) { | ||
| 41 | wini.put(settingCategoryHeader, setting.key, setting.valueAsString) | ||
| 42 | } | ||
| 43 | } | ||
| 119 | } | 44 | } |
| 120 | writer.store() | 45 | wini.store() |
| 121 | } catch (e: IOException) { | 46 | } catch (e: IOException) { |
| 122 | Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) | 47 | Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) |
| 123 | view.showToastMessage( | 48 | val context = YuzuApplication.appContext |
| 124 | YuzuApplication.appContext | 49 | Toast.makeText( |
| 125 | .getString(R.string.error_saving, fileName, e.message), | 50 | context, |
| 126 | false | 51 | context.getString(R.string.error_saving, fileName, e.message), |
| 127 | ) | 52 | Toast.LENGTH_SHORT |
| 128 | } | 53 | ).show() |
| 129 | } | ||
| 130 | |||
| 131 | fun saveCustomGameSettings(gameId: String?, sections: HashMap<String, SettingSection?>) { | ||
| 132 | val sortedSections: Set<String> = TreeSet(sections.keys) | ||
| 133 | for (sectionKey in sortedSections) { | ||
| 134 | val section = sections[sectionKey] | ||
| 135 | val settings = section!!.settings | ||
| 136 | val sortedKeySet: Set<String> = TreeSet(settings.keys) | ||
| 137 | for (settingKey in sortedKeySet) { | ||
| 138 | val setting = settings[settingKey] | ||
| 139 | NativeLibrary.setUserSetting( | ||
| 140 | gameId, | ||
| 141 | mapSectionNameFromIni( | ||
| 142 | section.name | ||
| 143 | ), | ||
| 144 | setting!!.key, | ||
| 145 | setting.valueAsString | ||
| 146 | ) | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | private fun mapSectionNameFromIni(generalSectionName: String): String? { | ||
| 152 | return if (sectionsMap.getForward(generalSectionName) != null) { | ||
| 153 | sectionsMap.getForward(generalSectionName) | ||
| 154 | } else { | ||
| 155 | generalSectionName | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | private fun mapSectionNameToIni(generalSectionName: String): String { | ||
| 160 | return if (sectionsMap.getBackward(generalSectionName) != null) { | ||
| 161 | sectionsMap.getBackward(generalSectionName).toString() | ||
| 162 | } else { | ||
| 163 | generalSectionName | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | fun getSettingsFile(fileName: String): File { | ||
| 168 | return File( | ||
| 169 | DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini" | ||
| 170 | ) | ||
| 171 | } | ||
| 172 | |||
| 173 | private fun getCustomGameSettingsFile(gameId: String): File { | ||
| 174 | return File(DirectoryInitialization.userDirectory + "/GameSettings/" + gameId + ".ini") | ||
| 175 | } | ||
| 176 | |||
| 177 | private fun sectionFromLine(line: String, isCustomGame: Boolean): SettingSection { | ||
| 178 | var sectionName: String = line.substring(1, line.length - 1) | ||
| 179 | if (isCustomGame) { | ||
| 180 | sectionName = mapSectionNameToIni(sectionName) | ||
| 181 | } | 54 | } |
| 182 | return SettingSection(sectionName) | ||
| 183 | } | 55 | } |
| 184 | 56 | ||
| 185 | /** | 57 | fun getSettingsFile(fileName: String): File = |
| 186 | * For a line of text, determines what type of data is being represented, and returns | 58 | File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") |
| 187 | * a Setting object containing this data. | ||
| 188 | * | ||
| 189 | * @param line The line of text being parsed. | ||
| 190 | * @return A typed Setting containing the key/value contained in the line. | ||
| 191 | */ | ||
| 192 | private fun settingFromLine(line: String): AbstractSetting? { | ||
| 193 | val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() | ||
| 194 | if (splitLine.size != 2) { | ||
| 195 | return null | ||
| 196 | } | ||
| 197 | val key = splitLine[0].trim { it <= ' ' } | ||
| 198 | val value = splitLine[1].trim { it <= ' ' } | ||
| 199 | if (value.isEmpty()) { | ||
| 200 | return null | ||
| 201 | } | ||
| 202 | |||
| 203 | val booleanSetting = BooleanSetting.from(key) | ||
| 204 | if (booleanSetting != null) { | ||
| 205 | booleanSetting.boolean = value.toBoolean() | ||
| 206 | return booleanSetting | ||
| 207 | } | ||
| 208 | |||
| 209 | val intSetting = IntSetting.from(key) | ||
| 210 | if (intSetting != null) { | ||
| 211 | intSetting.int = value.toInt() | ||
| 212 | return intSetting | ||
| 213 | } | ||
| 214 | |||
| 215 | val floatSetting = FloatSetting.from(key) | ||
| 216 | if (floatSetting != null) { | ||
| 217 | floatSetting.float = value.toFloat() | ||
| 218 | return floatSetting | ||
| 219 | } | ||
| 220 | |||
| 221 | val stringSetting = StringSetting.from(key) | ||
| 222 | if (stringSetting != null) { | ||
| 223 | stringSetting.string = value | ||
| 224 | return stringSetting | ||
| 225 | } | ||
| 226 | |||
| 227 | return null | ||
| 228 | } | ||
| 229 | |||
| 230 | /** | ||
| 231 | * Writes the contents of a Section HashMap to disk. | ||
| 232 | * | ||
| 233 | * @param parser A Wini pointed at a file on disk. | ||
| 234 | * @param section A section containing settings to be written to the file. | ||
| 235 | */ | ||
| 236 | private fun writeSection(parser: Wini, section: SettingSection) { | ||
| 237 | // Write the section header. | ||
| 238 | val header = section.name | ||
| 239 | |||
| 240 | // Write this section's values. | ||
| 241 | val settings = section.settings | ||
| 242 | val keySet: Set<String> = settings.keys | ||
| 243 | for (key in keySet) { | ||
| 244 | val setting = settings[key] | ||
| 245 | parser.put(header, setting!!.key, setting.valueAsString) | ||
| 246 | } | ||
| 247 | |||
| 248 | BooleanSetting.values().forEach { | ||
| 249 | if (!keySet.contains(it.key)) { | ||
| 250 | parser.put(header, it.key, it.valueAsString) | ||
| 251 | } | ||
| 252 | } | ||
| 253 | IntSetting.values().forEach { | ||
| 254 | if (!keySet.contains(it.key)) { | ||
| 255 | parser.put(header, it.key, it.valueAsString) | ||
| 256 | } | ||
| 257 | } | ||
| 258 | StringSetting.values().forEach { | ||
| 259 | if (!keySet.contains(it.key)) { | ||
| 260 | parser.put(header, it.key, it.valueAsString) | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | 59 | } |
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 25b9d4018..944ae652e 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 | |||
| @@ -7,11 +7,11 @@ import android.annotation.SuppressLint | |||
| 7 | import android.app.AlertDialog | 7 | import android.app.AlertDialog |
| 8 | import android.content.Context | 8 | import android.content.Context |
| 9 | import android.content.DialogInterface | 9 | import android.content.DialogInterface |
| 10 | import android.content.Intent | ||
| 11 | import android.content.SharedPreferences | 10 | import android.content.SharedPreferences |
| 12 | import android.content.pm.ActivityInfo | 11 | import android.content.pm.ActivityInfo |
| 13 | import android.content.res.Configuration | 12 | import android.content.res.Configuration |
| 14 | import android.graphics.Color | 13 | import android.graphics.Color |
| 14 | import android.net.Uri | ||
| 15 | import android.os.Bundle | 15 | import android.os.Bundle |
| 16 | import android.os.Handler | 16 | import android.os.Handler |
| 17 | import android.os.Looper | 17 | import android.os.Looper |
| @@ -19,18 +19,18 @@ import android.util.Rational | |||
| 19 | import android.view.* | 19 | import android.view.* |
| 20 | import android.widget.TextView | 20 | import android.widget.TextView |
| 21 | import androidx.activity.OnBackPressedCallback | 21 | import androidx.activity.OnBackPressedCallback |
| 22 | import androidx.activity.result.ActivityResultLauncher | ||
| 23 | import androidx.activity.result.contract.ActivityResultContracts | ||
| 24 | import androidx.appcompat.widget.PopupMenu | 22 | import androidx.appcompat.widget.PopupMenu |
| 25 | import androidx.core.content.res.ResourcesCompat | 23 | import androidx.core.content.res.ResourcesCompat |
| 26 | import androidx.core.graphics.Insets | 24 | import androidx.core.graphics.Insets |
| 27 | import androidx.core.view.ViewCompat | 25 | import androidx.core.view.ViewCompat |
| 28 | import androidx.core.view.WindowInsetsCompat | 26 | import androidx.core.view.WindowInsetsCompat |
| 29 | import androidx.core.view.isVisible | 27 | import androidx.drawerlayout.widget.DrawerLayout |
| 30 | import androidx.fragment.app.Fragment | 28 | import androidx.fragment.app.Fragment |
| 29 | import androidx.fragment.app.activityViewModels | ||
| 31 | import androidx.lifecycle.Lifecycle | 30 | import androidx.lifecycle.Lifecycle |
| 32 | import androidx.lifecycle.lifecycleScope | 31 | import androidx.lifecycle.lifecycleScope |
| 33 | import androidx.lifecycle.repeatOnLifecycle | 32 | import androidx.lifecycle.repeatOnLifecycle |
| 33 | import androidx.navigation.findNavController | ||
| 34 | import androidx.navigation.fragment.navArgs | 34 | import androidx.navigation.fragment.navArgs |
| 35 | import androidx.preference.PreferenceManager | 35 | import androidx.preference.PreferenceManager |
| 36 | import androidx.window.layout.FoldingFeature | 36 | import androidx.window.layout.FoldingFeature |
| @@ -40,6 +40,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder | |||
| 40 | import com.google.android.material.slider.Slider | 40 | import com.google.android.material.slider.Slider |
| 41 | import kotlinx.coroutines.Dispatchers | 41 | import kotlinx.coroutines.Dispatchers |
| 42 | import kotlinx.coroutines.launch | 42 | import kotlinx.coroutines.launch |
| 43 | import org.yuzu.yuzu_emu.HomeNavigationDirections | ||
| 43 | import org.yuzu.yuzu_emu.NativeLibrary | 44 | import org.yuzu.yuzu_emu.NativeLibrary |
| 44 | import org.yuzu.yuzu_emu.R | 45 | import org.yuzu.yuzu_emu.R |
| 45 | import org.yuzu.yuzu_emu.YuzuApplication | 46 | import org.yuzu.yuzu_emu.YuzuApplication |
| @@ -48,8 +49,9 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding | |||
| 48 | import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding | 49 | import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding |
| 49 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 50 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 50 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 51 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 51 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity | ||
| 52 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 52 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 53 | import org.yuzu.yuzu_emu.model.Game | ||
| 54 | import org.yuzu.yuzu_emu.model.EmulationViewModel | ||
| 53 | import org.yuzu.yuzu_emu.overlay.InputOverlay | 55 | import org.yuzu.yuzu_emu.overlay.InputOverlay |
| 54 | import org.yuzu.yuzu_emu.utils.* | 56 | import org.yuzu.yuzu_emu.utils.* |
| 55 | 57 | ||
| @@ -62,11 +64,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 62 | private var _binding: FragmentEmulationBinding? = null | 64 | private var _binding: FragmentEmulationBinding? = null |
| 63 | private val binding get() = _binding!! | 65 | private val binding get() = _binding!! |
| 64 | 66 | ||
| 65 | val args by navArgs<EmulationFragmentArgs>() | 67 | private val args by navArgs<EmulationFragmentArgs>() |
| 66 | 68 | ||
| 67 | private var isInFoldableLayout = false | 69 | private lateinit var game: Game |
| 70 | |||
| 71 | private val emulationViewModel: EmulationViewModel by activityViewModels() | ||
| 68 | 72 | ||
| 69 | private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent> | 73 | private var isInFoldableLayout = false |
| 70 | 74 | ||
| 71 | override fun onAttach(context: Context) { | 75 | override fun onAttach(context: Context) { |
| 72 | super.onAttach(context) | 76 | super.onAttach(context) |
| @@ -81,11 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 81 | .collect { updateFoldableLayout(context, it) } | 85 | .collect { updateFoldableLayout(context, it) } |
| 82 | } | 86 | } |
| 83 | } | 87 | } |
| 84 | |||
| 85 | onReturnFromSettings = context.activityResultRegistry.register( | ||
| 86 | "SettingsResult", | ||
| 87 | ActivityResultContracts.StartActivityForResult() | ||
| 88 | ) { updateScreenLayout() } | ||
| 89 | } else { | 88 | } else { |
| 90 | throw IllegalStateException("EmulationFragment must have EmulationActivity parent") | 89 | throw IllegalStateException("EmulationFragment must have EmulationActivity parent") |
| 91 | } | 90 | } |
| @@ -97,10 +96,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 97 | override fun onCreate(savedInstanceState: Bundle?) { | 96 | override fun onCreate(savedInstanceState: Bundle?) { |
| 98 | super.onCreate(savedInstanceState) | 97 | super.onCreate(savedInstanceState) |
| 99 | 98 | ||
| 99 | val intentUri: Uri? = requireActivity().intent.data | ||
| 100 | var intentGame: Game? = null | ||
| 101 | if (intentUri != null) { | ||
| 102 | intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) { | ||
| 103 | GameHelper.getGame(requireActivity().intent.data!!, false) | ||
| 104 | } else { | ||
| 105 | null | ||
| 106 | } | ||
| 107 | } | ||
| 108 | game = if (args.game != null) { | ||
| 109 | args.game!! | ||
| 110 | } else { | ||
| 111 | intentGame ?: error("[EmulationFragment] No bootable game present!") | ||
| 112 | } | ||
| 113 | |||
| 100 | // So this fragment doesn't restart on configuration changes; i.e. rotation. | 114 | // So this fragment doesn't restart on configuration changes; i.e. rotation. |
| 101 | retainInstance = true | 115 | retainInstance = true |
| 102 | preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 116 | preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 103 | emulationState = EmulationState(args.game.path) | 117 | emulationState = EmulationState(game.path) |
| 104 | } | 118 | } |
| 105 | 119 | ||
| 106 | /** | 120 | /** |
| @@ -120,11 +134,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 120 | binding.showFpsText.setTextColor(Color.YELLOW) | 134 | binding.showFpsText.setTextColor(Color.YELLOW) |
| 121 | binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } | 135 | binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } |
| 122 | 136 | ||
| 123 | // Setup overlay. | 137 | binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) |
| 124 | updateShowFpsOverlay() | ||
| 125 | |||
| 126 | binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = | 138 | binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = |
| 127 | args.game.title | 139 | game.title |
| 128 | binding.inGameMenu.setNavigationItemSelectedListener { | 140 | binding.inGameMenu.setNavigationItemSelectedListener { |
| 129 | when (it.itemId) { | 141 | when (it.itemId) { |
| 130 | R.id.menu_pause_emulation -> { | 142 | R.id.menu_pause_emulation -> { |
| @@ -149,12 +161,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 149 | } | 161 | } |
| 150 | 162 | ||
| 151 | R.id.menu_settings -> { | 163 | R.id.menu_settings -> { |
| 152 | SettingsActivity.launch( | 164 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( |
| 153 | requireContext(), | 165 | null, |
| 154 | onReturnFromSettings, | 166 | SettingsFile.FILE_NAME_CONFIG |
| 155 | SettingsFile.FILE_NAME_CONFIG, | ||
| 156 | "" | ||
| 157 | ) | 167 | ) |
| 168 | binding.root.findNavController().navigate(action) | ||
| 158 | true | 169 | true |
| 159 | } | 170 | } |
| 160 | 171 | ||
| @@ -165,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 165 | 176 | ||
| 166 | R.id.menu_exit -> { | 177 | R.id.menu_exit -> { |
| 167 | emulationState.stop() | 178 | emulationState.stop() |
| 168 | requireActivity().finish() | 179 | emulationViewModel.setIsEmulationStopping(true) |
| 180 | binding.drawerLayout.close() | ||
| 181 | binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) | ||
| 169 | true | 182 | true |
| 170 | } | 183 | } |
| 171 | 184 | ||
| @@ -179,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 179 | requireActivity(), | 192 | requireActivity(), |
| 180 | object : OnBackPressedCallback(true) { | 193 | object : OnBackPressedCallback(true) { |
| 181 | override fun handleOnBackPressed() { | 194 | override fun handleOnBackPressed() { |
| 195 | if (!NativeLibrary.isRunning()) { | ||
| 196 | return | ||
| 197 | } | ||
| 198 | |||
| 182 | if (binding.drawerLayout.isOpen) { | 199 | if (binding.drawerLayout.isOpen) { |
| 183 | binding.drawerLayout.close() | 200 | binding.drawerLayout.close() |
| 184 | } else { | 201 | } else { |
| @@ -195,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 195 | .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } | 212 | .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } |
| 196 | } | 213 | } |
| 197 | } | 214 | } |
| 215 | |||
| 216 | GameIconUtils.loadGameIcon(game, binding.loadingImage) | ||
| 217 | binding.loadingTitle.text = game.title | ||
| 218 | binding.loadingTitle.isSelected = true | ||
| 219 | binding.loadingText.isSelected = true | ||
| 220 | |||
| 221 | emulationViewModel.shaderProgress.observe(viewLifecycleOwner) { | ||
| 222 | if (it > 0 && it != emulationViewModel.totalShaders.value!!) { | ||
| 223 | binding.loadingProgressIndicator.isIndeterminate = false | ||
| 224 | |||
| 225 | if (it < binding.loadingProgressIndicator.max) { | ||
| 226 | binding.loadingProgressIndicator.progress = it | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | if (it == emulationViewModel.totalShaders.value!!) { | ||
| 231 | binding.loadingText.setText(R.string.loading) | ||
| 232 | binding.loadingProgressIndicator.isIndeterminate = true | ||
| 233 | } | ||
| 234 | } | ||
| 235 | emulationViewModel.totalShaders.observe(viewLifecycleOwner) { | ||
| 236 | binding.loadingProgressIndicator.max = it | ||
| 237 | } | ||
| 238 | emulationViewModel.shaderMessage.observe(viewLifecycleOwner) { | ||
| 239 | if (it.isNotEmpty()) { | ||
| 240 | binding.loadingText.text = it | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started -> | ||
| 245 | if (started) { | ||
| 246 | binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) | ||
| 247 | ViewUtils.showView(binding.surfaceInputOverlay) | ||
| 248 | ViewUtils.hideView(binding.loadingIndicator) | ||
| 249 | |||
| 250 | // Setup overlay | ||
| 251 | updateShowFpsOverlay() | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) { | ||
| 256 | if (it) { | ||
| 257 | binding.loadingText.setText(R.string.shutting_down) | ||
| 258 | ViewUtils.showView(binding.loadingIndicator) | ||
| 259 | ViewUtils.hideView(binding.inputContainer) | ||
| 260 | ViewUtils.hideView(binding.showFpsText) | ||
| 261 | } | ||
| 262 | } | ||
| 198 | } | 263 | } |
| 199 | 264 | ||
| 200 | override fun onConfigurationChanged(newConfig: Configuration) { | 265 | override fun onConfigurationChanged(newConfig: Configuration) { |
| @@ -204,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 204 | binding.drawerLayout.close() | 269 | binding.drawerLayout.close() |
| 205 | } | 270 | } |
| 206 | if (EmulationMenuSettings.showOverlay) { | 271 | if (EmulationMenuSettings.showOverlay) { |
| 207 | binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } | 272 | binding.surfaceInputOverlay.post { |
| 273 | binding.surfaceInputOverlay.visibility = View.VISIBLE | ||
| 274 | } | ||
| 208 | } | 275 | } |
| 209 | } else { | 276 | } else { |
| 210 | if (EmulationMenuSettings.showOverlay) { | 277 | if (EmulationMenuSettings.showOverlay && |
| 211 | binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } | 278 | emulationViewModel.emulationStarted.value == true |
| 279 | ) { | ||
| 280 | binding.surfaceInputOverlay.post { | ||
| 281 | binding.surfaceInputOverlay.visibility = View.VISIBLE | ||
| 282 | } | ||
| 283 | } else { | ||
| 284 | binding.surfaceInputOverlay.post { | ||
| 285 | binding.surfaceInputOverlay.visibility = View.INVISIBLE | ||
| 286 | } | ||
| 212 | } | 287 | } |
| 213 | if (!isInFoldableLayout) { | 288 | if (!isInFoldableLayout) { |
| 214 | if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | 289 | if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { |
| @@ -217,16 +292,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 217 | binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE | 292 | binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE |
| 218 | } | 293 | } |
| 219 | } | 294 | } |
| 220 | if (!binding.surfaceInputOverlay.isInEditMode) { | ||
| 221 | refreshInputOverlay() | ||
| 222 | } | ||
| 223 | } | 295 | } |
| 224 | } | 296 | } |
| 225 | 297 | ||
| 226 | override fun onResume() { | 298 | override fun onResume() { |
| 227 | super.onResume() | 299 | super.onResume() |
| 228 | if (!DirectoryInitialization.areDirectoriesReady) { | 300 | if (!DirectoryInitialization.areDirectoriesReady) { |
| 229 | DirectoryInitialization.start(requireContext()) | 301 | DirectoryInitialization.start() |
| 230 | } | 302 | } |
| 231 | 303 | ||
| 232 | updateScreenLayout() | 304 | updateScreenLayout() |
| @@ -251,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 251 | super.onDetach() | 323 | super.onDetach() |
| 252 | } | 324 | } |
| 253 | 325 | ||
| 254 | private fun refreshInputOverlay() { | ||
| 255 | binding.surfaceInputOverlay.refreshControls() | ||
| 256 | } | ||
| 257 | |||
| 258 | private fun resetInputOverlay() { | 326 | private fun resetInputOverlay() { |
| 259 | preferences.edit() | 327 | preferences.edit() |
| 260 | .remove(Settings.PREF_CONTROL_SCALE) | 328 | .remove(Settings.PREF_CONTROL_SCALE) |
| @@ -272,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 272 | val FRAMETIME = 2 | 340 | val FRAMETIME = 2 |
| 273 | val SPEED = 3 | 341 | val SPEED = 3 |
| 274 | perfStatsUpdater = { | 342 | perfStatsUpdater = { |
| 275 | val perfStats = NativeLibrary.getPerfStats() | 343 | if (emulationViewModel.emulationStarted.value == true) { |
| 276 | if (perfStats[FPS] > 0 && _binding != null) { | 344 | val perfStats = NativeLibrary.getPerfStats() |
| 277 | binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) | 345 | if (perfStats[FPS] > 0 && _binding != null) { |
| 278 | } | 346 | binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) |
| 279 | 347 | } | |
| 280 | if (!emulationState.isStopped) { | ||
| 281 | perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) | 348 | perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) |
| 282 | } | 349 | } |
| 283 | } | 350 | } |
| 284 | perfStatsUpdateHandler.post(perfStatsUpdater!!) | 351 | perfStatsUpdateHandler.post(perfStatsUpdater!!) |
| 285 | binding.showFpsText.text = resources.getString(R.string.emulation_game_loading) | ||
| 286 | binding.showFpsText.visibility = View.VISIBLE | 352 | binding.showFpsText.visibility = View.VISIBLE |
| 287 | } else { | 353 | } else { |
| 288 | if (perfStatsUpdater != null) { | 354 | if (perfStatsUpdater != null) { |
| @@ -340,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 340 | 406 | ||
| 341 | isInFoldableLayout = true | 407 | isInFoldableLayout = true |
| 342 | binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE | 408 | binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE |
| 343 | refreshInputOverlay() | ||
| 344 | } | 409 | } |
| 345 | } | 410 | } |
| 346 | it.isSeparating | 411 | it.isSeparating |
| @@ -428,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 428 | .apply() | 493 | .apply() |
| 429 | } | 494 | } |
| 430 | .setPositiveButton(android.R.string.ok) { _, _ -> | 495 | .setPositiveButton(android.R.string.ok) { _, _ -> |
| 431 | refreshInputOverlay() | 496 | binding.surfaceInputOverlay.refreshControls() |
| 432 | } | 497 | } |
| 433 | .setNegativeButton(android.R.string.cancel, null) | 498 | .setNegativeButton(android.R.string.cancel, null) |
| 434 | .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } | 499 | .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } |
| @@ -452,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 452 | R.id.menu_show_overlay -> { | 517 | R.id.menu_show_overlay -> { |
| 453 | it.isChecked = !it.isChecked | 518 | it.isChecked = !it.isChecked |
| 454 | EmulationMenuSettings.showOverlay = it.isChecked | 519 | EmulationMenuSettings.showOverlay = it.isChecked |
| 455 | refreshInputOverlay() | 520 | binding.surfaceInputOverlay.refreshControls() |
| 456 | true | 521 | true |
| 457 | } | 522 | } |
| 458 | 523 | ||
| @@ -558,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 558 | preferences.edit() | 623 | preferences.edit() |
| 559 | .putInt(Settings.PREF_CONTROL_SCALE, scale) | 624 | .putInt(Settings.PREF_CONTROL_SCALE, scale) |
| 560 | .apply() | 625 | .apply() |
| 561 | refreshInputOverlay() | 626 | binding.surfaceInputOverlay.refreshControls() |
| 562 | } | 627 | } |
| 563 | 628 | ||
| 564 | private fun setControlOpacity(opacity: Int) { | 629 | private fun setControlOpacity(opacity: Int) { |
| 565 | preferences.edit() | 630 | preferences.edit() |
| 566 | .putInt(Settings.PREF_CONTROL_OPACITY, opacity) | 631 | .putInt(Settings.PREF_CONTROL_OPACITY, opacity) |
| 567 | .apply() | 632 | .apply() |
| 568 | refreshInputOverlay() | 633 | binding.surfaceInputOverlay.refreshControls() |
| 569 | } | 634 | } |
| 570 | 635 | ||
| 571 | private fun setInsets() { | 636 | private fun setInsets() { |
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 d5e793491..cbbe14d22 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 | |||
| @@ -25,17 +25,18 @@ import androidx.core.view.updatePadding | |||
| 25 | import androidx.documentfile.provider.DocumentFile | 25 | import androidx.documentfile.provider.DocumentFile |
| 26 | import androidx.fragment.app.Fragment | 26 | import androidx.fragment.app.Fragment |
| 27 | import androidx.fragment.app.activityViewModels | 27 | import androidx.fragment.app.activityViewModels |
| 28 | import androidx.navigation.findNavController | ||
| 28 | import androidx.navigation.fragment.findNavController | 29 | import androidx.navigation.fragment.findNavController |
| 29 | import androidx.recyclerview.widget.LinearLayoutManager | 30 | import androidx.recyclerview.widget.LinearLayoutManager |
| 30 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | 31 | import com.google.android.material.dialog.MaterialAlertDialogBuilder |
| 31 | import com.google.android.material.transition.MaterialSharedAxis | 32 | import com.google.android.material.transition.MaterialSharedAxis |
| 32 | import org.yuzu.yuzu_emu.BuildConfig | 33 | import org.yuzu.yuzu_emu.BuildConfig |
| 34 | import org.yuzu.yuzu_emu.HomeNavigationDirections | ||
| 33 | import org.yuzu.yuzu_emu.R | 35 | import org.yuzu.yuzu_emu.R |
| 34 | import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter | 36 | import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter |
| 35 | import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding | 37 | import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding |
| 36 | import org.yuzu.yuzu_emu.features.DocumentProvider | 38 | import org.yuzu.yuzu_emu.features.DocumentProvider |
| 37 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 39 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 38 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity | ||
| 39 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 40 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 40 | import org.yuzu.yuzu_emu.model.HomeSetting | 41 | import org.yuzu.yuzu_emu.model.HomeSetting |
| 41 | import org.yuzu.yuzu_emu.model.HomeViewModel | 42 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| @@ -74,7 +75,13 @@ class HomeSettingsFragment : Fragment() { | |||
| 74 | R.string.advanced_settings, | 75 | R.string.advanced_settings, |
| 75 | R.string.settings_description, | 76 | R.string.settings_description, |
| 76 | R.drawable.ic_settings, | 77 | R.drawable.ic_settings, |
| 77 | { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } | 78 | { |
| 79 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( | ||
| 80 | null, | ||
| 81 | SettingsFile.FILE_NAME_CONFIG | ||
| 82 | ) | ||
| 83 | binding.root.findNavController().navigate(action) | ||
| 84 | } | ||
| 78 | ) | 85 | ) |
| 79 | ) | 86 | ) |
| 80 | add( | 87 | add( |
| @@ -90,7 +97,13 @@ class HomeSettingsFragment : Fragment() { | |||
| 90 | R.string.preferences_theme, | 97 | R.string.preferences_theme, |
| 91 | R.string.theme_and_color_description, | 98 | R.string.theme_and_color_description, |
| 92 | R.drawable.ic_palette, | 99 | R.drawable.ic_palette, |
| 93 | { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } | 100 | { |
| 101 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( | ||
| 102 | null, | ||
| 103 | Settings.SECTION_THEME | ||
| 104 | ) | ||
| 105 | binding.root.findNavController().navigate(action) | ||
| 106 | } | ||
| 94 | ) | 107 | ) |
| 95 | ) | 108 | ) |
| 96 | add( | 109 | add( |
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 e1495ee8c..f38aeea53 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 | |||
| @@ -187,8 +187,8 @@ class ImportExportSavesFragment : DialogFragment() { | |||
| 187 | withContext(Dispatchers.Main) { | 187 | withContext(Dispatchers.Main) { |
| 188 | if (!validZip) { | 188 | if (!validZip) { |
| 189 | MessageDialogFragment.newInstance( | 189 | MessageDialogFragment.newInstance( |
| 190 | R.string.save_file_invalid_zip_structure, | 190 | titleId = R.string.save_file_invalid_zip_structure, |
| 191 | R.string.save_file_invalid_zip_structure_description | 191 | descriptionId = R.string.save_file_invalid_zip_structure_description |
| 192 | ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) | 192 | ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) |
| 193 | return@withContext | 193 | return@withContext |
| 194 | } | 194 | } |
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 739b26f99..181bd983a 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 | |||
| @@ -34,7 +34,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() { | |||
| 34 | when (val result = taskViewModel.result.value) { | 34 | when (val result = taskViewModel.result.value) { |
| 35 | is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show() | 35 | is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show() |
| 36 | is MessageDialogFragment -> result.show( | 36 | is MessageDialogFragment -> result.show( |
| 37 | parentFragmentManager, | 37 | requireActivity().supportFragmentManager, |
| 38 | MessageDialogFragment.TAG | 38 | MessageDialogFragment.TAG |
| 39 | ) | 39 | ) |
| 40 | } | 40 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt deleted file mode 100644 index b29b627e9..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt +++ /dev/null | |||
| @@ -1,62 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.content.Intent | ||
| 8 | import android.net.Uri | ||
| 9 | import android.os.Bundle | ||
| 10 | import androidx.fragment.app.DialogFragment | ||
| 11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 12 | import org.yuzu.yuzu_emu.R | ||
| 13 | |||
| 14 | class LongMessageDialogFragment : DialogFragment() { | ||
| 15 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 16 | val titleId = requireArguments().getInt(TITLE) | ||
| 17 | val description = requireArguments().getString(DESCRIPTION) | ||
| 18 | val helpLinkId = requireArguments().getInt(HELP_LINK) | ||
| 19 | |||
| 20 | val dialog = MaterialAlertDialogBuilder(requireContext()) | ||
| 21 | .setPositiveButton(R.string.close, null) | ||
| 22 | .setTitle(titleId) | ||
| 23 | .setMessage(description) | ||
| 24 | |||
| 25 | if (helpLinkId != 0) { | ||
| 26 | dialog.setNeutralButton(R.string.learn_more) { _, _ -> | ||
| 27 | openLink(getString(helpLinkId)) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | return dialog.show() | ||
| 32 | } | ||
| 33 | |||
| 34 | private fun openLink(link: String) { | ||
| 35 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) | ||
| 36 | startActivity(intent) | ||
| 37 | } | ||
| 38 | |||
| 39 | companion object { | ||
| 40 | const val TAG = "LongMessageDialogFragment" | ||
| 41 | |||
| 42 | private const val TITLE = "Title" | ||
| 43 | private const val DESCRIPTION = "Description" | ||
| 44 | private const val HELP_LINK = "Link" | ||
| 45 | |||
| 46 | fun newInstance( | ||
| 47 | titleId: Int, | ||
| 48 | description: String, | ||
| 49 | helpLinkId: Int = 0 | ||
| 50 | ): LongMessageDialogFragment { | ||
| 51 | val dialog = LongMessageDialogFragment() | ||
| 52 | val bundle = Bundle() | ||
| 53 | bundle.apply { | ||
| 54 | putInt(TITLE, titleId) | ||
| 55 | putString(DESCRIPTION, description) | ||
| 56 | putInt(HELP_LINK, helpLinkId) | ||
| 57 | } | ||
| 58 | dialog.arguments = bundle | ||
| 59 | return dialog | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt index 2db38fdc2..7d1c2c8dd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt | |||
| @@ -13,14 +13,20 @@ import org.yuzu.yuzu_emu.R | |||
| 13 | 13 | ||
| 14 | class MessageDialogFragment : DialogFragment() { | 14 | class MessageDialogFragment : DialogFragment() { |
| 15 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | 15 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |
| 16 | val titleId = requireArguments().getInt(TITLE) | 16 | val titleId = requireArguments().getInt(TITLE_ID) |
| 17 | val descriptionId = requireArguments().getInt(DESCRIPTION) | 17 | val titleString = requireArguments().getString(TITLE_STRING)!! |
| 18 | val descriptionId = requireArguments().getInt(DESCRIPTION_ID) | ||
| 19 | val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! | ||
| 18 | val helpLinkId = requireArguments().getInt(HELP_LINK) | 20 | val helpLinkId = requireArguments().getInt(HELP_LINK) |
| 19 | 21 | ||
| 20 | val dialog = MaterialAlertDialogBuilder(requireContext()) | 22 | val dialog = MaterialAlertDialogBuilder(requireContext()) |
| 21 | .setPositiveButton(R.string.close, null) | 23 | .setPositiveButton(R.string.close, null) |
| 22 | .setTitle(titleId) | 24 | |
| 23 | .setMessage(descriptionId) | 25 | if (titleId != 0) dialog.setTitle(titleId) |
| 26 | if (titleString.isNotEmpty()) dialog.setTitle(titleString) | ||
| 27 | |||
| 28 | if (descriptionId != 0) dialog.setMessage(descriptionId) | ||
| 29 | if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString) | ||
| 24 | 30 | ||
| 25 | if (helpLinkId != 0) { | 31 | if (helpLinkId != 0) { |
| 26 | dialog.setNeutralButton(R.string.learn_more) { _, _ -> | 32 | dialog.setNeutralButton(R.string.learn_more) { _, _ -> |
| @@ -39,20 +45,26 @@ class MessageDialogFragment : DialogFragment() { | |||
| 39 | companion object { | 45 | companion object { |
| 40 | const val TAG = "MessageDialogFragment" | 46 | const val TAG = "MessageDialogFragment" |
| 41 | 47 | ||
| 42 | private const val TITLE = "Title" | 48 | private const val TITLE_ID = "Title" |
| 43 | private const val DESCRIPTION = "Description" | 49 | private const val TITLE_STRING = "TitleString" |
| 50 | private const val DESCRIPTION_ID = "DescriptionId" | ||
| 51 | private const val DESCRIPTION_STRING = "DescriptionString" | ||
| 44 | private const val HELP_LINK = "Link" | 52 | private const val HELP_LINK = "Link" |
| 45 | 53 | ||
| 46 | fun newInstance( | 54 | fun newInstance( |
| 47 | titleId: Int, | 55 | titleId: Int = 0, |
| 48 | descriptionId: Int, | 56 | titleString: String = "", |
| 57 | descriptionId: Int = 0, | ||
| 58 | descriptionString: String = "", | ||
| 49 | helpLinkId: Int = 0 | 59 | helpLinkId: Int = 0 |
| 50 | ): MessageDialogFragment { | 60 | ): MessageDialogFragment { |
| 51 | val dialog = MessageDialogFragment() | 61 | val dialog = MessageDialogFragment() |
| 52 | val bundle = Bundle() | 62 | val bundle = Bundle() |
| 53 | bundle.apply { | 63 | bundle.apply { |
| 54 | putInt(TITLE, titleId) | 64 | putInt(TITLE_ID, titleId) |
| 55 | putInt(DESCRIPTION, descriptionId) | 65 | putString(TITLE_STRING, titleString) |
| 66 | putInt(DESCRIPTION_ID, descriptionId) | ||
| 67 | putString(DESCRIPTION_STRING, descriptionString) | ||
| 56 | putInt(HELP_LINK, helpLinkId) | 68 | putInt(HELP_LINK, helpLinkId) |
| 57 | } | 69 | } |
| 58 | dialog.arguments = bundle | 70 | dialog.arguments = bundle |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt new file mode 100644 index 000000000..d18ec6974 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt | |||
| @@ -0,0 +1,235 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.content.DialogInterface | ||
| 8 | import android.os.Bundle | ||
| 9 | import android.view.LayoutInflater | ||
| 10 | import android.view.View | ||
| 11 | import android.view.ViewGroup | ||
| 12 | import androidx.fragment.app.DialogFragment | ||
| 13 | import androidx.fragment.app.activityViewModels | ||
| 14 | import androidx.lifecycle.Lifecycle | ||
| 15 | import androidx.lifecycle.lifecycleScope | ||
| 16 | import androidx.lifecycle.repeatOnLifecycle | ||
| 17 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 18 | import com.google.android.material.slider.Slider | ||
| 19 | import kotlinx.coroutines.launch | ||
| 20 | import org.yuzu.yuzu_emu.R | ||
| 21 | import org.yuzu.yuzu_emu.databinding.DialogSliderBinding | ||
| 22 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | ||
| 23 | import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting | ||
| 24 | import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting | ||
| 25 | import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting | ||
| 26 | import org.yuzu.yuzu_emu.model.SettingsViewModel | ||
| 27 | |||
| 28 | class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener { | ||
| 29 | private var type = 0 | ||
| 30 | private var position = 0 | ||
| 31 | |||
| 32 | private var defaultCancelListener = | ||
| 33 | DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() } | ||
| 34 | |||
| 35 | private val settingsViewModel: SettingsViewModel by activityViewModels() | ||
| 36 | |||
| 37 | private lateinit var sliderBinding: DialogSliderBinding | ||
| 38 | |||
| 39 | override fun onCreate(savedInstanceState: Bundle?) { | ||
| 40 | super.onCreate(savedInstanceState) | ||
| 41 | type = requireArguments().getInt(TYPE) | ||
| 42 | position = requireArguments().getInt(POSITION) | ||
| 43 | |||
| 44 | if (settingsViewModel.clickedItem == null) dismiss() | ||
| 45 | } | ||
| 46 | |||
| 47 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 48 | return when (type) { | ||
| 49 | TYPE_RESET_SETTING -> { | ||
| 50 | MaterialAlertDialogBuilder(requireContext()) | ||
| 51 | .setMessage(R.string.reset_setting_confirmation) | ||
| 52 | .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> | ||
| 53 | settingsViewModel.clickedItem!!.setting.reset() | ||
| 54 | settingsViewModel.setAdapterItemChanged(position) | ||
| 55 | settingsViewModel.shouldSave = true | ||
| 56 | } | ||
| 57 | .setNegativeButton(android.R.string.cancel, null) | ||
| 58 | .create() | ||
| 59 | } | ||
| 60 | |||
| 61 | SettingsItem.TYPE_SINGLE_CHOICE -> { | ||
| 62 | val item = settingsViewModel.clickedItem as SingleChoiceSetting | ||
| 63 | val value = getSelectionForSingleChoiceValue(item) | ||
| 64 | MaterialAlertDialogBuilder(requireContext()) | ||
| 65 | .setTitle(item.nameId) | ||
| 66 | .setSingleChoiceItems(item.choicesId, value, this) | ||
| 67 | .create() | ||
| 68 | } | ||
| 69 | |||
| 70 | SettingsItem.TYPE_SLIDER -> { | ||
| 71 | sliderBinding = DialogSliderBinding.inflate(layoutInflater) | ||
| 72 | val item = settingsViewModel.clickedItem as SliderSetting | ||
| 73 | |||
| 74 | settingsViewModel.setSliderTextValue(item.selectedValue.toFloat(), item.units) | ||
| 75 | sliderBinding.slider.apply { | ||
| 76 | valueFrom = item.min.toFloat() | ||
| 77 | valueTo = item.max.toFloat() | ||
| 78 | value = settingsViewModel.sliderProgress.value.toFloat() | ||
| 79 | addOnChangeListener { _: Slider, value: Float, _: Boolean -> | ||
| 80 | settingsViewModel.setSliderTextValue(value, item.units) | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | MaterialAlertDialogBuilder(requireContext()) | ||
| 85 | .setTitle(item.nameId) | ||
| 86 | .setView(sliderBinding.root) | ||
| 87 | .setPositiveButton(android.R.string.ok, this) | ||
| 88 | .setNegativeButton(android.R.string.cancel, defaultCancelListener) | ||
| 89 | .create() | ||
| 90 | } | ||
| 91 | |||
| 92 | SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { | ||
| 93 | val item = settingsViewModel.clickedItem as StringSingleChoiceSetting | ||
| 94 | MaterialAlertDialogBuilder(requireContext()) | ||
| 95 | .setTitle(item.nameId) | ||
| 96 | .setSingleChoiceItems(item.choices, item.selectValueIndex, this) | ||
| 97 | .create() | ||
| 98 | } | ||
| 99 | |||
| 100 | else -> super.onCreateDialog(savedInstanceState) | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | override fun onCreateView( | ||
| 105 | inflater: LayoutInflater, | ||
| 106 | container: ViewGroup?, | ||
| 107 | savedInstanceState: Bundle? | ||
| 108 | ): View? { | ||
| 109 | return when (type) { | ||
| 110 | SettingsItem.TYPE_SLIDER -> sliderBinding.root | ||
| 111 | else -> super.onCreateView(inflater, container, savedInstanceState) | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 116 | super.onViewCreated(view, savedInstanceState) | ||
| 117 | when (type) { | ||
| 118 | SettingsItem.TYPE_SLIDER -> { | ||
| 119 | viewLifecycleOwner.lifecycleScope.launch { | ||
| 120 | repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| 121 | settingsViewModel.sliderTextValue.collect { | ||
| 122 | sliderBinding.textValue.text = it | ||
| 123 | } | ||
| 124 | } | ||
| 125 | repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| 126 | settingsViewModel.sliderProgress.collect { | ||
| 127 | sliderBinding.slider.value = it.toFloat() | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | override fun onClick(dialog: DialogInterface, which: Int) { | ||
| 136 | when (settingsViewModel.clickedItem) { | ||
| 137 | is SingleChoiceSetting -> { | ||
| 138 | val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting | ||
| 139 | val value = getValueForSingleChoiceSelection(scSetting, which) | ||
| 140 | if (scSetting.selectedValue != value) { | ||
| 141 | settingsViewModel.shouldSave = true | ||
| 142 | } | ||
| 143 | scSetting.selectedValue = value | ||
| 144 | } | ||
| 145 | |||
| 146 | is StringSingleChoiceSetting -> { | ||
| 147 | val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting | ||
| 148 | val value = scSetting.getValueAt(which) | ||
| 149 | if (scSetting.selectedValue != value) settingsViewModel.shouldSave = true | ||
| 150 | scSetting.selectedValue = value | ||
| 151 | } | ||
| 152 | |||
| 153 | is SliderSetting -> { | ||
| 154 | val sliderSetting = settingsViewModel.clickedItem as SliderSetting | ||
| 155 | if (sliderSetting.selectedValue != settingsViewModel.sliderProgress.value) { | ||
| 156 | settingsViewModel.shouldSave = true | ||
| 157 | } | ||
| 158 | sliderSetting.selectedValue = settingsViewModel.sliderProgress.value | ||
| 159 | } | ||
| 160 | } | ||
| 161 | closeDialog() | ||
| 162 | } | ||
| 163 | |||
| 164 | private fun closeDialog() { | ||
| 165 | settingsViewModel.setAdapterItemChanged(position) | ||
| 166 | settingsViewModel.clickedItem = null | ||
| 167 | settingsViewModel.setSliderProgress(-1f) | ||
| 168 | dismiss() | ||
| 169 | } | ||
| 170 | |||
| 171 | private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { | ||
| 172 | val valuesId = item.valuesId | ||
| 173 | return if (valuesId > 0) { | ||
| 174 | val valuesArray = requireContext().resources.getIntArray(valuesId) | ||
| 175 | valuesArray[which] | ||
| 176 | } else { | ||
| 177 | which | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { | ||
| 182 | val value = item.selectedValue | ||
| 183 | val valuesId = item.valuesId | ||
| 184 | if (valuesId > 0) { | ||
| 185 | val valuesArray = requireContext().resources.getIntArray(valuesId) | ||
| 186 | for (index in valuesArray.indices) { | ||
| 187 | val current = valuesArray[index] | ||
| 188 | if (current == value) { | ||
| 189 | return index | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } else { | ||
| 193 | return value | ||
| 194 | } | ||
| 195 | return -1 | ||
| 196 | } | ||
| 197 | |||
| 198 | companion object { | ||
| 199 | const val TAG = "SettingsDialogFragment" | ||
| 200 | |||
| 201 | const val TYPE_RESET_SETTING = -1 | ||
| 202 | |||
| 203 | const val TITLE = "Title" | ||
| 204 | const val TYPE = "Type" | ||
| 205 | const val POSITION = "Position" | ||
| 206 | |||
| 207 | fun newInstance( | ||
| 208 | settingsViewModel: SettingsViewModel, | ||
| 209 | clickedItem: SettingsItem, | ||
| 210 | type: Int, | ||
| 211 | position: Int | ||
| 212 | ): SettingsDialogFragment { | ||
| 213 | when (type) { | ||
| 214 | SettingsItem.TYPE_HEADER, | ||
| 215 | SettingsItem.TYPE_SWITCH, | ||
| 216 | SettingsItem.TYPE_SUBMENU, | ||
| 217 | SettingsItem.TYPE_DATETIME_SETTING, | ||
| 218 | SettingsItem.TYPE_RUNNABLE -> | ||
| 219 | throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!") | ||
| 220 | |||
| 221 | SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress( | ||
| 222 | (clickedItem as SliderSetting).selectedValue.toFloat() | ||
| 223 | ) | ||
| 224 | } | ||
| 225 | settingsViewModel.clickedItem = clickedItem | ||
| 226 | |||
| 227 | val args = Bundle() | ||
| 228 | args.putInt(TYPE, type) | ||
| 229 | args.putInt(POSITION, position) | ||
| 230 | val fragment = SettingsDialogFragment() | ||
| 231 | fragment.arguments = args | ||
| 232 | return fragment | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt new file mode 100644 index 000000000..55b6a0367 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.content.Context | ||
| 7 | import android.os.Bundle | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.View | ||
| 10 | import android.view.ViewGroup | ||
| 11 | import android.view.inputmethod.InputMethodManager | ||
| 12 | import androidx.core.view.ViewCompat | ||
| 13 | import androidx.core.view.WindowInsetsCompat | ||
| 14 | import androidx.core.view.updatePadding | ||
| 15 | import androidx.core.widget.doOnTextChanged | ||
| 16 | import androidx.fragment.app.Fragment | ||
| 17 | import androidx.fragment.app.activityViewModels | ||
| 18 | import androidx.recyclerview.widget.LinearLayoutManager | ||
| 19 | import com.google.android.material.divider.MaterialDividerItemDecoration | ||
| 20 | import com.google.android.material.transition.MaterialSharedAxis | ||
| 21 | import info.debatty.java.stringsimilarity.Cosine | ||
| 22 | import org.yuzu.yuzu_emu.R | ||
| 23 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding | ||
| 24 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | ||
| 25 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | ||
| 26 | import org.yuzu.yuzu_emu.model.SettingsViewModel | ||
| 27 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 28 | |||
| 29 | class SettingsSearchFragment : Fragment() { | ||
| 30 | private var _binding: FragmentSettingsSearchBinding? = null | ||
| 31 | private val binding get() = _binding!! | ||
| 32 | |||
| 33 | private var settingsAdapter: SettingsAdapter? = null | ||
| 34 | |||
| 35 | private val settingsViewModel: SettingsViewModel by activityViewModels() | ||
| 36 | |||
| 37 | override fun onCreate(savedInstanceState: Bundle?) { | ||
| 38 | super.onCreate(savedInstanceState) | ||
| 39 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 40 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||
| 41 | } | ||
| 42 | |||
| 43 | override fun onCreateView( | ||
| 44 | inflater: LayoutInflater, | ||
| 45 | container: ViewGroup?, | ||
| 46 | savedInstanceState: Bundle? | ||
| 47 | ): View { | ||
| 48 | _binding = FragmentSettingsSearchBinding.inflate(layoutInflater) | ||
| 49 | return binding.root | ||
| 50 | } | ||
| 51 | |||
| 52 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 53 | super.onViewCreated(view, savedInstanceState) | ||
| 54 | settingsViewModel.setIsUsingSearch(true) | ||
| 55 | |||
| 56 | if (savedInstanceState != null) { | ||
| 57 | binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) | ||
| 58 | } | ||
| 59 | |||
| 60 | settingsAdapter = SettingsAdapter(this, requireContext()) | ||
| 61 | |||
| 62 | val dividerDecoration = MaterialDividerItemDecoration( | ||
| 63 | requireContext(), | ||
| 64 | LinearLayoutManager.VERTICAL | ||
| 65 | ) | ||
| 66 | dividerDecoration.isLastItemDecorated = false | ||
| 67 | binding.settingsList.apply { | ||
| 68 | adapter = settingsAdapter | ||
| 69 | layoutManager = LinearLayoutManager(requireContext()) | ||
| 70 | addItemDecoration(dividerDecoration) | ||
| 71 | } | ||
| 72 | |||
| 73 | focusSearch() | ||
| 74 | |||
| 75 | binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) } | ||
| 76 | binding.searchBackground.setOnClickListener { focusSearch() } | ||
| 77 | binding.clearButton.setOnClickListener { binding.searchText.setText("") } | ||
| 78 | binding.searchText.doOnTextChanged { _, _, _, _ -> | ||
| 79 | search() | ||
| 80 | binding.settingsList.smoothScrollToPosition(0) | ||
| 81 | } | ||
| 82 | settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { | ||
| 83 | if (it) { | ||
| 84 | settingsViewModel.setShouldReloadSettingsList(false) | ||
| 85 | search() | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | search() | ||
| 90 | |||
| 91 | setInsets() | ||
| 92 | } | ||
| 93 | |||
| 94 | override fun onSaveInstanceState(outState: Bundle) { | ||
| 95 | super.onSaveInstanceState(outState) | ||
| 96 | outState.putString(SEARCH_TEXT, binding.searchText.text.toString()) | ||
| 97 | } | ||
| 98 | |||
| 99 | private fun search() { | ||
| 100 | val searchTerm = binding.searchText.text.toString().lowercase() | ||
| 101 | binding.clearButton.visibility = | ||
| 102 | if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE | ||
| 103 | if (searchTerm.isEmpty()) { | ||
| 104 | binding.noResultsView.visibility = View.VISIBLE | ||
| 105 | settingsAdapter?.submitList(emptyList()) | ||
| 106 | return | ||
| 107 | } | ||
| 108 | |||
| 109 | val baseList = SettingsItem.settingsItems | ||
| 110 | val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1) | ||
| 111 | val sortedList: List<SettingsItem> = baseList.mapNotNull { item -> | ||
| 112 | val title = getString(item.value.nameId).lowercase() | ||
| 113 | val similarity = similarityAlgorithm.similarity(searchTerm, title) | ||
| 114 | if (similarity > 0.08) { | ||
| 115 | Pair(similarity, item) | ||
| 116 | } else { | ||
| 117 | null | ||
| 118 | } | ||
| 119 | }.sortedByDescending { it.first }.mapNotNull { | ||
| 120 | val item = it.second.value | ||
| 121 | val pairedSettingKey = item.setting.pairedSettingKey | ||
| 122 | val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) { | ||
| 123 | val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false) | ||
| 124 | if (pairedSettingValue) it.second.value else null | ||
| 125 | } else { | ||
| 126 | it.second.value | ||
| 127 | } | ||
| 128 | optionalSetting | ||
| 129 | } | ||
| 130 | settingsAdapter?.submitList(sortedList) | ||
| 131 | binding.noResultsView.visibility = | ||
| 132 | if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE | ||
| 133 | } | ||
| 134 | |||
| 135 | private fun focusSearch() { | ||
| 136 | binding.searchText.requestFocus() | ||
| 137 | val imm = requireActivity() | ||
| 138 | .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? | ||
| 139 | imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT) | ||
| 140 | } | ||
| 141 | |||
| 142 | private fun setInsets() = | ||
| 143 | ViewCompat.setOnApplyWindowInsetsListener( | ||
| 144 | binding.root | ||
| 145 | ) { _: View, windowInsets: WindowInsetsCompat -> | ||
| 146 | val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med) | ||
| 147 | val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) | ||
| 148 | val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip) | ||
| 149 | |||
| 150 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) | ||
| 151 | val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) | ||
| 152 | |||
| 153 | val leftInsets = barInsets.left + cutoutInsets.left | ||
| 154 | val rightInsets = barInsets.right + cutoutInsets.right | ||
| 155 | |||
| 156 | binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing) | ||
| 157 | binding.frameSearch.updatePadding( | ||
| 158 | left = leftInsets + sideMargin, | ||
| 159 | top = barInsets.top + topMargin, | ||
| 160 | right = rightInsets + sideMargin | ||
| 161 | ) | ||
| 162 | binding.noResultsView.updatePadding( | ||
| 163 | left = leftInsets, | ||
| 164 | right = rightInsets, | ||
| 165 | bottom = barInsets.bottom | ||
| 166 | ) | ||
| 167 | |||
| 168 | val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams | ||
| 169 | mlpSettingsList.leftMargin = leftInsets + sideMargin | ||
| 170 | mlpSettingsList.rightMargin = rightInsets + sideMargin | ||
| 171 | binding.settingsList.layoutParams = mlpSettingsList | ||
| 172 | |||
| 173 | val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams | ||
| 174 | mlpDivider.leftMargin = leftInsets + sideMargin | ||
| 175 | mlpDivider.rightMargin = rightInsets + sideMargin | ||
| 176 | binding.divider.layoutParams = mlpDivider | ||
| 177 | |||
| 178 | windowInsets | ||
| 179 | } | ||
| 180 | |||
| 181 | companion object { | ||
| 182 | const val SEARCH_TEXT = "SearchText" | ||
| 183 | } | ||
| 184 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt new file mode 100644 index 000000000..e35f51bc3 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.model | ||
| 5 | |||
| 6 | import androidx.lifecycle.LiveData | ||
| 7 | import androidx.lifecycle.MutableLiveData | ||
| 8 | import androidx.lifecycle.ViewModel | ||
| 9 | |||
| 10 | class EmulationViewModel : ViewModel() { | ||
| 11 | private val _emulationStarted = MutableLiveData(false) | ||
| 12 | val emulationStarted: LiveData<Boolean> get() = _emulationStarted | ||
| 13 | |||
| 14 | private val _isEmulationStopping = MutableLiveData(false) | ||
| 15 | val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping | ||
| 16 | |||
| 17 | private val _shaderProgress = MutableLiveData(0) | ||
| 18 | val shaderProgress: LiveData<Int> get() = _shaderProgress | ||
| 19 | |||
| 20 | private val _totalShaders = MutableLiveData(0) | ||
| 21 | val totalShaders: LiveData<Int> get() = _totalShaders | ||
| 22 | |||
| 23 | private val _shaderMessage = MutableLiveData("") | ||
| 24 | val shaderMessage: LiveData<String> get() = _shaderMessage | ||
| 25 | |||
| 26 | fun setEmulationStarted(started: Boolean) { | ||
| 27 | _emulationStarted.postValue(started) | ||
| 28 | } | ||
| 29 | |||
| 30 | fun setIsEmulationStopping(value: Boolean) { | ||
| 31 | _isEmulationStopping.value = value | ||
| 32 | } | ||
| 33 | |||
| 34 | fun setShaderProgress(progress: Int) { | ||
| 35 | _shaderProgress.value = progress | ||
| 36 | } | ||
| 37 | |||
| 38 | fun setTotalShaders(max: Int) { | ||
| 39 | _totalShaders.value = max | ||
| 40 | } | ||
| 41 | |||
| 42 | fun setShaderMessage(msg: String) { | ||
| 43 | _shaderMessage.value = msg | ||
| 44 | } | ||
| 45 | |||
| 46 | fun updateProgress(msg: String, progress: Int, max: Int) { | ||
| 47 | setShaderMessage(msg) | ||
| 48 | setShaderProgress(progress) | ||
| 49 | setTotalShaders(max) | ||
| 50 | } | ||
| 51 | |||
| 52 | fun clear() { | ||
| 53 | _emulationStarted.value = false | ||
| 54 | _isEmulationStopping.value = false | ||
| 55 | _shaderProgress.value = 0 | ||
| 56 | _totalShaders.value = 0 | ||
| 57 | _shaderMessage.value = "" | ||
| 58 | } | ||
| 59 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt new file mode 100644 index 000000000..d16d15fa6 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.model | ||
| 5 | |||
| 6 | import androidx.lifecycle.LiveData | ||
| 7 | import androidx.lifecycle.MutableLiveData | ||
| 8 | import androidx.lifecycle.SavedStateHandle | ||
| 9 | import androidx.lifecycle.ViewModel | ||
| 10 | import org.yuzu.yuzu_emu.R | ||
| 11 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 12 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | ||
| 13 | |||
| 14 | class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { | ||
| 15 | var game: Game? = null | ||
| 16 | |||
| 17 | var shouldSave = false | ||
| 18 | |||
| 19 | var clickedItem: SettingsItem? = null | ||
| 20 | |||
| 21 | private val _toolbarTitle = MutableLiveData("") | ||
| 22 | val toolbarTitle: LiveData<String> get() = _toolbarTitle | ||
| 23 | |||
| 24 | private val _shouldRecreate = MutableLiveData(false) | ||
| 25 | val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate | ||
| 26 | |||
| 27 | private val _shouldNavigateBack = MutableLiveData(false) | ||
| 28 | val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack | ||
| 29 | |||
| 30 | private val _shouldShowResetSettingsDialog = MutableLiveData(false) | ||
| 31 | val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog | ||
| 32 | |||
| 33 | private val _shouldReloadSettingsList = MutableLiveData(false) | ||
| 34 | val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList | ||
| 35 | |||
| 36 | private val _isUsingSearch = MutableLiveData(false) | ||
| 37 | val isUsingSearch: LiveData<Boolean> get() = _isUsingSearch | ||
| 38 | |||
| 39 | val sliderProgress = savedStateHandle.getStateFlow(KEY_SLIDER_PROGRESS, -1) | ||
| 40 | |||
| 41 | val sliderTextValue = savedStateHandle.getStateFlow(KEY_SLIDER_TEXT_VALUE, "") | ||
| 42 | |||
| 43 | val adapterItemChanged = savedStateHandle.getStateFlow(KEY_ADAPTER_ITEM_CHANGED, -1) | ||
| 44 | |||
| 45 | fun setToolbarTitle(value: String) { | ||
| 46 | _toolbarTitle.value = value | ||
| 47 | } | ||
| 48 | |||
| 49 | fun setShouldRecreate(value: Boolean) { | ||
| 50 | _shouldRecreate.value = value | ||
| 51 | } | ||
| 52 | |||
| 53 | fun setShouldNavigateBack(value: Boolean) { | ||
| 54 | _shouldNavigateBack.value = value | ||
| 55 | } | ||
| 56 | |||
| 57 | fun setShouldShowResetSettingsDialog(value: Boolean) { | ||
| 58 | _shouldShowResetSettingsDialog.value = value | ||
| 59 | } | ||
| 60 | |||
| 61 | fun setShouldReloadSettingsList(value: Boolean) { | ||
| 62 | _shouldReloadSettingsList.value = value | ||
| 63 | } | ||
| 64 | |||
| 65 | fun setIsUsingSearch(value: Boolean) { | ||
| 66 | _isUsingSearch.value = value | ||
| 67 | } | ||
| 68 | |||
| 69 | fun setSliderTextValue(value: Float, units: String) { | ||
| 70 | savedStateHandle[KEY_SLIDER_PROGRESS] = value | ||
| 71 | savedStateHandle[KEY_SLIDER_TEXT_VALUE] = String.format( | ||
| 72 | YuzuApplication.appContext.getString(R.string.value_with_units), | ||
| 73 | value.toInt().toString(), | ||
| 74 | units | ||
| 75 | ) | ||
| 76 | } | ||
| 77 | |||
| 78 | fun setSliderProgress(value: Float) { | ||
| 79 | savedStateHandle[KEY_SLIDER_PROGRESS] = value | ||
| 80 | } | ||
| 81 | |||
| 82 | fun setAdapterItemChanged(value: Int) { | ||
| 83 | savedStateHandle[KEY_ADAPTER_ITEM_CHANGED] = value | ||
| 84 | } | ||
| 85 | |||
| 86 | fun clear() { | ||
| 87 | game = null | ||
| 88 | shouldSave = false | ||
| 89 | } | ||
| 90 | |||
| 91 | companion object { | ||
| 92 | const val KEY_SLIDER_TEXT_VALUE = "SliderTextValue" | ||
| 93 | const val KEY_SLIDER_PROGRESS = "SliderProgress" | ||
| 94 | const val KEY_ADAPTER_ITEM_CHANGED = "AdapterItemChanged" | ||
| 95 | } | ||
| 96 | } | ||
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 aaf3a0ec1..7d8e06ad8 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 | |||
| @@ -33,17 +33,15 @@ import java.io.IOException | |||
| 33 | import kotlinx.coroutines.Dispatchers | 33 | import kotlinx.coroutines.Dispatchers |
| 34 | import kotlinx.coroutines.launch | 34 | import kotlinx.coroutines.launch |
| 35 | import kotlinx.coroutines.withContext | 35 | import kotlinx.coroutines.withContext |
| 36 | import org.yuzu.yuzu_emu.HomeNavigationDirections | ||
| 36 | import org.yuzu.yuzu_emu.NativeLibrary | 37 | import org.yuzu.yuzu_emu.NativeLibrary |
| 37 | import org.yuzu.yuzu_emu.R | 38 | import org.yuzu.yuzu_emu.R |
| 38 | import org.yuzu.yuzu_emu.activities.EmulationActivity | 39 | import org.yuzu.yuzu_emu.activities.EmulationActivity |
| 39 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding | 40 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding |
| 40 | import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding | 41 | import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding |
| 41 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 42 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 42 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | ||
| 43 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity | ||
| 44 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 43 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 45 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment | 44 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment |
| 46 | import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment | ||
| 47 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | 45 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment |
| 48 | import org.yuzu.yuzu_emu.model.GamesViewModel | 46 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 49 | import org.yuzu.yuzu_emu.model.HomeViewModel | 47 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| @@ -54,7 +52,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 54 | 52 | ||
| 55 | private val homeViewModel: HomeViewModel by viewModels() | 53 | private val homeViewModel: HomeViewModel by viewModels() |
| 56 | private val gamesViewModel: GamesViewModel by viewModels() | 54 | private val gamesViewModel: GamesViewModel by viewModels() |
| 57 | private val settingsViewModel: SettingsViewModel by viewModels() | ||
| 58 | 55 | ||
| 59 | override var themeId: Int = 0 | 56 | override var themeId: Int = 0 |
| 60 | 57 | ||
| @@ -62,8 +59,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 62 | val splashScreen = installSplashScreen() | 59 | val splashScreen = installSplashScreen() |
| 63 | splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } | 60 | splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } |
| 64 | 61 | ||
| 65 | settingsViewModel.settings.loadSettings() | ||
| 66 | |||
| 67 | ThemeHelper.setTheme(this) | 62 | ThemeHelper.setTheme(this) |
| 68 | 63 | ||
| 69 | super.onCreate(savedInstanceState) | 64 | super.onCreate(savedInstanceState) |
| @@ -109,11 +104,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 109 | when (it.itemId) { | 104 | when (it.itemId) { |
| 110 | R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true) | 105 | R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true) |
| 111 | R.id.searchFragment -> gamesViewModel.setSearchFocused(true) | 106 | R.id.searchFragment -> gamesViewModel.setSearchFocused(true) |
| 112 | R.id.homeSettingsFragment -> SettingsActivity.launch( | 107 | R.id.homeSettingsFragment -> { |
| 113 | this, | 108 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( |
| 114 | SettingsFile.FILE_NAME_CONFIG, | 109 | null, |
| 115 | "" | 110 | SettingsFile.FILE_NAME_CONFIG |
| 116 | ) | 111 | ) |
| 112 | navHostFragment.navController.navigate(action) | ||
| 113 | } | ||
| 117 | } | 114 | } |
| 118 | } | 115 | } |
| 119 | 116 | ||
| @@ -303,8 +300,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 303 | fun processKey(result: Uri): Boolean { | 300 | fun processKey(result: Uri): Boolean { |
| 304 | if (FileUtil.getExtension(result) != "keys") { | 301 | if (FileUtil.getExtension(result) != "keys") { |
| 305 | MessageDialogFragment.newInstance( | 302 | MessageDialogFragment.newInstance( |
| 306 | R.string.reading_keys_failure, | 303 | titleId = R.string.reading_keys_failure, |
| 307 | R.string.install_prod_keys_failure_extension_description | 304 | descriptionId = R.string.install_prod_keys_failure_extension_description |
| 308 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | 305 | ).show(supportFragmentManager, MessageDialogFragment.TAG) |
| 309 | return false | 306 | return false |
| 310 | } | 307 | } |
| @@ -332,9 +329,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 332 | return true | 329 | return true |
| 333 | } else { | 330 | } else { |
| 334 | MessageDialogFragment.newInstance( | 331 | MessageDialogFragment.newInstance( |
| 335 | R.string.invalid_keys_error, | 332 | titleId = R.string.invalid_keys_error, |
| 336 | R.string.install_keys_failure_description, | 333 | descriptionId = R.string.install_keys_failure_description, |
| 337 | R.string.dumping_keys_quickstart_link | 334 | helpLinkId = R.string.dumping_keys_quickstart_link |
| 338 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | 335 | ).show(supportFragmentManager, MessageDialogFragment.TAG) |
| 339 | return false | 336 | return false |
| 340 | } | 337 | } |
| @@ -372,8 +369,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 372 | val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 | 369 | val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 |
| 373 | messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { | 370 | messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { |
| 374 | MessageDialogFragment.newInstance( | 371 | MessageDialogFragment.newInstance( |
| 375 | R.string.firmware_installed_failure, | 372 | titleId = R.string.firmware_installed_failure, |
| 376 | R.string.firmware_installed_failure_description | 373 | descriptionId = R.string.firmware_installed_failure_description |
| 377 | ) | 374 | ) |
| 378 | } else { | 375 | } else { |
| 379 | firmwarePath.deleteRecursively() | 376 | firmwarePath.deleteRecursively() |
| @@ -403,8 +400,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 403 | 400 | ||
| 404 | if (FileUtil.getExtension(result) != "bin") { | 401 | if (FileUtil.getExtension(result) != "bin") { |
| 405 | MessageDialogFragment.newInstance( | 402 | MessageDialogFragment.newInstance( |
| 406 | R.string.reading_keys_failure, | 403 | titleId = R.string.reading_keys_failure, |
| 407 | R.string.install_amiibo_keys_failure_extension_description | 404 | descriptionId = R.string.install_amiibo_keys_failure_extension_description |
| 408 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | 405 | ).show(supportFragmentManager, MessageDialogFragment.TAG) |
| 409 | return@registerForActivityResult | 406 | return@registerForActivityResult |
| 410 | } | 407 | } |
| @@ -430,9 +427,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 430 | ).show() | 427 | ).show() |
| 431 | } else { | 428 | } else { |
| 432 | MessageDialogFragment.newInstance( | 429 | MessageDialogFragment.newInstance( |
| 433 | R.string.invalid_keys_error, | 430 | titleId = R.string.invalid_keys_error, |
| 434 | R.string.install_keys_failure_description, | 431 | descriptionId = R.string.install_keys_failure_description, |
| 435 | R.string.dumping_keys_quickstart_link | 432 | helpLinkId = R.string.dumping_keys_quickstart_link |
| 436 | ).show(supportFragmentManager, MessageDialogFragment.TAG) | 433 | ).show(supportFragmentManager, MessageDialogFragment.TAG) |
| 437 | } | 434 | } |
| 438 | } | 435 | } |
| @@ -504,96 +501,91 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 504 | var errorBaseGame = 0 | 501 | var errorBaseGame = 0 |
| 505 | var errorExtension = 0 | 502 | var errorExtension = 0 |
| 506 | var errorOther = 0 | 503 | var errorOther = 0 |
| 507 | var errorTotal = 0 | 504 | documents.forEach { |
| 508 | lifecycleScope.launch { | 505 | when (NativeLibrary.installFileToNand(it.toString())) { |
| 509 | documents.forEach { | 506 | NativeLibrary.InstallFileToNandResult.Success -> { |
| 510 | when (NativeLibrary.installFileToNand(it.toString())) { | 507 | installSuccess += 1 |
| 511 | NativeLibrary.InstallFileToNandResult.Success -> { | ||
| 512 | installSuccess += 1 | ||
| 513 | } | ||
| 514 | |||
| 515 | NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { | ||
| 516 | installOverwrite += 1 | ||
| 517 | } | ||
| 518 | |||
| 519 | NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { | ||
| 520 | errorBaseGame += 1 | ||
| 521 | } | ||
| 522 | |||
| 523 | NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { | ||
| 524 | errorExtension += 1 | ||
| 525 | } | ||
| 526 | |||
| 527 | else -> { | ||
| 528 | errorOther += 1 | ||
| 529 | } | ||
| 530 | } | 508 | } |
| 531 | } | 509 | |
| 532 | withContext(Dispatchers.Main) { | 510 | NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { |
| 533 | val separator = System.getProperty("line.separator") ?: "\n" | 511 | installOverwrite += 1 |
| 534 | val installResult = StringBuilder() | ||
| 535 | if (installSuccess > 0) { | ||
| 536 | installResult.append( | ||
| 537 | getString( | ||
| 538 | R.string.install_game_content_success_install, | ||
| 539 | installSuccess | ||
| 540 | ) | ||
| 541 | ) | ||
| 542 | installResult.append(separator) | ||
| 543 | } | 512 | } |
| 544 | if (installOverwrite > 0) { | 513 | |
| 545 | installResult.append( | 514 | NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { |
| 546 | getString( | 515 | errorBaseGame += 1 |
| 547 | R.string.install_game_content_success_overwrite, | ||
| 548 | installOverwrite | ||
| 549 | ) | ||
| 550 | ) | ||
| 551 | installResult.append(separator) | ||
| 552 | } | 516 | } |
| 553 | errorTotal = errorBaseGame + errorExtension + errorOther | 517 | |
| 554 | if (errorTotal > 0) { | 518 | NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { |
| 555 | installResult.append(separator) | 519 | errorExtension += 1 |
| 556 | installResult.append( | 520 | } |
| 557 | getString( | 521 | |
| 558 | R.string.install_game_content_failed_count, | 522 | else -> { |
| 559 | errorTotal | 523 | errorOther += 1 |
| 560 | ) | ||
| 561 | ) | ||
| 562 | installResult.append(separator) | ||
| 563 | if (errorBaseGame > 0) { | ||
| 564 | installResult.append(separator) | ||
| 565 | installResult.append( | ||
| 566 | getString(R.string.install_game_content_failure_base) | ||
| 567 | ) | ||
| 568 | installResult.append(separator) | ||
| 569 | } | ||
| 570 | if (errorExtension > 0) { | ||
| 571 | installResult.append(separator) | ||
| 572 | installResult.append( | ||
| 573 | getString(R.string.install_game_content_failure_file_extension) | ||
| 574 | ) | ||
| 575 | installResult.append(separator) | ||
| 576 | } | ||
| 577 | if (errorOther > 0) { | ||
| 578 | installResult.append( | ||
| 579 | getString(R.string.install_game_content_failure_description) | ||
| 580 | ) | ||
| 581 | installResult.append(separator) | ||
| 582 | } | ||
| 583 | LongMessageDialogFragment.newInstance( | ||
| 584 | R.string.install_game_content_failure, | ||
| 585 | installResult.toString().trim(), | ||
| 586 | R.string.install_game_content_help_link | ||
| 587 | ).show(supportFragmentManager, LongMessageDialogFragment.TAG) | ||
| 588 | } else { | ||
| 589 | LongMessageDialogFragment.newInstance( | ||
| 590 | R.string.install_game_content_success, | ||
| 591 | installResult.toString().trim() | ||
| 592 | ).show(supportFragmentManager, LongMessageDialogFragment.TAG) | ||
| 593 | } | 524 | } |
| 594 | } | 525 | } |
| 595 | } | 526 | } |
| 596 | return@newInstance installSuccess + installOverwrite + errorTotal | 527 | |
| 528 | val separator = System.getProperty("line.separator") ?: "\n" | ||
| 529 | val installResult = StringBuilder() | ||
| 530 | if (installSuccess > 0) { | ||
| 531 | installResult.append( | ||
| 532 | getString( | ||
| 533 | R.string.install_game_content_success_install, | ||
| 534 | installSuccess | ||
| 535 | ) | ||
| 536 | ) | ||
| 537 | installResult.append(separator) | ||
| 538 | } | ||
| 539 | if (installOverwrite > 0) { | ||
| 540 | installResult.append( | ||
| 541 | getString( | ||
| 542 | R.string.install_game_content_success_overwrite, | ||
| 543 | installOverwrite | ||
| 544 | ) | ||
| 545 | ) | ||
| 546 | installResult.append(separator) | ||
| 547 | } | ||
| 548 | val errorTotal: Int = errorBaseGame + errorExtension + errorOther | ||
| 549 | if (errorTotal > 0) { | ||
| 550 | installResult.append(separator) | ||
| 551 | installResult.append( | ||
| 552 | getString( | ||
| 553 | R.string.install_game_content_failed_count, | ||
| 554 | errorTotal | ||
| 555 | ) | ||
| 556 | ) | ||
| 557 | installResult.append(separator) | ||
| 558 | if (errorBaseGame > 0) { | ||
| 559 | installResult.append(separator) | ||
| 560 | installResult.append( | ||
| 561 | getString(R.string.install_game_content_failure_base) | ||
| 562 | ) | ||
| 563 | installResult.append(separator) | ||
| 564 | } | ||
| 565 | if (errorExtension > 0) { | ||
| 566 | installResult.append(separator) | ||
| 567 | installResult.append( | ||
| 568 | getString(R.string.install_game_content_failure_file_extension) | ||
| 569 | ) | ||
| 570 | installResult.append(separator) | ||
| 571 | } | ||
| 572 | if (errorOther > 0) { | ||
| 573 | installResult.append( | ||
| 574 | getString(R.string.install_game_content_failure_description) | ||
| 575 | ) | ||
| 576 | installResult.append(separator) | ||
| 577 | } | ||
| 578 | return@newInstance MessageDialogFragment.newInstance( | ||
| 579 | titleId = R.string.install_game_content_failure, | ||
| 580 | descriptionString = installResult.toString().trim(), | ||
| 581 | helpLinkId = R.string.install_game_content_help_link | ||
| 582 | ) | ||
| 583 | } else { | ||
| 584 | return@newInstance MessageDialogFragment.newInstance( | ||
| 585 | titleId = R.string.install_game_content_success, | ||
| 586 | descriptionString = installResult.toString().trim() | ||
| 587 | ) | ||
| 588 | } | ||
| 597 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) | 589 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) |
| 598 | } | 590 | } |
| 599 | } | 591 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt deleted file mode 100644 index 9cfda74ee..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt +++ /dev/null | |||
| @@ -1,25 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.utils | ||
| 5 | |||
| 6 | class BiMap<K, V> { | ||
| 7 | private val forward: MutableMap<K, V> = HashMap() | ||
| 8 | private val backward: MutableMap<V, K> = HashMap() | ||
| 9 | |||
| 10 | @Synchronized | ||
| 11 | fun add(key: K, value: V) { | ||
| 12 | forward[key] = value | ||
| 13 | backward[value] = key | ||
| 14 | } | ||
| 15 | |||
| 16 | @Synchronized | ||
| 17 | fun getForward(key: K): V? { | ||
| 18 | return forward[key] | ||
| 19 | } | ||
| 20 | |||
| 21 | @Synchronized | ||
| 22 | fun getBackward(key: V): K? { | ||
| 23 | return backward[key] | ||
| 24 | } | ||
| 25 | } | ||
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 2ee63697e..3c9f6bad0 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 | |||
| @@ -3,18 +3,18 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 7 | import java.io.IOException | 6 | import java.io.IOException |
| 8 | import org.yuzu.yuzu_emu.NativeLibrary | 7 | import org.yuzu.yuzu_emu.NativeLibrary |
| 8 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 9 | 9 | ||
| 10 | object DirectoryInitialization { | 10 | object DirectoryInitialization { |
| 11 | private var userPath: String? = null | 11 | private var userPath: String? = null |
| 12 | 12 | ||
| 13 | var areDirectoriesReady: Boolean = false | 13 | var areDirectoriesReady: Boolean = false |
| 14 | 14 | ||
| 15 | fun start(context: Context) { | 15 | fun start() { |
| 16 | if (!areDirectoriesReady) { | 16 | if (!areDirectoriesReady) { |
| 17 | initializeInternalStorage(context) | 17 | initializeInternalStorage() |
| 18 | NativeLibrary.initializeEmulation() | 18 | NativeLibrary.initializeEmulation() |
| 19 | areDirectoriesReady = true | 19 | areDirectoriesReady = true |
| 20 | } | 20 | } |
| @@ -26,9 +26,9 @@ object DirectoryInitialization { | |||
| 26 | return userPath | 26 | return userPath |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | private fun initializeInternalStorage(context: Context) { | 29 | private fun initializeInternalStorage() { |
| 30 | try { | 30 | try { |
| 31 | userPath = context.getExternalFilesDir(null)!!.canonicalPath | 31 | userPath = YuzuApplication.appContext.getExternalFilesDir(null)!!.canonicalPath |
| 32 | NativeLibrary.setAppDirectory(userPath!!) | 32 | NativeLibrary.setAppDirectory(userPath!!) |
| 33 | } catch (e: IOException) { | 33 | } catch (e: IOException) { |
| 34 | e.printStackTrace() | 34 | e.printStackTrace() |
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 f71d0a098..e0ee29c9b 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 | |||
| @@ -63,13 +63,13 @@ object GameHelper { | |||
| 63 | ) | 63 | ) |
| 64 | } else { | 64 | } else { |
| 65 | if (Game.extensions.contains(FileUtil.getExtension(it.uri))) { | 65 | if (Game.extensions.contains(FileUtil.getExtension(it.uri))) { |
| 66 | games.add(getGame(it.uri)) | 66 | games.add(getGame(it.uri, true)) |
| 67 | } | 67 | } |
| 68 | } | 68 | } |
| 69 | } | 69 | } |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | private fun getGame(uri: Uri): Game { | 72 | fun getGame(uri: Uri, addedToLibrary: Boolean): Game { |
| 73 | val filePath = uri.toString() | 73 | val filePath = uri.toString() |
| 74 | var name = NativeLibrary.getTitle(filePath) | 74 | var name = NativeLibrary.getTitle(filePath) |
| 75 | 75 | ||
| @@ -94,11 +94,13 @@ object GameHelper { | |||
| 94 | NativeLibrary.isHomebrew(filePath) | 94 | NativeLibrary.isHomebrew(filePath) |
| 95 | ) | 95 | ) |
| 96 | 96 | ||
| 97 | val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) | 97 | if (addedToLibrary) { |
| 98 | if (addedTime == 0L) { | 98 | val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) |
| 99 | preferences.edit() | 99 | if (addedTime == 0L) { |
| 100 | .putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis()) | 100 | preferences.edit() |
| 101 | .apply() | 101 | .putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis()) |
| 102 | .apply() | ||
| 103 | } | ||
| 102 | } | 104 | } |
| 103 | 105 | ||
| 104 | return newGame | 106 | return newGame |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt new file mode 100644 index 000000000..c0fe596d7 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.utils | ||
| 5 | |||
| 6 | import android.graphics.Bitmap | ||
| 7 | import android.graphics.BitmapFactory | ||
| 8 | import android.widget.ImageView | ||
| 9 | import androidx.core.graphics.drawable.toDrawable | ||
| 10 | import coil.ImageLoader | ||
| 11 | import coil.decode.DataSource | ||
| 12 | import coil.fetch.DrawableResult | ||
| 13 | import coil.fetch.FetchResult | ||
| 14 | import coil.fetch.Fetcher | ||
| 15 | import coil.key.Keyer | ||
| 16 | import coil.memory.MemoryCache | ||
| 17 | import coil.request.ImageRequest | ||
| 18 | import coil.request.Options | ||
| 19 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 20 | import org.yuzu.yuzu_emu.R | ||
| 21 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 22 | import org.yuzu.yuzu_emu.model.Game | ||
| 23 | |||
| 24 | class GameIconFetcher( | ||
| 25 | private val game: Game, | ||
| 26 | private val options: Options | ||
| 27 | ) : Fetcher { | ||
| 28 | override suspend fun fetch(): FetchResult { | ||
| 29 | return DrawableResult( | ||
| 30 | drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources), | ||
| 31 | isSampled = false, | ||
| 32 | dataSource = DataSource.DISK | ||
| 33 | ) | ||
| 34 | } | ||
| 35 | |||
| 36 | private fun decodeGameIcon(uri: String): Bitmap? { | ||
| 37 | val data = NativeLibrary.getIcon(uri) | ||
| 38 | return BitmapFactory.decodeByteArray( | ||
| 39 | data, | ||
| 40 | 0, | ||
| 41 | data.size, | ||
| 42 | BitmapFactory.Options() | ||
| 43 | ) | ||
| 44 | } | ||
| 45 | |||
| 46 | class Factory : Fetcher.Factory<Game> { | ||
| 47 | override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher = | ||
| 48 | GameIconFetcher(data, options) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | class GameIconKeyer : Keyer<Game> { | ||
| 53 | override fun key(data: Game, options: Options): String = data.path | ||
| 54 | } | ||
| 55 | |||
| 56 | object GameIconUtils { | ||
| 57 | private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext) | ||
| 58 | .components { | ||
| 59 | add(GameIconKeyer()) | ||
| 60 | add(GameIconFetcher.Factory()) | ||
| 61 | } | ||
| 62 | .memoryCache { | ||
| 63 | MemoryCache.Builder(YuzuApplication.appContext) | ||
| 64 | .maxSizePercent(0.25) | ||
| 65 | .build() | ||
| 66 | } | ||
| 67 | .build() | ||
| 68 | |||
| 69 | fun loadGameIcon(game: Game, imageView: ImageView) { | ||
| 70 | val request = ImageRequest.Builder(YuzuApplication.appContext) | ||
| 71 | .data(game) | ||
| 72 | .target(imageView) | ||
| 73 | .error(R.drawable.default_icon) | ||
| 74 | .build() | ||
| 75 | imageLoader.enqueue(request) | ||
| 76 | } | ||
| 77 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt new file mode 100644 index 000000000..9425f8b99 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.utils | ||
| 5 | |||
| 6 | object NativeConfig { | ||
| 7 | external fun getBoolean(key: String, getDefault: Boolean): Boolean | ||
| 8 | external fun setBoolean(key: String, value: Boolean) | ||
| 9 | |||
| 10 | external fun getByte(key: String, getDefault: Boolean): Byte | ||
| 11 | external fun setByte(key: String, value: Byte) | ||
| 12 | |||
| 13 | external fun getShort(key: String, getDefault: Boolean): Short | ||
| 14 | external fun setShort(key: String, value: Short) | ||
| 15 | |||
| 16 | external fun getInt(key: String, getDefault: Boolean): Int | ||
| 17 | external fun setInt(key: String, value: Int) | ||
| 18 | |||
| 19 | external fun getFloat(key: String, getDefault: Boolean): Float | ||
| 20 | external fun setFloat(key: String, value: Float) | ||
| 21 | |||
| 22 | external fun getLong(key: String, getDefault: Boolean): Long | ||
| 23 | external fun setLong(key: String, value: Long) | ||
| 24 | |||
| 25 | external fun getString(key: String, getDefault: Boolean): String | ||
| 26 | external fun setString(key: String, value: String) | ||
| 27 | |||
| 28 | external fun getIsRuntimeModifiable(key: String): Boolean | ||
| 29 | |||
| 30 | external fun getConfigHeader(category: Int): String | ||
| 31 | |||
| 32 | external fun getPairedSettingKey(key: String): String | ||
| 33 | } | ||
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 685ccaa76..2f0868c63 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 | |||
| @@ -7,7 +7,6 @@ import android.content.Context | |||
| 7 | import android.util.AttributeSet | 7 | import android.util.AttributeSet |
| 8 | import android.util.Rational | 8 | import android.util.Rational |
| 9 | import android.view.SurfaceView | 9 | import android.view.SurfaceView |
| 10 | import kotlin.math.roundToInt | ||
| 11 | 10 | ||
| 12 | class FixedRatioSurfaceView @JvmOverloads constructor( | 11 | class FixedRatioSurfaceView @JvmOverloads constructor( |
| 13 | context: Context, | 12 | context: Context, |
| @@ -22,27 +21,44 @@ class FixedRatioSurfaceView @JvmOverloads constructor( | |||
| 22 | */ | 21 | */ |
| 23 | fun setAspectRatio(ratio: Rational?) { | 22 | fun setAspectRatio(ratio: Rational?) { |
| 24 | aspectRatio = ratio?.toFloat() ?: 0f | 23 | aspectRatio = ratio?.toFloat() ?: 0f |
| 24 | requestLayout() | ||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | 27 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { |
| 28 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) | 28 | val displayWidth: Float = MeasureSpec.getSize(widthMeasureSpec).toFloat() |
| 29 | val width = MeasureSpec.getSize(widthMeasureSpec) | 29 | val displayHeight: Float = MeasureSpec.getSize(heightMeasureSpec).toFloat() |
| 30 | val height = MeasureSpec.getSize(heightMeasureSpec) | ||
| 31 | if (aspectRatio != 0f) { | 30 | if (aspectRatio != 0f) { |
| 32 | val newWidth: Int | 31 | val displayAspect = displayWidth / displayHeight |
| 33 | val newHeight: Int | 32 | if (displayAspect < aspectRatio) { |
| 34 | if (height * aspectRatio < width) { | 33 | // Max out width |
| 35 | newWidth = (height * aspectRatio).roundToInt() | 34 | val halfHeight = displayHeight / 2 |
| 36 | newHeight = height | 35 | val surfaceHeight = displayWidth / aspectRatio |
| 36 | val newTop: Float = halfHeight - (surfaceHeight / 2) | ||
| 37 | val newBottom: Float = halfHeight + (surfaceHeight / 2) | ||
| 38 | super.onMeasure( | ||
| 39 | widthMeasureSpec, | ||
| 40 | MeasureSpec.makeMeasureSpec( | ||
| 41 | newBottom.toInt() - newTop.toInt(), | ||
| 42 | MeasureSpec.EXACTLY | ||
| 43 | ) | ||
| 44 | ) | ||
| 45 | return | ||
| 37 | } else { | 46 | } else { |
| 38 | newWidth = width | 47 | // Max out height |
| 39 | newHeight = (width / aspectRatio).roundToInt() | 48 | val halfWidth = displayWidth / 2 |
| 49 | val surfaceWidth = displayHeight * aspectRatio | ||
| 50 | val newLeft: Float = halfWidth - (surfaceWidth / 2) | ||
| 51 | val newRight: Float = halfWidth + (surfaceWidth / 2) | ||
| 52 | super.onMeasure( | ||
| 53 | MeasureSpec.makeMeasureSpec( | ||
| 54 | newRight.toInt() - newLeft.toInt(), | ||
| 55 | MeasureSpec.EXACTLY | ||
| 56 | ), | ||
| 57 | heightMeasureSpec | ||
| 58 | ) | ||
| 59 | return | ||
| 40 | } | 60 | } |
| 41 | val left = (width - newWidth) / 2 | ||
| 42 | val top = (height - newHeight) / 2 | ||
| 43 | setLeftTopRightBottom(left, top, left + newWidth, top + newHeight) | ||
| 44 | } else { | ||
| 45 | setLeftTopRightBottom(0, 0, width, height) | ||
| 46 | } | 61 | } |
| 62 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) | ||
| 47 | } | 63 | } |
| 48 | } | 64 | } |
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index e2ed08e9f..e15d1480b 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt | |||
| @@ -14,6 +14,8 @@ add_library(yuzu-android SHARED | |||
| 14 | id_cache.cpp | 14 | id_cache.cpp |
| 15 | id_cache.h | 15 | id_cache.h |
| 16 | native.cpp | 16 | native.cpp |
| 17 | native_config.cpp | ||
| 18 | uisettings.cpp | ||
| 17 | ) | 19 | ) |
| 18 | 20 | ||
| 19 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) | 21 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) |
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 5e1f10f99..34b425cb4 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp | |||
| @@ -11,22 +11,25 @@ | |||
| 11 | #include "common/fs/path_util.h" | 11 | #include "common/fs/path_util.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/settings.h" | 13 | #include "common/settings.h" |
| 14 | #include "common/settings_enums.h" | ||
| 14 | #include "core/hle/service/acc/profile_manager.h" | 15 | #include "core/hle/service/acc/profile_manager.h" |
| 15 | #include "input_common/main.h" | 16 | #include "input_common/main.h" |
| 16 | #include "jni/config.h" | 17 | #include "jni/config.h" |
| 17 | #include "jni/default_ini.h" | 18 | #include "jni/default_ini.h" |
| 19 | #include "uisettings.h" | ||
| 18 | 20 | ||
| 19 | namespace FS = Common::FS; | 21 | namespace FS = Common::FS; |
| 20 | 22 | ||
| 21 | Config::Config(std::optional<std::filesystem::path> config_path) | 23 | Config::Config(const std::string& config_name, ConfigType config_type) |
| 22 | : config_loc{config_path.value_or(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini")}, | 24 | : type(config_type), global{config_type == ConfigType::GlobalConfig} { |
| 23 | config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} { | 25 | Initialize(config_name); |
| 24 | Reload(); | ||
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | Config::~Config() = default; | 28 | Config::~Config() = default; |
| 28 | 29 | ||
| 29 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | 30 | bool Config::LoadINI(const std::string& default_contents, bool retry) { |
| 31 | void(FS::CreateParentDir(config_loc)); | ||
| 32 | config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc)); | ||
| 30 | const auto config_loc_str = FS::PathToUTF8String(config_loc); | 33 | const auto config_loc_str = FS::PathToUTF8String(config_loc); |
| 31 | if (config->ParseError() < 0) { | 34 | if (config->ParseError() < 0) { |
| 32 | if (retry) { | 35 | if (retry) { |
| @@ -144,7 +147,9 @@ void Config::ReadValues() { | |||
| 144 | Service::Account::MAX_USERS - 1); | 147 | Service::Account::MAX_USERS - 1); |
| 145 | 148 | ||
| 146 | // Disable docked mode by default on Android | 149 | // Disable docked mode by default on Android |
| 147 | Settings::values.use_docked_mode = config->GetBoolean("System", "use_docked_mode", false); | 150 | Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false) |
| 151 | ? Settings::ConsoleMode::Docked | ||
| 152 | : Settings::ConsoleMode::Handheld); | ||
| 148 | 153 | ||
| 149 | const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); | 154 | const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); |
| 150 | if (rng_seed_enabled) { | 155 | if (rng_seed_enabled) { |
| @@ -298,9 +303,28 @@ void Config::ReadValues() { | |||
| 298 | 303 | ||
| 299 | // Network | 304 | // Network |
| 300 | ReadSetting("Network", Settings::values.network_interface); | 305 | ReadSetting("Network", Settings::values.network_interface); |
| 306 | |||
| 307 | // Android | ||
| 308 | ReadSetting("Android", AndroidSettings::values.picture_in_picture); | ||
| 309 | ReadSetting("Android", AndroidSettings::values.screen_layout); | ||
| 301 | } | 310 | } |
| 302 | 311 | ||
| 303 | void Config::Reload() { | 312 | void Config::Initialize(const std::string& config_name) { |
| 313 | const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); | ||
| 314 | const auto config_file = fmt::format("{}.ini", config_name); | ||
| 315 | |||
| 316 | switch (type) { | ||
| 317 | case ConfigType::GlobalConfig: | ||
| 318 | config_loc = FS::PathToUTF8String(fs_config_loc / config_file); | ||
| 319 | break; | ||
| 320 | case ConfigType::PerGameConfig: | ||
| 321 | config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); | ||
| 322 | break; | ||
| 323 | case ConfigType::InputProfile: | ||
| 324 | config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); | ||
| 325 | LoadINI(DefaultINI::android_config_file); | ||
| 326 | return; | ||
| 327 | } | ||
| 304 | LoadINI(DefaultINI::android_config_file); | 328 | LoadINI(DefaultINI::android_config_file); |
| 305 | ReadValues(); | 329 | ReadValues(); |
| 306 | } | 330 | } |
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h index 0d7d6e94d..e1e8f47ed 100644 --- a/src/android/app/src/main/jni/config.h +++ b/src/android/app/src/main/jni/config.h | |||
| @@ -13,25 +13,35 @@ | |||
| 13 | class INIReader; | 13 | class INIReader; |
| 14 | 14 | ||
| 15 | class Config { | 15 | class Config { |
| 16 | std::filesystem::path config_loc; | ||
| 17 | std::unique_ptr<INIReader> config; | ||
| 18 | |||
| 19 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | 16 | bool LoadINI(const std::string& default_contents = "", bool retry = true); |
| 20 | void ReadValues(); | ||
| 21 | 17 | ||
| 22 | public: | 18 | public: |
| 23 | explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt); | 19 | enum class ConfigType { |
| 20 | GlobalConfig, | ||
| 21 | PerGameConfig, | ||
| 22 | InputProfile, | ||
| 23 | }; | ||
| 24 | |||
| 25 | explicit Config(const std::string& config_name = "config", | ||
| 26 | ConfigType config_type = ConfigType::GlobalConfig); | ||
| 24 | ~Config(); | 27 | ~Config(); |
| 25 | 28 | ||
| 26 | void Reload(); | 29 | void Initialize(const std::string& config_name); |
| 27 | 30 | ||
| 28 | private: | 31 | private: |
| 29 | /** | 32 | /** |
| 30 | * Applies a value read from the sdl2_config to a Setting. | 33 | * Applies a value read from the config to a Setting. |
| 31 | * | 34 | * |
| 32 | * @param group The name of the INI group | 35 | * @param group The name of the INI group |
| 33 | * @param setting The yuzu setting to modify | 36 | * @param setting The yuzu setting to modify |
| 34 | */ | 37 | */ |
| 35 | template <typename Type, bool ranged> | 38 | template <typename Type, bool ranged> |
| 36 | void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); | 39 | void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); |
| 40 | |||
| 41 | void ReadValues(); | ||
| 42 | |||
| 43 | const ConfigType type; | ||
| 44 | std::unique_ptr<INIReader> config; | ||
| 45 | std::string config_loc; | ||
| 46 | const bool global; | ||
| 37 | }; | 47 | }; |
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index 9cbbf23a3..960abf95a 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp | |||
| @@ -15,6 +15,8 @@ static jclass s_disk_cache_progress_class; | |||
| 15 | static jclass s_load_callback_stage_class; | 15 | static jclass s_load_callback_stage_class; |
| 16 | static jmethodID s_exit_emulation_activity; | 16 | static jmethodID s_exit_emulation_activity; |
| 17 | static jmethodID s_disk_cache_load_progress; | 17 | static jmethodID s_disk_cache_load_progress; |
| 18 | static jmethodID s_on_emulation_started; | ||
| 19 | static jmethodID s_on_emulation_stopped; | ||
| 18 | 20 | ||
| 19 | static constexpr jint JNI_VERSION = JNI_VERSION_1_6; | 21 | static constexpr jint JNI_VERSION = JNI_VERSION_1_6; |
| 20 | 22 | ||
| @@ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() { | |||
| 59 | return s_disk_cache_load_progress; | 61 | return s_disk_cache_load_progress; |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 64 | jmethodID GetOnEmulationStarted() { | ||
| 65 | return s_on_emulation_started; | ||
| 66 | } | ||
| 67 | |||
| 68 | jmethodID GetOnEmulationStopped() { | ||
| 69 | return s_on_emulation_stopped; | ||
| 70 | } | ||
| 71 | |||
| 62 | } // namespace IDCache | 72 | } // namespace IDCache |
| 63 | 73 | ||
| 64 | #ifdef __cplusplus | 74 | #ifdef __cplusplus |
| @@ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |||
| 85 | env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); | 95 | env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); |
| 86 | s_disk_cache_load_progress = | 96 | s_disk_cache_load_progress = |
| 87 | env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); | 97 | env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); |
| 98 | s_on_emulation_started = | ||
| 99 | env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); | ||
| 100 | s_on_emulation_stopped = | ||
| 101 | env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); | ||
| 88 | 102 | ||
| 89 | // Initialize Android Storage | 103 | // Initialize Android Storage |
| 90 | Common::FS::Android::RegisterCallbacks(env, s_native_library_class); | 104 | Common::FS::Android::RegisterCallbacks(env, s_native_library_class); |
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h index be535fe1e..b76158928 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h | |||
| @@ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass(); | |||
| 15 | jclass GetDiskCacheLoadCallbackStageClass(); | 15 | jclass GetDiskCacheLoadCallbackStageClass(); |
| 16 | jmethodID GetExitEmulationActivity(); | 16 | jmethodID GetExitEmulationActivity(); |
| 17 | jmethodID GetDiskCacheLoadProgress(); | 17 | jmethodID GetDiskCacheLoadProgress(); |
| 18 | jmethodID GetOnEmulationStarted(); | ||
| 19 | jmethodID GetOnEmulationStopped(); | ||
| 18 | 20 | ||
| 19 | } // namespace IDCache | 21 | } // namespace IDCache |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 8b99d1d6e..b9ecefa74 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -203,12 +203,10 @@ public: | |||
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | bool IsRunning() const { | 205 | bool IsRunning() const { |
| 206 | std::scoped_lock lock(m_mutex); | ||
| 207 | return m_is_running; | 206 | return m_is_running; |
| 208 | } | 207 | } |
| 209 | 208 | ||
| 210 | bool IsPaused() const { | 209 | bool IsPaused() const { |
| 211 | std::scoped_lock lock(m_mutex); | ||
| 212 | return m_is_running && m_is_paused; | 210 | return m_is_running && m_is_paused; |
| 213 | } | 211 | } |
| 214 | 212 | ||
| @@ -272,6 +270,7 @@ public: | |||
| 272 | m_vulkan_library); | 270 | m_vulkan_library); |
| 273 | 271 | ||
| 274 | m_system.SetFilesystem(m_vfs); | 272 | m_system.SetFilesystem(m_vfs); |
| 273 | m_system.GetUserChannel().clear(); | ||
| 275 | 274 | ||
| 276 | // Initialize system. | 275 | // Initialize system. |
| 277 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); | 276 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); |
| @@ -335,6 +334,8 @@ public: | |||
| 335 | 334 | ||
| 336 | // Tear down the render window. | 335 | // Tear down the render window. |
| 337 | m_window.reset(); | 336 | m_window.reset(); |
| 337 | |||
| 338 | OnEmulationStopped(m_load_result); | ||
| 338 | } | 339 | } |
| 339 | 340 | ||
| 340 | void PauseEmulation() { | 341 | void PauseEmulation() { |
| @@ -376,6 +377,8 @@ public: | |||
| 376 | m_system.InitializeDebugger(); | 377 | m_system.InitializeDebugger(); |
| 377 | } | 378 | } |
| 378 | 379 | ||
| 380 | OnEmulationStarted(); | ||
| 381 | |||
| 379 | while (true) { | 382 | while (true) { |
| 380 | { | 383 | { |
| 381 | [[maybe_unused]] std::unique_lock lock(m_mutex); | 384 | [[maybe_unused]] std::unique_lock lock(m_mutex); |
| @@ -420,7 +423,7 @@ public: | |||
| 420 | return false; | 423 | return false; |
| 421 | } | 424 | } |
| 422 | 425 | ||
| 423 | return !Settings::values.use_docked_mode.GetValue(); | 426 | return !Settings::IsDockedMode(); |
| 424 | } | 427 | } |
| 425 | 428 | ||
| 426 | void SetDeviceType([[maybe_unused]] int index, int type) { | 429 | void SetDeviceType([[maybe_unused]] int index, int type) { |
| @@ -511,6 +514,18 @@ private: | |||
| 511 | static_cast<jint>(progress), static_cast<jint>(max)); | 514 | static_cast<jint>(progress), static_cast<jint>(max)); |
| 512 | } | 515 | } |
| 513 | 516 | ||
| 517 | static void OnEmulationStarted() { | ||
| 518 | JNIEnv* env = IDCache::GetEnvForThread(); | ||
| 519 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | ||
| 520 | IDCache::GetOnEmulationStarted()); | ||
| 521 | } | ||
| 522 | |||
| 523 | static void OnEmulationStopped(Core::SystemResultStatus result) { | ||
| 524 | JNIEnv* env = IDCache::GetEnvForThread(); | ||
| 525 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | ||
| 526 | IDCache::GetOnEmulationStopped(), static_cast<jint>(result)); | ||
| 527 | } | ||
| 528 | |||
| 514 | private: | 529 | private: |
| 515 | static EmulationSession s_instance; | 530 | static EmulationSession s_instance; |
| 516 | 531 | ||
| @@ -528,8 +543,8 @@ private: | |||
| 528 | Core::PerfStatsResults m_perf_stats{}; | 543 | Core::PerfStatsResults m_perf_stats{}; |
| 529 | std::shared_ptr<FileSys::VfsFilesystem> m_vfs; | 544 | std::shared_ptr<FileSys::VfsFilesystem> m_vfs; |
| 530 | Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; | 545 | Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; |
| 531 | bool m_is_running{}; | 546 | std::atomic<bool> m_is_running = false; |
| 532 | bool m_is_paused{}; | 547 | std::atomic<bool> m_is_paused = false; |
| 533 | SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; | 548 | SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; |
| 534 | std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; | 549 | std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; |
| 535 | std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; | 550 | std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; |
| @@ -824,34 +839,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass cl | |||
| 824 | Config{}; | 839 | Config{}; |
| 825 | } | 840 | } |
| 826 | 841 | ||
| 827 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz, | ||
| 828 | jstring j_game_id, jstring j_section, | ||
| 829 | jstring j_key) { | ||
| 830 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | ||
| 831 | std::string_view section = env->GetStringUTFChars(j_section, 0); | ||
| 832 | std::string_view key = env->GetStringUTFChars(j_key, 0); | ||
| 833 | |||
| 834 | env->ReleaseStringUTFChars(j_game_id, game_id.data()); | ||
| 835 | env->ReleaseStringUTFChars(j_section, section.data()); | ||
| 836 | env->ReleaseStringUTFChars(j_key, key.data()); | ||
| 837 | |||
| 838 | return env->NewStringUTF(""); | ||
| 839 | } | ||
| 840 | |||
| 841 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz, | ||
| 842 | jstring j_game_id, jstring j_section, | ||
| 843 | jstring j_key, jstring j_value) { | ||
| 844 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | ||
| 845 | std::string_view section = env->GetStringUTFChars(j_section, 0); | ||
| 846 | std::string_view key = env->GetStringUTFChars(j_key, 0); | ||
| 847 | std::string_view value = env->GetStringUTFChars(j_value, 0); | ||
| 848 | |||
| 849 | env->ReleaseStringUTFChars(j_game_id, game_id.data()); | ||
| 850 | env->ReleaseStringUTFChars(j_section, section.data()); | ||
| 851 | env->ReleaseStringUTFChars(j_key, key.data()); | ||
| 852 | env->ReleaseStringUTFChars(j_value, value.data()); | ||
| 853 | } | ||
| 854 | |||
| 855 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, | 842 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, |
| 856 | jstring j_game_id) { | 843 | jstring j_game_id) { |
| 857 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | 844 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); |
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp new file mode 100644 index 000000000..8a704960c --- /dev/null +++ b/src/android/app/src/main/jni/native_config.cpp | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | |||
| 6 | #include <jni.h> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/settings.h" | ||
| 10 | #include "jni/android_common/android_common.h" | ||
| 11 | #include "jni/config.h" | ||
| 12 | #include "uisettings.h" | ||
| 13 | |||
| 14 | template <typename T> | ||
| 15 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | ||
| 16 | auto key = GetJString(env, jkey); | ||
| 17 | auto basicSetting = Settings::values.linkage.by_key[key]; | ||
| 18 | auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key]; | ||
| 19 | if (basicSetting != 0) { | ||
| 20 | return static_cast<Settings::Setting<T>*>(basicSetting); | ||
| 21 | } | ||
| 22 | if (basicAndroidSetting != 0) { | ||
| 23 | return static_cast<Settings::Setting<T>*>(basicAndroidSetting); | ||
| 24 | } | ||
| 25 | LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key); | ||
| 26 | return nullptr; | ||
| 27 | } | ||
| 28 | |||
| 29 | extern "C" { | ||
| 30 | |||
| 31 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, | ||
| 32 | jstring jkey, jboolean getDefault) { | ||
| 33 | auto setting = getSetting<bool>(env, jkey); | ||
| 34 | if (setting == nullptr) { | ||
| 35 | return false; | ||
| 36 | } | ||
| 37 | setting->SetGlobal(true); | ||
| 38 | |||
| 39 | if (static_cast<bool>(getDefault)) { | ||
| 40 | return setting->GetDefault(); | ||
| 41 | } | ||
| 42 | |||
| 43 | return setting->GetValue(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey, | ||
| 47 | jboolean value) { | ||
| 48 | auto setting = getSetting<bool>(env, jkey); | ||
| 49 | if (setting == nullptr) { | ||
| 50 | return; | ||
| 51 | } | ||
| 52 | setting->SetGlobal(true); | ||
| 53 | setting->SetValue(static_cast<bool>(value)); | ||
| 54 | } | ||
| 55 | |||
| 56 | jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey, | ||
| 57 | jboolean getDefault) { | ||
| 58 | auto setting = getSetting<u8>(env, jkey); | ||
| 59 | if (setting == nullptr) { | ||
| 60 | return -1; | ||
| 61 | } | ||
| 62 | setting->SetGlobal(true); | ||
| 63 | |||
| 64 | if (static_cast<bool>(getDefault)) { | ||
| 65 | return setting->GetDefault(); | ||
| 66 | } | ||
| 67 | |||
| 68 | return setting->GetValue(); | ||
| 69 | } | ||
| 70 | |||
| 71 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey, | ||
| 72 | jbyte value) { | ||
| 73 | auto setting = getSetting<u8>(env, jkey); | ||
| 74 | if (setting == nullptr) { | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | setting->SetGlobal(true); | ||
| 78 | setting->SetValue(value); | ||
| 79 | } | ||
| 80 | |||
| 81 | jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey, | ||
| 82 | jboolean getDefault) { | ||
| 83 | auto setting = getSetting<u16>(env, jkey); | ||
| 84 | if (setting == nullptr) { | ||
| 85 | return -1; | ||
| 86 | } | ||
| 87 | setting->SetGlobal(true); | ||
| 88 | |||
| 89 | if (static_cast<bool>(getDefault)) { | ||
| 90 | return setting->GetDefault(); | ||
| 91 | } | ||
| 92 | |||
| 93 | return setting->GetValue(); | ||
| 94 | } | ||
| 95 | |||
| 96 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey, | ||
| 97 | jshort value) { | ||
| 98 | auto setting = getSetting<u16>(env, jkey); | ||
| 99 | if (setting == nullptr) { | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | setting->SetGlobal(true); | ||
| 103 | setting->SetValue(value); | ||
| 104 | } | ||
| 105 | |||
| 106 | jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey, | ||
| 107 | jboolean getDefault) { | ||
| 108 | auto setting = getSetting<int>(env, jkey); | ||
| 109 | if (setting == nullptr) { | ||
| 110 | return -1; | ||
| 111 | } | ||
| 112 | setting->SetGlobal(true); | ||
| 113 | |||
| 114 | if (static_cast<bool>(getDefault)) { | ||
| 115 | return setting->GetDefault(); | ||
| 116 | } | ||
| 117 | |||
| 118 | return setting->GetValue(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey, | ||
| 122 | jint value) { | ||
| 123 | auto setting = getSetting<int>(env, jkey); | ||
| 124 | if (setting == nullptr) { | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | setting->SetGlobal(true); | ||
| 128 | setting->SetValue(value); | ||
| 129 | } | ||
| 130 | |||
| 131 | jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey, | ||
| 132 | jboolean getDefault) { | ||
| 133 | auto setting = getSetting<float>(env, jkey); | ||
| 134 | if (setting == nullptr) { | ||
| 135 | return -1; | ||
| 136 | } | ||
| 137 | setting->SetGlobal(true); | ||
| 138 | |||
| 139 | if (static_cast<bool>(getDefault)) { | ||
| 140 | return setting->GetDefault(); | ||
| 141 | } | ||
| 142 | |||
| 143 | return setting->GetValue(); | ||
| 144 | } | ||
| 145 | |||
| 146 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey, | ||
| 147 | jfloat value) { | ||
| 148 | auto setting = getSetting<float>(env, jkey); | ||
| 149 | if (setting == nullptr) { | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | setting->SetGlobal(true); | ||
| 153 | setting->SetValue(value); | ||
| 154 | } | ||
| 155 | |||
| 156 | jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey, | ||
| 157 | jboolean getDefault) { | ||
| 158 | auto setting = getSetting<long>(env, jkey); | ||
| 159 | if (setting == nullptr) { | ||
| 160 | return -1; | ||
| 161 | } | ||
| 162 | setting->SetGlobal(true); | ||
| 163 | |||
| 164 | if (static_cast<bool>(getDefault)) { | ||
| 165 | return setting->GetDefault(); | ||
| 166 | } | ||
| 167 | |||
| 168 | return setting->GetValue(); | ||
| 169 | } | ||
| 170 | |||
| 171 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey, | ||
| 172 | jlong value) { | ||
| 173 | auto setting = getSetting<long>(env, jkey); | ||
| 174 | if (setting == nullptr) { | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | setting->SetGlobal(true); | ||
| 178 | setting->SetValue(value); | ||
| 179 | } | ||
| 180 | |||
| 181 | jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey, | ||
| 182 | jboolean getDefault) { | ||
| 183 | auto setting = getSetting<std::string>(env, jkey); | ||
| 184 | if (setting == nullptr) { | ||
| 185 | return ToJString(env, ""); | ||
| 186 | } | ||
| 187 | setting->SetGlobal(true); | ||
| 188 | |||
| 189 | if (static_cast<bool>(getDefault)) { | ||
| 190 | return ToJString(env, setting->GetDefault()); | ||
| 191 | } | ||
| 192 | |||
| 193 | return ToJString(env, setting->GetValue()); | ||
| 194 | } | ||
| 195 | |||
| 196 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey, | ||
| 197 | jstring value) { | ||
| 198 | auto setting = getSetting<std::string>(env, jkey); | ||
| 199 | if (setting == nullptr) { | ||
| 200 | return; | ||
| 201 | } | ||
| 202 | |||
| 203 | setting->SetGlobal(true); | ||
| 204 | setting->SetValue(GetJString(env, value)); | ||
| 205 | } | ||
| 206 | |||
| 207 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj, | ||
| 208 | jstring jkey) { | ||
| 209 | auto key = GetJString(env, jkey); | ||
| 210 | auto setting = Settings::values.linkage.by_key[key]; | ||
| 211 | if (setting != 0) { | ||
| 212 | return setting->RuntimeModfiable(); | ||
| 213 | } | ||
| 214 | LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key); | ||
| 215 | return true; | ||
| 216 | } | ||
| 217 | |||
| 218 | jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj, | ||
| 219 | jint jcategory) { | ||
| 220 | auto category = static_cast<Settings::Category>(jcategory); | ||
| 221 | return ToJString(env, Settings::TranslateCategory(category)); | ||
| 222 | } | ||
| 223 | |||
| 224 | jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* env, jobject obj, | ||
| 225 | jstring jkey) { | ||
| 226 | auto setting = getSetting<std::string>(env, jkey); | ||
| 227 | if (setting == nullptr) { | ||
| 228 | return ToJString(env, ""); | ||
| 229 | } | ||
| 230 | if (setting->PairedSetting() == nullptr) { | ||
| 231 | return ToJString(env, ""); | ||
| 232 | } | ||
| 233 | |||
| 234 | return ToJString(env, setting->PairedSetting()->GetLabel()); | ||
| 235 | } | ||
| 236 | |||
| 237 | } // extern "C" | ||
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/uisettings.cpp new file mode 100644 index 000000000..f2f0bad50 --- /dev/null +++ b/src/android/app/src/main/jni/uisettings.cpp | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "uisettings.h" | ||
| 5 | |||
| 6 | namespace AndroidSettings { | ||
| 7 | |||
| 8 | Values values; | ||
| 9 | |||
| 10 | } // namespace AndroidSettings | ||
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/uisettings.h new file mode 100644 index 000000000..494654af7 --- /dev/null +++ b/src/android/app/src/main/jni/uisettings.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <common/settings_common.h> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/settings_setting.h" | ||
| 9 | |||
| 10 | namespace AndroidSettings { | ||
| 11 | |||
| 12 | struct Values { | ||
| 13 | Settings::Linkage linkage; | ||
| 14 | |||
| 15 | // Android | ||
| 16 | Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture", | ||
| 17 | Settings::Category::Android}; | ||
| 18 | Settings::Setting<s32> screen_layout{linkage, | ||
| 19 | 5, | ||
| 20 | "screen_layout", | ||
| 21 | Settings::Category::Android, | ||
| 22 | Settings::Specialization::Default, | ||
| 23 | true, | ||
| 24 | true}; | ||
| 25 | }; | ||
| 26 | |||
| 27 | extern Values values; | ||
| 28 | |||
| 29 | } // namespace AndroidSettings | ||
diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml deleted file mode 100644 index 9f49c133a..000000000 --- a/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <alpha | ||
| 5 | android:duration="125" | ||
| 6 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 7 | android:fromAlpha="1" | ||
| 8 | android:toAlpha="0" /> | ||
| 9 | |||
| 10 | <translate | ||
| 11 | android:duration="125" | ||
| 12 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 13 | android:fromXDelta="0" | ||
| 14 | android:toXDelta="-75" /> | ||
| 15 | |||
| 16 | </set> | ||
diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml deleted file mode 100644 index 82fd719db..000000000 --- a/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <alpha | ||
| 5 | android:duration="@android:integer/config_shortAnimTime" | ||
| 6 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 7 | android:fromAlpha="0" | ||
| 8 | android:toAlpha="1" /> | ||
| 9 | |||
| 10 | <translate | ||
| 11 | android:duration="@android:integer/config_shortAnimTime" | ||
| 12 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 13 | android:fromXDelta="-200" | ||
| 14 | android:toXDelta="0" /> | ||
| 15 | |||
| 16 | </set> | ||
diff --git a/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml deleted file mode 100644 index 5892128f1..000000000 --- a/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <alpha | ||
| 5 | android:duration="125" | ||
| 6 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 7 | android:fromAlpha="1" | ||
| 8 | android:toAlpha="0" /> | ||
| 9 | |||
| 10 | <translate | ||
| 11 | android:duration="125" | ||
| 12 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 13 | android:fromXDelta="0" | ||
| 14 | android:toXDelta="75" /> | ||
| 15 | |||
| 16 | </set> | ||
diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml deleted file mode 100644 index 98e0cf8bd..000000000 --- a/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <alpha | ||
| 5 | android:duration="@android:integer/config_shortAnimTime" | ||
| 6 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 7 | android:fromAlpha="0" | ||
| 8 | android:toAlpha="1" /> | ||
| 9 | |||
| 10 | <translate | ||
| 11 | android:duration="@android:integer/config_shortAnimTime" | ||
| 12 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 13 | android:fromXDelta="200" | ||
| 14 | android:toXDelta="0" /> | ||
| 15 | |||
| 16 | </set> | ||
diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml deleted file mode 100644 index 77a40a4d1..000000000 --- a/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <alpha | ||
| 5 | android:duration="@android:integer/config_shortAnimTime" | ||
| 6 | android:interpolator="@android:anim/decelerate_interpolator" | ||
| 7 | android:fromAlpha="1" | ||
| 8 | android:toAlpha="0" /> | ||
| 9 | |||
| 10 | </set> | ||
diff --git a/src/android/app/src/main/res/animator/menu_slide_in_from_start.xml b/src/android/app/src/main/res/animator/menu_slide_in_from_start.xml deleted file mode 100644 index 4612aee13..000000000 --- a/src/android/app/src/main/res/animator/menu_slide_in_from_start.xml +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <objectAnimator | ||
| 5 | android:propertyName="translationX" | ||
| 6 | android:valueType="floatType" | ||
| 7 | android:valueFrom="-1280dp" | ||
| 8 | android:valueTo="0" | ||
| 9 | android:interpolator="@android:interpolator/decelerate_quad" | ||
| 10 | android:duration="300"/> | ||
| 11 | |||
| 12 | <objectAnimator | ||
| 13 | android:propertyName="alpha" | ||
| 14 | android:valueType="floatType" | ||
| 15 | android:valueFrom="0" | ||
| 16 | android:valueTo="1" | ||
| 17 | android:interpolator="@android:interpolator/accelerate_quad" | ||
| 18 | android:duration="300"/> | ||
| 19 | |||
| 20 | </set> \ No newline at end of file | ||
diff --git a/src/android/app/src/main/res/animator/menu_slide_out_to_start.xml b/src/android/app/src/main/res/animator/menu_slide_out_to_start.xml deleted file mode 100644 index c00478946..000000000 --- a/src/android/app/src/main/res/animator/menu_slide_out_to_start.xml +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | |||
| 4 | <!-- This animation is used ONLY when a submenu is replaced. --> | ||
| 5 | <objectAnimator | ||
| 6 | android:propertyName="translationX" | ||
| 7 | android:valueType="floatType" | ||
| 8 | android:valueFrom="0" | ||
| 9 | android:valueTo="-1280dp" | ||
| 10 | android:interpolator="@android:interpolator/decelerate_quad" | ||
| 11 | android:duration="200"/> | ||
| 12 | |||
| 13 | <objectAnimator | ||
| 14 | android:propertyName="alpha" | ||
| 15 | android:valueType="floatType" | ||
| 16 | android:valueFrom="1" | ||
| 17 | android:valueTo="0" | ||
| 18 | android:interpolator="@android:interpolator/decelerate_quad" | ||
| 19 | android:duration="200"/> | ||
| 20 | |||
| 21 | </set> \ No newline at end of file | ||
diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml index 14ae83b04..8a026a30a 100644 --- a/src/android/app/src/main/res/layout/activity_settings.xml +++ b/src/android/app/src/main/res/layout/activity_settings.xml | |||
| @@ -1,42 +1,24 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <androidx.coordinatorlayout.widget.CoordinatorLayout | 2 | <androidx.constraintlayout.widget.ConstraintLayout |
| 3 | android:id="@+id/coordinator_main" | ||
| 4 | xmlns:android="http://schemas.android.com/apk/res/android" | 3 | xmlns:android="http://schemas.android.com/apk/res/android" |
| 5 | xmlns:app="http://schemas.android.com/apk/res-auto" | 4 | xmlns:app="http://schemas.android.com/apk/res-auto" |
| 5 | xmlns:tools="http://schemas.android.com/tools" | ||
| 6 | android:id="@+id/constraint_settings" | ||
| 6 | android:layout_width="match_parent" | 7 | android:layout_width="match_parent" |
| 7 | android:layout_height="match_parent" | 8 | android:layout_height="match_parent" |
| 8 | android:background="?attr/colorSurface"> | 9 | android:background="?attr/colorSurface"> |
| 9 | 10 | ||
| 10 | <com.google.android.material.appbar.AppBarLayout | 11 | <androidx.fragment.app.FragmentContainerView |
| 11 | android:id="@+id/appbar_settings" | 12 | android:id="@+id/fragment_container" |
| 12 | android:layout_width="match_parent" | 13 | android:name="androidx.navigation.fragment.NavHostFragment" |
| 13 | android:layout_height="wrap_content" | 14 | android:layout_width="0dp" |
| 14 | android:fitsSystemWindows="true" | 15 | android:layout_height="0dp" |
| 15 | app:elevation="0dp"> | 16 | app:defaultNavHost="true" |
| 16 | 17 | app:layout_constraintBottom_toBottomOf="parent" | |
| 17 | <com.google.android.material.appbar.CollapsingToolbarLayout | 18 | app:layout_constraintLeft_toLeftOf="parent" |
| 18 | style="?attr/collapsingToolbarLayoutMediumStyle" | 19 | app:layout_constraintRight_toRightOf="parent" |
| 19 | android:id="@+id/toolbar_settings_layout" | 20 | app:layout_constraintTop_toTopOf="parent" |
| 20 | android:layout_width="match_parent" | 21 | tools:layout="@layout/fragment_settings" /> |
| 21 | android:layout_height="?attr/collapsingToolbarLayoutMediumSize" | ||
| 22 | app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> | ||
| 23 | |||
| 24 | <com.google.android.material.appbar.MaterialToolbar | ||
| 25 | android:id="@+id/toolbar_settings" | ||
| 26 | android:layout_width="match_parent" | ||
| 27 | android:layout_height="?attr/actionBarSize" | ||
| 28 | app:layout_collapseMode="pin" /> | ||
| 29 | |||
| 30 | </com.google.android.material.appbar.CollapsingToolbarLayout> | ||
| 31 | |||
| 32 | </com.google.android.material.appbar.AppBarLayout> | ||
| 33 | |||
| 34 | <FrameLayout | ||
| 35 | android:id="@+id/frame_content" | ||
| 36 | android:layout_width="match_parent" | ||
| 37 | android:layout_height="match_parent" | ||
| 38 | android:layout_marginHorizontal="12dp" | ||
| 39 | app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | ||
| 40 | 22 | ||
| 41 | <View | 23 | <View |
| 42 | android:id="@+id/navigation_bar_shade" | 24 | android:id="@+id/navigation_bar_shade" |
| @@ -45,6 +27,8 @@ | |||
| 45 | android:background="@android:color/transparent" | 27 | android:background="@android:color/transparent" |
| 46 | android:clickable="false" | 28 | android:clickable="false" |
| 47 | android:focusable="false" | 29 | android:focusable="false" |
| 48 | android:layout_gravity="bottom|center_horizontal" /> | 30 | app:layout_constraintBottom_toBottomOf="parent" |
| 31 | app:layout_constraintEnd_toEndOf="parent" | ||
| 32 | app:layout_constraintStart_toStartOf="parent" /> | ||
| 49 | 33 | ||
| 50 | </androidx.coordinatorlayout.widget.CoordinatorLayout> | 34 | </androidx.constraintlayout.widget.ConstraintLayout> |
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index e54a10e8f..da97d85c1 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml | |||
| @@ -26,6 +26,81 @@ | |||
| 26 | android:focusable="false" | 26 | android:focusable="false" |
| 27 | android:focusableInTouchMode="false" /> | 27 | android:focusableInTouchMode="false" /> |
| 28 | 28 | ||
| 29 | <com.google.android.material.card.MaterialCardView | ||
| 30 | android:id="@+id/loading_indicator" | ||
| 31 | style="?attr/materialCardViewOutlinedStyle" | ||
| 32 | android:layout_width="wrap_content" | ||
| 33 | android:layout_height="wrap_content" | ||
| 34 | android:layout_gravity="center" | ||
| 35 | android:focusable="false"> | ||
| 36 | |||
| 37 | <androidx.constraintlayout.widget.ConstraintLayout | ||
| 38 | android:id="@+id/loading_layout" | ||
| 39 | android:layout_width="wrap_content" | ||
| 40 | android:layout_height="wrap_content" | ||
| 41 | android:gravity="center_horizontal"> | ||
| 42 | |||
| 43 | <ImageView | ||
| 44 | android:id="@+id/loading_image" | ||
| 45 | android:layout_width="wrap_content" | ||
| 46 | android:layout_height="0dp" | ||
| 47 | android:adjustViewBounds="true" | ||
| 48 | app:layout_constraintBottom_toBottomOf="@+id/linearLayout" | ||
| 49 | app:layout_constraintStart_toStartOf="parent" | ||
| 50 | app:layout_constraintTop_toTopOf="@+id/linearLayout" | ||
| 51 | tools:src="@drawable/default_icon" /> | ||
| 52 | |||
| 53 | <LinearLayout | ||
| 54 | android:id="@+id/linearLayout" | ||
| 55 | android:layout_width="wrap_content" | ||
| 56 | android:layout_height="wrap_content" | ||
| 57 | android:orientation="vertical" | ||
| 58 | android:paddingHorizontal="24dp" | ||
| 59 | android:paddingVertical="36dp" | ||
| 60 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 61 | app:layout_constraintEnd_toEndOf="parent" | ||
| 62 | app:layout_constraintStart_toEndOf="@id/loading_image" | ||
| 63 | app:layout_constraintTop_toTopOf="parent"> | ||
| 64 | |||
| 65 | <com.google.android.material.textview.MaterialTextView | ||
| 66 | android:id="@+id/loading_title" | ||
| 67 | style="@style/TextAppearance.Material3.TitleMedium" | ||
| 68 | android:layout_width="match_parent" | ||
| 69 | android:layout_height="wrap_content" | ||
| 70 | android:ellipsize="marquee" | ||
| 71 | android:marqueeRepeatLimit="marquee_forever" | ||
| 72 | android:requiresFadingEdge="horizontal" | ||
| 73 | android:singleLine="true" | ||
| 74 | android:textAlignment="viewStart" | ||
| 75 | tools:text="@string/games" /> | ||
| 76 | |||
| 77 | <com.google.android.material.textview.MaterialTextView | ||
| 78 | android:id="@+id/loading_text" | ||
| 79 | style="@style/TextAppearance.Material3.TitleSmall" | ||
| 80 | android:layout_width="match_parent" | ||
| 81 | android:layout_height="wrap_content" | ||
| 82 | android:layout_marginTop="4dp" | ||
| 83 | android:ellipsize="marquee" | ||
| 84 | android:marqueeRepeatLimit="marquee_forever" | ||
| 85 | android:requiresFadingEdge="horizontal" | ||
| 86 | android:singleLine="true" | ||
| 87 | android:text="@string/loading" | ||
| 88 | android:textAlignment="viewStart" /> | ||
| 89 | |||
| 90 | <com.google.android.material.progressindicator.LinearProgressIndicator | ||
| 91 | android:id="@+id/loading_progress_indicator" | ||
| 92 | android:layout_width="192dp" | ||
| 93 | android:layout_height="wrap_content" | ||
| 94 | android:layout_marginTop="12dp" | ||
| 95 | android:indeterminate="true" | ||
| 96 | app:trackCornerRadius="8dp" /> | ||
| 97 | |||
| 98 | </LinearLayout> | ||
| 99 | |||
| 100 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
| 101 | |||
| 102 | </com.google.android.material.card.MaterialCardView> | ||
| 103 | |||
| 29 | </FrameLayout> | 104 | </FrameLayout> |
| 30 | 105 | ||
| 31 | <FrameLayout | 106 | <FrameLayout |
| @@ -41,11 +116,12 @@ | |||
| 41 | android:layout_height="match_parent" | 116 | android:layout_height="match_parent" |
| 42 | android:layout_gravity="center" | 117 | android:layout_gravity="center" |
| 43 | android:focusable="true" | 118 | android:focusable="true" |
| 44 | android:focusableInTouchMode="true" /> | 119 | android:focusableInTouchMode="true" |
| 120 | android:visibility="invisible" /> | ||
| 45 | 121 | ||
| 46 | <Button | 122 | <Button |
| 47 | style="@style/Widget.Material3.Button.ElevatedButton" | ||
| 48 | android:id="@+id/done_control_config" | 123 | android:id="@+id/done_control_config" |
| 124 | style="@style/Widget.Material3.Button.ElevatedButton" | ||
| 49 | android:layout_width="wrap_content" | 125 | android:layout_width="wrap_content" |
| 50 | android:layout_height="wrap_content" | 126 | android:layout_height="wrap_content" |
| 51 | android:layout_gravity="center" | 127 | android:layout_gravity="center" |
| @@ -81,6 +157,7 @@ | |||
| 81 | android:layout_height="match_parent" | 157 | android:layout_height="match_parent" |
| 82 | android:layout_gravity="start|bottom" | 158 | android:layout_gravity="start|bottom" |
| 83 | app:headerLayout="@layout/header_in_game" | 159 | app:headerLayout="@layout/header_in_game" |
| 84 | app:menu="@menu/menu_in_game" /> | 160 | app:menu="@menu/menu_in_game" |
| 161 | tools:visibility="gone" /> | ||
| 85 | 162 | ||
| 86 | </androidx.drawerlayout.widget.DrawerLayout> | 163 | </androidx.drawerlayout.widget.DrawerLayout> |
diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml index 167720347..ebedbf1ec 100644 --- a/src/android/app/src/main/res/layout/fragment_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_settings.xml | |||
| @@ -1,14 +1,41 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <FrameLayout | 2 | <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | xmlns:android="http://schemas.android.com/apk/res/android" | 3 | xmlns:app="http://schemas.android.com/apk/res-auto" |
| 4 | android:id="@+id/coordinator_main" | ||
| 4 | android:layout_width="match_parent" | 5 | android:layout_width="match_parent" |
| 5 | android:layout_height="match_parent"> | 6 | android:layout_height="match_parent" |
| 7 | android:background="?attr/colorSurface"> | ||
| 8 | |||
| 9 | <com.google.android.material.appbar.AppBarLayout | ||
| 10 | android:id="@+id/appbar_settings" | ||
| 11 | android:layout_width="match_parent" | ||
| 12 | android:layout_height="wrap_content" | ||
| 13 | android:fitsSystemWindows="true" | ||
| 14 | app:elevation="0dp"> | ||
| 15 | |||
| 16 | <com.google.android.material.appbar.CollapsingToolbarLayout | ||
| 17 | android:id="@+id/toolbar_settings_layout" | ||
| 18 | style="?attr/collapsingToolbarLayoutMediumStyle" | ||
| 19 | android:layout_width="match_parent" | ||
| 20 | android:layout_height="?attr/collapsingToolbarLayoutMediumSize" | ||
| 21 | app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> | ||
| 22 | |||
| 23 | <com.google.android.material.appbar.MaterialToolbar | ||
| 24 | android:id="@+id/toolbar_settings" | ||
| 25 | android:layout_width="match_parent" | ||
| 26 | android:layout_height="?attr/actionBarSize" | ||
| 27 | app:layout_collapseMode="pin" | ||
| 28 | app:navigationIcon="@drawable/ic_back" /> | ||
| 29 | |||
| 30 | </com.google.android.material.appbar.CollapsingToolbarLayout> | ||
| 31 | |||
| 32 | </com.google.android.material.appbar.AppBarLayout> | ||
| 6 | 33 | ||
| 7 | <androidx.recyclerview.widget.RecyclerView | 34 | <androidx.recyclerview.widget.RecyclerView |
| 8 | android:id="@+id/list_settings" | 35 | android:id="@+id/list_settings" |
| 9 | android:layout_width="match_parent" | 36 | android:layout_width="match_parent" |
| 10 | android:layout_height="match_parent" | 37 | android:layout_height="match_parent" |
| 11 | android:background="?attr/colorSurface" | 38 | android:clipToPadding="false" |
| 12 | android:clipToPadding="false" /> | 39 | app:layout_behavior="@string/appbar_scrolling_view_behavior" /> |
| 13 | 40 | ||
| 14 | </FrameLayout> | 41 | </androidx.coordinatorlayout.widget.CoordinatorLayout> |
diff --git a/src/android/app/src/main/res/layout/fragment_settings_search.xml b/src/android/app/src/main/res/layout/fragment_settings_search.xml new file mode 100644 index 000000000..c779ed2fc --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_settings_search.xml | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | xmlns:tools="http://schemas.android.com/tools" | ||
| 5 | android:layout_width="match_parent" | ||
| 6 | android:layout_height="match_parent"> | ||
| 7 | |||
| 8 | <RelativeLayout | ||
| 9 | android:id="@+id/relativeLayout" | ||
| 10 | android:layout_width="0dp" | ||
| 11 | android:layout_height="0dp" | ||
| 12 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 13 | app:layout_constraintEnd_toEndOf="parent" | ||
| 14 | app:layout_constraintStart_toStartOf="parent" | ||
| 15 | app:layout_constraintTop_toBottomOf="@+id/divider"> | ||
| 16 | |||
| 17 | <LinearLayout | ||
| 18 | android:id="@+id/no_results_view" | ||
| 19 | android:layout_width="match_parent" | ||
| 20 | android:layout_height="match_parent" | ||
| 21 | android:gravity="center" | ||
| 22 | android:orientation="vertical"> | ||
| 23 | |||
| 24 | <ImageView | ||
| 25 | android:id="@+id/icon_no_results" | ||
| 26 | android:layout_width="match_parent" | ||
| 27 | android:layout_height="80dp" | ||
| 28 | android:src="@drawable/ic_search" /> | ||
| 29 | |||
| 30 | <com.google.android.material.textview.MaterialTextView | ||
| 31 | android:id="@+id/notice_text" | ||
| 32 | style="@style/TextAppearance.Material3.TitleLarge" | ||
| 33 | android:layout_width="match_parent" | ||
| 34 | android:layout_height="wrap_content" | ||
| 35 | android:gravity="center" | ||
| 36 | android:paddingTop="8dp" | ||
| 37 | android:text="@string/search_settings" | ||
| 38 | tools:visibility="visible" /> | ||
| 39 | |||
| 40 | </LinearLayout> | ||
| 41 | |||
| 42 | <androidx.recyclerview.widget.RecyclerView | ||
| 43 | android:id="@+id/settings_list" | ||
| 44 | android:layout_width="match_parent" | ||
| 45 | android:layout_height="match_parent" | ||
| 46 | android:clipToPadding="false" /> | ||
| 47 | |||
| 48 | </RelativeLayout> | ||
| 49 | |||
| 50 | <FrameLayout | ||
| 51 | android:id="@+id/frame_search" | ||
| 52 | android:layout_width="match_parent" | ||
| 53 | android:layout_height="wrap_content" | ||
| 54 | android:clipToPadding="false" | ||
| 55 | app:layout_constraintEnd_toEndOf="parent" | ||
| 56 | app:layout_constraintStart_toStartOf="parent" | ||
| 57 | app:layout_constraintTop_toTopOf="parent"> | ||
| 58 | |||
| 59 | <com.google.android.material.card.MaterialCardView | ||
| 60 | android:id="@+id/search_background" | ||
| 61 | style="?attr/materialCardViewFilledStyle" | ||
| 62 | android:layout_width="match_parent" | ||
| 63 | android:layout_height="56dp" | ||
| 64 | app:cardCornerRadius="28dp"> | ||
| 65 | |||
| 66 | <LinearLayout | ||
| 67 | android:id="@+id/search_container" | ||
| 68 | android:layout_width="match_parent" | ||
| 69 | android:layout_height="match_parent" | ||
| 70 | android:layout_marginEnd="56dp" | ||
| 71 | android:orientation="horizontal"> | ||
| 72 | |||
| 73 | <Button | ||
| 74 | android:id="@+id/back_button" | ||
| 75 | style="?attr/materialIconButtonFilledTonalStyle" | ||
| 76 | android:layout_width="wrap_content" | ||
| 77 | android:layout_height="wrap_content" | ||
| 78 | android:layout_gravity="center_vertical" | ||
| 79 | android:layout_marginStart="8dp" | ||
| 80 | app:backgroundTint="@android:color/transparent" | ||
| 81 | app:icon="@drawable/ic_back" /> | ||
| 82 | |||
| 83 | <EditText | ||
| 84 | android:id="@+id/search_text" | ||
| 85 | android:layout_width="match_parent" | ||
| 86 | android:layout_height="match_parent" | ||
| 87 | android:background="@android:color/transparent" | ||
| 88 | android:hint="@string/search_settings" | ||
| 89 | android:imeOptions="flagNoFullscreen" | ||
| 90 | android:inputType="text" | ||
| 91 | android:maxLines="1" /> | ||
| 92 | |||
| 93 | </LinearLayout> | ||
| 94 | |||
| 95 | <Button | ||
| 96 | android:id="@+id/clear_button" | ||
| 97 | style="?attr/materialIconButtonFilledTonalStyle" | ||
| 98 | android:layout_width="wrap_content" | ||
| 99 | android:layout_height="wrap_content" | ||
| 100 | android:layout_gravity="center_vertical|end" | ||
| 101 | android:layout_marginEnd="8dp" | ||
| 102 | android:visibility="invisible" | ||
| 103 | app:backgroundTint="@android:color/transparent" | ||
| 104 | app:icon="@drawable/ic_clear" | ||
| 105 | tools:visibility="visible" /> | ||
| 106 | |||
| 107 | </com.google.android.material.card.MaterialCardView> | ||
| 108 | |||
| 109 | </FrameLayout> | ||
| 110 | |||
| 111 | <com.google.android.material.divider.MaterialDivider | ||
| 112 | android:id="@+id/divider" | ||
| 113 | android:layout_width="match_parent" | ||
| 114 | android:layout_height="wrap_content" | ||
| 115 | android:layout_marginTop="20dp" | ||
| 116 | app:layout_constraintEnd_toEndOf="parent" | ||
| 117 | app:layout_constraintStart_toStartOf="parent" | ||
| 118 | app:layout_constraintTop_toBottomOf="@+id/frame_search" /> | ||
| 119 | |||
| 120 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
diff --git a/src/android/app/src/main/res/menu/menu_settings.xml b/src/android/app/src/main/res/menu/menu_settings.xml index 1fe7aa6d4..21501a471 100644 --- a/src/android/app/src/main/res/menu/menu_settings.xml +++ b/src/android/app/src/main/res/menu/menu_settings.xml | |||
| @@ -1,2 +1,11 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <menu /> \ No newline at end of file | 2 | <menu xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto"> | ||
| 4 | |||
| 5 | <item | ||
| 6 | android:id="@+id/action_search" | ||
| 7 | android:icon="@drawable/ic_search" | ||
| 8 | android:title="@string/home_search" | ||
| 9 | app:showAsAction="always" /> | ||
| 10 | |||
| 11 | </menu> | ||
diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml index 8208f4c2c..c7be37f9b 100644 --- a/src/android/app/src/main/res/navigation/emulation_navigation.xml +++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml | |||
| @@ -12,7 +12,26 @@ | |||
| 12 | tools:layout="@layout/fragment_emulation" > | 12 | tools:layout="@layout/fragment_emulation" > |
| 13 | <argument | 13 | <argument |
| 14 | android:name="game" | 14 | android:name="game" |
| 15 | app:argType="org.yuzu.yuzu_emu.model.Game" /> | 15 | app:argType="org.yuzu.yuzu_emu.model.Game" |
| 16 | app:nullable="true" | ||
| 17 | android:defaultValue="@null" /> | ||
| 16 | </fragment> | 18 | </fragment> |
| 17 | 19 | ||
| 20 | <activity | ||
| 21 | android:id="@+id/settingsActivity" | ||
| 22 | android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity" | ||
| 23 | android:label="SettingsActivity"> | ||
| 24 | <argument | ||
| 25 | android:name="game" | ||
| 26 | app:argType="org.yuzu.yuzu_emu.model.Game" | ||
| 27 | app:nullable="true" /> | ||
| 28 | <argument | ||
| 29 | android:name="menuTag" | ||
| 30 | app:argType="string" /> | ||
| 31 | </activity> | ||
| 32 | |||
| 33 | <action | ||
| 34 | android:id="@+id/action_global_settingsActivity" | ||
| 35 | app:destination="@id/settingsActivity" /> | ||
| 36 | |||
| 18 | </navigation> | 37 | </navigation> |
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index fcebba726..2085430bf 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml | |||
| @@ -62,7 +62,9 @@ | |||
| 62 | android:label="EmulationActivity"> | 62 | android:label="EmulationActivity"> |
| 63 | <argument | 63 | <argument |
| 64 | android:name="game" | 64 | android:name="game" |
| 65 | app:argType="org.yuzu.yuzu_emu.model.Game" /> | 65 | app:argType="org.yuzu.yuzu_emu.model.Game" |
| 66 | app:nullable="true" | ||
| 67 | android:defaultValue="@null" /> | ||
| 66 | </activity> | 68 | </activity> |
| 67 | 69 | ||
| 68 | <action | 70 | <action |
| @@ -70,4 +72,21 @@ | |||
| 70 | app:destination="@id/emulationActivity" | 72 | app:destination="@id/emulationActivity" |
| 71 | app:launchSingleTop="true" /> | 73 | app:launchSingleTop="true" /> |
| 72 | 74 | ||
| 75 | <activity | ||
| 76 | android:id="@+id/settingsActivity" | ||
| 77 | android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity" | ||
| 78 | android:label="SettingsActivity"> | ||
| 79 | <argument | ||
| 80 | android:name="game" | ||
| 81 | app:argType="org.yuzu.yuzu_emu.model.Game" | ||
| 82 | app:nullable="true" /> | ||
| 83 | <argument | ||
| 84 | android:name="menuTag" | ||
| 85 | app:argType="string" /> | ||
| 86 | </activity> | ||
| 87 | |||
| 88 | <action | ||
| 89 | android:id="@+id/action_global_settingsActivity" | ||
| 90 | app:destination="@id/settingsActivity" /> | ||
| 91 | |||
| 73 | </navigation> | 92 | </navigation> |
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml new file mode 100644 index 000000000..88e1b4587 --- /dev/null +++ b/src/android/app/src/main/res/navigation/settings_navigation.xml | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <navigation xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | android:id="@+id/settings_navigation" | ||
| 5 | app:startDestination="@id/settingsFragment"> | ||
| 6 | |||
| 7 | <fragment | ||
| 8 | android:id="@+id/settingsFragment" | ||
| 9 | android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsFragment" | ||
| 10 | android:label="SettingsFragment"> | ||
| 11 | <argument | ||
| 12 | android:name="menuTag" | ||
| 13 | app:argType="string" /> | ||
| 14 | <argument | ||
| 15 | android:name="game" | ||
| 16 | app:argType="org.yuzu.yuzu_emu.model.Game" | ||
| 17 | app:nullable="true" /> | ||
| 18 | <action | ||
| 19 | android:id="@+id/action_settingsFragment_to_settingsSearchFragment" | ||
| 20 | app:destination="@id/settingsSearchFragment" /> | ||
| 21 | </fragment> | ||
| 22 | |||
| 23 | <action | ||
| 24 | android:id="@+id/action_global_settingsFragment" | ||
| 25 | app:destination="@id/settingsFragment" /> | ||
| 26 | |||
| 27 | <fragment | ||
| 28 | android:id="@+id/settingsSearchFragment" | ||
| 29 | android:name="org.yuzu.yuzu_emu.fragments.SettingsSearchFragment" | ||
| 30 | android:label="SettingsSearchFragment" /> | ||
| 31 | |||
| 32 | </navigation> | ||
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 0c1d91264..daaa7ffde 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml | |||
| @@ -209,7 +209,6 @@ | |||
| 209 | <string name="emulation_pause">Emulation pausieren</string> | 209 | <string name="emulation_pause">Emulation pausieren</string> |
| 210 | <string name="emulation_unpause">Emulation fortsetzen</string> | 210 | <string name="emulation_unpause">Emulation fortsetzen</string> |
| 211 | <string name="emulation_input_overlay">Overlay-Optionen</string> | 211 | <string name="emulation_input_overlay">Overlay-Optionen</string> |
| 212 | <string name="emulation_game_loading">Spiel lädt…</string> | ||
| 213 | 212 | ||
| 214 | <string name="load_settings">Lädt Einstellungen...</string> | 213 | <string name="load_settings">Lädt Einstellungen...</string> |
| 215 | 214 | ||
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 357f956d1..e9129cb00 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Pausar Emulación</string> | 213 | <string name="emulation_pause">Pausar Emulación</string> |
| 214 | <string name="emulation_unpause">Reanudar Emulación</string> | 214 | <string name="emulation_unpause">Reanudar Emulación</string> |
| 215 | <string name="emulation_input_overlay">Opciones de pantalla </string> | 215 | <string name="emulation_input_overlay">Opciones de pantalla </string> |
| 216 | <string name="emulation_game_loading">Cargando juego...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Cargando configuración...</string> | 217 | <string name="load_settings">Cargando configuración...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index dfca1c830..2d99d618e 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Mettre en pause l\'émulation</string> | 213 | <string name="emulation_pause">Mettre en pause l\'émulation</string> |
| 214 | <string name="emulation_unpause">Reprendre l\'émulation</string> | 214 | <string name="emulation_unpause">Reprendre l\'émulation</string> |
| 215 | <string name="emulation_input_overlay">Options de l\'overlay</string> | 215 | <string name="emulation_input_overlay">Options de l\'overlay</string> |
| 216 | <string name="emulation_game_loading">Chargement du jeu...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Chargement des paramètres…</string> | 217 | <string name="load_settings">Chargement des paramètres…</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index 089d93ed6..d9c3de385 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Metti in pausa l\'emulazione</string> | 213 | <string name="emulation_pause">Metti in pausa l\'emulazione</string> |
| 214 | <string name="emulation_unpause">Riprendi Emulazione</string> | 214 | <string name="emulation_unpause">Riprendi Emulazione</string> |
| 215 | <string name="emulation_input_overlay">Impostazioni Overlay</string> | 215 | <string name="emulation_input_overlay">Impostazioni Overlay</string> |
| 216 | <string name="emulation_game_loading">Caricamento del gioco...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Caricamento delle impostazioni...</string> | 217 | <string name="load_settings">Caricamento delle impostazioni...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index 39b590bee..7a226cd5c 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml | |||
| @@ -211,7 +211,6 @@ | |||
| 211 | <string name="emulation_pause">エミュレーションを一時停止</string> | 211 | <string name="emulation_pause">エミュレーションを一時停止</string> |
| 212 | <string name="emulation_unpause">エミュレーションを再開</string> | 212 | <string name="emulation_unpause">エミュレーションを再開</string> |
| 213 | <string name="emulation_input_overlay">オーバーレイオプション</string> | 213 | <string name="emulation_input_overlay">オーバーレイオプション</string> |
| 214 | <string name="emulation_game_loading">ロード中…</string> | ||
| 215 | 214 | ||
| 216 | <string name="load_settings">設定をロード中…</string> | 215 | <string name="load_settings">設定をロード中…</string> |
| 217 | 216 | ||
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index cbcb2873f..427b6e5a0 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">에뮬레이션 일시 중지</string> | 213 | <string name="emulation_pause">에뮬레이션 일시 중지</string> |
| 214 | <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> | 214 | <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> |
| 215 | <string name="emulation_input_overlay">오버레이 옵션</string> | 215 | <string name="emulation_input_overlay">오버레이 옵션</string> |
| 216 | <string name="emulation_game_loading">게임 불러오기 중...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">설정 불러오기 중...</string> | 217 | <string name="load_settings">설정 불러오기 중...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index e48a4be38..ce8d7a9e4 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Pause Emulering</string> | 213 | <string name="emulation_pause">Pause Emulering</string> |
| 214 | <string name="emulation_unpause">Opphev pausing av emulering</string> | 214 | <string name="emulation_unpause">Opphev pausing av emulering</string> |
| 215 | <string name="emulation_input_overlay">Alternativer for overlegg</string> | 215 | <string name="emulation_input_overlay">Alternativer for overlegg</string> |
| 216 | <string name="emulation_game_loading">Spillet lastes inn...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Laster inn innstillinger...</string> | 217 | <string name="load_settings">Laster inn innstillinger...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index bc9c0f7f4..c2c24b48f 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Wstrzymaj emulację</string> | 213 | <string name="emulation_pause">Wstrzymaj emulację</string> |
| 214 | <string name="emulation_unpause">Wznów emulację</string> | 214 | <string name="emulation_unpause">Wznów emulację</string> |
| 215 | <string name="emulation_input_overlay">Opcje nakładki</string> | 215 | <string name="emulation_input_overlay">Opcje nakładki</string> |
| 216 | <string name="emulation_game_loading">Wczytywanie gry...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Wczytywanie ustawień...</string> | 217 | <string name="load_settings">Wczytywanie ustawień...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 75fe0edbf..04f276108 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Pausa emulação</string> | 213 | <string name="emulation_pause">Pausa emulação</string> |
| 214 | <string name="emulation_unpause">Retomar emulação</string> | 214 | <string name="emulation_unpause">Retomar emulação</string> |
| 215 | <string name="emulation_input_overlay">Opções de sobreposição </string> | 215 | <string name="emulation_input_overlay">Opções de sobreposição </string> |
| 216 | <string name="emulation_game_loading">Jogo a carregar...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Configurações a carregar...</string> | 217 | <string name="load_settings">Configurações a carregar...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 96b040c66..66a3a1a2e 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Pausa emulação</string> | 213 | <string name="emulation_pause">Pausa emulação</string> |
| 214 | <string name="emulation_unpause">Retomar emulação</string> | 214 | <string name="emulation_unpause">Retomar emulação</string> |
| 215 | <string name="emulation_input_overlay">Opções de sobreposição </string> | 215 | <string name="emulation_input_overlay">Opções de sobreposição </string> |
| 216 | <string name="emulation_game_loading">Jogo a carregar...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Configurações a carregar...</string> | 217 | <string name="load_settings">Configurações a carregar...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 8d954f59e..f770e954f 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Пауза эмуляции</string> | 213 | <string name="emulation_pause">Пауза эмуляции</string> |
| 214 | <string name="emulation_unpause">Возобновление эмуляции</string> | 214 | <string name="emulation_unpause">Возобновление эмуляции</string> |
| 215 | <string name="emulation_input_overlay">Настройки оверлея</string> | 215 | <string name="emulation_input_overlay">Настройки оверлея</string> |
| 216 | <string name="emulation_game_loading">Загрузка игры...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Загрузка настроек...</string> | 217 | <string name="load_settings">Загрузка настроек...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 6c028535b..ea3ab1b15 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">Пауза емуляції</string> | 213 | <string name="emulation_pause">Пауза емуляції</string> |
| 214 | <string name="emulation_unpause">Відновлення емуляції</string> | 214 | <string name="emulation_unpause">Відновлення емуляції</string> |
| 215 | <string name="emulation_input_overlay">Налаштування оверлея</string> | 215 | <string name="emulation_input_overlay">Налаштування оверлея</string> |
| 216 | <string name="emulation_game_loading">Завантаження гри...</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">Завантаження налаштувань...</string> | 217 | <string name="load_settings">Завантаження налаштувань...</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index e4ad2ed07..b45a5a528 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">暂停模拟</string> | 213 | <string name="emulation_pause">暂停模拟</string> |
| 214 | <string name="emulation_unpause">继续模拟</string> | 214 | <string name="emulation_unpause">继续模拟</string> |
| 215 | <string name="emulation_input_overlay">虚拟按键选项</string> | 215 | <string name="emulation_input_overlay">虚拟按键选项</string> |
| 216 | <string name="emulation_game_loading">载入游戏中…</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">正在载入设定…</string> | 217 | <string name="load_settings">正在载入设定…</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 0d32f23df..3aab889e4 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml | |||
| @@ -213,7 +213,6 @@ | |||
| 213 | <string name="emulation_pause">暫停模擬</string> | 213 | <string name="emulation_pause">暫停模擬</string> |
| 214 | <string name="emulation_unpause">取消暫停模擬</string> | 214 | <string name="emulation_unpause">取消暫停模擬</string> |
| 215 | <string name="emulation_input_overlay">覆疊選項</string> | 215 | <string name="emulation_input_overlay">覆疊選項</string> |
| 216 | <string name="emulation_game_loading">遊戲正在載入…</string> | ||
| 217 | 216 | ||
| 218 | <string name="load_settings">正在載入設定…</string> | 217 | <string name="load_settings">正在載入設定…</string> |
| 219 | 218 | ||
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 200b99185..dc10159c9 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml | |||
| @@ -243,10 +243,10 @@ | |||
| 243 | <item>@string/cubeb</item> | 243 | <item>@string/cubeb</item> |
| 244 | <item>@string/string_null</item> | 244 | <item>@string/string_null</item> |
| 245 | </string-array> | 245 | </string-array> |
| 246 | <string-array name="outputEngineValues"> | 246 | <integer-array name="outputEngineValues"> |
| 247 | <item>auto</item> | 247 | <item>0</item> |
| 248 | <item>cubeb</item> | 248 | <item>1</item> |
| 249 | <item>null</item> | 249 | <item>3</item> |
| 250 | </string-array> | 250 | </integer-array> |
| 251 | 251 | ||
| 252 | </resources> | 252 | </resources> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index de1b2909b..b163e6fc1 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> | 43 | <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> |
| 44 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 44 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 45 | <string name="home_search_games">Search games</string> | 45 | <string name="home_search_games">Search games</string> |
| 46 | <string name="search_settings">Search settings</string> | ||
| 46 | <string name="games_dir_selected">Games directory selected</string> | 47 | <string name="games_dir_selected">Games directory selected</string> |
| 47 | <string name="install_prod_keys">Install prod.keys</string> | 48 | <string name="install_prod_keys">Install prod.keys</string> |
| 48 | <string name="install_prod_keys_description">Required to decrypt retail games</string> | 49 | <string name="install_prod_keys_description">Required to decrypt retail games</string> |
| @@ -74,6 +75,7 @@ | |||
| 74 | <string name="install_gpu_driver">Install GPU driver</string> | 75 | <string name="install_gpu_driver">Install GPU driver</string> |
| 75 | <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> | 76 | <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> |
| 76 | <string name="advanced_settings">Advanced settings</string> | 77 | <string name="advanced_settings">Advanced settings</string> |
| 78 | <string name="advanced_settings_game">Advanced settings: %1$s</string> | ||
| 77 | <string name="settings_description">Configure emulator settings</string> | 79 | <string name="settings_description">Configure emulator settings</string> |
| 78 | <string name="search_recently_played">Recently played</string> | 80 | <string name="search_recently_played">Recently played</string> |
| 79 | <string name="search_recently_added">Recently added</string> | 81 | <string name="search_recently_added">Recently added</string> |
| @@ -200,7 +202,9 @@ | |||
| 200 | <string name="ini_saved">Saved settings</string> | 202 | <string name="ini_saved">Saved settings</string> |
| 201 | <string name="gameid_saved">Saved settings for %1$s</string> | 203 | <string name="gameid_saved">Saved settings for %1$s</string> |
| 202 | <string name="error_saving">Error saving %1$s.ini: %2$s</string> | 204 | <string name="error_saving">Error saving %1$s.ini: %2$s</string> |
| 205 | <string name="unimplemented_menu">Unimplemented Menu</string> | ||
| 203 | <string name="loading">Loading…</string> | 206 | <string name="loading">Loading…</string> |
| 207 | <string name="shutting_down">Shutting down…</string> | ||
| 204 | <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> | 208 | <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> |
| 205 | <string name="reset_to_default">Reset to default</string> | 209 | <string name="reset_to_default">Reset to default</string> |
| 206 | <string name="reset_all_settings">Reset all settings?</string> | 210 | <string name="reset_all_settings">Reset all settings?</string> |
| @@ -259,7 +263,6 @@ | |||
| 259 | <string name="emulation_pause">Pause emulation</string> | 263 | <string name="emulation_pause">Pause emulation</string> |
| 260 | <string name="emulation_unpause">Unpause emulation</string> | 264 | <string name="emulation_unpause">Unpause emulation</string> |
| 261 | <string name="emulation_input_overlay">Overlay options</string> | 265 | <string name="emulation_input_overlay">Overlay options</string> |
| 262 | <string name="emulation_game_loading">Game loading…</string> | ||
| 263 | 266 | ||
| 264 | <string name="load_settings">Loading settings…</string> | 267 | <string name="load_settings">Loading settings…</string> |
| 265 | 268 | ||
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index e7b595459..67dfe0290 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -2,6 +2,14 @@ | |||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | 2 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | add_library(audio_core STATIC | 4 | add_library(audio_core STATIC |
| 5 | adsp/adsp.cpp | ||
| 6 | adsp/adsp.h | ||
| 7 | adsp/mailbox.h | ||
| 8 | adsp/apps/audio_renderer/audio_renderer.cpp | ||
| 9 | adsp/apps/audio_renderer/audio_renderer.h | ||
| 10 | adsp/apps/audio_renderer/command_buffer.h | ||
| 11 | adsp/apps/audio_renderer/command_list_processor.cpp | ||
| 12 | adsp/apps/audio_renderer/command_list_processor.h | ||
| 5 | audio_core.cpp | 13 | audio_core.cpp |
| 6 | audio_core.h | 14 | audio_core.h |
| 7 | audio_event.h | 15 | audio_event.h |
| @@ -32,13 +40,6 @@ add_library(audio_core STATIC | |||
| 32 | out/audio_out_system.cpp | 40 | out/audio_out_system.cpp |
| 33 | out/audio_out_system.h | 41 | out/audio_out_system.h |
| 34 | precompiled_headers.h | 42 | precompiled_headers.h |
| 35 | renderer/adsp/adsp.cpp | ||
| 36 | renderer/adsp/adsp.h | ||
| 37 | renderer/adsp/audio_renderer.cpp | ||
| 38 | renderer/adsp/audio_renderer.h | ||
| 39 | renderer/adsp/command_buffer.h | ||
| 40 | renderer/adsp/command_list_processor.cpp | ||
| 41 | renderer/adsp/command_list_processor.h | ||
| 42 | renderer/audio_device.cpp | 43 | renderer/audio_device.cpp |
| 43 | renderer/audio_device.h | 44 | renderer/audio_device.h |
| 44 | renderer/audio_renderer.h | 45 | renderer/audio_renderer.h |
diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp new file mode 100644 index 000000000..0580990f5 --- /dev/null +++ b/src/audio_core/adsp/adsp.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/adsp/adsp.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | |||
| 7 | namespace AudioCore::ADSP { | ||
| 8 | |||
| 9 | ADSP::ADSP(Core::System& system, Sink::Sink& sink) { | ||
| 10 | audio_renderer = | ||
| 11 | std::make_unique<AudioRenderer::AudioRenderer>(system, system.ApplicationMemory(), sink); | ||
| 12 | } | ||
| 13 | |||
| 14 | AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { | ||
| 15 | return *audio_renderer.get(); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace AudioCore::ADSP | ||
diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h new file mode 100644 index 000000000..bd5bcc63b --- /dev/null +++ b/src/audio_core/adsp/adsp.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } // namespace Core | ||
| 12 | |||
| 13 | namespace AudioCore { | ||
| 14 | namespace Sink { | ||
| 15 | class Sink; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace ADSP { | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Represents the ADSP embedded within the audio sysmodule. | ||
| 22 | * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot. | ||
| 23 | * | ||
| 24 | * The kernel will run the apps you write for it, Nintendo have the following: | ||
| 25 | * | ||
| 26 | * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all | ||
| 27 | * audio samples end up, and we skip it entirely, since we have very different backends and | ||
| 28 | * mixing is implicitly handled by the OS (but also due to lack of research/simplicity). | ||
| 29 | * | ||
| 30 | * AudioRenderer - Receives command lists generated by the audio render | ||
| 31 | * system on the host, processes them, and sends the samples to Gmix. | ||
| 32 | * | ||
| 33 | * OpusDecoder - Contains libopus, and decodes Opus audio packets into raw pcm data. | ||
| 34 | * | ||
| 35 | * Communication between the host and ADSP is done through mailboxes, and mapping of shared memory. | ||
| 36 | */ | ||
| 37 | class ADSP { | ||
| 38 | public: | ||
| 39 | explicit ADSP(Core::System& system, Sink::Sink& sink); | ||
| 40 | ~ADSP() = default; | ||
| 41 | |||
| 42 | AudioRenderer::AudioRenderer& AudioRenderer(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | /// AudioRenderer app | ||
| 46 | std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{}; | ||
| 47 | }; | ||
| 48 | |||
| 49 | } // namespace ADSP | ||
| 50 | } // namespace AudioCore | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp new file mode 100644 index 000000000..2e549bc6f --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 7 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" | ||
| 8 | #include "audio_core/audio_core.h" | ||
| 9 | #include "audio_core/common/common.h" | ||
| 10 | #include "audio_core/sink/sink.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/microprofile.h" | ||
| 13 | #include "common/thread.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_timing.h" | ||
| 16 | |||
| 17 | MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); | ||
| 18 | |||
| 19 | namespace AudioCore::ADSP::AudioRenderer { | ||
| 20 | |||
| 21 | AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_, | ||
| 22 | Sink::Sink& sink_) | ||
| 23 | : system{system_}, memory{memory_}, sink{sink_} {} | ||
| 24 | |||
| 25 | AudioRenderer::~AudioRenderer() { | ||
| 26 | Stop(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void AudioRenderer::Start() { | ||
| 30 | CreateSinkStreams(); | ||
| 31 | |||
| 32 | mailbox.Initialize(AppMailboxId::AudioRenderer); | ||
| 33 | |||
| 34 | main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); | ||
| 35 | |||
| 36 | mailbox.Send(Direction::DSP, {Message::InitializeOK, {}}); | ||
| 37 | if (mailbox.Receive(Direction::Host).msg != Message::InitializeOK) { | ||
| 38 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | ||
| 39 | "message response from ADSP!"); | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | running = true; | ||
| 43 | } | ||
| 44 | |||
| 45 | void AudioRenderer::Stop() { | ||
| 46 | if (!running) { | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | |||
| 50 | mailbox.Send(Direction::DSP, {Message::Shutdown, {}}); | ||
| 51 | if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) { | ||
| 52 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | ||
| 53 | "message response from ADSP!"); | ||
| 54 | } | ||
| 55 | main_thread.request_stop(); | ||
| 56 | main_thread.join(); | ||
| 57 | |||
| 58 | for (auto& stream : streams) { | ||
| 59 | if (stream) { | ||
| 60 | stream->Stop(); | ||
| 61 | sink.CloseStream(stream); | ||
| 62 | stream = nullptr; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | running = false; | ||
| 66 | } | ||
| 67 | |||
| 68 | void AudioRenderer::Signal() { | ||
| 69 | signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 70 | Send(Direction::DSP, {Message::Render, {}}); | ||
| 71 | } | ||
| 72 | |||
| 73 | void AudioRenderer::Wait() { | ||
| 74 | auto received = Receive(Direction::Host); | ||
| 75 | if (received.msg != Message::RenderResponse) { | ||
| 76 | LOG_ERROR(Service_Audio, | ||
| 77 | "Did not receive the expected render response from the AudioRenderer! Expected " | ||
| 78 | "{}, got {}", | ||
| 79 | Message::RenderResponse, received.msg); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | void AudioRenderer::Send(Direction dir, MailboxMessage message) { | ||
| 84 | mailbox.Send(dir, std::move(message)); | ||
| 85 | } | ||
| 86 | |||
| 87 | MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { | ||
| 88 | return mailbox.Receive(dir, block); | ||
| 89 | } | ||
| 90 | |||
| 91 | void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, | ||
| 92 | u64 applet_resource_user_id, bool reset) noexcept { | ||
| 93 | command_buffers[session_id].buffer = buffer; | ||
| 94 | command_buffers[session_id].size = size; | ||
| 95 | command_buffers[session_id].time_limit = time_limit; | ||
| 96 | command_buffers[session_id].applet_resource_user_id = applet_resource_user_id; | ||
| 97 | command_buffers[session_id].reset_buffer = reset; | ||
| 98 | } | ||
| 99 | |||
| 100 | u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { | ||
| 101 | return command_buffers[session_id].remaining_command_count; | ||
| 102 | } | ||
| 103 | |||
| 104 | void AudioRenderer::ClearRemainCommandCount(s32 session_id) noexcept { | ||
| 105 | command_buffers[session_id].remaining_command_count = 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | u64 AudioRenderer::GetRenderingStartTick(s32 session_id) const noexcept { | ||
| 109 | return (1000 * command_buffers[session_id].render_time_taken_us) + signalled_tick; | ||
| 110 | } | ||
| 111 | |||
| 112 | void AudioRenderer::CreateSinkStreams() { | ||
| 113 | u32 channels{sink.GetDeviceChannels()}; | ||
| 114 | for (u32 i = 0; i < MaxRendererSessions; i++) { | ||
| 115 | std::string name{fmt::format("ADSP_RenderStream-{}", i)}; | ||
| 116 | streams[i] = | ||
| 117 | sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); | ||
| 118 | streams[i]->SetRingSize(4); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | void AudioRenderer::Main(std::stop_token stop_token) { | ||
| 123 | static constexpr char name[]{"AudioRenderer"}; | ||
| 124 | MicroProfileOnThreadCreate(name); | ||
| 125 | Common::SetCurrentThreadName(name); | ||
| 126 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 127 | |||
| 128 | // TODO: Create buffer map/unmap thread + mailbox | ||
| 129 | // TODO: Create gMix devices, initialize them here | ||
| 130 | |||
| 131 | if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) { | ||
| 132 | LOG_ERROR(Service_Audio, | ||
| 133 | "ADSP Audio Renderer -- Failed to receive initialize message from host!"); | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | |||
| 137 | mailbox.Send(Direction::Host, {Message::InitializeOK, {}}); | ||
| 138 | |||
| 139 | // 0.12 seconds (2,304,000 / 19,200,000) | ||
| 140 | constexpr u64 max_process_time{2'304'000ULL}; | ||
| 141 | |||
| 142 | while (!stop_token.stop_requested()) { | ||
| 143 | auto received{mailbox.Receive(Direction::DSP)}; | ||
| 144 | switch (received.msg) { | ||
| 145 | case Message::Shutdown: | ||
| 146 | mailbox.Send(Direction::Host, {Message::Shutdown, {}}); | ||
| 147 | return; | ||
| 148 | |||
| 149 | case Message::Render: { | ||
| 150 | if (system.IsShuttingDown()) [[unlikely]] { | ||
| 151 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); | ||
| 152 | mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); | ||
| 153 | continue; | ||
| 154 | } | ||
| 155 | std::array<bool, MaxRendererSessions> buffers_reset{}; | ||
| 156 | std::array<u64, MaxRendererSessions> render_times_taken{}; | ||
| 157 | const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()}; | ||
| 158 | |||
| 159 | for (u32 index = 0; index < MaxRendererSessions; index++) { | ||
| 160 | auto& command_buffer{command_buffers[index]}; | ||
| 161 | auto& command_list_processor{command_list_processors[index]}; | ||
| 162 | |||
| 163 | // Check this buffer is valid, as it may not be used. | ||
| 164 | if (command_buffer.buffer != 0) { | ||
| 165 | // If there are no remaining commands (from the previous list), | ||
| 166 | // this is a new command list, initialize it. | ||
| 167 | if (command_buffer.remaining_command_count == 0) { | ||
| 168 | command_list_processor.Initialize(system, command_buffer.buffer, | ||
| 169 | command_buffer.size, streams[index]); | ||
| 170 | } | ||
| 171 | |||
| 172 | if (command_buffer.reset_buffer && !buffers_reset[index]) { | ||
| 173 | streams[index]->ClearQueue(); | ||
| 174 | buffers_reset[index] = true; | ||
| 175 | } | ||
| 176 | |||
| 177 | u64 max_time{max_process_time}; | ||
| 178 | if (index == 1 && command_buffer.applet_resource_user_id == | ||
| 179 | command_buffers[0].applet_resource_user_id) { | ||
| 180 | max_time = max_process_time - render_times_taken[0]; | ||
| 181 | if (render_times_taken[0] > max_process_time) { | ||
| 182 | max_time = 0; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | max_time = std::min(command_buffer.time_limit, max_time); | ||
| 187 | command_list_processor.SetProcessTimeMax(max_time); | ||
| 188 | |||
| 189 | if (index == 0) { | ||
| 190 | streams[index]->WaitFreeSpace(stop_token); | ||
| 191 | } | ||
| 192 | |||
| 193 | // Process the command list | ||
| 194 | { | ||
| 195 | MICROPROFILE_SCOPE(Audio_Renderer); | ||
| 196 | render_times_taken[index] = | ||
| 197 | command_list_processor.Process(index) - start_time; | ||
| 198 | } | ||
| 199 | |||
| 200 | const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()}; | ||
| 201 | |||
| 202 | command_buffer.remaining_command_count = | ||
| 203 | command_list_processor.GetRemainingCommandCount(); | ||
| 204 | command_buffer.render_time_taken_us = end_time - start_time; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); | ||
| 209 | } break; | ||
| 210 | |||
| 211 | default: | ||
| 212 | LOG_WARNING(Service_Audio, | ||
| 213 | "ADSP AudioRenderer received an invalid message, msg={:02X}!", | ||
| 214 | received.msg); | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | } // namespace AudioCore::ADSP::AudioRenderer | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h new file mode 100644 index 000000000..3f5b7dca2 --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <memory> | ||
| 8 | #include <thread> | ||
| 9 | |||
| 10 | #include "audio_core/adsp/apps/audio_renderer/command_buffer.h" | ||
| 11 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" | ||
| 12 | #include "audio_core/adsp/mailbox.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "common/polyfill_thread.h" | ||
| 15 | #include "common/reader_writer_queue.h" | ||
| 16 | #include "common/thread.h" | ||
| 17 | |||
| 18 | namespace Core { | ||
| 19 | class System; | ||
| 20 | namespace Timing { | ||
| 21 | struct EventType; | ||
| 22 | } | ||
| 23 | namespace Memory { | ||
| 24 | class Memory; | ||
| 25 | } | ||
| 26 | class System; | ||
| 27 | } // namespace Core | ||
| 28 | |||
| 29 | namespace AudioCore { | ||
| 30 | namespace Sink { | ||
| 31 | class Sink; | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace ADSP::AudioRenderer { | ||
| 35 | |||
| 36 | enum Message : u32 { | ||
| 37 | Invalid = 0x00, | ||
| 38 | MapUnmap_Map = 0x01, | ||
| 39 | MapUnmap_MapResponse = 0x02, | ||
| 40 | MapUnmap_Unmap = 0x03, | ||
| 41 | MapUnmap_UnmapResponse = 0x04, | ||
| 42 | MapUnmap_InvalidateCache = 0x05, | ||
| 43 | MapUnmap_InvalidateCacheResponse = 0x06, | ||
| 44 | MapUnmap_Shutdown = 0x07, | ||
| 45 | MapUnmap_ShutdownResponse = 0x08, | ||
| 46 | InitializeOK = 0x16, | ||
| 47 | RenderResponse = 0x20, | ||
| 48 | Render = 0x2A, | ||
| 49 | Shutdown = 0x34, | ||
| 50 | }; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * The AudioRenderer application running on the ADSP. | ||
| 54 | */ | ||
| 55 | class AudioRenderer { | ||
| 56 | public: | ||
| 57 | explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink); | ||
| 58 | ~AudioRenderer(); | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Start the AudioRenderer. | ||
| 62 | * | ||
| 63 | * @param mailbox The mailbox to use for this session. | ||
| 64 | */ | ||
| 65 | void Start(); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Stop the AudioRenderer. | ||
| 69 | */ | ||
| 70 | void Stop(); | ||
| 71 | |||
| 72 | void Signal(); | ||
| 73 | void Wait(); | ||
| 74 | |||
| 75 | void Send(Direction dir, MailboxMessage message); | ||
| 76 | MailboxMessage Receive(Direction dir, bool block = true); | ||
| 77 | |||
| 78 | void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, | ||
| 79 | u64 applet_resource_user_id, bool reset) noexcept; | ||
| 80 | u32 GetRemainCommandCount(s32 session_id) const noexcept; | ||
| 81 | void ClearRemainCommandCount(s32 session_id) noexcept; | ||
| 82 | u64 GetRenderingStartTick(s32 session_id) const noexcept; | ||
| 83 | |||
| 84 | private: | ||
| 85 | /** | ||
| 86 | * Main AudioRenderer thread, responsible for processing the command lists. | ||
| 87 | */ | ||
| 88 | void Main(std::stop_token stop_token); | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Creates the streams which will receive the processed samples. | ||
| 92 | */ | ||
| 93 | void CreateSinkStreams(); | ||
| 94 | |||
| 95 | /// Core system | ||
| 96 | Core::System& system; | ||
| 97 | /// Memory | ||
| 98 | Core::Memory::Memory& memory; | ||
| 99 | /// The output sink the AudioRenderer will use | ||
| 100 | Sink::Sink& sink; | ||
| 101 | /// The active mailbox | ||
| 102 | Mailbox mailbox; | ||
| 103 | /// Main thread | ||
| 104 | std::jthread main_thread{}; | ||
| 105 | /// The current state | ||
| 106 | std::atomic<bool> running{}; | ||
| 107 | std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; | ||
| 108 | /// The command lists to process | ||
| 109 | std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; | ||
| 110 | /// The streams which will receive the processed samples | ||
| 111 | std::array<Sink::SinkStream*, MaxRendererSessions> streams{}; | ||
| 112 | u64 signalled_tick{0}; | ||
| 113 | }; | ||
| 114 | |||
| 115 | } // namespace ADSP::AudioRenderer | ||
| 116 | } // namespace AudioCore | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/command_buffer.h b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h new file mode 100644 index 000000000..3fd1b09dc --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/common/common.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::ADSP::AudioRenderer { | ||
| 10 | |||
| 11 | struct CommandBuffer { | ||
| 12 | // Set by the host | ||
| 13 | CpuAddr buffer{}; | ||
| 14 | u64 size{}; | ||
| 15 | u64 time_limit{}; | ||
| 16 | u64 applet_resource_user_id{}; | ||
| 17 | bool reset_buffer{}; | ||
| 18 | // Set by the DSP | ||
| 19 | u32 remaining_command_count{}; | ||
| 20 | u64 render_time_taken_us{}; | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace AudioCore::ADSP::AudioRenderer | ||
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp index 3a0f1ae38..24e4d0496 100644 --- a/src/audio_core/renderer/adsp/command_list_processor.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <string> | 4 | #include <string> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/command_list_header.h" | 7 | #include "audio_core/renderer/command/command_list_header.h" |
| 8 | #include "audio_core/renderer/command/commands.h" | 8 | #include "audio_core/renderer/command/commands.h" |
| 9 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| @@ -11,15 +11,15 @@ | |||
| 11 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 12 | #include "core/memory.h" | 12 | #include "core/memory.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer::ADSP { | 14 | namespace AudioCore::ADSP::AudioRenderer { |
| 15 | 15 | ||
| 16 | void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size, | 16 | void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size, |
| 17 | Sink::SinkStream* stream_) { | 17 | Sink::SinkStream* stream_) { |
| 18 | system = &system_; | 18 | system = &system_; |
| 19 | memory = &system->ApplicationMemory(); | 19 | memory = &system->ApplicationMemory(); |
| 20 | stream = stream_; | 20 | stream = stream_; |
| 21 | header = reinterpret_cast<CommandListHeader*>(buffer); | 21 | header = reinterpret_cast<Renderer::CommandListHeader*>(buffer); |
| 22 | commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); | 22 | commands = reinterpret_cast<u8*>(buffer + sizeof(Renderer::CommandListHeader)); |
| 23 | commands_buffer_size = size; | 23 | commands_buffer_size = size; |
| 24 | command_count = header->command_count; | 24 | command_count = header->command_count; |
| 25 | sample_count = header->sample_count; | 25 | sample_count = header->sample_count; |
| @@ -37,17 +37,12 @@ u32 CommandListProcessor::GetRemainingCommandCount() const { | |||
| 37 | return command_count - processed_command_count; | 37 | return command_count - processed_command_count; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) { | ||
| 41 | commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); | ||
| 42 | commands_buffer_size = size; | ||
| 43 | } | ||
| 44 | |||
| 45 | Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { | 40 | Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { |
| 46 | return stream; | 41 | return stream; |
| 47 | } | 42 | } |
| 48 | 43 | ||
| 49 | u64 CommandListProcessor::Process(u32 session_id) { | 44 | u64 CommandListProcessor::Process(u32 session_id) { |
| 50 | const auto start_time_{system->CoreTiming().GetClockTicks()}; | 45 | const auto start_time_{system->CoreTiming().GetGlobalTimeUs().count()}; |
| 51 | const auto command_base{CpuAddr(commands)}; | 46 | const auto command_base{CpuAddr(commands)}; |
| 52 | 47 | ||
| 53 | if (processed_command_count > 0) { | 48 | if (processed_command_count > 0) { |
| @@ -60,12 +55,12 @@ u64 CommandListProcessor::Process(u32 session_id) { | |||
| 60 | std::string dump{fmt::format("\nSession {}\n", session_id)}; | 55 | std::string dump{fmt::format("\nSession {}\n", session_id)}; |
| 61 | 56 | ||
| 62 | for (u32 index = 0; index < command_count; index++) { | 57 | for (u32 index = 0; index < command_count; index++) { |
| 63 | auto& command{*reinterpret_cast<ICommand*>(commands)}; | 58 | auto& command{*reinterpret_cast<Renderer::ICommand*>(commands)}; |
| 64 | 59 | ||
| 65 | if (command.magic != 0xCAFEBABE) { | 60 | if (command.magic != 0xCAFEBABE) { |
| 66 | LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}", | 61 | LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}", |
| 67 | command.magic); | 62 | command.magic); |
| 68 | return system->CoreTiming().GetClockTicks() - start_time_; | 63 | return system->CoreTiming().GetGlobalTimeUs().count() - start_time_; |
| 69 | } | 64 | } |
| 70 | 65 | ||
| 71 | auto current_offset{CpuAddr(commands) - command_base}; | 66 | auto current_offset{CpuAddr(commands) - command_base}; |
| @@ -74,8 +69,8 @@ u64 CommandListProcessor::Process(u32 session_id) { | |||
| 74 | LOG_ERROR(Service_Audio, | 69 | LOG_ERROR(Service_Audio, |
| 75 | "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}", | 70 | "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}", |
| 76 | commands_buffer_size, | 71 | commands_buffer_size, |
| 77 | CpuAddr(commands) + command.size - sizeof(CommandListHeader)); | 72 | CpuAddr(commands) + command.size - sizeof(Renderer::CommandListHeader)); |
| 78 | return system->CoreTiming().GetClockTicks() - start_time_; | 73 | return system->CoreTiming().GetGlobalTimeUs().count() - start_time_; |
| 79 | } | 74 | } |
| 80 | 75 | ||
| 81 | if (Settings::values.dump_audio_commands) { | 76 | if (Settings::values.dump_audio_commands) { |
| @@ -101,8 +96,8 @@ u64 CommandListProcessor::Process(u32 session_id) { | |||
| 101 | last_dump = dump; | 96 | last_dump = dump; |
| 102 | } | 97 | } |
| 103 | 98 | ||
| 104 | end_time = system->CoreTiming().GetClockTicks(); | 99 | end_time = system->CoreTiming().GetGlobalTimeUs().count(); |
| 105 | return end_time - start_time_; | 100 | return end_time - start_time_; |
| 106 | } | 101 | } |
| 107 | 102 | ||
| 108 | } // namespace AudioCore::AudioRenderer::ADSP | 103 | } // namespace AudioCore::ADSP::AudioRenderer |
diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h index d78269e1d..4e5fb793e 100644 --- a/src/audio_core/renderer/adsp/command_list_processor.h +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <span> | 6 | #include <span> |
| 7 | 7 | ||
| 8 | #include "audio_core/common/common.h" | 8 | #include "audio_core/common/common.h" |
| 9 | #include "audio_core/renderer/command/command_list_header.h" | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| 11 | namespace Core { | 12 | namespace Core { |
| @@ -20,10 +21,11 @@ namespace Sink { | |||
| 20 | class SinkStream; | 21 | class SinkStream; |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | namespace AudioRenderer { | 24 | namespace Renderer { |
| 24 | struct CommandListHeader; | 25 | struct CommandListHeader; |
| 26 | } | ||
| 25 | 27 | ||
| 26 | namespace ADSP { | 28 | namespace ADSP::AudioRenderer { |
| 27 | 29 | ||
| 28 | /** | 30 | /** |
| 29 | * A processor for command lists given to the AudioRenderer. | 31 | * A processor for command lists given to the AudioRenderer. |
| @@ -55,14 +57,6 @@ public: | |||
| 55 | u32 GetRemainingCommandCount() const; | 57 | u32 GetRemainingCommandCount() const; |
| 56 | 58 | ||
| 57 | /** | 59 | /** |
| 58 | * Set the command buffer. | ||
| 59 | * | ||
| 60 | * @param buffer - The buffer to use. | ||
| 61 | * @param size - The size of the buffer. | ||
| 62 | */ | ||
| 63 | void SetBuffer(CpuAddr buffer, u64 size); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Get the stream for this command list. | 60 | * Get the stream for this command list. |
| 67 | * | 61 | * |
| 68 | * @return The stream associated with this command list. | 62 | * @return The stream associated with this command list. |
| @@ -85,7 +79,7 @@ public: | |||
| 85 | /// Stream for the processed samples | 79 | /// Stream for the processed samples |
| 86 | Sink::SinkStream* stream{}; | 80 | Sink::SinkStream* stream{}; |
| 87 | /// Header info for this command list | 81 | /// Header info for this command list |
| 88 | CommandListHeader* header{}; | 82 | Renderer::CommandListHeader* header{}; |
| 89 | /// The command buffer | 83 | /// The command buffer |
| 90 | u8* commands{}; | 84 | u8* commands{}; |
| 91 | /// The command buffer size | 85 | /// The command buffer size |
| @@ -114,6 +108,5 @@ public: | |||
| 114 | std::string last_dump{}; | 108 | std::string last_dump{}; |
| 115 | }; | 109 | }; |
| 116 | 110 | ||
| 117 | } // namespace ADSP | 111 | } // namespace ADSP::AudioRenderer |
| 118 | } // namespace AudioRenderer | ||
| 119 | } // namespace AudioCore | 112 | } // namespace AudioCore |
diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h new file mode 100644 index 000000000..c31b73717 --- /dev/null +++ b/src/audio_core/adsp/mailbox.h | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/bounded_threadsafe_queue.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::ADSP { | ||
| 10 | |||
| 11 | enum class AppMailboxId : u32 { | ||
| 12 | Invalid = 0, | ||
| 13 | AudioRenderer = 50, | ||
| 14 | AudioRendererMemoryMapUnmap = 51, | ||
| 15 | }; | ||
| 16 | |||
| 17 | enum class Direction : u32 { | ||
| 18 | Host, | ||
| 19 | DSP, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct MailboxMessage { | ||
| 23 | u32 msg; | ||
| 24 | std::span<u8> data; | ||
| 25 | }; | ||
| 26 | |||
| 27 | class Mailbox { | ||
| 28 | public: | ||
| 29 | void Initialize(AppMailboxId id_) { | ||
| 30 | Reset(); | ||
| 31 | id = id_; | ||
| 32 | } | ||
| 33 | |||
| 34 | AppMailboxId Id() const noexcept { | ||
| 35 | return id; | ||
| 36 | } | ||
| 37 | |||
| 38 | void Send(Direction dir, MailboxMessage&& message) { | ||
| 39 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; | ||
| 40 | queue.EmplaceWait(std::move(message)); | ||
| 41 | } | ||
| 42 | |||
| 43 | MailboxMessage Receive(Direction dir, bool block = true) { | ||
| 44 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; | ||
| 45 | MailboxMessage t; | ||
| 46 | if (block) { | ||
| 47 | queue.PopWait(t); | ||
| 48 | } else { | ||
| 49 | queue.TryPop(t); | ||
| 50 | } | ||
| 51 | return t; | ||
| 52 | } | ||
| 53 | |||
| 54 | void Reset() { | ||
| 55 | id = AppMailboxId::Invalid; | ||
| 56 | MailboxMessage t; | ||
| 57 | while (host_queue.TryPop(t)) { | ||
| 58 | } | ||
| 59 | while (adsp_queue.TryPop(t)) { | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | private: | ||
| 64 | AppMailboxId id{0}; | ||
| 65 | Common::SPSCQueue<MailboxMessage> host_queue; | ||
| 66 | Common::SPSCQueue<MailboxMessage> adsp_queue; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace AudioCore::ADSP | ||
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 703ef4494..fcaab2b32 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp | |||
| @@ -11,7 +11,7 @@ namespace AudioCore { | |||
| 11 | AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} { | 11 | AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} { |
| 12 | CreateSinks(); | 12 | CreateSinks(); |
| 13 | // Must be created after the sinks | 13 | // Must be created after the sinks |
| 14 | adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink); | 14 | adsp = std::make_unique<ADSP::ADSP>(system, *output_sink); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | AudioCore ::~AudioCore() { | 17 | AudioCore ::~AudioCore() { |
| @@ -43,7 +43,7 @@ Sink::Sink& AudioCore::GetInputSink() { | |||
| 43 | return *input_sink; | 43 | return *input_sink; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() { | 46 | ADSP::ADSP& AudioCore::ADSP() { |
| 47 | return *adsp; | 47 | return *adsp; |
| 48 | } | 48 | } |
| 49 | 49 | ||
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index ea047773e..e4e27fc66 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | 7 | ||
| 8 | #include "audio_core/adsp/adsp.h" | ||
| 8 | #include "audio_core/audio_manager.h" | 9 | #include "audio_core/audio_manager.h" |
| 9 | #include "audio_core/renderer/adsp/adsp.h" | ||
| 10 | #include "audio_core/sink/sink.h" | 10 | #include "audio_core/sink/sink.h" |
| 11 | 11 | ||
| 12 | namespace Core { | 12 | namespace Core { |
| @@ -55,7 +55,7 @@ public: | |||
| 55 | * | 55 | * |
| 56 | * @return Ref to the ADSP. | 56 | * @return Ref to the ADSP. |
| 57 | */ | 57 | */ |
| 58 | AudioRenderer::ADSP::ADSP& GetADSP(); | 58 | ADSP::ADSP& ADSP(); |
| 59 | 59 | ||
| 60 | private: | 60 | private: |
| 61 | /** | 61 | /** |
| @@ -70,7 +70,7 @@ private: | |||
| 70 | /// Sink used for audio input | 70 | /// Sink used for audio input |
| 71 | std::unique_ptr<Sink::Sink> input_sink; | 71 | std::unique_ptr<Sink::Sink> input_sink; |
| 72 | /// The ADSP in the sysmodule | 72 | /// The ADSP in the sysmodule |
| 73 | std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; | 73 | std::unique_ptr<ADSP::ADSP> adsp; |
| 74 | }; | 74 | }; |
| 75 | 75 | ||
| 76 | } // namespace AudioCore | 76 | } // namespace AudioCore |
diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp index d15568e1f..c23ef0990 100644 --- a/src/audio_core/audio_event.cpp +++ b/src/audio_core/audio_event.cpp | |||
| @@ -20,7 +20,6 @@ size_t Event::GetManagerIndex(const Type type) const { | |||
| 20 | default: | 20 | default: |
| 21 | UNREACHABLE(); | 21 | UNREACHABLE(); |
| 22 | } | 22 | } |
| 23 | return 3; | ||
| 24 | } | 23 | } |
| 25 | 24 | ||
| 26 | void Event::SetAudioEvent(const Type type, const bool signalled) { | 25 | void Event::SetAudioEvent(const Type type, const bool signalled) { |
diff --git a/src/audio_core/audio_in_manager.cpp b/src/audio_core/audio_in_manager.cpp index 3dfb613cb..a3667524f 100644 --- a/src/audio_core/audio_in_manager.cpp +++ b/src/audio_core/audio_in_manager.cpp | |||
| @@ -73,7 +73,7 @@ void Manager::BufferReleaseAndRegister() { | |||
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, | 76 | u32 Manager::GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, |
| 77 | [[maybe_unused]] const u32 max_count, | 77 | [[maybe_unused]] const u32 max_count, |
| 78 | [[maybe_unused]] const bool filter) { | 78 | [[maybe_unused]] const bool filter) { |
| 79 | std::scoped_lock l{mutex}; | 79 | std::scoped_lock l{mutex}; |
diff --git a/src/audio_core/audio_in_manager.h b/src/audio_core/audio_in_manager.h index 8a519df99..5c4614cd1 100644 --- a/src/audio_core/audio_in_manager.h +++ b/src/audio_core/audio_in_manager.h | |||
| @@ -65,8 +65,8 @@ public: | |||
| 65 | * | 65 | * |
| 66 | * @return Number of names written. | 66 | * @return Number of names written. |
| 67 | */ | 67 | */ |
| 68 | u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, | 68 | u32 GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, u32 max_count, |
| 69 | u32 max_count, bool filter); | 69 | bool filter); |
| 70 | 70 | ||
| 71 | /// Core system | 71 | /// Core system |
| 72 | Core::System& system; | 72 | Core::System& system; |
diff --git a/src/audio_core/audio_out_manager.cpp b/src/audio_core/audio_out_manager.cpp index f22821360..316ea7c81 100644 --- a/src/audio_core/audio_out_manager.cpp +++ b/src/audio_core/audio_out_manager.cpp | |||
| @@ -73,7 +73,7 @@ void Manager::BufferReleaseAndRegister() { | |||
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | u32 Manager::GetAudioOutDeviceNames( | 75 | u32 Manager::GetAudioOutDeviceNames( |
| 76 | std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const { | 76 | std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const { |
| 77 | names.emplace_back("DeviceOut"); | 77 | names.emplace_back("DeviceOut"); |
| 78 | return 1; | 78 | return 1; |
| 79 | } | 79 | } |
diff --git a/src/audio_core/audio_out_manager.h b/src/audio_core/audio_out_manager.h index 1e05ec5ed..c3e445d5d 100644 --- a/src/audio_core/audio_out_manager.h +++ b/src/audio_core/audio_out_manager.h | |||
| @@ -61,8 +61,7 @@ public: | |||
| 61 | * @param names - Output container to write names to. | 61 | * @param names - Output container to write names to. |
| 62 | * @return Number of names written. | 62 | * @return Number of names written. |
| 63 | */ | 63 | */ |
| 64 | u32 GetAudioOutDeviceNames( | 64 | u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const; |
| 65 | std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const; | ||
| 66 | 65 | ||
| 67 | /// Core system | 66 | /// Core system |
| 68 | Core::System& system; | 67 | Core::System& system; |
diff --git a/src/audio_core/audio_render_manager.cpp b/src/audio_core/audio_render_manager.cpp index 320715727..3c53e3afd 100644 --- a/src/audio_core/audio_render_manager.cpp +++ b/src/audio_core/audio_render_manager.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/common/feature_support.h" | 6 | #include "audio_core/common/feature_support.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | Manager::Manager(Core::System& system_) | 11 | Manager::Manager(Core::System& system_) |
| 12 | : system{system_}, system_manager{std::make_unique<SystemManager>(system)} { | 12 | : system{system_}, system_manager{std::make_unique<SystemManager>(system)} { |
| @@ -67,4 +67,4 @@ bool Manager::RemoveSystem(System& system_) { | |||
| 67 | return system_manager->Remove(system_); | 67 | return system_manager->Remove(system_); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | } // namespace AudioCore::AudioRenderer | 70 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h index fffa5944d..45537b270 100644 --- a/src/audio_core/audio_render_manager.h +++ b/src/audio_core/audio_render_manager.h | |||
| @@ -20,7 +20,7 @@ class System; | |||
| 20 | namespace AudioCore { | 20 | namespace AudioCore { |
| 21 | struct AudioRendererParameterInternal; | 21 | struct AudioRendererParameterInternal; |
| 22 | 22 | ||
| 23 | namespace AudioRenderer { | 23 | namespace Renderer { |
| 24 | /** | 24 | /** |
| 25 | * Wrapper for the audio system manager, handles service calls. | 25 | * Wrapper for the audio system manager, handles service calls. |
| 26 | */ | 26 | */ |
| @@ -101,5 +101,5 @@ private: | |||
| 101 | std::unique_ptr<SystemManager> system_manager{}; | 101 | std::unique_ptr<SystemManager> system_manager{}; |
| 102 | }; | 102 | }; |
| 103 | 103 | ||
| 104 | } // namespace AudioRenderer | 104 | } // namespace Renderer |
| 105 | } // namespace AudioCore | 105 | } // namespace AudioCore |
diff --git a/src/audio_core/common/audio_renderer_parameter.h b/src/audio_core/common/audio_renderer_parameter.h index 8c7892bcf..6c4e9fdc6 100644 --- a/src/audio_core/common/audio_renderer_parameter.h +++ b/src/audio_core/common/audio_renderer_parameter.h | |||
| @@ -51,10 +51,10 @@ struct AudioRendererSystemContext { | |||
| 51 | s32 session_id; | 51 | s32 session_id; |
| 52 | s8 channels; | 52 | s8 channels; |
| 53 | s16 mix_buffer_count; | 53 | s16 mix_buffer_count; |
| 54 | AudioRenderer::BehaviorInfo* behavior; | 54 | Renderer::BehaviorInfo* behavior; |
| 55 | std::span<s32> depop_buffer; | 55 | std::span<s32> depop_buffer; |
| 56 | AudioRenderer::UpsamplerManager* upsampler_manager; | 56 | Renderer::UpsamplerManager* upsampler_manager; |
| 57 | AudioRenderer::MemoryPoolInfo* memory_pool_info; | 57 | Renderer::MemoryPoolInfo* memory_pool_info; |
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | } // namespace AudioCore | 60 | } // namespace AudioCore |
diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp deleted file mode 100644 index b1db31e93..000000000 --- a/src/audio_core/renderer/adsp/adsp.cpp +++ /dev/null | |||
| @@ -1,117 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/adsp/adsp.h" | ||
| 5 | #include "audio_core/renderer/adsp/command_buffer.h" | ||
| 6 | #include "audio_core/sink/sink.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/memory.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer::ADSP { | ||
| 13 | |||
| 14 | ADSP::ADSP(Core::System& system_, Sink::Sink& sink_) | ||
| 15 | : system{system_}, memory{system.ApplicationMemory()}, sink{sink_} {} | ||
| 16 | |||
| 17 | ADSP::~ADSP() { | ||
| 18 | ClearCommandBuffers(); | ||
| 19 | } | ||
| 20 | |||
| 21 | State ADSP::GetState() const { | ||
| 22 | if (running) { | ||
| 23 | return State::Started; | ||
| 24 | } | ||
| 25 | return State::Stopped; | ||
| 26 | } | ||
| 27 | |||
| 28 | AudioRenderer_Mailbox* ADSP::GetRenderMailbox() { | ||
| 29 | return &render_mailbox; | ||
| 30 | } | ||
| 31 | |||
| 32 | void ADSP::ClearRemainCount(const u32 session_id) { | ||
| 33 | render_mailbox.ClearRemainCount(session_id); | ||
| 34 | } | ||
| 35 | |||
| 36 | u64 ADSP::GetSignalledTick() const { | ||
| 37 | return render_mailbox.GetSignalledTick(); | ||
| 38 | } | ||
| 39 | |||
| 40 | u64 ADSP::GetTimeTaken() const { | ||
| 41 | return render_mailbox.GetRenderTimeTaken(); | ||
| 42 | } | ||
| 43 | |||
| 44 | u64 ADSP::GetRenderTimeTaken(const u32 session_id) { | ||
| 45 | return render_mailbox.GetCommandBuffer(session_id).render_time_taken; | ||
| 46 | } | ||
| 47 | |||
| 48 | u32 ADSP::GetRemainCommandCount(const u32 session_id) const { | ||
| 49 | return render_mailbox.GetRemainCommandCount(session_id); | ||
| 50 | } | ||
| 51 | |||
| 52 | void ADSP::SendCommandBuffer(const u32 session_id, const CommandBuffer& command_buffer) { | ||
| 53 | render_mailbox.SetCommandBuffer(session_id, command_buffer); | ||
| 54 | } | ||
| 55 | |||
| 56 | u64 ADSP::GetRenderingStartTick(const u32 session_id) { | ||
| 57 | return render_mailbox.GetSignalledTick() + | ||
| 58 | render_mailbox.GetCommandBuffer(session_id).render_time_taken; | ||
| 59 | } | ||
| 60 | |||
| 61 | bool ADSP::Start() { | ||
| 62 | if (running) { | ||
| 63 | return running; | ||
| 64 | } | ||
| 65 | |||
| 66 | running = true; | ||
| 67 | systems_active++; | ||
| 68 | audio_renderer = std::make_unique<AudioRenderer>(system); | ||
| 69 | audio_renderer->Start(&render_mailbox); | ||
| 70 | render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_InitializeOK); | ||
| 71 | if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) { | ||
| 72 | LOG_ERROR( | ||
| 73 | Service_Audio, | ||
| 74 | "Host Audio Renderer -- Failed to receive initialize message response from ADSP!"); | ||
| 75 | } | ||
| 76 | return running; | ||
| 77 | } | ||
| 78 | |||
| 79 | void ADSP::Stop() { | ||
| 80 | systems_active--; | ||
| 81 | if (running && systems_active == 0) { | ||
| 82 | { | ||
| 83 | std::scoped_lock l{mailbox_lock}; | ||
| 84 | render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Shutdown); | ||
| 85 | if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_Shutdown) { | ||
| 86 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | ||
| 87 | "message response from ADSP!"); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | audio_renderer->Stop(); | ||
| 91 | running = false; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | void ADSP::Signal() { | ||
| 96 | const auto signalled_tick{system.CoreTiming().GetClockTicks()}; | ||
| 97 | render_mailbox.SetSignalledTick(signalled_tick); | ||
| 98 | render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Render); | ||
| 99 | } | ||
| 100 | |||
| 101 | void ADSP::Wait() { | ||
| 102 | std::scoped_lock l{mailbox_lock}; | ||
| 103 | auto response{render_mailbox.HostWaitMessage()}; | ||
| 104 | if (response != RenderMessage::AudioRenderer_RenderResponse) { | ||
| 105 | LOG_ERROR(Service_Audio, "Invalid ADSP response message, expected 0x{:02X}, got 0x{:02X}", | ||
| 106 | static_cast<u32>(RenderMessage::AudioRenderer_RenderResponse), | ||
| 107 | static_cast<u32>(response)); | ||
| 108 | } | ||
| 109 | |||
| 110 | ClearCommandBuffers(); | ||
| 111 | } | ||
| 112 | |||
| 113 | void ADSP::ClearCommandBuffers() { | ||
| 114 | render_mailbox.ClearCommandBuffers(); | ||
| 115 | } | ||
| 116 | |||
| 117 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/adsp/adsp.h b/src/audio_core/renderer/adsp/adsp.h deleted file mode 100644 index f7a2f25e4..000000000 --- a/src/audio_core/renderer/adsp/adsp.h +++ /dev/null | |||
| @@ -1,171 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | #include "audio_core/renderer/adsp/audio_renderer.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | namespace Memory { | ||
| 14 | class Memory; | ||
| 15 | } | ||
| 16 | class System; | ||
| 17 | } // namespace Core | ||
| 18 | |||
| 19 | namespace AudioCore { | ||
| 20 | namespace Sink { | ||
| 21 | class Sink; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace AudioRenderer::ADSP { | ||
| 25 | struct CommandBuffer; | ||
| 26 | |||
| 27 | enum class State { | ||
| 28 | Started, | ||
| 29 | Stopped, | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Represents the ADSP embedded within the audio sysmodule. | ||
| 34 | * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot. | ||
| 35 | * | ||
| 36 | * The kernel will run apps you program for it, Nintendo have the following: | ||
| 37 | * | ||
| 38 | * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all | ||
| 39 | * audio samples end up, and we skip it entirely, since we have very different backends and | ||
| 40 | * mixing is implicitly handled by the OS (but also due to lack of research/simplicity). | ||
| 41 | * | ||
| 42 | * AudioRenderer - Receives command lists generated by the audio render | ||
| 43 | * system, processes them, and sends the samples to Gmix. | ||
| 44 | * | ||
| 45 | * OpusDecoder - Contains libopus, and controls processing Opus audio and sends it to Gmix. | ||
| 46 | * Not much research done here, TODO if needed. | ||
| 47 | * | ||
| 48 | * We only implement the AudioRenderer for now. | ||
| 49 | * | ||
| 50 | * Communication for the apps is done through mailboxes, and some shared memory. | ||
| 51 | */ | ||
| 52 | class ADSP { | ||
| 53 | public: | ||
| 54 | explicit ADSP(Core::System& system, Sink::Sink& sink); | ||
| 55 | ~ADSP(); | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Start the ADSP. | ||
| 59 | * | ||
| 60 | * @return True if started or already running, otherwise false. | ||
| 61 | */ | ||
| 62 | bool Start(); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Stop the ADSP. | ||
| 66 | */ | ||
| 67 | void Stop(); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Get the ADSP's state. | ||
| 71 | * | ||
| 72 | * @return Started or Stopped. | ||
| 73 | */ | ||
| 74 | State GetState() const; | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Get the AudioRenderer mailbox to communicate with it. | ||
| 78 | * | ||
| 79 | * @return The AudioRenderer mailbox. | ||
| 80 | */ | ||
| 81 | AudioRenderer_Mailbox* GetRenderMailbox(); | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Get the tick the ADSP was signalled. | ||
| 85 | * | ||
| 86 | * @return The tick the ADSP was signalled. | ||
| 87 | */ | ||
| 88 | u64 GetSignalledTick() const; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Get the total time it took for the ADSP to run the last command lists (both command lists). | ||
| 92 | * | ||
| 93 | * @return The tick the ADSP was signalled. | ||
| 94 | */ | ||
| 95 | u64 GetTimeTaken() const; | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Get the last time a given command list took to run. | ||
| 99 | * | ||
| 100 | * @param session_id - The session id to check (0 or 1). | ||
| 101 | * @return The time it took. | ||
| 102 | */ | ||
| 103 | u64 GetRenderTimeTaken(u32 session_id); | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Clear the remaining command count for a given session. | ||
| 107 | * | ||
| 108 | * @param session_id - The session id to check (0 or 1). | ||
| 109 | */ | ||
| 110 | void ClearRemainCount(u32 session_id); | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Get the remaining number of commands left to process for a command list. | ||
| 114 | * | ||
| 115 | * @param session_id - The session id to check (0 or 1). | ||
| 116 | * @return The number of commands remaining. | ||
| 117 | */ | ||
| 118 | u32 GetRemainCommandCount(u32 session_id) const; | ||
| 119 | |||
| 120 | /** | ||
| 121 | * Get the last tick a command list started processing. | ||
| 122 | * | ||
| 123 | * @param session_id - The session id to check (0 or 1). | ||
| 124 | * @return The last tick the given command list started. | ||
| 125 | */ | ||
| 126 | u64 GetRenderingStartTick(u32 session_id); | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Set a command buffer to be processed. | ||
| 130 | * | ||
| 131 | * @param session_id - The session id to check (0 or 1). | ||
| 132 | * @param command_buffer - The command buffer to process. | ||
| 133 | */ | ||
| 134 | void SendCommandBuffer(u32 session_id, const CommandBuffer& command_buffer); | ||
| 135 | |||
| 136 | /** | ||
| 137 | * Clear the command buffers (does not clear the time taken or the remaining command count) | ||
| 138 | */ | ||
| 139 | void ClearCommandBuffers(); | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Signal the AudioRenderer to begin processing. | ||
| 143 | */ | ||
| 144 | void Signal(); | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Wait for the AudioRenderer to finish processing. | ||
| 148 | */ | ||
| 149 | void Wait(); | ||
| 150 | |||
| 151 | private: | ||
| 152 | /// Core system | ||
| 153 | Core::System& system; | ||
| 154 | /// Core memory | ||
| 155 | Core::Memory::Memory& memory; | ||
| 156 | /// Number of systems active, used to prevent accidental shutdowns | ||
| 157 | u8 systems_active{0}; | ||
| 158 | /// ADSP running state | ||
| 159 | std::atomic<bool> running{false}; | ||
| 160 | /// Output sink used by the ADSP | ||
| 161 | Sink::Sink& sink; | ||
| 162 | /// AudioRenderer app | ||
| 163 | std::unique_ptr<AudioRenderer> audio_renderer{}; | ||
| 164 | /// Communication for the AudioRenderer | ||
| 165 | AudioRenderer_Mailbox render_mailbox{}; | ||
| 166 | /// Mailbox lock ffor the render mailbox | ||
| 167 | std::mutex mailbox_lock; | ||
| 168 | }; | ||
| 169 | |||
| 170 | } // namespace AudioRenderer::ADSP | ||
| 171 | } // namespace AudioCore | ||
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp deleted file mode 100644 index 9ca716b60..000000000 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ /dev/null | |||
| @@ -1,225 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 7 | #include "audio_core/audio_core.h" | ||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/adsp/audio_renderer.h" | ||
| 10 | #include "audio_core/sink/sink.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/microprofile.h" | ||
| 13 | #include "common/thread.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_timing.h" | ||
| 16 | |||
| 17 | MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); | ||
| 18 | |||
| 19 | namespace AudioCore::AudioRenderer::ADSP { | ||
| 20 | |||
| 21 | void AudioRenderer_Mailbox::HostSendMessage(RenderMessage message_) { | ||
| 22 | adsp_messages.enqueue(message_); | ||
| 23 | adsp_event.Set(); | ||
| 24 | } | ||
| 25 | |||
| 26 | RenderMessage AudioRenderer_Mailbox::HostWaitMessage() { | ||
| 27 | host_event.Wait(); | ||
| 28 | RenderMessage msg{RenderMessage::Invalid}; | ||
| 29 | if (!host_messages.try_dequeue(msg)) { | ||
| 30 | LOG_ERROR(Service_Audio, "Failed to dequeue host message!"); | ||
| 31 | } | ||
| 32 | return msg; | ||
| 33 | } | ||
| 34 | |||
| 35 | void AudioRenderer_Mailbox::ADSPSendMessage(const RenderMessage message_) { | ||
| 36 | host_messages.enqueue(message_); | ||
| 37 | host_event.Set(); | ||
| 38 | } | ||
| 39 | |||
| 40 | RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() { | ||
| 41 | adsp_event.Wait(); | ||
| 42 | RenderMessage msg{RenderMessage::Invalid}; | ||
| 43 | if (!adsp_messages.try_dequeue(msg)) { | ||
| 44 | LOG_ERROR(Service_Audio, "Failed to dequeue ADSP message!"); | ||
| 45 | } | ||
| 46 | return msg; | ||
| 47 | } | ||
| 48 | |||
| 49 | CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) { | ||
| 50 | return command_buffers[session_id]; | ||
| 51 | } | ||
| 52 | |||
| 53 | void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, const CommandBuffer& buffer) { | ||
| 54 | command_buffers[session_id] = buffer; | ||
| 55 | } | ||
| 56 | |||
| 57 | u64 AudioRenderer_Mailbox::GetRenderTimeTaken() const { | ||
| 58 | return command_buffers[0].render_time_taken + command_buffers[1].render_time_taken; | ||
| 59 | } | ||
| 60 | |||
| 61 | u64 AudioRenderer_Mailbox::GetSignalledTick() const { | ||
| 62 | return signalled_tick; | ||
| 63 | } | ||
| 64 | |||
| 65 | void AudioRenderer_Mailbox::SetSignalledTick(const u64 tick) { | ||
| 66 | signalled_tick = tick; | ||
| 67 | } | ||
| 68 | |||
| 69 | void AudioRenderer_Mailbox::ClearRemainCount(const u32 session_id) { | ||
| 70 | command_buffers[session_id].remaining_command_count = 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | u32 AudioRenderer_Mailbox::GetRemainCommandCount(const u32 session_id) const { | ||
| 74 | return command_buffers[session_id].remaining_command_count; | ||
| 75 | } | ||
| 76 | |||
| 77 | void AudioRenderer_Mailbox::ClearCommandBuffers() { | ||
| 78 | command_buffers[0].buffer = 0; | ||
| 79 | command_buffers[0].size = 0; | ||
| 80 | command_buffers[0].reset_buffers = false; | ||
| 81 | command_buffers[1].buffer = 0; | ||
| 82 | command_buffers[1].size = 0; | ||
| 83 | command_buffers[1].reset_buffers = false; | ||
| 84 | } | ||
| 85 | |||
| 86 | AudioRenderer::AudioRenderer(Core::System& system_) | ||
| 87 | : system{system_}, sink{system.AudioCore().GetOutputSink()} { | ||
| 88 | CreateSinkStreams(); | ||
| 89 | } | ||
| 90 | |||
| 91 | AudioRenderer::~AudioRenderer() { | ||
| 92 | Stop(); | ||
| 93 | for (auto& stream : streams) { | ||
| 94 | if (stream) { | ||
| 95 | sink.CloseStream(stream); | ||
| 96 | } | ||
| 97 | stream = nullptr; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | void AudioRenderer::Start(AudioRenderer_Mailbox* mailbox_) { | ||
| 102 | if (running) { | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | |||
| 106 | mailbox = mailbox_; | ||
| 107 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); | ||
| 108 | running = true; | ||
| 109 | } | ||
| 110 | |||
| 111 | void AudioRenderer::Stop() { | ||
| 112 | if (!running) { | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | |||
| 116 | for (auto& stream : streams) { | ||
| 117 | stream->Stop(); | ||
| 118 | } | ||
| 119 | thread.join(); | ||
| 120 | running = false; | ||
| 121 | } | ||
| 122 | |||
| 123 | void AudioRenderer::CreateSinkStreams() { | ||
| 124 | u32 channels{sink.GetDeviceChannels()}; | ||
| 125 | for (u32 i = 0; i < MaxRendererSessions; i++) { | ||
| 126 | std::string name{fmt::format("ADSP_RenderStream-{}", i)}; | ||
| 127 | streams[i] = | ||
| 128 | sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); | ||
| 129 | streams[i]->SetRingSize(4); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | void AudioRenderer::ThreadFunc(std::stop_token stop_token) { | ||
| 134 | static constexpr char name[]{"AudioRenderer"}; | ||
| 135 | MicroProfileOnThreadCreate(name); | ||
| 136 | Common::SetCurrentThreadName(name); | ||
| 137 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 138 | if (mailbox->ADSPWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) { | ||
| 139 | LOG_ERROR(Service_Audio, | ||
| 140 | "ADSP Audio Renderer -- Failed to receive initialize message from host!"); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 144 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK); | ||
| 145 | |||
| 146 | // 0.12 seconds (2304000 / 19200000) | ||
| 147 | constexpr u64 max_process_time{2'304'000ULL}; | ||
| 148 | |||
| 149 | while (!stop_token.stop_requested()) { | ||
| 150 | auto message{mailbox->ADSPWaitMessage()}; | ||
| 151 | switch (message) { | ||
| 152 | case RenderMessage::AudioRenderer_Shutdown: | ||
| 153 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_Shutdown); | ||
| 154 | return; | ||
| 155 | |||
| 156 | case RenderMessage::AudioRenderer_Render: { | ||
| 157 | if (system.IsShuttingDown()) [[unlikely]] { | ||
| 158 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); | ||
| 159 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse); | ||
| 160 | continue; | ||
| 161 | } | ||
| 162 | std::array<bool, MaxRendererSessions> buffers_reset{}; | ||
| 163 | std::array<u64, MaxRendererSessions> render_times_taken{}; | ||
| 164 | const auto start_time{system.CoreTiming().GetClockTicks()}; | ||
| 165 | |||
| 166 | for (u32 index = 0; index < 2; index++) { | ||
| 167 | auto& command_buffer{mailbox->GetCommandBuffer(index)}; | ||
| 168 | auto& command_list_processor{command_list_processors[index]}; | ||
| 169 | |||
| 170 | // Check this buffer is valid, as it may not be used. | ||
| 171 | if (command_buffer.buffer != 0) { | ||
| 172 | // If there are no remaining commands (from the previous list), | ||
| 173 | // this is a new command list, initialize it. | ||
| 174 | if (command_buffer.remaining_command_count == 0) { | ||
| 175 | command_list_processor.Initialize(system, command_buffer.buffer, | ||
| 176 | command_buffer.size, streams[index]); | ||
| 177 | } | ||
| 178 | |||
| 179 | if (command_buffer.reset_buffers && !buffers_reset[index]) { | ||
| 180 | streams[index]->ClearQueue(); | ||
| 181 | buffers_reset[index] = true; | ||
| 182 | } | ||
| 183 | |||
| 184 | u64 max_time{max_process_time}; | ||
| 185 | if (index == 1 && command_buffer.applet_resource_user_id == | ||
| 186 | mailbox->GetCommandBuffer(0).applet_resource_user_id) { | ||
| 187 | max_time = max_process_time - render_times_taken[0]; | ||
| 188 | if (render_times_taken[0] > max_process_time) { | ||
| 189 | max_time = 0; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | max_time = std::min(command_buffer.time_limit, max_time); | ||
| 194 | command_list_processor.SetProcessTimeMax(max_time); | ||
| 195 | |||
| 196 | streams[index]->WaitFreeSpace(stop_token); | ||
| 197 | |||
| 198 | // Process the command list | ||
| 199 | { | ||
| 200 | MICROPROFILE_SCOPE(Audio_Renderer); | ||
| 201 | render_times_taken[index] = | ||
| 202 | command_list_processor.Process(index) - start_time; | ||
| 203 | } | ||
| 204 | |||
| 205 | const auto end_time{system.CoreTiming().GetClockTicks()}; | ||
| 206 | |||
| 207 | command_buffer.remaining_command_count = | ||
| 208 | command_list_processor.GetRemainingCommandCount(); | ||
| 209 | command_buffer.render_time_taken = end_time - start_time; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse); | ||
| 214 | } break; | ||
| 215 | |||
| 216 | default: | ||
| 217 | LOG_WARNING(Service_Audio, | ||
| 218 | "ADSP AudioRenderer received an invalid message, msg={:02X}!", | ||
| 219 | static_cast<u32>(message)); | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h deleted file mode 100644 index 88e558183..000000000 --- a/src/audio_core/renderer/adsp/audio_renderer.h +++ /dev/null | |||
| @@ -1,204 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <memory> | ||
| 8 | #include <thread> | ||
| 9 | |||
| 10 | #include "audio_core/renderer/adsp/command_buffer.h" | ||
| 11 | #include "audio_core/renderer/adsp/command_list_processor.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/polyfill_thread.h" | ||
| 14 | #include "common/reader_writer_queue.h" | ||
| 15 | #include "common/thread.h" | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | namespace Timing { | ||
| 19 | struct EventType; | ||
| 20 | } | ||
| 21 | class System; | ||
| 22 | } // namespace Core | ||
| 23 | |||
| 24 | namespace AudioCore { | ||
| 25 | namespace Sink { | ||
| 26 | class Sink; | ||
| 27 | } | ||
| 28 | |||
| 29 | namespace AudioRenderer::ADSP { | ||
| 30 | |||
| 31 | enum class RenderMessage { | ||
| 32 | /* 0x00 */ Invalid, | ||
| 33 | /* 0x01 */ AudioRenderer_MapUnmap_Map, | ||
| 34 | /* 0x02 */ AudioRenderer_MapUnmap_MapResponse, | ||
| 35 | /* 0x03 */ AudioRenderer_MapUnmap_Unmap, | ||
| 36 | /* 0x04 */ AudioRenderer_MapUnmap_UnmapResponse, | ||
| 37 | /* 0x05 */ AudioRenderer_MapUnmap_InvalidateCache, | ||
| 38 | /* 0x06 */ AudioRenderer_MapUnmap_InvalidateCacheResponse, | ||
| 39 | /* 0x07 */ AudioRenderer_MapUnmap_Shutdown, | ||
| 40 | /* 0x08 */ AudioRenderer_MapUnmap_ShutdownResponse, | ||
| 41 | /* 0x16 */ AudioRenderer_InitializeOK = 0x16, | ||
| 42 | /* 0x20 */ AudioRenderer_RenderResponse = 0x20, | ||
| 43 | /* 0x2A */ AudioRenderer_Render = 0x2A, | ||
| 44 | /* 0x34 */ AudioRenderer_Shutdown = 0x34, | ||
| 45 | }; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * A mailbox for the AudioRenderer, allowing communication between the host and the AudioRenderer | ||
| 49 | * running on the ADSP. | ||
| 50 | */ | ||
| 51 | class AudioRenderer_Mailbox { | ||
| 52 | public: | ||
| 53 | /** | ||
| 54 | * Send a message from the host to the AudioRenderer. | ||
| 55 | * | ||
| 56 | * @param message - The message to send to the AudioRenderer. | ||
| 57 | */ | ||
| 58 | void HostSendMessage(RenderMessage message); | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Host wait for a message from the AudioRenderer. | ||
| 62 | * | ||
| 63 | * @return The message returned from the AudioRenderer. | ||
| 64 | */ | ||
| 65 | RenderMessage HostWaitMessage(); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Send a message from the AudioRenderer to the host. | ||
| 69 | * | ||
| 70 | * @param message - The message to send to the host. | ||
| 71 | */ | ||
| 72 | void ADSPSendMessage(RenderMessage message); | ||
| 73 | |||
| 74 | /** | ||
| 75 | * AudioRenderer wait for a message from the host. | ||
| 76 | * | ||
| 77 | * @return The message returned from the AudioRenderer. | ||
| 78 | */ | ||
| 79 | RenderMessage ADSPWaitMessage(); | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Get the command buffer with the given session id (0 or 1). | ||
| 83 | * | ||
| 84 | * @param session_id - The session id to get (0 or 1). | ||
| 85 | * @return The command buffer. | ||
| 86 | */ | ||
| 87 | CommandBuffer& GetCommandBuffer(u32 session_id); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Set the command buffer with the given session id (0 or 1). | ||
| 91 | * | ||
| 92 | * @param session_id - The session id to get (0 or 1). | ||
| 93 | * @param buffer - The command buffer to set. | ||
| 94 | */ | ||
| 95 | void SetCommandBuffer(u32 session_id, const CommandBuffer& buffer); | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Get the total render time taken for the last command lists sent. | ||
| 99 | * | ||
| 100 | * @return Total render time taken for the last command lists. | ||
| 101 | */ | ||
| 102 | u64 GetRenderTimeTaken() const; | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Get the tick the AudioRenderer was signalled. | ||
| 106 | * | ||
| 107 | * @return The tick the AudioRenderer was signalled. | ||
| 108 | */ | ||
| 109 | u64 GetSignalledTick() const; | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Set the tick the AudioRenderer was signalled. | ||
| 113 | * | ||
| 114 | * @param tick - The tick the AudioRenderer was signalled. | ||
| 115 | */ | ||
| 116 | void SetSignalledTick(u64 tick); | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Clear the remaining command count. | ||
| 120 | * | ||
| 121 | * @param session_id - Index for which command list to clear (0 or 1). | ||
| 122 | */ | ||
| 123 | void ClearRemainCount(u32 session_id); | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Get the remaining command count for a given command list. | ||
| 127 | * | ||
| 128 | * @param session_id - Index for which command list to clear (0 or 1). | ||
| 129 | * @return The remaining command count. | ||
| 130 | */ | ||
| 131 | u32 GetRemainCommandCount(u32 session_id) const; | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Clear the command buffers (does not clear the time taken or the remaining command count). | ||
| 135 | */ | ||
| 136 | void ClearCommandBuffers(); | ||
| 137 | |||
| 138 | private: | ||
| 139 | /// Host signalling event | ||
| 140 | Common::Event host_event{}; | ||
| 141 | /// AudioRenderer signalling event | ||
| 142 | Common::Event adsp_event{}; | ||
| 143 | /// Host message queue | ||
| 144 | |||
| 145 | Common::ReaderWriterQueue<RenderMessage> host_messages{}; | ||
| 146 | /// AudioRenderer message queue | ||
| 147 | |||
| 148 | Common::ReaderWriterQueue<RenderMessage> adsp_messages{}; | ||
| 149 | /// Command buffers | ||
| 150 | |||
| 151 | std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; | ||
| 152 | /// Tick the AudioRnederer was signalled | ||
| 153 | u64 signalled_tick{}; | ||
| 154 | }; | ||
| 155 | |||
| 156 | /** | ||
| 157 | * The AudioRenderer application running on the ADSP. | ||
| 158 | */ | ||
| 159 | class AudioRenderer { | ||
| 160 | public: | ||
| 161 | explicit AudioRenderer(Core::System& system); | ||
| 162 | ~AudioRenderer(); | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Start the AudioRenderer. | ||
| 166 | * | ||
| 167 | * @param mailbox The mailbox to use for this session. | ||
| 168 | */ | ||
| 169 | void Start(AudioRenderer_Mailbox* mailbox); | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Stop the AudioRenderer. | ||
| 173 | */ | ||
| 174 | void Stop(); | ||
| 175 | |||
| 176 | private: | ||
| 177 | /** | ||
| 178 | * Main AudioRenderer thread, responsible for processing the command lists. | ||
| 179 | */ | ||
| 180 | void ThreadFunc(std::stop_token stop_token); | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Creates the streams which will receive the processed samples. | ||
| 184 | */ | ||
| 185 | void CreateSinkStreams(); | ||
| 186 | |||
| 187 | /// Core system | ||
| 188 | Core::System& system; | ||
| 189 | /// Main thread | ||
| 190 | std::jthread thread{}; | ||
| 191 | /// The current state | ||
| 192 | std::atomic<bool> running{}; | ||
| 193 | /// The active mailbox | ||
| 194 | AudioRenderer_Mailbox* mailbox{}; | ||
| 195 | /// The command lists to process | ||
| 196 | std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; | ||
| 197 | /// The output sink the AudioRenderer will use | ||
| 198 | Sink::Sink& sink; | ||
| 199 | /// The streams which will receive the processed samples | ||
| 200 | std::array<Sink::SinkStream*, MaxRendererSessions> streams; | ||
| 201 | }; | ||
| 202 | |||
| 203 | } // namespace AudioRenderer::ADSP | ||
| 204 | } // namespace AudioCore | ||
diff --git a/src/audio_core/renderer/adsp/command_buffer.h b/src/audio_core/renderer/adsp/command_buffer.h deleted file mode 100644 index 880b279d8..000000000 --- a/src/audio_core/renderer/adsp/command_buffer.h +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/common/common.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::AudioRenderer::ADSP { | ||
| 10 | |||
| 11 | struct CommandBuffer { | ||
| 12 | CpuAddr buffer; | ||
| 13 | u64 size; | ||
| 14 | u64 time_limit; | ||
| 15 | u32 remaining_command_count; | ||
| 16 | bool reset_buffers; | ||
| 17 | u64 applet_resource_user_id; | ||
| 18 | u64 render_time_taken; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/audio_device.cpp b/src/audio_core/renderer/audio_device.cpp index 0d9d8f6ce..2d9bf82bb 100644 --- a/src/audio_core/renderer/audio_device.cpp +++ b/src/audio_core/renderer/audio_device.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "audio_core/sink/sink.h" | 10 | #include "audio_core/sink/sink.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | 14 | ||
| 15 | constexpr std::array usb_device_names{ | 15 | constexpr std::array usb_device_names{ |
| 16 | AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, | 16 | AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, |
| @@ -71,4 +71,4 @@ f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const { | |||
| 71 | return output_sink.GetDeviceVolume(); | 71 | return output_sink.GetDeviceVolume(); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | } // namespace AudioCore::AudioRenderer | 74 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/audio_device.h b/src/audio_core/renderer/audio_device.h index dd6be70ee..ca4040add 100644 --- a/src/audio_core/renderer/audio_device.h +++ b/src/audio_core/renderer/audio_device.h | |||
| @@ -16,7 +16,7 @@ namespace Sink { | |||
| 16 | class Sink; | 16 | class Sink; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace AudioRenderer { | 19 | namespace Renderer { |
| 20 | /** | 20 | /** |
| 21 | * An interface to an output audio device available to the Switch. | 21 | * An interface to an output audio device available to the Switch. |
| 22 | */ | 22 | */ |
| @@ -76,5 +76,5 @@ private: | |||
| 76 | const u32 user_revision; | 76 | const u32 user_revision; |
| 77 | }; | 77 | }; |
| 78 | 78 | ||
| 79 | } // namespace AudioRenderer | 79 | } // namespace Renderer |
| 80 | } // namespace AudioCore | 80 | } // namespace AudioCore |
diff --git a/src/audio_core/renderer/audio_renderer.cpp b/src/audio_core/renderer/audio_renderer.cpp index a8257eb2e..09efe9be9 100644 --- a/src/audio_core/renderer/audio_renderer.cpp +++ b/src/audio_core/renderer/audio_renderer.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/k_transfer_memory.h" | 9 | #include "core/hle/kernel/k_transfer_memory.h" |
| 10 | #include "core/hle/service/audio/errors.h" | 10 | #include "core/hle/service/audio/errors.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | 13 | ||
| 14 | Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) | 14 | Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) |
| 15 | : core{system_}, manager{manager_}, system{system_, rendered_event} {} | 15 | : core{system_}, manager{manager_}, system{system_, rendered_event} {} |
| @@ -64,4 +64,4 @@ Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performa | |||
| 64 | return system.Update(input, performance, output); | 64 | return system.Update(input, performance, output); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | } // namespace AudioCore::AudioRenderer | 67 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/audio_renderer.h b/src/audio_core/renderer/audio_renderer.h index 90c6f9727..24650278b 100644 --- a/src/audio_core/renderer/audio_renderer.h +++ b/src/audio_core/renderer/audio_renderer.h | |||
| @@ -19,7 +19,7 @@ class KTransferMemory; | |||
| 19 | namespace AudioCore { | 19 | namespace AudioCore { |
| 20 | struct AudioRendererParameterInternal; | 20 | struct AudioRendererParameterInternal; |
| 21 | 21 | ||
| 22 | namespace AudioRenderer { | 22 | namespace Renderer { |
| 23 | class Manager; | 23 | class Manager; |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| @@ -31,7 +31,7 @@ public: | |||
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * Initialize the renderer. | 33 | * Initialize the renderer. |
| 34 | * Registers the system with the AudioRenderer::Manager, allocates workbuffers and initializes | 34 | * Registers the system with the Renderer::Manager, allocates workbuffers and initializes |
| 35 | * everything to a default state. | 35 | * everything to a default state. |
| 36 | * | 36 | * |
| 37 | * @param params - Input parameters to initialize the system with. | 37 | * @param params - Input parameters to initialize the system with. |
| @@ -93,5 +93,5 @@ private: | |||
| 93 | System system; | 93 | System system; |
| 94 | }; | 94 | }; |
| 95 | 95 | ||
| 96 | } // namespace AudioRenderer | 96 | } // namespace Renderer |
| 97 | } // namespace AudioCore | 97 | } // namespace AudioCore |
diff --git a/src/audio_core/renderer/behavior/behavior_info.cpp b/src/audio_core/renderer/behavior/behavior_info.cpp index 3d2a91312..058539042 100644 --- a/src/audio_core/renderer/behavior/behavior_info.cpp +++ b/src/audio_core/renderer/behavior/behavior_info.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include "audio_core/common/feature_support.h" | 4 | #include "audio_core/common/feature_support.h" |
| 5 | #include "audio_core/renderer/behavior/behavior_info.h" | 5 | #include "audio_core/renderer/behavior/behavior_info.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | BehaviorInfo::BehaviorInfo() : process_revision{CurrentRevision} {} | 9 | BehaviorInfo::BehaviorInfo() : process_revision{CurrentRevision} {} |
| 10 | 10 | ||
| @@ -190,4 +190,4 @@ bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const { | |||
| 190 | return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision); | 190 | return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision); |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | } // namespace AudioCore::AudioRenderer | 193 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/behavior/behavior_info.h b/src/audio_core/renderer/behavior/behavior_info.h index b52340229..a4958857a 100644 --- a/src/audio_core/renderer/behavior/behavior_info.h +++ b/src/audio_core/renderer/behavior/behavior_info.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/hle/service/audio/errors.h" | 11 | #include "core/hle/service/audio/errors.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | /** | 14 | /** |
| 15 | * Holds host and user revisions, checks whether render features can be enabled, and reports errors. | 15 | * Holds host and user revisions, checks whether render features can be enabled, and reports errors. |
| 16 | */ | 16 | */ |
| @@ -264,7 +264,7 @@ public: | |||
| 264 | /** | 264 | /** |
| 265 | * Check if skipping voice pitch and sample rate conversion is supported. | 265 | * Check if skipping voice pitch and sample rate conversion is supported. |
| 266 | * This speeds up the data source commands by skipping resampling if unwanted. | 266 | * This speeds up the data source commands by skipping resampling if unwanted. |
| 267 | * See AudioCore::AudioRenderer::DecodeFromWaveBuffers | 267 | * See AudioCore::Renderer::DecodeFromWaveBuffers |
| 268 | * | 268 | * |
| 269 | * @return True if supported, otherwise false. | 269 | * @return True if supported, otherwise false. |
| 270 | */ | 270 | */ |
| @@ -273,7 +273,7 @@ public: | |||
| 273 | /** | 273 | /** |
| 274 | * Check if resetting played sample count at loop points is supported. | 274 | * Check if resetting played sample count at loop points is supported. |
| 275 | * This resets the number of samples played in a voice state when a loop point is reached. | 275 | * This resets the number of samples played in a voice state when a loop point is reached. |
| 276 | * See AudioCore::AudioRenderer::DecodeFromWaveBuffers | 276 | * See AudioCore::Renderer::DecodeFromWaveBuffers |
| 277 | * | 277 | * |
| 278 | * @return True if supported, otherwise false. | 278 | * @return True if supported, otherwise false. |
| 279 | */ | 279 | */ |
| @@ -373,4 +373,4 @@ public: | |||
| 373 | u32 error_count{}; | 373 | u32 error_count{}; |
| 374 | }; | 374 | }; |
| 375 | 375 | ||
| 376 | } // namespace AudioCore::AudioRenderer | 376 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index e312eb166..667711e17 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | #include "audio_core/renderer/splitter/splitter_context.h" | 15 | #include "audio_core/renderer/splitter/splitter_context.h" |
| 16 | #include "audio_core/renderer/voice/voice_context.h" | 16 | #include "audio_core/renderer/voice/voice_context.h" |
| 17 | 17 | ||
| 18 | namespace AudioCore::AudioRenderer { | 18 | namespace AudioCore::Renderer { |
| 19 | 19 | ||
| 20 | InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_, | 20 | InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_, |
| 21 | const u32 process_handle_, BehaviorInfo& behaviour_) | 21 | const u32 process_handle_, BehaviorInfo& behaviour_) |
| @@ -536,4 +536,4 @@ Result InfoUpdater::CheckConsumedSize() { | |||
| 536 | return ResultSuccess; | 536 | return ResultSuccess; |
| 537 | } | 537 | } |
| 538 | 538 | ||
| 539 | } // namespace AudioCore::AudioRenderer | 539 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/behavior/info_updater.h b/src/audio_core/renderer/behavior/info_updater.h index c817d8d8d..fb4b7d25a 100644 --- a/src/audio_core/renderer/behavior/info_updater.h +++ b/src/audio_core/renderer/behavior/info_updater.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/service/audio/errors.h" | 9 | #include "core/hle/service/audio/errors.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | class BehaviorInfo; | 12 | class BehaviorInfo; |
| 13 | class VoiceContext; | 13 | class VoiceContext; |
| 14 | class MixContext; | 14 | class MixContext; |
| @@ -202,4 +202,4 @@ private: | |||
| 202 | BehaviorInfo& behaviour; | 202 | BehaviorInfo& behaviour; |
| 203 | }; | 203 | }; |
| 204 | 204 | ||
| 205 | } // namespace AudioCore::AudioRenderer | 205 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp index 0bd418306..67d43e69a 100644 --- a/src/audio_core/renderer/command/command_buffer.cpp +++ b/src/audio_core/renderer/command/command_buffer.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include "audio_core/renderer/voice/voice_info.h" | 16 | #include "audio_core/renderer/voice/voice_info.h" |
| 17 | #include "audio_core/renderer/voice/voice_state.h" | 17 | #include "audio_core/renderer/voice/voice_state.h" |
| 18 | 18 | ||
| 19 | namespace AudioCore::AudioRenderer { | 19 | namespace AudioCore::Renderer { |
| 20 | 20 | ||
| 21 | template <typename T, CommandId Id> | 21 | template <typename T, CommandId Id> |
| 22 | T& CommandBuffer::GenerateStart(const s32 node_id) { | 22 | T& CommandBuffer::GenerateStart(const s32 node_id) { |
| @@ -713,4 +713,4 @@ void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& | |||
| 713 | GenerateEnd<CompressorCommand>(cmd); | 713 | GenerateEnd<CompressorCommand>(cmd); |
| 714 | } | 714 | } |
| 715 | 715 | ||
| 716 | } // namespace AudioCore::AudioRenderer | 716 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/command_buffer.h b/src/audio_core/renderer/command/command_buffer.h index 162170846..12e8c2c81 100644 --- a/src/audio_core/renderer/command/command_buffer.h +++ b/src/audio_core/renderer/command/command_buffer.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "audio_core/renderer/performance/performance_manager.h" | 10 | #include "audio_core/renderer/performance/performance_manager.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | struct UpsamplerInfo; | 14 | struct UpsamplerInfo; |
| 15 | struct VoiceState; | 15 | struct VoiceState; |
| 16 | class EffectInfoBase; | 16 | class EffectInfoBase; |
| @@ -465,4 +465,4 @@ private: | |||
| 465 | void GenerateEnd(T& cmd); | 465 | void GenerateEnd(T& cmd); |
| 466 | }; | 466 | }; |
| 467 | 467 | ||
| 468 | } // namespace AudioCore::AudioRenderer | 468 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp index fba84c7bd..ccb186209 100644 --- a/src/audio_core/renderer/command/command_generator.cpp +++ b/src/audio_core/renderer/command/command_generator.cpp | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | #include "audio_core/renderer/voice/voice_context.h" | 21 | #include "audio_core/renderer/voice/voice_context.h" |
| 22 | #include "common/alignment.h" | 22 | #include "common/alignment.h" |
| 23 | 23 | ||
| 24 | namespace AudioCore::AudioRenderer { | 24 | namespace AudioCore::Renderer { |
| 25 | 25 | ||
| 26 | CommandGenerator::CommandGenerator(CommandBuffer& command_buffer_, | 26 | CommandGenerator::CommandGenerator(CommandBuffer& command_buffer_, |
| 27 | const CommandListHeader& command_list_header_, | 27 | const CommandListHeader& command_list_header_, |
| @@ -793,4 +793,4 @@ void CommandGenerator::GeneratePerformanceCommand( | |||
| 793 | command_buffer.GeneratePerformanceCommand(node_id, state, entry_addresses); | 793 | command_buffer.GeneratePerformanceCommand(node_id, state, entry_addresses); |
| 794 | } | 794 | } |
| 795 | 795 | ||
| 796 | } // namespace AudioCore::AudioRenderer | 796 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/command_generator.h b/src/audio_core/renderer/command/command_generator.h index b3cd7b408..38ee2a64e 100644 --- a/src/audio_core/renderer/command/command_generator.h +++ b/src/audio_core/renderer/command/command_generator.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | namespace AudioCore { | 12 | namespace AudioCore { |
| 13 | struct AudioRendererSystemContext; | 13 | struct AudioRendererSystemContext; |
| 14 | 14 | ||
| 15 | namespace AudioRenderer { | 15 | namespace Renderer { |
| 16 | class CommandBuffer; | 16 | class CommandBuffer; |
| 17 | struct CommandListHeader; | 17 | struct CommandListHeader; |
| 18 | class VoiceContext; | 18 | class VoiceContext; |
| @@ -345,5 +345,5 @@ private: | |||
| 345 | PerformanceManager* performance_manager; | 345 | PerformanceManager* performance_manager; |
| 346 | }; | 346 | }; |
| 347 | 347 | ||
| 348 | } // namespace AudioRenderer | 348 | } // namespace Renderer |
| 349 | } // namespace AudioCore | 349 | } // namespace AudioCore |
diff --git a/src/audio_core/renderer/command/command_list_header.h b/src/audio_core/renderer/command/command_list_header.h index 988530b1f..de9ee070b 100644 --- a/src/audio_core/renderer/command/command_list_header.h +++ b/src/audio_core/renderer/command/command_list_header.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "audio_core/common/common.h" | 8 | #include "audio_core/common/common.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | 12 | ||
| 13 | struct CommandListHeader { | 13 | struct CommandListHeader { |
| 14 | u64 buffer_size; | 14 | u64 buffer_size; |
| @@ -19,4 +19,4 @@ struct CommandListHeader { | |||
| 19 | u32 sample_rate; | 19 | u32 sample_rate; |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | } // namespace AudioCore::AudioRenderer | 22 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.cpp b/src/audio_core/renderer/command/command_processing_time_estimator.cpp index 3091f587a..a48a016b1 100644 --- a/src/audio_core/renderer/command/command_processing_time_estimator.cpp +++ b/src/audio_core/renderer/command/command_processing_time_estimator.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/command/command_processing_time_estimator.h" | 4 | #include "audio_core/renderer/command/command_processing_time_estimator.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | u32 CommandProcessingTimeEstimatorVersion1::Estimate( | 8 | u32 CommandProcessingTimeEstimatorVersion1::Estimate( |
| 9 | const PcmInt16DataSourceVersion1Command& command) const { | 9 | const PcmInt16DataSourceVersion1Command& command) const { |
| @@ -3617,4 +3617,4 @@ u32 CommandProcessingTimeEstimatorVersion5::Estimate(const CompressorCommand& co | |||
| 3617 | } | 3617 | } |
| 3618 | } | 3618 | } |
| 3619 | 3619 | ||
| 3620 | } // namespace AudioCore::AudioRenderer | 3620 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.h b/src/audio_core/renderer/command/command_processing_time_estimator.h index 452217196..1c76e4ba4 100644 --- a/src/audio_core/renderer/command/command_processing_time_estimator.h +++ b/src/audio_core/renderer/command/command_processing_time_estimator.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/command/commands.h" | 6 | #include "audio_core/renderer/command/commands.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Estimate the processing time required for all commands. | 11 | * Estimate the processing time required for all commands. |
| 12 | */ | 12 | */ |
| @@ -251,4 +251,4 @@ private: | |||
| 251 | u32 buffer_count{}; | 251 | u32 buffer_count{}; |
| 252 | }; | 252 | }; |
| 253 | 253 | ||
| 254 | } // namespace AudioCore::AudioRenderer | 254 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/adpcm.cpp b/src/audio_core/renderer/command/data_source/adpcm.cpp index e66ed2990..e7f82d3b3 100644 --- a/src/audio_core/renderer/command/data_source/adpcm.cpp +++ b/src/audio_core/renderer/command/data_source/adpcm.cpp | |||
| @@ -3,23 +3,29 @@ | |||
| 3 | 3 | ||
| 4 | #include <span> | 4 | #include <span> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/data_source/adpcm.h" | 7 | #include "audio_core/renderer/command/data_source/adpcm.h" |
| 8 | #include "audio_core/renderer/command/data_source/decode.h" | 8 | #include "audio_core/renderer/command/data_source/decode.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | void AdpcmDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, | 12 | void AdpcmDataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 13 | std::string& string) { | 13 | std::string& string) { |
| 14 | string += fmt::format("AdpcmDataSourceVersion1Command\n\toutput_index {:02X} source sample " | 14 | string += fmt::format("AdpcmDataSourceVersion1Command\n\toutput_index {:02X} source sample " |
| 15 | "rate {} target sample rate {} src quality {}\n", | 15 | "rate {} target sample rate {} src quality {}\n", |
| 16 | output_index, sample_rate, processor.target_sample_rate, src_quality); | 16 | output_index, sample_rate, processor.target_sample_rate, src_quality); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { | 19 | void AdpcmDataSourceVersion1Command::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 20 | auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, | 20 | auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 21 | processor.sample_count)}; | 21 | processor.sample_count)}; |
| 22 | 22 | ||
| 23 | for (auto& wave_buffer : wave_buffers) { | ||
| 24 | wave_buffer.loop_start_offset = wave_buffer.start_offset; | ||
| 25 | wave_buffer.loop_end_offset = wave_buffer.end_offset; | ||
| 26 | wave_buffer.loop_count = wave_buffer.loop ? -1 : 0; | ||
| 27 | } | ||
| 28 | |||
| 23 | DecodeFromWaveBuffersArgs args{ | 29 | DecodeFromWaveBuffersArgs args{ |
| 24 | .sample_format{SampleFormat::Adpcm}, | 30 | .sample_format{SampleFormat::Adpcm}, |
| 25 | .output{out_buffer}, | 31 | .output{out_buffer}, |
| @@ -41,18 +47,18 @@ void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& p | |||
| 41 | DecodeFromWaveBuffers(*processor.memory, args); | 47 | DecodeFromWaveBuffers(*processor.memory, args); |
| 42 | } | 48 | } |
| 43 | 49 | ||
| 44 | bool AdpcmDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { | 50 | bool AdpcmDataSourceVersion1Command::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 45 | return true; | 51 | return true; |
| 46 | } | 52 | } |
| 47 | 53 | ||
| 48 | void AdpcmDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, | 54 | void AdpcmDataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 49 | std::string& string) { | 55 | std::string& string) { |
| 50 | string += fmt::format("AdpcmDataSourceVersion2Command\n\toutput_index {:02X} source sample " | 56 | string += fmt::format("AdpcmDataSourceVersion2Command\n\toutput_index {:02X} source sample " |
| 51 | "rate {} target sample rate {} src quality {}\n", | 57 | "rate {} target sample rate {} src quality {}\n", |
| 52 | output_index, sample_rate, processor.target_sample_rate, src_quality); | 58 | output_index, sample_rate, processor.target_sample_rate, src_quality); |
| 53 | } | 59 | } |
| 54 | 60 | ||
| 55 | void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { | 61 | void AdpcmDataSourceVersion2Command::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 56 | auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, | 62 | auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 57 | processor.sample_count)}; | 63 | processor.sample_count)}; |
| 58 | 64 | ||
| @@ -77,8 +83,8 @@ void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& p | |||
| 77 | DecodeFromWaveBuffers(*processor.memory, args); | 83 | DecodeFromWaveBuffers(*processor.memory, args); |
| 78 | } | 84 | } |
| 79 | 85 | ||
| 80 | bool AdpcmDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { | 86 | bool AdpcmDataSourceVersion2Command::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 81 | return true; | 87 | return true; |
| 82 | } | 88 | } |
| 83 | 89 | ||
| 84 | } // namespace AudioCore::AudioRenderer | 90 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/adpcm.h b/src/audio_core/renderer/command/data_source/adpcm.h index a9cf9cee4..487846f0c 100644 --- a/src/audio_core/renderer/command/data_source/adpcm.h +++ b/src/audio_core/renderer/command/data_source/adpcm.h | |||
| @@ -11,11 +11,12 @@ | |||
| 11 | #include "audio_core/renderer/command/icommand.h" | 11 | #include "audio_core/renderer/command/icommand.h" |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::ADSP::AudioRenderer { |
| 15 | namespace ADSP { | ||
| 16 | class CommandListProcessor; | 15 | class CommandListProcessor; |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 18 | namespace AudioCore::Renderer { | ||
| 19 | |||
| 19 | /** | 20 | /** |
| 20 | * AudioRenderer command to decode ADPCM-encoded version 1 wavebuffers | 21 | * AudioRenderer command to decode ADPCM-encoded version 1 wavebuffers |
| 21 | * into the output_index mix buffer. | 22 | * into the output_index mix buffer. |
| @@ -27,14 +28,14 @@ struct AdpcmDataSourceVersion1Command : ICommand { | |||
| 27 | * @param processor - The CommandListProcessor processing this command. | 28 | * @param processor - The CommandListProcessor processing this command. |
| 28 | * @param string - The string to print into. | 29 | * @param string - The string to print into. |
| 29 | */ | 30 | */ |
| 30 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 31 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 31 | 32 | ||
| 32 | /** | 33 | /** |
| 33 | * Process this command. | 34 | * Process this command. |
| 34 | * | 35 | * |
| 35 | * @param processor - The CommandListProcessor processing this command. | 36 | * @param processor - The CommandListProcessor processing this command. |
| 36 | */ | 37 | */ |
| 37 | void Process(const ADSP::CommandListProcessor& processor) override; | 38 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 38 | 39 | ||
| 39 | /** | 40 | /** |
| 40 | * Verify this command's data is valid. | 41 | * Verify this command's data is valid. |
| @@ -42,13 +43,13 @@ struct AdpcmDataSourceVersion1Command : ICommand { | |||
| 42 | * @param processor - The CommandListProcessor processing this command. | 43 | * @param processor - The CommandListProcessor processing this command. |
| 43 | * @return True if the command is valid, otherwise false. | 44 | * @return True if the command is valid, otherwise false. |
| 44 | */ | 45 | */ |
| 45 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 46 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 46 | 47 | ||
| 47 | /// Quality used for sample rate conversion | 48 | /// Quality used for sample rate conversion |
| 48 | SrcQuality src_quality; | 49 | SrcQuality src_quality; |
| 49 | /// Mix buffer index for decoded samples | 50 | /// Mix buffer index for decoded samples |
| 50 | s16 output_index; | 51 | s16 output_index; |
| 51 | /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) | 52 | /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) |
| 52 | u16 flags; | 53 | u16 flags; |
| 53 | /// Wavebuffer sample rate | 54 | /// Wavebuffer sample rate |
| 54 | u32 sample_rate; | 55 | u32 sample_rate; |
| @@ -75,14 +76,14 @@ struct AdpcmDataSourceVersion2Command : ICommand { | |||
| 75 | * @param processor - The CommandListProcessor processing this command. | 76 | * @param processor - The CommandListProcessor processing this command. |
| 76 | * @param string - The string to print into. | 77 | * @param string - The string to print into. |
| 77 | */ | 78 | */ |
| 78 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 79 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 79 | 80 | ||
| 80 | /** | 81 | /** |
| 81 | * Process this command. | 82 | * Process this command. |
| 82 | * | 83 | * |
| 83 | * @param processor - The CommandListProcessor processing this command. | 84 | * @param processor - The CommandListProcessor processing this command. |
| 84 | */ | 85 | */ |
| 85 | void Process(const ADSP::CommandListProcessor& processor) override; | 86 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 86 | 87 | ||
| 87 | /** | 88 | /** |
| 88 | * Verify this command's data is valid. | 89 | * Verify this command's data is valid. |
| @@ -90,13 +91,13 @@ struct AdpcmDataSourceVersion2Command : ICommand { | |||
| 90 | * @param processor - The CommandListProcessor processing this command. | 91 | * @param processor - The CommandListProcessor processing this command. |
| 91 | * @return True if the command is valid, otherwise false. | 92 | * @return True if the command is valid, otherwise false. |
| 92 | */ | 93 | */ |
| 93 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 94 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 94 | 95 | ||
| 95 | /// Quality used for sample rate conversion | 96 | /// Quality used for sample rate conversion |
| 96 | SrcQuality src_quality; | 97 | SrcQuality src_quality; |
| 97 | /// Mix buffer index for decoded samples | 98 | /// Mix buffer index for decoded samples |
| 98 | s16 output_index; | 99 | s16 output_index; |
| 99 | /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) | 100 | /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) |
| 100 | u16 flags; | 101 | u16 flags; |
| 101 | /// Wavebuffer sample rate | 102 | /// Wavebuffer sample rate |
| 102 | u32 sample_rate; | 103 | u32 sample_rate; |
| @@ -116,4 +117,4 @@ struct AdpcmDataSourceVersion2Command : ICommand { | |||
| 116 | u64 data_size; | 117 | u64 data_size; |
| 117 | }; | 118 | }; |
| 118 | 119 | ||
| 119 | } // namespace AudioCore::AudioRenderer | 120 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index 257aa866e..911dae3c1 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/scratch_buffer.h" | 11 | #include "common/scratch_buffer.h" |
| 12 | #include "core/memory.h" | 12 | #include "core/memory.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::Renderer { |
| 15 | 15 | ||
| 16 | constexpr u32 TempBufferSize = 0x3F00; | 16 | constexpr u32 TempBufferSize = 0x3F00; |
| 17 | constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4}; | 17 | constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4}; |
| @@ -123,11 +123,13 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, | |||
| 123 | return 0; | 123 | return 0; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | auto samples_to_process{ | 126 | auto start_pos{req.start_offset + req.offset}; |
| 127 | std::min(req.end_offset - req.start_offset - req.offset, req.samples_to_read)}; | 127 | auto samples_to_process{std::min(req.end_offset - start_pos, req.samples_to_read)}; |
| 128 | if (samples_to_process == 0) { | ||
| 129 | return 0; | ||
| 130 | } | ||
| 128 | 131 | ||
| 129 | auto samples_to_read{samples_to_process}; | 132 | auto samples_to_read{samples_to_process}; |
| 130 | auto start_pos{req.start_offset + req.offset}; | ||
| 131 | auto samples_remaining_in_frame{start_pos % SamplesPerFrame}; | 133 | auto samples_remaining_in_frame{start_pos % SamplesPerFrame}; |
| 132 | auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame + | 134 | auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame + |
| 133 | samples_remaining_in_frame}; | 135 | samples_remaining_in_frame}; |
| @@ -225,13 +227,24 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, | |||
| 225 | * @param args - The wavebuffer data, and information for how to decode it. | 227 | * @param args - The wavebuffer data, and information for how to decode it. |
| 226 | */ | 228 | */ |
| 227 | void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) { | 229 | void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) { |
| 230 | static constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index, | ||
| 231 | auto& played_samples, auto& consumed) -> void { | ||
| 232 | voice_state.wave_buffer_valid[index] = false; | ||
| 233 | voice_state.loop_count = 0; | ||
| 234 | |||
| 235 | if (wavebuffer.stream_ended) { | ||
| 236 | played_samples = 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | index = (index + 1) % MaxWaveBuffers; | ||
| 240 | consumed++; | ||
| 241 | }; | ||
| 228 | auto& voice_state{*args.voice_state}; | 242 | auto& voice_state{*args.voice_state}; |
| 229 | auto remaining_sample_count{args.sample_count}; | 243 | auto remaining_sample_count{args.sample_count}; |
| 230 | auto fraction{voice_state.fraction}; | 244 | auto fraction{voice_state.fraction}; |
| 231 | 245 | ||
| 232 | const auto sample_rate_ratio{ | 246 | const auto sample_rate_ratio{Common::FixedPoint<49, 15>( |
| 233 | (Common::FixedPoint<49, 15>(args.source_sample_rate) / args.target_sample_rate) * | 247 | (f32)args.source_sample_rate / (f32)args.target_sample_rate * (f32)args.pitch)}; |
| 234 | args.pitch}; | ||
| 235 | const auto size_required{fraction + remaining_sample_count * sample_rate_ratio}; | 248 | const auto size_required{fraction + remaining_sample_count * sample_rate_ratio}; |
| 236 | 249 | ||
| 237 | if (size_required < 0) { | 250 | if (size_required < 0) { |
| @@ -298,22 +311,23 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf | |||
| 298 | auto end_offset{wavebuffer.end_offset}; | 311 | auto end_offset{wavebuffer.end_offset}; |
| 299 | 312 | ||
| 300 | if (wavebuffer.loop && voice_state.loop_count > 0 && | 313 | if (wavebuffer.loop && voice_state.loop_count > 0 && |
| 301 | wavebuffer.loop_start_offset != 0 && wavebuffer.loop_end_offset != 0 && | ||
| 302 | wavebuffer.loop_start_offset <= wavebuffer.loop_end_offset) { | 314 | wavebuffer.loop_start_offset <= wavebuffer.loop_end_offset) { |
| 303 | start_offset = wavebuffer.loop_start_offset; | 315 | start_offset = wavebuffer.loop_start_offset; |
| 304 | end_offset = wavebuffer.loop_end_offset; | 316 | end_offset = wavebuffer.loop_end_offset; |
| 305 | } | 317 | } |
| 306 | 318 | ||
| 307 | DecodeArg decode_arg{.buffer{wavebuffer.buffer}, | 319 | DecodeArg decode_arg{ |
| 308 | .buffer_size{wavebuffer.buffer_size}, | 320 | .buffer{wavebuffer.buffer}, |
| 309 | .start_offset{start_offset}, | 321 | .buffer_size{wavebuffer.buffer_size}, |
| 310 | .end_offset{end_offset}, | 322 | .start_offset{start_offset}, |
| 311 | .channel_count{args.channel_count}, | 323 | .end_offset{end_offset}, |
| 312 | .coefficients{}, | 324 | .channel_count{args.channel_count}, |
| 313 | .adpcm_context{nullptr}, | 325 | .coefficients{}, |
| 314 | .target_channel{args.channel}, | 326 | .adpcm_context{nullptr}, |
| 315 | .offset{offset}, | 327 | .target_channel{args.channel}, |
| 316 | .samples_to_read{samples_to_read - samples_read}}; | 328 | .offset{offset}, |
| 329 | .samples_to_read{samples_to_read - samples_read}, | ||
| 330 | }; | ||
| 317 | 331 | ||
| 318 | s32 samples_decoded{0}; | 332 | s32 samples_decoded{0}; |
| 319 | 333 | ||
| @@ -350,42 +364,30 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf | |||
| 350 | temp_buffer_pos += samples_decoded; | 364 | temp_buffer_pos += samples_decoded; |
| 351 | offset += samples_decoded; | 365 | offset += samples_decoded; |
| 352 | 366 | ||
| 353 | if (samples_decoded == 0 || offset >= end_offset - start_offset) { | 367 | if (samples_decoded && offset < end_offset - start_offset) { |
| 354 | offset = 0; | 368 | continue; |
| 355 | if (!wavebuffer.loop) { | 369 | } |
| 356 | voice_state.wave_buffer_valid[wavebuffer_index] = false; | 370 | |
| 357 | voice_state.loop_count = 0; | 371 | offset = 0; |
| 358 | 372 | if (wavebuffer.loop) { | |
| 359 | if (wavebuffer.stream_ended) { | 373 | voice_state.loop_count++; |
| 360 | played_sample_count = 0; | 374 | if (wavebuffer.loop_count >= 0 && |
| 361 | } | 375 | (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) { |
| 362 | 376 | EndWaveBuffer(voice_state, wavebuffer, wavebuffer_index, played_sample_count, | |
| 363 | wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers; | 377 | wavebuffers_consumed); |
| 364 | wavebuffers_consumed++; | 378 | } |
| 365 | } else { | 379 | |
| 366 | voice_state.loop_count++; | 380 | if (samples_decoded == 0) { |
| 367 | if (wavebuffer.loop_count > 0 && | 381 | is_buffer_starved = true; |
| 368 | (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) { | 382 | break; |
| 369 | voice_state.wave_buffer_valid[wavebuffer_index] = false; | 383 | } |
| 370 | voice_state.loop_count = 0; | 384 | |
| 371 | 385 | if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) { | |
| 372 | if (wavebuffer.stream_ended) { | 386 | played_sample_count = 0; |
| 373 | played_sample_count = 0; | ||
| 374 | } | ||
| 375 | |||
| 376 | wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers; | ||
| 377 | wavebuffers_consumed++; | ||
| 378 | } | ||
| 379 | |||
| 380 | if (samples_decoded == 0) { | ||
| 381 | is_buffer_starved = true; | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | |||
| 385 | if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) { | ||
| 386 | played_sample_count = 0; | ||
| 387 | } | ||
| 388 | } | 387 | } |
| 388 | } else { | ||
| 389 | EndWaveBuffer(voice_state, wavebuffer, wavebuffer_index, played_sample_count, | ||
| 390 | wavebuffers_consumed); | ||
| 389 | } | 391 | } |
| 390 | } | 392 | } |
| 391 | 393 | ||
| @@ -423,4 +425,4 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf | |||
| 423 | voice_state.fraction = fraction; | 425 | voice_state.fraction = fraction; |
| 424 | } | 426 | } |
| 425 | 427 | ||
| 426 | } // namespace AudioCore::AudioRenderer | 428 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/decode.h b/src/audio_core/renderer/command/data_source/decode.h index 4d63d6fa8..5f52f32f0 100644 --- a/src/audio_core/renderer/command/data_source/decode.h +++ b/src/audio_core/renderer/command/data_source/decode.h | |||
| @@ -15,7 +15,7 @@ namespace Core::Memory { | |||
| 15 | class Memory; | 15 | class Memory; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace AudioCore::AudioRenderer { | 18 | namespace AudioCore::Renderer { |
| 19 | 19 | ||
| 20 | struct DecodeFromWaveBuffersArgs { | 20 | struct DecodeFromWaveBuffersArgs { |
| 21 | SampleFormat sample_format; | 21 | SampleFormat sample_format; |
| @@ -56,4 +56,4 @@ struct DecodeArg { | |||
| 56 | */ | 56 | */ |
| 57 | void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args); | 57 | void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args); |
| 58 | 58 | ||
| 59 | } // namespace AudioCore::AudioRenderer | 59 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/pcm_float.cpp b/src/audio_core/renderer/command/data_source/pcm_float.cpp index be77fab69..d1f685656 100644 --- a/src/audio_core/renderer/command/data_source/pcm_float.cpp +++ b/src/audio_core/renderer/command/data_source/pcm_float.cpp | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/data_source/decode.h" | 5 | #include "audio_core/renderer/command/data_source/decode.h" |
| 6 | #include "audio_core/renderer/command/data_source/pcm_float.h" | 6 | #include "audio_core/renderer/command/data_source/pcm_float.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, | 10 | void PcmFloatDataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 11 | std::string& string) { | 11 | std::string& string) { |
| 12 | string += | 12 | string += |
| 13 | fmt::format("PcmFloatDataSourceVersion1Command\n\toutput_index {:02X} channel {} " | 13 | fmt::format("PcmFloatDataSourceVersion1Command\n\toutput_index {:02X} channel {} " |
| @@ -16,10 +16,17 @@ void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& p | |||
| 16 | processor.target_sample_rate, src_quality); | 16 | processor.target_sample_rate, src_quality); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { | 19 | void PcmFloatDataSourceVersion1Command::Process( |
| 20 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 20 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, | 21 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 21 | processor.sample_count); | 22 | processor.sample_count); |
| 22 | 23 | ||
| 24 | for (auto& wave_buffer : wave_buffers) { | ||
| 25 | wave_buffer.loop_start_offset = wave_buffer.start_offset; | ||
| 26 | wave_buffer.loop_end_offset = wave_buffer.end_offset; | ||
| 27 | wave_buffer.loop_count = wave_buffer.loop ? -1 : 0; | ||
| 28 | } | ||
| 29 | |||
| 23 | DecodeFromWaveBuffersArgs args{ | 30 | DecodeFromWaveBuffersArgs args{ |
| 24 | .sample_format{SampleFormat::PcmFloat}, | 31 | .sample_format{SampleFormat::PcmFloat}, |
| 25 | .output{out_buffer}, | 32 | .output{out_buffer}, |
| @@ -41,11 +48,12 @@ void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor | |||
| 41 | DecodeFromWaveBuffers(*processor.memory, args); | 48 | DecodeFromWaveBuffers(*processor.memory, args); |
| 42 | } | 49 | } |
| 43 | 50 | ||
| 44 | bool PcmFloatDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { | 51 | bool PcmFloatDataSourceVersion1Command::Verify( |
| 52 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 45 | return true; | 53 | return true; |
| 46 | } | 54 | } |
| 47 | 55 | ||
| 48 | void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, | 56 | void PcmFloatDataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 49 | std::string& string) { | 57 | std::string& string) { |
| 50 | string += | 58 | string += |
| 51 | fmt::format("PcmFloatDataSourceVersion2Command\n\toutput_index {:02X} channel {} " | 59 | fmt::format("PcmFloatDataSourceVersion2Command\n\toutput_index {:02X} channel {} " |
| @@ -54,7 +62,8 @@ void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& p | |||
| 54 | processor.target_sample_rate, src_quality); | 62 | processor.target_sample_rate, src_quality); |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { | 65 | void PcmFloatDataSourceVersion2Command::Process( |
| 66 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 58 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, | 67 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 59 | processor.sample_count); | 68 | processor.sample_count); |
| 60 | 69 | ||
| @@ -79,8 +88,9 @@ void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor | |||
| 79 | DecodeFromWaveBuffers(*processor.memory, args); | 88 | DecodeFromWaveBuffers(*processor.memory, args); |
| 80 | } | 89 | } |
| 81 | 90 | ||
| 82 | bool PcmFloatDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { | 91 | bool PcmFloatDataSourceVersion2Command::Verify( |
| 92 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 83 | return true; | 93 | return true; |
| 84 | } | 94 | } |
| 85 | 95 | ||
| 86 | } // namespace AudioCore::AudioRenderer | 96 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/pcm_float.h b/src/audio_core/renderer/command/data_source/pcm_float.h index e4af77c20..2c9d1877e 100644 --- a/src/audio_core/renderer/command/data_source/pcm_float.h +++ b/src/audio_core/renderer/command/data_source/pcm_float.h | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | #include "audio_core/renderer/command/icommand.h" | 9 | #include "audio_core/renderer/command/icommand.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::ADSP::AudioRenderer { |
| 13 | namespace ADSP { | ||
| 14 | class CommandListProcessor; | 13 | class CommandListProcessor; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 16 | namespace AudioCore::Renderer { | ||
| 17 | |||
| 17 | /** | 18 | /** |
| 18 | * AudioRenderer command to decode PCM float-encoded version 1 wavebuffers | 19 | * AudioRenderer command to decode PCM float-encoded version 1 wavebuffers |
| 19 | * into the output_index mix buffer. | 20 | * into the output_index mix buffer. |
| @@ -25,14 +26,14 @@ struct PcmFloatDataSourceVersion1Command : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,13 +41,13 @@ struct PcmFloatDataSourceVersion1Command : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Quality used for sample rate conversion | 46 | /// Quality used for sample rate conversion |
| 46 | SrcQuality src_quality; | 47 | SrcQuality src_quality; |
| 47 | /// Mix buffer index for decoded samples | 48 | /// Mix buffer index for decoded samples |
| 48 | s16 output_index; | 49 | s16 output_index; |
| 49 | /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) | 50 | /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) |
| 50 | u16 flags; | 51 | u16 flags; |
| 51 | /// Wavebuffer sample rate | 52 | /// Wavebuffer sample rate |
| 52 | u32 sample_rate; | 53 | u32 sample_rate; |
| @@ -73,14 +74,14 @@ struct PcmFloatDataSourceVersion2Command : ICommand { | |||
| 73 | * @param processor - The CommandListProcessor processing this command. | 74 | * @param processor - The CommandListProcessor processing this command. |
| 74 | * @param string - The string to print into. | 75 | * @param string - The string to print into. |
| 75 | */ | 76 | */ |
| 76 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 77 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 77 | 78 | ||
| 78 | /** | 79 | /** |
| 79 | * Process this command. | 80 | * Process this command. |
| 80 | * | 81 | * |
| 81 | * @param processor - The CommandListProcessor processing this command. | 82 | * @param processor - The CommandListProcessor processing this command. |
| 82 | */ | 83 | */ |
| 83 | void Process(const ADSP::CommandListProcessor& processor) override; | 84 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 84 | 85 | ||
| 85 | /** | 86 | /** |
| 86 | * Verify this command's data is valid. | 87 | * Verify this command's data is valid. |
| @@ -88,13 +89,13 @@ struct PcmFloatDataSourceVersion2Command : ICommand { | |||
| 88 | * @param processor - The CommandListProcessor processing this command. | 89 | * @param processor - The CommandListProcessor processing this command. |
| 89 | * @return True if the command is valid, otherwise false. | 90 | * @return True if the command is valid, otherwise false. |
| 90 | */ | 91 | */ |
| 91 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 92 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 92 | 93 | ||
| 93 | /// Quality used for sample rate conversion | 94 | /// Quality used for sample rate conversion |
| 94 | SrcQuality src_quality; | 95 | SrcQuality src_quality; |
| 95 | /// Mix buffer index for decoded samples | 96 | /// Mix buffer index for decoded samples |
| 96 | s16 output_index; | 97 | s16 output_index; |
| 97 | /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) | 98 | /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) |
| 98 | u16 flags; | 99 | u16 flags; |
| 99 | /// Wavebuffer sample rate | 100 | /// Wavebuffer sample rate |
| 100 | u32 sample_rate; | 101 | u32 sample_rate; |
| @@ -110,4 +111,4 @@ struct PcmFloatDataSourceVersion2Command : ICommand { | |||
| 110 | CpuAddr voice_state; | 111 | CpuAddr voice_state; |
| 111 | }; | 112 | }; |
| 112 | 113 | ||
| 113 | } // namespace AudioCore::AudioRenderer | 114 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.cpp b/src/audio_core/renderer/command/data_source/pcm_int16.cpp index 7a27463e4..c89a5aaac 100644 --- a/src/audio_core/renderer/command/data_source/pcm_int16.cpp +++ b/src/audio_core/renderer/command/data_source/pcm_int16.cpp | |||
| @@ -3,13 +3,13 @@ | |||
| 3 | 3 | ||
| 4 | #include <span> | 4 | #include <span> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/data_source/decode.h" | 7 | #include "audio_core/renderer/command/data_source/decode.h" |
| 8 | #include "audio_core/renderer/command/data_source/pcm_int16.h" | 8 | #include "audio_core/renderer/command/data_source/pcm_int16.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, | 12 | void PcmInt16DataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 13 | std::string& string) { | 13 | std::string& string) { |
| 14 | string += | 14 | string += |
| 15 | fmt::format("PcmInt16DataSourceVersion1Command\n\toutput_index {:02X} channel {} " | 15 | fmt::format("PcmInt16DataSourceVersion1Command\n\toutput_index {:02X} channel {} " |
| @@ -18,10 +18,17 @@ void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& p | |||
| 18 | processor.target_sample_rate, src_quality); | 18 | processor.target_sample_rate, src_quality); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { | 21 | void PcmInt16DataSourceVersion1Command::Process( |
| 22 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 22 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, | 23 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 23 | processor.sample_count); | 24 | processor.sample_count); |
| 24 | 25 | ||
| 26 | for (auto& wave_buffer : wave_buffers) { | ||
| 27 | wave_buffer.loop_start_offset = wave_buffer.start_offset; | ||
| 28 | wave_buffer.loop_end_offset = wave_buffer.end_offset; | ||
| 29 | wave_buffer.loop_count = wave_buffer.loop ? -1 : 0; | ||
| 30 | } | ||
| 31 | |||
| 25 | DecodeFromWaveBuffersArgs args{ | 32 | DecodeFromWaveBuffersArgs args{ |
| 26 | .sample_format{SampleFormat::PcmInt16}, | 33 | .sample_format{SampleFormat::PcmInt16}, |
| 27 | .output{out_buffer}, | 34 | .output{out_buffer}, |
| @@ -43,11 +50,12 @@ void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor | |||
| 43 | DecodeFromWaveBuffers(*processor.memory, args); | 50 | DecodeFromWaveBuffers(*processor.memory, args); |
| 44 | } | 51 | } |
| 45 | 52 | ||
| 46 | bool PcmInt16DataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { | 53 | bool PcmInt16DataSourceVersion1Command::Verify( |
| 54 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 47 | return true; | 55 | return true; |
| 48 | } | 56 | } |
| 49 | 57 | ||
| 50 | void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, | 58 | void PcmInt16DataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 51 | std::string& string) { | 59 | std::string& string) { |
| 52 | string += | 60 | string += |
| 53 | fmt::format("PcmInt16DataSourceVersion2Command\n\toutput_index {:02X} channel {} " | 61 | fmt::format("PcmInt16DataSourceVersion2Command\n\toutput_index {:02X} channel {} " |
| @@ -56,7 +64,8 @@ void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& p | |||
| 56 | processor.target_sample_rate, src_quality); | 64 | processor.target_sample_rate, src_quality); |
| 57 | } | 65 | } |
| 58 | 66 | ||
| 59 | void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { | 67 | void PcmInt16DataSourceVersion2Command::Process( |
| 68 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 60 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, | 69 | auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 61 | processor.sample_count); | 70 | processor.sample_count); |
| 62 | DecodeFromWaveBuffersArgs args{ | 71 | DecodeFromWaveBuffersArgs args{ |
| @@ -80,8 +89,9 @@ void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor | |||
| 80 | DecodeFromWaveBuffers(*processor.memory, args); | 89 | DecodeFromWaveBuffers(*processor.memory, args); |
| 81 | } | 90 | } |
| 82 | 91 | ||
| 83 | bool PcmInt16DataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { | 92 | bool PcmInt16DataSourceVersion2Command::Verify( |
| 93 | const AudioRenderer::CommandListProcessor& processor) { | ||
| 84 | return true; | 94 | return true; |
| 85 | } | 95 | } |
| 86 | 96 | ||
| 87 | } // namespace AudioCore::AudioRenderer | 97 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.h b/src/audio_core/renderer/command/data_source/pcm_int16.h index 5de1ad60d..2c013f003 100644 --- a/src/audio_core/renderer/command/data_source/pcm_int16.h +++ b/src/audio_core/renderer/command/data_source/pcm_int16.h | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | #include "audio_core/renderer/command/icommand.h" | 9 | #include "audio_core/renderer/command/icommand.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::ADSP::AudioRenderer { |
| 13 | namespace ADSP { | ||
| 14 | class CommandListProcessor; | 13 | class CommandListProcessor; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 16 | namespace AudioCore::Renderer { | ||
| 17 | |||
| 17 | /** | 18 | /** |
| 18 | * AudioRenderer command to decode PCM s16-encoded version 1 wavebuffers | 19 | * AudioRenderer command to decode PCM s16-encoded version 1 wavebuffers |
| 19 | * into the output_index mix buffer. | 20 | * into the output_index mix buffer. |
| @@ -25,14 +26,14 @@ struct PcmInt16DataSourceVersion1Command : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,13 +41,13 @@ struct PcmInt16DataSourceVersion1Command : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Quality used for sample rate conversion | 46 | /// Quality used for sample rate conversion |
| 46 | SrcQuality src_quality; | 47 | SrcQuality src_quality; |
| 47 | /// Mix buffer index for decoded samples | 48 | /// Mix buffer index for decoded samples |
| 48 | s16 output_index; | 49 | s16 output_index; |
| 49 | /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) | 50 | /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) |
| 50 | u16 flags; | 51 | u16 flags; |
| 51 | /// Wavebuffer sample rate | 52 | /// Wavebuffer sample rate |
| 52 | u32 sample_rate; | 53 | u32 sample_rate; |
| @@ -72,26 +73,26 @@ struct PcmInt16DataSourceVersion2Command : ICommand { | |||
| 72 | * @param processor - The CommandListProcessor processing this command. | 73 | * @param processor - The CommandListProcessor processing this command. |
| 73 | * @param string - The string to print into. | 74 | * @param string - The string to print into. |
| 74 | */ | 75 | */ |
| 75 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 76 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 76 | 77 | ||
| 77 | /** | 78 | /** |
| 78 | * Process this command. | 79 | * Process this command. |
| 79 | * @param processor - The CommandListProcessor processing this command. | 80 | * @param processor - The CommandListProcessor processing this command. |
| 80 | */ | 81 | */ |
| 81 | void Process(const ADSP::CommandListProcessor& processor) override; | 82 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 82 | 83 | ||
| 83 | /** | 84 | /** |
| 84 | * Verify this command's data is valid. | 85 | * Verify this command's data is valid. |
| 85 | * @param processor - The CommandListProcessor processing this command. | 86 | * @param processor - The CommandListProcessor processing this command. |
| 86 | * @return True if the command is valid, otherwise false. | 87 | * @return True if the command is valid, otherwise false. |
| 87 | */ | 88 | */ |
| 88 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 89 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 89 | 90 | ||
| 90 | /// Quality used for sample rate conversion | 91 | /// Quality used for sample rate conversion |
| 91 | SrcQuality src_quality; | 92 | SrcQuality src_quality; |
| 92 | /// Mix buffer index for decoded samples | 93 | /// Mix buffer index for decoded samples |
| 93 | s16 output_index; | 94 | s16 output_index; |
| 94 | /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) | 95 | /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) |
| 95 | u16 flags; | 96 | u16 flags; |
| 96 | /// Wavebuffer sample rate | 97 | /// Wavebuffer sample rate |
| 97 | u32 sample_rate; | 98 | u32 sample_rate; |
| @@ -107,4 +108,4 @@ struct PcmInt16DataSourceVersion2Command : ICommand { | |||
| 107 | CpuAddr voice_state; | 108 | CpuAddr voice_state; |
| 108 | }; | 109 | }; |
| 109 | 110 | ||
| 110 | } // namespace AudioCore::AudioRenderer | 111 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp index a3e12b3e7..74d9c229f 100644 --- a/src/audio_core/renderer/command/effect/aux_.cpp +++ b/src/audio_core/renderer/command/effect/aux_.cpp | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/effect/aux_.h" | 5 | #include "audio_core/renderer/command/effect/aux_.h" |
| 6 | #include "audio_core/renderer/effect/aux_.h" | 6 | #include "audio_core/renderer/effect/aux_.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/memory.h" | 8 | #include "core/memory.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | /** | 11 | /** |
| 12 | * Reset an AuxBuffer. | 12 | * Reset an AuxBuffer. |
| 13 | * | 13 | * |
| @@ -175,13 +175,13 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_, | |||
| 175 | return read_count_; | 175 | return read_count_; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | void AuxCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 178 | void AuxCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 179 | std::string& string) { | 179 | std::string& string) { |
| 180 | string += fmt::format("AuxCommand\n\tenabled {} input {:02X} output {:02X}\n", effect_enabled, | 180 | string += fmt::format("AuxCommand\n\tenabled {} input {:02X} output {:02X}\n", effect_enabled, |
| 181 | input, output); | 181 | input, output); |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | void AuxCommand::Process(const ADSP::CommandListProcessor& processor) { | 184 | void AuxCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 185 | auto input_buffer{ | 185 | auto input_buffer{ |
| 186 | processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; | 186 | processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; |
| 187 | auto output_buffer{ | 187 | auto output_buffer{ |
| @@ -208,8 +208,8 @@ void AuxCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 208 | } | 208 | } |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | bool AuxCommand::Verify(const ADSP::CommandListProcessor& processor) { | 211 | bool AuxCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 212 | return true; | 212 | return true; |
| 213 | } | 213 | } |
| 214 | 214 | ||
| 215 | } // namespace AudioCore::AudioRenderer | 215 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/aux_.h b/src/audio_core/renderer/command/effect/aux_.h index 825c93732..da1e55261 100644 --- a/src/audio_core/renderer/command/effect/aux_.h +++ b/src/audio_core/renderer/command/effect/aux_.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command to read and write an auxiliary buffer, writing the input mix buffer to game | 18 | * AudioRenderer command to read and write an auxiliary buffer, writing the input mix buffer to game |
| 18 | * memory, and reading into the output buffer from game memory. | 19 | * memory, and reading into the output buffer from game memory. |
| @@ -24,14 +25,14 @@ struct AuxCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct AuxCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | 44 | ||
| 44 | /// Input mix buffer index | 45 | /// Input mix buffer index |
| 45 | s16 input; | 46 | s16 input; |
| @@ -63,4 +64,4 @@ struct AuxCommand : ICommand { | |||
| 63 | bool effect_enabled; | 64 | bool effect_enabled; |
| 64 | }; | 65 | }; |
| 65 | 66 | ||
| 66 | } // namespace AudioCore::AudioRenderer | 67 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp index dea6423dc..3392e7747 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/effect/biquad_filter.h" | 5 | #include "audio_core/renderer/command/effect/biquad_filter.h" |
| 6 | #include "audio_core/renderer/voice/voice_state.h" | 6 | #include "audio_core/renderer/voice/voice_state.h" |
| 7 | #include "common/bit_cast.h" | 7 | #include "common/bit_cast.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Biquad filter float implementation. | 11 | * Biquad filter float implementation. |
| 12 | * | 12 | * |
| @@ -76,14 +76,14 @@ static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> inp | |||
| 76 | } | 76 | } |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 79 | void BiquadFilterCommand::Dump( |
| 80 | std::string& string) { | 80 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 81 | string += fmt::format( | 81 | string += fmt::format( |
| 82 | "BiquadFilterCommand\n\tinput {:02X} output {:02X} needs_init {} use_float_processing {}\n", | 82 | "BiquadFilterCommand\n\tinput {:02X} output {:02X} needs_init {} use_float_processing {}\n", |
| 83 | input, output, needs_init, use_float_processing); | 83 | input, output, needs_init, use_float_processing); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { | 86 | void BiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 87 | auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; | 87 | auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; |
| 88 | if (needs_init) { | 88 | if (needs_init) { |
| 89 | *state_ = {}; | 89 | *state_ = {}; |
| @@ -103,8 +103,8 @@ void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 103 | } | 103 | } |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | bool BiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) { | 106 | bool BiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 107 | return true; | 107 | return true; |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | } // namespace AudioCore::AudioRenderer | 110 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.h b/src/audio_core/renderer/command/effect/biquad_filter.h index 4c9c42d29..0e903930a 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.h +++ b/src/audio_core/renderer/command/effect/biquad_filter.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/voice/voice_state.h" | 10 | #include "audio_core/renderer/voice/voice_state.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for applying a biquad filter to the input mix buffer, saving the results to | 20 | * AudioRenderer command for applying a biquad filter to the input mix buffer, saving the results to |
| 20 | * the output mix buffer. | 21 | * the output mix buffer. |
| @@ -26,14 +27,14 @@ struct BiquadFilterCommand : ICommand { | |||
| 26 | * @param processor - The CommandListProcessor processing this command. | 27 | * @param processor - The CommandListProcessor processing this command. |
| 27 | * @param string - The string to print into. | 28 | * @param string - The string to print into. |
| 28 | */ | 29 | */ |
| 29 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 30 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * Process this command. | 33 | * Process this command. |
| 33 | * | 34 | * |
| 34 | * @param processor - The CommandListProcessor processing this command. | 35 | * @param processor - The CommandListProcessor processing this command. |
| 35 | */ | 36 | */ |
| 36 | void Process(const ADSP::CommandListProcessor& processor) override; | 37 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Verify this command's data is valid. | 40 | * Verify this command's data is valid. |
| @@ -41,7 +42,7 @@ struct BiquadFilterCommand : ICommand { | |||
| 41 | * @param processor - The CommandListProcessor processing this command. | 42 | * @param processor - The CommandListProcessor processing this command. |
| 42 | * @return True if the command is valid, otherwise false. | 43 | * @return True if the command is valid, otherwise false. |
| 43 | */ | 44 | */ |
| 44 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 45 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 45 | 46 | ||
| 46 | /// Input mix buffer index | 47 | /// Input mix buffer index |
| 47 | s16 input; | 48 | s16 input; |
| @@ -71,4 +72,4 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input, | |||
| 71 | std::array<s16, 3>& b, std::array<s16, 2>& a, | 72 | std::array<s16, 3>& b, std::array<s16, 2>& a, |
| 72 | VoiceState::BiquadFilterState& state, const u32 sample_count); | 73 | VoiceState::BiquadFilterState& state, const u32 sample_count); |
| 73 | 74 | ||
| 74 | } // namespace AudioCore::AudioRenderer | 75 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/capture.cpp b/src/audio_core/renderer/command/effect/capture.cpp index 042fd286e..f235ce027 100644 --- a/src/audio_core/renderer/command/effect/capture.cpp +++ b/src/audio_core/renderer/command/effect/capture.cpp | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/effect/capture.h" | 5 | #include "audio_core/renderer/command/effect/capture.h" |
| 6 | #include "audio_core/renderer/effect/aux_.h" | 6 | #include "audio_core/renderer/effect/aux_.h" |
| 7 | #include "core/memory.h" | 7 | #include "core/memory.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Reset an AuxBuffer. | 11 | * Reset an AuxBuffer. |
| 12 | * | 12 | * |
| @@ -118,13 +118,13 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in | |||
| 118 | return write_count_; | 118 | return write_count_; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | void CaptureCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 121 | void CaptureCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 122 | std::string& string) { | 122 | std::string& string) { |
| 123 | string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled, | 123 | string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled, |
| 124 | input, output); | 124 | input, output); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) { | 127 | void CaptureCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 128 | if (effect_enabled) { | 128 | if (effect_enabled) { |
| 129 | auto input_buffer{ | 129 | auto input_buffer{ |
| 130 | processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; | 130 | processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; |
| @@ -135,8 +135,8 @@ void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 135 | } | 135 | } |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | bool CaptureCommand::Verify(const ADSP::CommandListProcessor& processor) { | 138 | bool CaptureCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 139 | return true; | 139 | return true; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | } // namespace AudioCore::AudioRenderer | 142 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/capture.h b/src/audio_core/renderer/command/effect/capture.h index 8670acb24..a0016c6f6 100644 --- a/src/audio_core/renderer/command/effect/capture.h +++ b/src/audio_core/renderer/command/effect/capture.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for capturing a mix buffer. That is, writing it back to a given game memory | 18 | * AudioRenderer command for capturing a mix buffer. That is, writing it back to a given game memory |
| 18 | * address. | 19 | * address. |
| @@ -24,14 +25,14 @@ struct CaptureCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct CaptureCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | 44 | ||
| 44 | /// Input mix buffer index | 45 | /// Input mix buffer index |
| 45 | s16 input; | 46 | s16 input; |
| @@ -59,4 +60,4 @@ struct CaptureCommand : ICommand { | |||
| 59 | bool effect_enabled; | 60 | bool effect_enabled; |
| 60 | }; | 61 | }; |
| 61 | 62 | ||
| 62 | } // namespace AudioCore::AudioRenderer | 63 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp index ee9b68d5b..7ff707f4e 100644 --- a/src/audio_core/renderer/command/effect/compressor.cpp +++ b/src/audio_core/renderer/command/effect/compressor.cpp | |||
| @@ -5,11 +5,11 @@ | |||
| 5 | #include <span> | 5 | #include <span> |
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | 7 | ||
| 8 | #include "audio_core/renderer/adsp/command_list_processor.h" | 8 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 9 | #include "audio_core/renderer/command/effect/compressor.h" | 9 | #include "audio_core/renderer/command/effect/compressor.h" |
| 10 | #include "audio_core/renderer/effect/compressor.h" | 10 | #include "audio_core/renderer/effect/compressor.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | 13 | ||
| 14 | static void SetCompressorEffectParameter(const CompressorInfo::ParameterVersion2& params, | 14 | static void SetCompressorEffectParameter(const CompressorInfo::ParameterVersion2& params, |
| 15 | CompressorInfo::State& state) { | 15 | CompressorInfo::State& state) { |
| @@ -110,7 +110,7 @@ static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& param | |||
| 110 | } | 110 | } |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 113 | void CompressorCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 114 | std::string& string) { | 114 | std::string& string) { |
| 115 | string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled); | 115 | string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled); |
| 116 | for (s16 i = 0; i < parameter.channel_count; i++) { | 116 | for (s16 i = 0; i < parameter.channel_count; i++) { |
| @@ -123,7 +123,7 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& | |||
| 123 | string += "\n"; | 123 | string += "\n"; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { | 126 | void CompressorCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 127 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; | 127 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; |
| 128 | std::array<std::span<s32>, MaxChannels> output_buffers{}; | 128 | std::array<std::span<s32>, MaxChannels> output_buffers{}; |
| 129 | 129 | ||
| @@ -148,8 +148,8 @@ void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 148 | processor.sample_count); | 148 | processor.sample_count); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | bool CompressorCommand::Verify(const ADSP::CommandListProcessor& processor) { | 151 | bool CompressorCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 152 | return true; | 152 | return true; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | } // namespace AudioCore::AudioRenderer | 155 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/compressor.h b/src/audio_core/renderer/command/effect/compressor.h index f8e96cb43..c011aa927 100644 --- a/src/audio_core/renderer/command/effect/compressor.h +++ b/src/audio_core/renderer/command/effect/compressor.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/effect/compressor.h" | 10 | #include "audio_core/renderer/effect/compressor.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for limiting volume between a high and low threshold. | 20 | * AudioRenderer command for limiting volume between a high and low threshold. |
| 20 | * Version 1. | 21 | * Version 1. |
| @@ -26,14 +27,14 @@ struct CompressorCommand : ICommand { | |||
| 26 | * @param processor - The CommandListProcessor processing this command. | 27 | * @param processor - The CommandListProcessor processing this command. |
| 27 | * @param string - The string to print into. | 28 | * @param string - The string to print into. |
| 28 | */ | 29 | */ |
| 29 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 30 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * Process this command. | 33 | * Process this command. |
| 33 | * | 34 | * |
| 34 | * @param processor - The CommandListProcessor processing this command. | 35 | * @param processor - The CommandListProcessor processing this command. |
| 35 | */ | 36 | */ |
| 36 | void Process(const ADSP::CommandListProcessor& processor) override; | 37 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Verify this command's data is valid. | 40 | * Verify this command's data is valid. |
| @@ -41,7 +42,7 @@ struct CompressorCommand : ICommand { | |||
| 41 | * @param processor - The CommandListProcessor processing this command. | 42 | * @param processor - The CommandListProcessor processing this command. |
| 42 | * @return True if the command is valid, otherwise false. | 43 | * @return True if the command is valid, otherwise false. |
| 43 | */ | 44 | */ |
| 44 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 45 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 45 | 46 | ||
| 46 | /// Input mix buffer offsets for each channel | 47 | /// Input mix buffer offsets for each channel |
| 47 | std::array<s16, MaxChannels> inputs; | 48 | std::array<s16, MaxChannels> inputs; |
| @@ -57,4 +58,4 @@ struct CompressorCommand : ICommand { | |||
| 57 | bool effect_enabled; | 58 | bool effect_enabled; |
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 60 | } // namespace AudioCore::AudioRenderer | 61 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp index e536cbb1e..ffb298c07 100644 --- a/src/audio_core/renderer/command/effect/delay.cpp +++ b/src/audio_core/renderer/command/effect/delay.cpp | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/effect/delay.h" | 5 | #include "audio_core/renderer/command/effect/delay.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | /** | 8 | /** |
| 9 | * Update the DelayInfo state according to the given parameters. | 9 | * Update the DelayInfo state according to the given parameters. |
| 10 | * | 10 | * |
| @@ -194,7 +194,7 @@ static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayIn | |||
| 194 | } | 194 | } |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 197 | void DelayCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 198 | std::string& string) { | 198 | std::string& string) { |
| 199 | string += fmt::format("DelayCommand\n\tenabled {} \n\tinputs: ", effect_enabled); | 199 | string += fmt::format("DelayCommand\n\tenabled {} \n\tinputs: ", effect_enabled); |
| 200 | for (u32 i = 0; i < MaxChannels; i++) { | 200 | for (u32 i = 0; i < MaxChannels; i++) { |
| @@ -207,7 +207,7 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce | |||
| 207 | string += "\n"; | 207 | string += "\n"; |
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { | 210 | void DelayCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 211 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; | 211 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; |
| 212 | std::array<std::span<s32>, MaxChannels> output_buffers{}; | 212 | std::array<std::span<s32>, MaxChannels> output_buffers{}; |
| 213 | 213 | ||
| @@ -231,8 +231,8 @@ void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 231 | processor.sample_count); | 231 | processor.sample_count); |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | bool DelayCommand::Verify(const ADSP::CommandListProcessor& processor) { | 234 | bool DelayCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 235 | return true; | 235 | return true; |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | } // namespace AudioCore::AudioRenderer | 238 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/delay.h b/src/audio_core/renderer/command/effect/delay.h index b7a15ae6b..bfeac7af4 100644 --- a/src/audio_core/renderer/command/effect/delay.h +++ b/src/audio_core/renderer/command/effect/delay.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/effect/delay.h" | 10 | #include "audio_core/renderer/effect/delay.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for a delay effect. Delays inputs mix buffers according to the parameters | 20 | * AudioRenderer command for a delay effect. Delays inputs mix buffers according to the parameters |
| 20 | * and state, outputs receives the delayed samples. | 21 | * and state, outputs receives the delayed samples. |
| @@ -26,14 +27,14 @@ struct DelayCommand : ICommand { | |||
| 26 | * @param processor - The CommandListProcessor processing this command. | 27 | * @param processor - The CommandListProcessor processing this command. |
| 27 | * @param string - The string to print into. | 28 | * @param string - The string to print into. |
| 28 | */ | 29 | */ |
| 29 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 30 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * Process this command. | 33 | * Process this command. |
| 33 | * | 34 | * |
| 34 | * @param processor - The CommandListProcessor processing this command. | 35 | * @param processor - The CommandListProcessor processing this command. |
| 35 | */ | 36 | */ |
| 36 | void Process(const ADSP::CommandListProcessor& processor) override; | 37 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Verify this command's data is valid. | 40 | * Verify this command's data is valid. |
| @@ -41,7 +42,7 @@ struct DelayCommand : ICommand { | |||
| 41 | * @param processor - The CommandListProcessor processing this command. | 42 | * @param processor - The CommandListProcessor processing this command. |
| 42 | * @return True if the command is valid, otherwise false. | 43 | * @return True if the command is valid, otherwise false. |
| 43 | */ | 44 | */ |
| 44 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 45 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 45 | 46 | ||
| 46 | /// Input mix buffer offsets for each channel | 47 | /// Input mix buffer offsets for each channel |
| 47 | std::array<s16, MaxChannels> inputs; | 48 | std::array<s16, MaxChannels> inputs; |
| @@ -57,4 +58,4 @@ struct DelayCommand : ICommand { | |||
| 57 | bool effect_enabled; | 58 | bool effect_enabled; |
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 60 | } // namespace AudioCore::AudioRenderer | 61 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp index d2bfb67cc..ecfdfabc6 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp | |||
| @@ -3,11 +3,11 @@ | |||
| 3 | 3 | ||
| 4 | #include <numbers> | 4 | #include <numbers> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/effect/i3dl2_reverb.h" | 7 | #include "audio_core/renderer/command/effect/i3dl2_reverb.h" |
| 8 | #include "common/polyfill_ranges.h" | 8 | #include "common/polyfill_ranges.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MinDelayLineTimes{ | 12 | constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MinDelayLineTimes{ |
| 13 | 5.0f, | 13 | 5.0f, |
| @@ -394,7 +394,7 @@ static void ApplyI3dl2ReverbEffect(const I3dl2ReverbInfo::ParameterVersion1& par | |||
| 394 | } | 394 | } |
| 395 | } | 395 | } |
| 396 | 396 | ||
| 397 | void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 397 | void I3dl2ReverbCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 398 | std::string& string) { | 398 | std::string& string) { |
| 399 | string += fmt::format("I3dl2ReverbCommand\n\tenabled {} \n\tinputs: ", effect_enabled); | 399 | string += fmt::format("I3dl2ReverbCommand\n\tenabled {} \n\tinputs: ", effect_enabled); |
| 400 | for (u32 i = 0; i < parameter.channel_count; i++) { | 400 | for (u32 i = 0; i < parameter.channel_count; i++) { |
| @@ -407,7 +407,7 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& | |||
| 407 | string += "\n"; | 407 | string += "\n"; |
| 408 | } | 408 | } |
| 409 | 409 | ||
| 410 | void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { | 410 | void I3dl2ReverbCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 411 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; | 411 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; |
| 412 | std::array<std::span<s32>, MaxChannels> output_buffers{}; | 412 | std::array<std::span<s32>, MaxChannels> output_buffers{}; |
| 413 | 413 | ||
| @@ -431,8 +431,8 @@ void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 431 | processor.sample_count); | 431 | processor.sample_count); |
| 432 | } | 432 | } |
| 433 | 433 | ||
| 434 | bool I3dl2ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) { | 434 | bool I3dl2ReverbCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 435 | return true; | 435 | return true; |
| 436 | } | 436 | } |
| 437 | 437 | ||
| 438 | } // namespace AudioCore::AudioRenderer | 438 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.h b/src/audio_core/renderer/command/effect/i3dl2_reverb.h index 243877056..e4c538ae8 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.h +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/effect/i3dl2.h" | 10 | #include "audio_core/renderer/effect/i3dl2.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for a I3DL2Reverb effect. Apply a reverb to inputs mix buffer according to | 20 | * AudioRenderer command for a I3DL2Reverb effect. Apply a reverb to inputs mix buffer according to |
| 20 | * the I3DL2 spec, outputs receives the results. | 21 | * the I3DL2 spec, outputs receives the results. |
| @@ -26,14 +27,14 @@ struct I3dl2ReverbCommand : ICommand { | |||
| 26 | * @param processor - The CommandListProcessor processing this command. | 27 | * @param processor - The CommandListProcessor processing this command. |
| 27 | * @param string - The string to print into. | 28 | * @param string - The string to print into. |
| 28 | */ | 29 | */ |
| 29 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 30 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * Process this command. | 33 | * Process this command. |
| 33 | * | 34 | * |
| 34 | * @param processor - The CommandListProcessor processing this command. | 35 | * @param processor - The CommandListProcessor processing this command. |
| 35 | */ | 36 | */ |
| 36 | void Process(const ADSP::CommandListProcessor& processor) override; | 37 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Verify this command's data is valid. | 40 | * Verify this command's data is valid. |
| @@ -41,7 +42,7 @@ struct I3dl2ReverbCommand : ICommand { | |||
| 41 | * @param processor - The CommandListProcessor processing this command. | 42 | * @param processor - The CommandListProcessor processing this command. |
| 42 | * @return True if the command is valid, otherwise false. | 43 | * @return True if the command is valid, otherwise false. |
| 43 | */ | 44 | */ |
| 44 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 45 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 45 | 46 | ||
| 46 | /// Input mix buffer offsets for each channel | 47 | /// Input mix buffer offsets for each channel |
| 47 | std::array<s16, MaxChannels> inputs; | 48 | std::array<s16, MaxChannels> inputs; |
| @@ -57,4 +58,4 @@ struct I3dl2ReverbCommand : ICommand { | |||
| 57 | bool effect_enabled; | 58 | bool effect_enabled; |
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 60 | } // namespace AudioCore::AudioRenderer | 61 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp index 4161a9821..63aa06f5c 100644 --- a/src/audio_core/renderer/command/effect/light_limiter.cpp +++ b/src/audio_core/renderer/command/effect/light_limiter.cpp | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/effect/light_limiter.h" | 5 | #include "audio_core/renderer/command/effect/light_limiter.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | /** | 8 | /** |
| 9 | * Update the LightLimiterInfo state according to the given parameters. | 9 | * Update the LightLimiterInfo state according to the given parameters. |
| 10 | * A no-op. | 10 | * A no-op. |
| @@ -133,8 +133,8 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p | |||
| 133 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 136 | void LightLimiterVersion1Command::Dump( |
| 137 | std::string& string) { | 137 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 138 | string += fmt::format("LightLimiterVersion1Command\n\tinputs: "); | 138 | string += fmt::format("LightLimiterVersion1Command\n\tinputs: "); |
| 139 | for (u32 i = 0; i < MaxChannels; i++) { | 139 | for (u32 i = 0; i < MaxChannels; i++) { |
| 140 | string += fmt::format("{:02X}, ", inputs[i]); | 140 | string += fmt::format("{:02X}, ", inputs[i]); |
| @@ -146,7 +146,7 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP | |||
| 146 | string += "\n"; | 146 | string += "\n"; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { | 149 | void LightLimiterVersion1Command::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 150 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; | 150 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; |
| 151 | std::array<std::span<s32>, MaxChannels> output_buffers{}; | 151 | std::array<std::span<s32>, MaxChannels> output_buffers{}; |
| 152 | 152 | ||
| @@ -172,12 +172,12 @@ void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& proc | |||
| 172 | processor.sample_count, statistics); | 172 | processor.sample_count, statistics); |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | bool LightLimiterVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { | 175 | bool LightLimiterVersion1Command::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 176 | return true; | 176 | return true; |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 179 | void LightLimiterVersion2Command::Dump( |
| 180 | std::string& string) { | 180 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 181 | string += fmt::format("LightLimiterVersion2Command\n\tinputs: \n"); | 181 | string += fmt::format("LightLimiterVersion2Command\n\tinputs: \n"); |
| 182 | for (u32 i = 0; i < MaxChannels; i++) { | 182 | for (u32 i = 0; i < MaxChannels; i++) { |
| 183 | string += fmt::format("{:02X}, ", inputs[i]); | 183 | string += fmt::format("{:02X}, ", inputs[i]); |
| @@ -189,7 +189,7 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP | |||
| 189 | string += "\n"; | 189 | string += "\n"; |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { | 192 | void LightLimiterVersion2Command::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 193 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; | 193 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; |
| 194 | std::array<std::span<s32>, MaxChannels> output_buffers{}; | 194 | std::array<std::span<s32>, MaxChannels> output_buffers{}; |
| 195 | 195 | ||
| @@ -215,8 +215,8 @@ void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& proc | |||
| 215 | processor.sample_count, statistics); | 215 | processor.sample_count, statistics); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | bool LightLimiterVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { | 218 | bool LightLimiterVersion2Command::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 219 | return true; | 219 | return true; |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | } // namespace AudioCore::AudioRenderer | 222 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/light_limiter.h b/src/audio_core/renderer/command/effect/light_limiter.h index 5d98272c7..6e3ee1b53 100644 --- a/src/audio_core/renderer/command/effect/light_limiter.h +++ b/src/audio_core/renderer/command/effect/light_limiter.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/effect/light_limiter.h" | 10 | #include "audio_core/renderer/effect/light_limiter.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for limiting volume between a high and low threshold. | 20 | * AudioRenderer command for limiting volume between a high and low threshold. |
| 20 | * Version 1. | 21 | * Version 1. |
| @@ -26,14 +27,14 @@ struct LightLimiterVersion1Command : ICommand { | |||
| 26 | * @param processor - The CommandListProcessor processing this command. | 27 | * @param processor - The CommandListProcessor processing this command. |
| 27 | * @param string - The string to print into. | 28 | * @param string - The string to print into. |
| 28 | */ | 29 | */ |
| 29 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 30 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * Process this command. | 33 | * Process this command. |
| 33 | * | 34 | * |
| 34 | * @param processor - The CommandListProcessor processing this command. | 35 | * @param processor - The CommandListProcessor processing this command. |
| 35 | */ | 36 | */ |
| 36 | void Process(const ADSP::CommandListProcessor& processor) override; | 37 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Verify this command's data is valid. | 40 | * Verify this command's data is valid. |
| @@ -41,7 +42,7 @@ struct LightLimiterVersion1Command : ICommand { | |||
| 41 | * @param processor - The CommandListProcessor processing this command. | 42 | * @param processor - The CommandListProcessor processing this command. |
| 42 | * @return True if the command is valid, otherwise false. | 43 | * @return True if the command is valid, otherwise false. |
| 43 | */ | 44 | */ |
| 44 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 45 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 45 | 46 | ||
| 46 | /// Input mix buffer offsets for each channel | 47 | /// Input mix buffer offsets for each channel |
| 47 | std::array<s16, MaxChannels> inputs; | 48 | std::array<s16, MaxChannels> inputs; |
| @@ -68,21 +69,21 @@ struct LightLimiterVersion2Command : ICommand { | |||
| 68 | * @param processor - The CommandListProcessor processing this command. | 69 | * @param processor - The CommandListProcessor processing this command. |
| 69 | * @param string - The string to print into. | 70 | * @param string - The string to print into. |
| 70 | */ | 71 | */ |
| 71 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 72 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 72 | 73 | ||
| 73 | /** | 74 | /** |
| 74 | * Process this command. | 75 | * Process this command. |
| 75 | * | 76 | * |
| 76 | * @param processor - The CommandListProcessor processing this command. | 77 | * @param processor - The CommandListProcessor processing this command. |
| 77 | */ | 78 | */ |
| 78 | void Process(const ADSP::CommandListProcessor& processor) override; | 79 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 79 | 80 | ||
| 80 | /** | 81 | /** |
| 81 | * Verify this command's data is valid. | 82 | * Verify this command's data is valid. |
| 82 | * | 83 | * |
| 83 | * @param processor - The CommandListProcessor processing this command. | 84 | * @param processor - The CommandListProcessor processing this command. |
| 84 | */ | 85 | */ |
| 85 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 86 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 86 | 87 | ||
| 87 | /// Input mix buffer offsets for each channel | 88 | /// Input mix buffer offsets for each channel |
| 88 | std::array<s16, MaxChannels> inputs; | 89 | std::array<s16, MaxChannels> inputs; |
| @@ -100,4 +101,4 @@ struct LightLimiterVersion2Command : ICommand { | |||
| 100 | bool effect_enabled; | 101 | bool effect_enabled; |
| 101 | }; | 102 | }; |
| 102 | 103 | ||
| 103 | } // namespace AudioCore::AudioRenderer | 104 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp index 48a7cba8a..208bbeaf2 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp | |||
| @@ -1,20 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/effect/biquad_filter.h" | 5 | #include "audio_core/renderer/command/effect/biquad_filter.h" |
| 6 | #include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h" | 6 | #include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | void MultiTapBiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 10 | void MultiTapBiquadFilterCommand::Dump( |
| 11 | std::string& string) { | 11 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 12 | string += fmt::format( | 12 | string += fmt::format( |
| 13 | "MultiTapBiquadFilterCommand\n\tinput {:02X}\n\toutput {:02X}\n\tneeds_init ({}, {})\n", | 13 | "MultiTapBiquadFilterCommand\n\tinput {:02X}\n\toutput {:02X}\n\tneeds_init ({}, {})\n", |
| 14 | input, output, needs_init[0], needs_init[1]); | 14 | input, output, needs_init[0], needs_init[1]); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { | 17 | void MultiTapBiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 18 | if (filter_tap_count > MaxBiquadFilters) { | 18 | if (filter_tap_count > MaxBiquadFilters) { |
| 19 | LOG_ERROR(Service_Audio, "Too many filter taps! {}", filter_tap_count); | 19 | LOG_ERROR(Service_Audio, "Too many filter taps! {}", filter_tap_count); |
| 20 | filter_tap_count = MaxBiquadFilters; | 20 | filter_tap_count = MaxBiquadFilters; |
| @@ -38,8 +38,8 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc | |||
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | bool MultiTapBiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) { | 41 | bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 42 | return true; | 42 | return true; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | } // namespace AudioCore::AudioRenderer | 45 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h index 99c2c0830..50fce80b0 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/voice/voice_info.h" | 10 | #include "audio_core/renderer/voice/voice_info.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for applying multiple biquads at once. | 20 | * AudioRenderer command for applying multiple biquads at once. |
| 20 | */ | 21 | */ |
| @@ -25,14 +26,14 @@ struct MultiTapBiquadFilterCommand : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,7 +41,7 @@ struct MultiTapBiquadFilterCommand : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Input mix buffer index | 46 | /// Input mix buffer index |
| 46 | s16 input; | 47 | s16 input; |
| @@ -56,4 +57,4 @@ struct MultiTapBiquadFilterCommand : ICommand { | |||
| 56 | u8 filter_tap_count; | 57 | u8 filter_tap_count; |
| 57 | }; | 58 | }; |
| 58 | 59 | ||
| 59 | } // namespace AudioCore::AudioRenderer | 60 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index fc2f15a5e..7f152a962 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp | |||
| @@ -4,11 +4,11 @@ | |||
| 4 | #include <numbers> | 4 | #include <numbers> |
| 5 | #include <ranges> | 5 | #include <ranges> |
| 6 | 6 | ||
| 7 | #include "audio_core/renderer/adsp/command_list_processor.h" | 7 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 8 | #include "audio_core/renderer/command/effect/reverb.h" | 8 | #include "audio_core/renderer/command/effect/reverb.h" |
| 9 | #include "common/polyfill_ranges.h" | 9 | #include "common/polyfill_ranges.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | 12 | ||
| 13 | constexpr std::array<f32, ReverbInfo::MaxDelayLines> FdnMaxDelayLineTimes = { | 13 | constexpr std::array<f32, ReverbInfo::MaxDelayLines> FdnMaxDelayLineTimes = { |
| 14 | 53.9532470703125f, | 14 | 53.9532470703125f, |
| @@ -396,7 +396,7 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever | |||
| 396 | } | 396 | } |
| 397 | } | 397 | } |
| 398 | 398 | ||
| 399 | void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 399 | void ReverbCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 400 | std::string& string) { | 400 | std::string& string) { |
| 401 | string += fmt::format( | 401 | string += fmt::format( |
| 402 | "ReverbCommand\n\tenabled {} long_size_pre_delay_supported {}\n\tinputs: ", effect_enabled, | 402 | "ReverbCommand\n\tenabled {} long_size_pre_delay_supported {}\n\tinputs: ", effect_enabled, |
| @@ -411,7 +411,7 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc | |||
| 411 | string += "\n"; | 411 | string += "\n"; |
| 412 | } | 412 | } |
| 413 | 413 | ||
| 414 | void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { | 414 | void ReverbCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 415 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; | 415 | std::array<std::span<const s32>, MaxChannels> input_buffers{}; |
| 416 | std::array<std::span<s32>, MaxChannels> output_buffers{}; | 416 | std::array<std::span<s32>, MaxChannels> output_buffers{}; |
| 417 | 417 | ||
| @@ -435,8 +435,8 @@ void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 435 | processor.sample_count); | 435 | processor.sample_count); |
| 436 | } | 436 | } |
| 437 | 437 | ||
| 438 | bool ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) { | 438 | bool ReverbCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 439 | return true; | 439 | return true; |
| 440 | } | 440 | } |
| 441 | 441 | ||
| 442 | } // namespace AudioCore::AudioRenderer | 442 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/effect/reverb.h b/src/audio_core/renderer/command/effect/reverb.h index 328756150..2056c73f2 100644 --- a/src/audio_core/renderer/command/effect/reverb.h +++ b/src/audio_core/renderer/command/effect/reverb.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/effect/reverb.h" | 10 | #include "audio_core/renderer/effect/reverb.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for a Reverb effect. Apply a reverb to inputs mix buffer, outputs receives | 20 | * AudioRenderer command for a Reverb effect. Apply a reverb to inputs mix buffer, outputs receives |
| 20 | * the results. | 21 | * the results. |
| @@ -26,14 +27,14 @@ struct ReverbCommand : ICommand { | |||
| 26 | * @param processor - The CommandListProcessor processing this command. | 27 | * @param processor - The CommandListProcessor processing this command. |
| 27 | * @param string - The string to print into. | 28 | * @param string - The string to print into. |
| 28 | */ | 29 | */ |
| 29 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 30 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * Process this command. | 33 | * Process this command. |
| 33 | * | 34 | * |
| 34 | * @param processor - The CommandListProcessor processing this command. | 35 | * @param processor - The CommandListProcessor processing this command. |
| 35 | */ | 36 | */ |
| 36 | void Process(const ADSP::CommandListProcessor& processor) override; | 37 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Verify this command's data is valid. | 40 | * Verify this command's data is valid. |
| @@ -41,7 +42,7 @@ struct ReverbCommand : ICommand { | |||
| 41 | * @param processor - The CommandListProcessor processing this command. | 42 | * @param processor - The CommandListProcessor processing this command. |
| 42 | * @return True if the command is valid, otherwise false. | 43 | * @return True if the command is valid, otherwise false. |
| 43 | */ | 44 | */ |
| 44 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 45 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 45 | 46 | ||
| 46 | /// Input mix buffer offsets for each channel | 47 | /// Input mix buffer offsets for each channel |
| 47 | std::array<s16, MaxChannels> inputs; | 48 | std::array<s16, MaxChannels> inputs; |
| @@ -59,4 +60,4 @@ struct ReverbCommand : ICommand { | |||
| 59 | bool long_size_pre_delay_supported; | 60 | bool long_size_pre_delay_supported; |
| 60 | }; | 61 | }; |
| 61 | 62 | ||
| 62 | } // namespace AudioCore::AudioRenderer | 63 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/icommand.h b/src/audio_core/renderer/command/icommand.h index f2dd41254..10a78ddf2 100644 --- a/src/audio_core/renderer/command/icommand.h +++ b/src/audio_core/renderer/command/icommand.h | |||
| @@ -3,14 +3,18 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <string> | ||
| 7 | |||
| 6 | #include "audio_core/common/common.h" | 8 | #include "audio_core/common/common.h" |
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 9 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 10 | namespace ADSP { | ||
| 11 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 12 | } | 13 | } |
| 13 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | using namespace ::AudioCore::ADSP; | ||
| 17 | |||
| 14 | enum class CommandId : u8 { | 18 | enum class CommandId : u8 { |
| 15 | /* 0x00 */ Invalid, | 19 | /* 0x00 */ Invalid, |
| 16 | /* 0x01 */ DataSourcePcmInt16Version1, | 20 | /* 0x01 */ DataSourcePcmInt16Version1, |
| @@ -59,14 +63,15 @@ struct ICommand { | |||
| 59 | * @param processor - The CommandListProcessor processing this command. | 63 | * @param processor - The CommandListProcessor processing this command. |
| 60 | * @param string - The string to print into. | 64 | * @param string - The string to print into. |
| 61 | */ | 65 | */ |
| 62 | virtual void Dump(const ADSP::CommandListProcessor& processor, std::string& string) = 0; | 66 | virtual void Dump(const AudioRenderer::CommandListProcessor& processor, |
| 67 | std::string& string) = 0; | ||
| 63 | 68 | ||
| 64 | /** | 69 | /** |
| 65 | * Process this command. | 70 | * Process this command. |
| 66 | * | 71 | * |
| 67 | * @param processor - The CommandListProcessor processing this command. | 72 | * @param processor - The CommandListProcessor processing this command. |
| 68 | */ | 73 | */ |
| 69 | virtual void Process(const ADSP::CommandListProcessor& processor) = 0; | 74 | virtual void Process(const AudioRenderer::CommandListProcessor& processor) = 0; |
| 70 | 75 | ||
| 71 | /** | 76 | /** |
| 72 | * Verify this command's data is valid. | 77 | * Verify this command's data is valid. |
| @@ -74,7 +79,7 @@ struct ICommand { | |||
| 74 | * @param processor - The CommandListProcessor processing this command. | 79 | * @param processor - The CommandListProcessor processing this command. |
| 75 | * @return True if the command is valid, otherwise false. | 80 | * @return True if the command is valid, otherwise false. |
| 76 | */ | 81 | */ |
| 77 | virtual bool Verify(const ADSP::CommandListProcessor& processor) = 0; | 82 | virtual bool Verify(const AudioRenderer::CommandListProcessor& processor) = 0; |
| 78 | 83 | ||
| 79 | /// Command magic 0xCAFEBABE | 84 | /// Command magic 0xCAFEBABE |
| 80 | u32 magic{}; | 85 | u32 magic{}; |
| @@ -90,4 +95,4 @@ struct ICommand { | |||
| 90 | u32 node_id{}; | 95 | u32 node_id{}; |
| 91 | }; | 96 | }; |
| 92 | 97 | ||
| 93 | } // namespace AudioCore::AudioRenderer | 98 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/clear_mix.cpp b/src/audio_core/renderer/command/mix/clear_mix.cpp index 4f649d6a8..060d7cb28 100644 --- a/src/audio_core/renderer/command/mix/clear_mix.cpp +++ b/src/audio_core/renderer/command/mix/clear_mix.cpp | |||
| @@ -3,22 +3,22 @@ | |||
| 3 | 3 | ||
| 4 | #include <string> | 4 | #include <string> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/mix/clear_mix.h" | 7 | #include "audio_core/renderer/command/mix/clear_mix.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | void ClearMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 11 | void ClearMixBufferCommand::Dump( |
| 12 | std::string& string) { | 12 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 13 | string += fmt::format("ClearMixBufferCommand\n"); | 13 | string += fmt::format("ClearMixBufferCommand\n"); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | void ClearMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) { | 16 | void ClearMixBufferCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 17 | memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes()); | 17 | memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes()); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | bool ClearMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) { | 20 | bool ClearMixBufferCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 21 | return true; | 21 | return true; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | } // namespace AudioCore::AudioRenderer | 24 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/clear_mix.h b/src/audio_core/renderer/command/mix/clear_mix.h index 956ec0b65..650fa1a8a 100644 --- a/src/audio_core/renderer/command/mix/clear_mix.h +++ b/src/audio_core/renderer/command/mix/clear_mix.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for a clearing the mix buffers. | 18 | * AudioRenderer command for a clearing the mix buffers. |
| 18 | * Used at the start of each command list. | 19 | * Used at the start of each command list. |
| @@ -24,14 +25,14 @@ struct ClearMixBufferCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct ClearMixBufferCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | }; | 44 | }; |
| 44 | 45 | ||
| 45 | } // namespace AudioCore::AudioRenderer | 46 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/copy_mix.cpp b/src/audio_core/renderer/command/mix/copy_mix.cpp index 1d49f1644..5d386f95a 100644 --- a/src/audio_core/renderer/command/mix/copy_mix.cpp +++ b/src/audio_core/renderer/command/mix/copy_mix.cpp | |||
| @@ -1,18 +1,18 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/mix/copy_mix.h" | 5 | #include "audio_core/renderer/command/mix/copy_mix.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | void CopyMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 9 | void CopyMixBufferCommand::Dump( |
| 10 | std::string& string) { | 10 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 11 | string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index, | 11 | string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index, |
| 12 | output_index); | 12 | output_index); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) { | 15 | void CopyMixBufferCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 16 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, | 16 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 17 | processor.sample_count)}; | 17 | processor.sample_count)}; |
| 18 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, | 18 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, |
| @@ -20,8 +20,8 @@ void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) | |||
| 20 | std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32)); | 20 | std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32)); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | bool CopyMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) { | 23 | bool CopyMixBufferCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 24 | return true; | 24 | return true; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | } // namespace AudioCore::AudioRenderer | 27 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/copy_mix.h b/src/audio_core/renderer/command/mix/copy_mix.h index a59007fb6..ae247c3f8 100644 --- a/src/audio_core/renderer/command/mix/copy_mix.h +++ b/src/audio_core/renderer/command/mix/copy_mix.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for a copying a mix buffer from input to output. | 18 | * AudioRenderer command for a copying a mix buffer from input to output. |
| 18 | */ | 19 | */ |
| @@ -23,14 +24,14 @@ struct CopyMixBufferCommand : ICommand { | |||
| 23 | * @param processor - The CommandListProcessor processing this command. | 24 | * @param processor - The CommandListProcessor processing this command. |
| 24 | * @param string - The string to print into. | 25 | * @param string - The string to print into. |
| 25 | */ | 26 | */ |
| 26 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 27 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 27 | 28 | ||
| 28 | /** | 29 | /** |
| 29 | * Process this command. | 30 | * Process this command. |
| 30 | * | 31 | * |
| 31 | * @param processor - The CommandListProcessor processing this command. | 32 | * @param processor - The CommandListProcessor processing this command. |
| 32 | */ | 33 | */ |
| 33 | void Process(const ADSP::CommandListProcessor& processor) override; | 34 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| 36 | * Verify this command's data is valid. | 37 | * Verify this command's data is valid. |
| @@ -38,7 +39,7 @@ struct CopyMixBufferCommand : ICommand { | |||
| 38 | * @param processor - The CommandListProcessor processing this command. | 39 | * @param processor - The CommandListProcessor processing this command. |
| 39 | * @return True if the command is valid, otherwise false. | 40 | * @return True if the command is valid, otherwise false. |
| 40 | */ | 41 | */ |
| 41 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 42 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 42 | 43 | ||
| 43 | /// Input mix buffer index | 44 | /// Input mix buffer index |
| 44 | s16 input_index; | 45 | s16 input_index; |
| @@ -46,4 +47,4 @@ struct CopyMixBufferCommand : ICommand { | |||
| 46 | s16 output_index; | 47 | s16 output_index; |
| 47 | }; | 48 | }; |
| 48 | 49 | ||
| 49 | } // namespace AudioCore::AudioRenderer | 50 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp index c2bc10061..caedb56b7 100644 --- a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp +++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" | ||
| 4 | #include "audio_core/common/common.h" | 5 | #include "audio_core/common/common.h" |
| 5 | #include "audio_core/renderer/adsp/command_list_processor.h" | ||
| 6 | #include "audio_core/renderer/command/mix/depop_for_mix_buffers.h" | 6 | #include "audio_core/renderer/command/mix/depop_for_mix_buffers.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | /** | 9 | /** |
| 10 | * Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time | 10 | * Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time |
| 11 | * according to decay. | 11 | * according to decay. |
| @@ -36,13 +36,13 @@ static s32 ApplyDepopMix(std::span<s32> output, const s32 depop_sample, | |||
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 39 | void DepopForMixBuffersCommand::Dump( |
| 40 | std::string& string) { | 40 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 41 | string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input, | 41 | string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input, |
| 42 | count, decay.to_float()); | 42 | count, decay.to_float()); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) { | 45 | void DepopForMixBuffersCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 46 | auto end_index{std::min(processor.buffer_count, input + count)}; | 46 | auto end_index{std::min(processor.buffer_count, input + count)}; |
| 47 | std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index}; | 47 | std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index}; |
| 48 | 48 | ||
| @@ -57,8 +57,8 @@ void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& proces | |||
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | bool DepopForMixBuffersCommand::Verify(const ADSP::CommandListProcessor& processor) { | 60 | bool DepopForMixBuffersCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 61 | return true; | 61 | return true; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | } // namespace AudioCore::AudioRenderer | 64 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h index e7268ff27..699d38988 100644 --- a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h +++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/fixed_point.h" | 10 | #include "common/fixed_point.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::ADSP::AudioRenderer { |
| 13 | namespace ADSP { | ||
| 14 | class CommandListProcessor; | 13 | class CommandListProcessor; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 16 | namespace AudioCore::Renderer { | ||
| 17 | |||
| 17 | /** | 18 | /** |
| 18 | * AudioRenderer command for depopping a mix buffer. | 19 | * AudioRenderer command for depopping a mix buffer. |
| 19 | * Adds a cumulation of previous samples to the current mix buffer with a decay. | 20 | * Adds a cumulation of previous samples to the current mix buffer with a decay. |
| @@ -25,14 +26,14 @@ struct DepopForMixBuffersCommand : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,7 +41,7 @@ struct DepopForMixBuffersCommand : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Starting input mix buffer index | 46 | /// Starting input mix buffer index |
| 46 | u32 input; | 47 | u32 input; |
| @@ -52,4 +53,4 @@ struct DepopForMixBuffersCommand : ICommand { | |||
| 52 | CpuAddr depop_buffer; | 53 | CpuAddr depop_buffer; |
| 53 | }; | 54 | }; |
| 54 | 55 | ||
| 55 | } // namespace AudioCore::AudioRenderer | 56 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.cpp b/src/audio_core/renderer/command/mix/depop_prepare.cpp index 69bb78ccc..2faf4681a 100644 --- a/src/audio_core/renderer/command/mix/depop_prepare.cpp +++ b/src/audio_core/renderer/command/mix/depop_prepare.cpp | |||
| @@ -1,15 +1,15 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/mix/depop_prepare.h" | 5 | #include "audio_core/renderer/command/mix/depop_prepare.h" |
| 6 | #include "audio_core/renderer/voice/voice_state.h" | 6 | #include "audio_core/renderer/voice/voice_state.h" |
| 7 | #include "common/fixed_point.h" | 7 | #include "common/fixed_point.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 11 | void DepopPrepareCommand::Dump( |
| 12 | std::string& string) { | 12 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 13 | string += fmt::format("DepopPrepareCommand\n\tinputs: "); | 13 | string += fmt::format("DepopPrepareCommand\n\tinputs: "); |
| 14 | for (u32 i = 0; i < buffer_count; i++) { | 14 | for (u32 i = 0; i < buffer_count; i++) { |
| 15 | string += fmt::format("{:02X}, ", inputs[i]); | 15 | string += fmt::format("{:02X}, ", inputs[i]); |
| @@ -17,7 +17,7 @@ void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor | |||
| 17 | string += "\n"; | 17 | string += "\n"; |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) { | 20 | void DepopPrepareCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 21 | auto samples{reinterpret_cast<s32*>(previous_samples)}; | 21 | auto samples{reinterpret_cast<s32*>(previous_samples)}; |
| 22 | auto buffer{reinterpret_cast<s32*>(depop_buffer)}; | 22 | auto buffer{reinterpret_cast<s32*>(depop_buffer)}; |
| 23 | 23 | ||
| @@ -29,8 +29,8 @@ void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | bool DepopPrepareCommand::Verify(const ADSP::CommandListProcessor& processor) { | 32 | bool DepopPrepareCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 33 | return true; | 33 | return true; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | } // namespace AudioCore::AudioRenderer | 36 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.h b/src/audio_core/renderer/command/mix/depop_prepare.h index a5465da9a..161a94461 100644 --- a/src/audio_core/renderer/command/mix/depop_prepare.h +++ b/src/audio_core/renderer/command/mix/depop_prepare.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for preparing depop. | 18 | * AudioRenderer command for preparing depop. |
| 18 | * Adds the previusly output last samples to the depop buffer. | 19 | * Adds the previusly output last samples to the depop buffer. |
| @@ -24,14 +25,14 @@ struct DepopPrepareCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct DepopPrepareCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | 44 | ||
| 44 | /// Depop buffer offset for each mix buffer | 45 | /// Depop buffer offset for each mix buffer |
| 45 | std::array<s16, MaxMixBuffers> inputs; | 46 | std::array<s16, MaxMixBuffers> inputs; |
| @@ -51,4 +52,4 @@ struct DepopPrepareCommand : ICommand { | |||
| 51 | CpuAddr depop_buffer; | 52 | CpuAddr depop_buffer; |
| 52 | }; | 53 | }; |
| 53 | 54 | ||
| 54 | } // namespace AudioCore::AudioRenderer | 55 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/mix.cpp b/src/audio_core/renderer/command/mix/mix.cpp index 8ecf9b05a..8bd689b88 100644 --- a/src/audio_core/renderer/command/mix/mix.cpp +++ b/src/audio_core/renderer/command/mix/mix.cpp | |||
| @@ -5,11 +5,11 @@ | |||
| 5 | #include <limits> | 5 | #include <limits> |
| 6 | #include <span> | 6 | #include <span> |
| 7 | 7 | ||
| 8 | #include "audio_core/renderer/adsp/command_list_processor.h" | 8 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 9 | #include "audio_core/renderer/command/mix/mix.h" | 9 | #include "audio_core/renderer/command/mix/mix.h" |
| 10 | #include "common/fixed_point.h" | 10 | #include "common/fixed_point.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * Mix input mix buffer into output mix buffer, with volume applied to the input. | 14 | * Mix input mix buffer into output mix buffer, with volume applied to the input. |
| 15 | * | 15 | * |
| @@ -28,7 +28,7 @@ static void ApplyMix(std::span<s32> output, std::span<const s32> input, const f3 | |||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 31 | void MixCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 32 | std::string& string) { | 32 | std::string& string) { |
| 33 | string += fmt::format("MixCommand"); | 33 | string += fmt::format("MixCommand"); |
| 34 | string += fmt::format("\n\tinput {:02X}", input_index); | 34 | string += fmt::format("\n\tinput {:02X}", input_index); |
| @@ -37,7 +37,7 @@ void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& process | |||
| 37 | string += "\n"; | 37 | string += "\n"; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void MixCommand::Process(const ADSP::CommandListProcessor& processor) { | 40 | void MixCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 41 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, | 41 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 42 | processor.sample_count)}; | 42 | processor.sample_count)}; |
| 43 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, | 43 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, |
| @@ -63,8 +63,8 @@ void MixCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 63 | } | 63 | } |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | bool MixCommand::Verify(const ADSP::CommandListProcessor& processor) { | 66 | bool MixCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 67 | return true; | 67 | return true; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | } // namespace AudioCore::AudioRenderer | 70 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/mix.h b/src/audio_core/renderer/command/mix/mix.h index 0201cf171..64c812382 100644 --- a/src/audio_core/renderer/command/mix/mix.h +++ b/src/audio_core/renderer/command/mix/mix.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume | 18 | * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume |
| 18 | * applied to the input. | 19 | * applied to the input. |
| @@ -24,14 +25,14 @@ struct MixCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct MixCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | 44 | ||
| 44 | /// Fixed point precision | 45 | /// Fixed point precision |
| 45 | u8 precision; | 46 | u8 precision; |
| @@ -51,4 +52,4 @@ struct MixCommand : ICommand { | |||
| 51 | f32 volume; | 52 | f32 volume; |
| 52 | }; | 53 | }; |
| 53 | 54 | ||
| 54 | } // namespace AudioCore::AudioRenderer | 55 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.cpp b/src/audio_core/renderer/command/mix/mix_ramp.cpp index d67123cd8..2f6500da5 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp.cpp +++ b/src/audio_core/renderer/command/mix/mix_ramp.cpp | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/mix/mix_ramp.h" | 5 | #include "audio_core/renderer/command/mix/mix_ramp.h" |
| 6 | #include "common/fixed_point.h" | 6 | #include "common/fixed_point.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | template <size_t Q> | 11 | template <size_t Q> |
| 12 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, | 12 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, |
| @@ -33,7 +33,8 @@ s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 vo | |||
| 33 | template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32); | 33 | template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32); |
| 34 | template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32); | 34 | template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32); |
| 35 | 35 | ||
| 36 | void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { | 36 | void MixRampCommand::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 37 | std::string& string) { | ||
| 37 | const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; | 38 | const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; |
| 38 | string += fmt::format("MixRampCommand"); | 39 | string += fmt::format("MixRampCommand"); |
| 39 | string += fmt::format("\n\tinput {:02X}", input_index); | 40 | string += fmt::format("\n\tinput {:02X}", input_index); |
| @@ -44,7 +45,7 @@ void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::stri | |||
| 44 | string += "\n"; | 45 | string += "\n"; |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) { | 48 | void MixRampCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 48 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, | 49 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 49 | processor.sample_count)}; | 50 | processor.sample_count)}; |
| 50 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, | 51 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, |
| @@ -75,8 +76,8 @@ void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 75 | } | 76 | } |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | bool MixRampCommand::Verify(const ADSP::CommandListProcessor& processor) { | 79 | bool MixRampCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 79 | return true; | 80 | return true; |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | } // namespace AudioCore::AudioRenderer | 83 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.h b/src/audio_core/renderer/command/mix/mix_ramp.h index 52f74a273..92209b53a 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp.h +++ b/src/audio_core/renderer/command/mix/mix_ramp.h | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | #include "audio_core/renderer/command/icommand.h" | 9 | #include "audio_core/renderer/command/icommand.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::ADSP::AudioRenderer { |
| 13 | namespace ADSP { | ||
| 14 | class CommandListProcessor; | 13 | class CommandListProcessor; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 16 | namespace AudioCore::Renderer { | ||
| 17 | |||
| 17 | /** | 18 | /** |
| 18 | * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume | 19 | * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume |
| 19 | * applied to the input, and volume ramping to smooth out the transition. | 20 | * applied to the input, and volume ramping to smooth out the transition. |
| @@ -25,14 +26,14 @@ struct MixRampCommand : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,7 +41,7 @@ struct MixRampCommand : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Fixed point precision | 46 | /// Fixed point precision |
| 46 | u8 precision; | 47 | u8 precision; |
| @@ -70,4 +71,4 @@ template <size_t Q> | |||
| 70 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_, | 71 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_, |
| 71 | u32 sample_count); | 72 | u32 sample_count); |
| 72 | 73 | ||
| 73 | } // namespace AudioCore::AudioRenderer | 74 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp index 43dbef9fc..64138a9bf 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp +++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp | |||
| @@ -1,13 +1,14 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/mix/mix_ramp.h" | 5 | #include "audio_core/renderer/command/mix/mix_ramp.h" |
| 6 | #include "audio_core/renderer/command/mix/mix_ramp_grouped.h" | 6 | #include "audio_core/renderer/command/mix/mix_ramp_grouped.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { | 10 | void MixRampGroupedCommand::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 11 | std::string& string) { | ||
| 11 | string += "MixRampGroupedCommand"; | 12 | string += "MixRampGroupedCommand"; |
| 12 | for (u32 i = 0; i < buffer_count; i++) { | 13 | for (u32 i = 0; i < buffer_count; i++) { |
| 13 | string += fmt::format("\n\t{}", i); | 14 | string += fmt::format("\n\t{}", i); |
| @@ -21,7 +22,7 @@ void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, st | |||
| 21 | } | 22 | } |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) { | 25 | void MixRampGroupedCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 25 | std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers}; | 26 | std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers}; |
| 26 | 27 | ||
| 27 | for (u32 i = 0; i < buffer_count; i++) { | 28 | for (u32 i = 0; i < buffer_count; i++) { |
| @@ -58,8 +59,8 @@ void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) | |||
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | bool MixRampGroupedCommand::Verify(const ADSP::CommandListProcessor& processor) { | 62 | bool MixRampGroupedCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 62 | return true; | 63 | return true; |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | } // namespace AudioCore::AudioRenderer | 66 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h index 3b0ce67ef..9621e42a3 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h +++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | #include "audio_core/renderer/command/icommand.h" | 9 | #include "audio_core/renderer/command/icommand.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::ADSP::AudioRenderer { |
| 13 | namespace ADSP { | ||
| 14 | class CommandListProcessor; | 13 | class CommandListProcessor; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 16 | namespace AudioCore::Renderer { | ||
| 17 | |||
| 17 | /** | 18 | /** |
| 18 | * AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with | 19 | * AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with |
| 19 | * a volume applied to the input, and volume ramping to smooth out the transition. | 20 | * a volume applied to the input, and volume ramping to smooth out the transition. |
| @@ -25,14 +26,14 @@ struct MixRampGroupedCommand : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,7 +41,7 @@ struct MixRampGroupedCommand : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Fixed point precision | 46 | /// Fixed point precision |
| 46 | u8 precision; | 47 | u8 precision; |
| @@ -58,4 +59,4 @@ struct MixRampGroupedCommand : ICommand { | |||
| 58 | CpuAddr previous_samples; | 59 | CpuAddr previous_samples; |
| 59 | }; | 60 | }; |
| 60 | 61 | ||
| 61 | } // namespace AudioCore::AudioRenderer | 62 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/volume.cpp b/src/audio_core/renderer/command/mix/volume.cpp index b045fb062..92baf6cc3 100644 --- a/src/audio_core/renderer/command/mix/volume.cpp +++ b/src/audio_core/renderer/command/mix/volume.cpp | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/mix/volume.h" | 5 | #include "audio_core/renderer/command/mix/volume.h" |
| 6 | #include "common/fixed_point.h" | 6 | #include "common/fixed_point.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Apply volume to the input mix buffer, saving to the output buffer. | 11 | * Apply volume to the input mix buffer, saving to the output buffer. |
| 12 | * | 12 | * |
| @@ -29,7 +29,7 @@ static void ApplyUniformGain(std::span<s32> output, std::span<const s32> input, | |||
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 32 | void VolumeCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 33 | std::string& string) { | 33 | std::string& string) { |
| 34 | string += fmt::format("VolumeCommand"); | 34 | string += fmt::format("VolumeCommand"); |
| 35 | string += fmt::format("\n\tinput {:02X}", input_index); | 35 | string += fmt::format("\n\tinput {:02X}", input_index); |
| @@ -38,7 +38,7 @@ void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc | |||
| 38 | string += "\n"; | 38 | string += "\n"; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) { | 41 | void VolumeCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 42 | // If input and output buffers are the same, and the volume is 1.0f, this won't do | 42 | // If input and output buffers are the same, and the volume is 1.0f, this won't do |
| 43 | // anything, so just skip. | 43 | // anything, so just skip. |
| 44 | if (input_index == output_index && volume == 1.0f) { | 44 | if (input_index == output_index && volume == 1.0f) { |
| @@ -65,8 +65,8 @@ void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 65 | } | 65 | } |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | bool VolumeCommand::Verify(const ADSP::CommandListProcessor& processor) { | 68 | bool VolumeCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 69 | return true; | 69 | return true; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | } // namespace AudioCore::AudioRenderer | 72 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/volume.h b/src/audio_core/renderer/command/mix/volume.h index 6ae9fb794..fbb8156ca 100644 --- a/src/audio_core/renderer/command/mix/volume.h +++ b/src/audio_core/renderer/command/mix/volume.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for applying volume to a mix buffer. | 18 | * AudioRenderer command for applying volume to a mix buffer. |
| 18 | */ | 19 | */ |
| @@ -23,14 +24,14 @@ struct VolumeCommand : ICommand { | |||
| 23 | * @param processor - The CommandListProcessor processing this command. | 24 | * @param processor - The CommandListProcessor processing this command. |
| 24 | * @param string - The string to print into. | 25 | * @param string - The string to print into. |
| 25 | */ | 26 | */ |
| 26 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 27 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 27 | 28 | ||
| 28 | /** | 29 | /** |
| 29 | * Process this command. | 30 | * Process this command. |
| 30 | * | 31 | * |
| 31 | * @param processor - The CommandListProcessor processing this command. | 32 | * @param processor - The CommandListProcessor processing this command. |
| 32 | */ | 33 | */ |
| 33 | void Process(const ADSP::CommandListProcessor& processor) override; | 34 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| 36 | * Verify this command's data is valid. | 37 | * Verify this command's data is valid. |
| @@ -38,7 +39,7 @@ struct VolumeCommand : ICommand { | |||
| 38 | * @param processor - The CommandListProcessor processing this command. | 39 | * @param processor - The CommandListProcessor processing this command. |
| 39 | * @return True if the command is valid, otherwise false. | 40 | * @return True if the command is valid, otherwise false. |
| 40 | */ | 41 | */ |
| 41 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 42 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 42 | 43 | ||
| 43 | /// Fixed point precision | 44 | /// Fixed point precision |
| 44 | u8 precision; | 45 | u8 precision; |
| @@ -50,4 +51,4 @@ struct VolumeCommand : ICommand { | |||
| 50 | f32 volume; | 51 | f32 volume; |
| 51 | }; | 52 | }; |
| 52 | 53 | ||
| 53 | } // namespace AudioCore::AudioRenderer | 54 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/volume_ramp.cpp b/src/audio_core/renderer/command/mix/volume_ramp.cpp index 424307148..fdc751957 100644 --- a/src/audio_core/renderer/command/mix/volume_ramp.cpp +++ b/src/audio_core/renderer/command/mix/volume_ramp.cpp | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/mix/volume_ramp.h" | 5 | #include "audio_core/renderer/command/mix/volume_ramp.h" |
| 6 | #include "common/fixed_point.h" | 6 | #include "common/fixed_point.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | /** | 9 | /** |
| 10 | * Apply volume with ramping to the input mix buffer, saving to the output buffer. | 10 | * Apply volume with ramping to the input mix buffer, saving to the output buffer. |
| 11 | * | 11 | * |
| @@ -38,7 +38,8 @@ static void ApplyLinearEnvelopeGain(std::span<s32> output, std::span<const s32> | |||
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { | 41 | void VolumeRampCommand::Dump(const AudioRenderer::CommandListProcessor& processor, |
| 42 | std::string& string) { | ||
| 42 | const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; | 43 | const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; |
| 43 | string += fmt::format("VolumeRampCommand"); | 44 | string += fmt::format("VolumeRampCommand"); |
| 44 | string += fmt::format("\n\tinput {:02X}", input_index); | 45 | string += fmt::format("\n\tinput {:02X}", input_index); |
| @@ -49,7 +50,7 @@ void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::s | |||
| 49 | string += "\n"; | 50 | string += "\n"; |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) { | 53 | void VolumeRampCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 53 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, | 54 | auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, |
| 54 | processor.sample_count)}; | 55 | processor.sample_count)}; |
| 55 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, | 56 | auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, |
| @@ -77,8 +78,8 @@ void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 77 | } | 78 | } |
| 78 | } | 79 | } |
| 79 | 80 | ||
| 80 | bool VolumeRampCommand::Verify(const ADSP::CommandListProcessor& processor) { | 81 | bool VolumeRampCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 81 | return true; | 82 | return true; |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | } // namespace AudioCore::AudioRenderer | 85 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/mix/volume_ramp.h b/src/audio_core/renderer/command/mix/volume_ramp.h index 77b61547e..d9794fb95 100644 --- a/src/audio_core/renderer/command/mix/volume_ramp.h +++ b/src/audio_core/renderer/command/mix/volume_ramp.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth | 18 | * AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth |
| 18 | * out the transition. | 19 | * out the transition. |
| @@ -24,14 +25,14 @@ struct VolumeRampCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct VolumeRampCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | 44 | ||
| 44 | /// Fixed point precision | 45 | /// Fixed point precision |
| 45 | u8 precision; | 46 | u8 precision; |
| @@ -53,4 +54,4 @@ struct VolumeRampCommand : ICommand { | |||
| 53 | f32 volume; | 54 | f32 volume; |
| 54 | }; | 55 | }; |
| 55 | 56 | ||
| 56 | } // namespace AudioCore::AudioRenderer | 57 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp index 4a881547f..f0cfcc9fd 100644 --- a/src/audio_core/renderer/command/performance/performance.cpp +++ b/src/audio_core/renderer/command/performance/performance.cpp | |||
| @@ -1,25 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/performance/performance.h" | 5 | #include "audio_core/renderer/command/performance/performance.h" |
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | void PerformanceCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 11 | void PerformanceCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 12 | std::string& string) { | 12 | std::string& string) { |
| 13 | string += fmt::format("PerformanceCommand\n\tstate {}\n", static_cast<u32>(state)); | 13 | string += fmt::format("PerformanceCommand\n\tstate {}\n", static_cast<u32>(state)); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) { | 16 | void PerformanceCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 17 | auto base{entry_address.translated_address}; | 17 | auto base{entry_address.translated_address}; |
| 18 | if (state == PerformanceState::Start) { | 18 | if (state == PerformanceState::Start) { |
| 19 | auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; | 19 | auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; |
| 20 | *start_time_ptr = | 20 | *start_time_ptr = |
| 21 | static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - | 21 | static_cast<u32>(processor.system->CoreTiming().GetGlobalTimeUs().count() - |
| 22 | processor.current_processing_time); | 22 | processor.start_time - processor.current_processing_time); |
| 23 | } else if (state == PerformanceState::Stop) { | 23 | } else if (state == PerformanceState::Stop) { |
| 24 | auto processed_time_ptr{ | 24 | auto processed_time_ptr{ |
| 25 | reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; | 25 | reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; |
| @@ -27,14 +27,14 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 27 | reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; | 27 | reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; |
| 28 | 28 | ||
| 29 | *processed_time_ptr = | 29 | *processed_time_ptr = |
| 30 | static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - | 30 | static_cast<u32>(processor.system->CoreTiming().GetGlobalTimeUs().count() - |
| 31 | processor.current_processing_time); | 31 | processor.start_time - processor.current_processing_time); |
| 32 | (*entry_count_ptr)++; | 32 | (*entry_count_ptr)++; |
| 33 | } | 33 | } |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | bool PerformanceCommand::Verify(const ADSP::CommandListProcessor& processor) { | 36 | bool PerformanceCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 37 | return true; | 37 | return true; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | } // namespace AudioCore::AudioRenderer | 40 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/performance/performance.h b/src/audio_core/renderer/command/performance/performance.h index 11a7d6c08..522e51e34 100644 --- a/src/audio_core/renderer/command/performance/performance.h +++ b/src/audio_core/renderer/command/performance/performance.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/performance/performance_manager.h" | 10 | #include "audio_core/renderer/performance/performance_manager.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for writing AudioRenderer performance metrics back to the sysmodule. | 20 | * AudioRenderer command for writing AudioRenderer performance metrics back to the sysmodule. |
| 20 | */ | 21 | */ |
| @@ -25,14 +26,14 @@ struct PerformanceCommand : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,7 +41,7 @@ struct PerformanceCommand : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// State of the performance | 46 | /// State of the performance |
| 46 | PerformanceState state; | 47 | PerformanceState state; |
| @@ -48,4 +49,4 @@ struct PerformanceCommand : ICommand { | |||
| 48 | PerformanceEntryAddresses entry_address; | 49 | PerformanceEntryAddresses entry_address; |
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| 51 | } // namespace AudioCore::AudioRenderer | 52 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp index 1fd90308a..f9b289887 100644 --- a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp +++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/adsp/command_list_processor.h" | 4 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 5 | #include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h" | 5 | #include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 9 | void DownMix6chTo2chCommand::Dump( |
| 10 | std::string& string) { | 10 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 11 | string += fmt::format("DownMix6chTo2chCommand\n\tinputs: "); | 11 | string += fmt::format("DownMix6chTo2chCommand\n\tinputs: "); |
| 12 | for (u32 i = 0; i < MaxChannels; i++) { | 12 | for (u32 i = 0; i < MaxChannels; i++) { |
| 13 | string += fmt::format("{:02X}, ", inputs[i]); | 13 | string += fmt::format("{:02X}, ", inputs[i]); |
| @@ -19,7 +19,7 @@ void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProces | |||
| 19 | string += "\n"; | 19 | string += "\n"; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor) { | 22 | void DownMix6chTo2chCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 23 | auto in_front_left{ | 23 | auto in_front_left{ |
| 24 | processor.mix_buffers.subspan(inputs[0] * processor.sample_count, processor.sample_count)}; | 24 | processor.mix_buffers.subspan(inputs[0] * processor.sample_count, processor.sample_count)}; |
| 25 | auto in_front_right{ | 25 | auto in_front_right{ |
| @@ -67,8 +67,8 @@ void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor | |||
| 67 | std::memset(out_back_right.data(), 0, out_back_right.size_bytes()); | 67 | std::memset(out_back_right.data(), 0, out_back_right.size_bytes()); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | bool DownMix6chTo2chCommand::Verify(const ADSP::CommandListProcessor& processor) { | 70 | bool DownMix6chTo2chCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 71 | return true; | 71 | return true; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | } // namespace AudioCore::AudioRenderer | 74 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h index dc133a73b..96cbc5506 100644 --- a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h +++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/fixed_point.h" | 10 | #include "common/fixed_point.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::ADSP::AudioRenderer { |
| 13 | namespace ADSP { | ||
| 14 | class CommandListProcessor; | 13 | class CommandListProcessor; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 16 | namespace AudioCore::Renderer { | ||
| 17 | |||
| 17 | /** | 18 | /** |
| 18 | * AudioRenderer command for downmixing 6 channels to 2. | 19 | * AudioRenderer command for downmixing 6 channels to 2. |
| 19 | * Channel layout (SMPTE): | 20 | * Channel layout (SMPTE): |
| @@ -31,14 +32,14 @@ struct DownMix6chTo2chCommand : ICommand { | |||
| 31 | * @param processor - The CommandListProcessor processing this command. | 32 | * @param processor - The CommandListProcessor processing this command. |
| 32 | * @param string - The string to print into. | 33 | * @param string - The string to print into. |
| 33 | */ | 34 | */ |
| 34 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 35 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Process this command. | 38 | * Process this command. |
| 38 | * | 39 | * |
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | */ | 41 | */ |
| 41 | void Process(const ADSP::CommandListProcessor& processor) override; | 42 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 42 | 43 | ||
| 43 | /** | 44 | /** |
| 44 | * Verify this command's data is valid. | 45 | * Verify this command's data is valid. |
| @@ -46,7 +47,7 @@ struct DownMix6chTo2chCommand : ICommand { | |||
| 46 | * @param processor - The CommandListProcessor processing this command. | 47 | * @param processor - The CommandListProcessor processing this command. |
| 47 | * @return True if the command is valid, otherwise false. | 48 | * @return True if the command is valid, otherwise false. |
| 48 | */ | 49 | */ |
| 49 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 50 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 50 | 51 | ||
| 51 | /// Input mix buffer offsets for each channel | 52 | /// Input mix buffer offsets for each channel |
| 52 | std::array<s16, MaxChannels> inputs; | 53 | std::array<s16, MaxChannels> inputs; |
| @@ -56,4 +57,4 @@ struct DownMix6chTo2chCommand : ICommand { | |||
| 56 | std::array<Common::FixedPoint<48, 16>, 4> down_mix_coeff; | 57 | std::array<Common::FixedPoint<48, 16>, 4> down_mix_coeff; |
| 57 | }; | 58 | }; |
| 58 | 59 | ||
| 59 | } // namespace AudioCore::AudioRenderer | 60 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/resample/resample.cpp b/src/audio_core/renderer/command/resample/resample.cpp index 070c9d2b8..51f4ba39e 100644 --- a/src/audio_core/renderer/command/resample/resample.cpp +++ b/src/audio_core/renderer/command/resample/resample.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/command/resample/resample.h" | 4 | #include "audio_core/renderer/command/resample/resample.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input, | 8 | static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input, |
| 9 | const Common::FixedPoint<49, 15>& sample_rate_ratio, | 9 | const Common::FixedPoint<49, 15>& sample_rate_ratio, |
| @@ -880,4 +880,4 @@ void Resample(std::span<s32> output, std::span<const s16> input, | |||
| 880 | } | 880 | } |
| 881 | } | 881 | } |
| 882 | 882 | ||
| 883 | } // namespace AudioCore::AudioRenderer | 883 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/resample/resample.h b/src/audio_core/renderer/command/resample/resample.h index ba9209b82..134aff0c9 100644 --- a/src/audio_core/renderer/command/resample/resample.h +++ b/src/audio_core/renderer/command/resample/resample.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/fixed_point.h" | 10 | #include "common/fixed_point.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * Resample an input buffer into an output buffer, according to the sample_rate_ratio. | 14 | * Resample an input buffer into an output buffer, according to the sample_rate_ratio. |
| 15 | * | 15 | * |
| @@ -26,4 +26,4 @@ void Resample(std::span<s32> output, std::span<const s16> input, | |||
| 26 | const Common::FixedPoint<49, 15>& sample_rate_ratio, | 26 | const Common::FixedPoint<49, 15>& sample_rate_ratio, |
| 27 | Common::FixedPoint<49, 15>& fraction, u32 samples_to_write, SrcQuality src_quality); | 27 | Common::FixedPoint<49, 15>& fraction, u32 samples_to_write, SrcQuality src_quality); |
| 28 | 28 | ||
| 29 | } // namespace AudioCore::AudioRenderer | 29 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp index 86ddee1a4..691d70390 100644 --- a/src/audio_core/renderer/command/resample/upsample.cpp +++ b/src/audio_core/renderer/command/resample/upsample.cpp | |||
| @@ -3,11 +3,11 @@ | |||
| 3 | 3 | ||
| 4 | #include <array> | 4 | #include <array> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/resample/upsample.h" | 7 | #include "audio_core/renderer/command/resample/upsample.h" |
| 8 | #include "audio_core/renderer/upsampler/upsampler_info.h" | 8 | #include "audio_core/renderer/upsampler/upsampler_info.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | /** | 11 | /** |
| 12 | * Upsampling impl. Input must be 8K, 16K or 32K, output is 48K. | 12 | * Upsampling impl. Input must be 8K, 16K or 32K, output is 48K. |
| 13 | * | 13 | * |
| @@ -198,7 +198,7 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, | |||
| 198 | } | 198 | } |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 201 | auto UpsampleCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 202 | std::string& string) -> void { | 202 | std::string& string) -> void { |
| 203 | string += fmt::format("UpsampleCommand\n\tsource_sample_count {} source_sample_rate {}", | 203 | string += fmt::format("UpsampleCommand\n\tsource_sample_count {} source_sample_rate {}", |
| 204 | source_sample_count, source_sample_rate); | 204 | source_sample_count, source_sample_rate); |
| @@ -213,7 +213,7 @@ auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& pr | |||
| 213 | string += "\n"; | 213 | string += "\n"; |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) { | 216 | void UpsampleCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 217 | const auto info{reinterpret_cast<UpsamplerInfo*>(upsampler_info)}; | 217 | const auto info{reinterpret_cast<UpsamplerInfo*>(upsampler_info)}; |
| 218 | const auto input_count{std::min(info->input_count, buffer_count)}; | 218 | const auto input_count{std::min(info->input_count, buffer_count)}; |
| 219 | const std::span<const s16> inputs_{reinterpret_cast<const s16*>(inputs), input_count}; | 219 | const std::span<const s16> inputs_{reinterpret_cast<const s16*>(inputs), input_count}; |
| @@ -234,8 +234,8 @@ void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 234 | } | 234 | } |
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | bool UpsampleCommand::Verify(const ADSP::CommandListProcessor& processor) { | 237 | bool UpsampleCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 238 | return true; | 238 | return true; |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | } // namespace AudioCore::AudioRenderer | 241 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/resample/upsample.h b/src/audio_core/renderer/command/resample/upsample.h index bfc94e8af..877271ba9 100644 --- a/src/audio_core/renderer/command/resample/upsample.h +++ b/src/audio_core/renderer/command/resample/upsample.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for upsampling a mix buffer to 48Khz. | 18 | * AudioRenderer command for upsampling a mix buffer to 48Khz. |
| 18 | * Input must be 8Khz, 16Khz or 32Khz, and output will be 48Khz. | 19 | * Input must be 8Khz, 16Khz or 32Khz, and output will be 48Khz. |
| @@ -24,14 +25,14 @@ struct UpsampleCommand : ICommand { | |||
| 24 | * @param processor - The CommandListProcessor processing this command. | 25 | * @param processor - The CommandListProcessor processing this command. |
| 25 | * @param string - The string to print into. | 26 | * @param string - The string to print into. |
| 26 | */ | 27 | */ |
| 27 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 28 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 28 | 29 | ||
| 29 | /** | 30 | /** |
| 30 | * Process this command. | 31 | * Process this command. |
| 31 | * | 32 | * |
| 32 | * @param processor - The CommandListProcessor processing this command. | 33 | * @param processor - The CommandListProcessor processing this command. |
| 33 | */ | 34 | */ |
| 34 | void Process(const ADSP::CommandListProcessor& processor) override; | 35 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | * Verify this command's data is valid. | 38 | * Verify this command's data is valid. |
| @@ -39,7 +40,7 @@ struct UpsampleCommand : ICommand { | |||
| 39 | * @param processor - The CommandListProcessor processing this command. | 40 | * @param processor - The CommandListProcessor processing this command. |
| 40 | * @return True if the command is valid, otherwise false. | 41 | * @return True if the command is valid, otherwise false. |
| 41 | */ | 42 | */ |
| 42 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 43 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 43 | 44 | ||
| 44 | /// Pointer to the output samples buffer. | 45 | /// Pointer to the output samples buffer. |
| 45 | CpuAddr samples_buffer; | 46 | CpuAddr samples_buffer; |
| @@ -57,4 +58,4 @@ struct UpsampleCommand : ICommand { | |||
| 57 | CpuAddr upsampler_info; | 58 | CpuAddr upsampler_info; |
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 60 | } // namespace AudioCore::AudioRenderer | 61 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp index e2ce59792..e056d15a6 100644 --- a/src/audio_core/renderer/command/sink/circular_buffer.cpp +++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp | |||
| @@ -3,14 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | #include <vector> | 4 | #include <vector> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/sink/circular_buffer.h" | 7 | #include "audio_core/renderer/command/sink/circular_buffer.h" |
| 8 | #include "core/memory.h" | 8 | #include "core/memory.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 12 | void CircularBufferSinkCommand::Dump( |
| 13 | std::string& string) { | 13 | [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { |
| 14 | string += fmt::format( | 14 | string += fmt::format( |
| 15 | "CircularBufferSinkCommand\n\tinput_count {} ring size {:04X} ring pos {:04X}\n\tinputs: ", | 15 | "CircularBufferSinkCommand\n\tinput_count {} ring size {:04X} ring pos {:04X}\n\tinputs: ", |
| 16 | input_count, size, pos); | 16 | input_count, size, pos); |
| @@ -20,7 +20,7 @@ void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListPro | |||
| 20 | string += "\n"; | 20 | string += "\n"; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& processor) { | 23 | void CircularBufferSinkCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 24 | constexpr s32 min{std::numeric_limits<s16>::min()}; | 24 | constexpr s32 min{std::numeric_limits<s16>::min()}; |
| 25 | constexpr s32 max{std::numeric_limits<s16>::max()}; | 25 | constexpr s32 max{std::numeric_limits<s16>::max()}; |
| 26 | 26 | ||
| @@ -41,8 +41,8 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces | |||
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | bool CircularBufferSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { | 44 | bool CircularBufferSinkCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 45 | return true; | 45 | return true; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | } // namespace AudioCore::AudioRenderer | 48 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.h b/src/audio_core/renderer/command/sink/circular_buffer.h index e7d5be26e..a3234a406 100644 --- a/src/audio_core/renderer/command/sink/circular_buffer.h +++ b/src/audio_core/renderer/command/sink/circular_buffer.h | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | #include "audio_core/renderer/command/icommand.h" | 8 | #include "audio_core/renderer/command/icommand.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::ADSP::AudioRenderer { |
| 12 | namespace ADSP { | ||
| 13 | class CommandListProcessor; | 12 | class CommandListProcessor; |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 15 | namespace AudioCore::Renderer { | ||
| 16 | |||
| 16 | /** | 17 | /** |
| 17 | * AudioRenderer command for sinking samples to a circular buffer. | 18 | * AudioRenderer command for sinking samples to a circular buffer. |
| 18 | */ | 19 | */ |
| @@ -23,14 +24,14 @@ struct CircularBufferSinkCommand : ICommand { | |||
| 23 | * @param processor - The CommandListProcessor processing this command. | 24 | * @param processor - The CommandListProcessor processing this command. |
| 24 | * @param string - The string to print into. | 25 | * @param string - The string to print into. |
| 25 | */ | 26 | */ |
| 26 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 27 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 27 | 28 | ||
| 28 | /** | 29 | /** |
| 29 | * Process this command. | 30 | * Process this command. |
| 30 | * | 31 | * |
| 31 | * @param processor - The CommandListProcessor processing this command. | 32 | * @param processor - The CommandListProcessor processing this command. |
| 32 | */ | 33 | */ |
| 33 | void Process(const ADSP::CommandListProcessor& processor) override; | 34 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| 36 | * Verify this command's data is valid. | 37 | * Verify this command's data is valid. |
| @@ -38,7 +39,7 @@ struct CircularBufferSinkCommand : ICommand { | |||
| 38 | * @param processor - The CommandListProcessor processing this command. | 39 | * @param processor - The CommandListProcessor processing this command. |
| 39 | * @return True if the command is valid, otherwise false. | 40 | * @return True if the command is valid, otherwise false. |
| 40 | */ | 41 | */ |
| 41 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 42 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 42 | 43 | ||
| 43 | /// Number of input mix buffers | 44 | /// Number of input mix buffers |
| 44 | u32 input_count; | 45 | u32 input_count; |
| @@ -52,4 +53,4 @@ struct CircularBufferSinkCommand : ICommand { | |||
| 52 | u32 pos; | 53 | u32 pos; |
| 53 | }; | 54 | }; |
| 54 | 55 | ||
| 55 | } // namespace AudioCore::AudioRenderer | 56 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp index 5f74dd7ad..3480ed475 100644 --- a/src/audio_core/renderer/command/sink/device.cpp +++ b/src/audio_core/renderer/command/sink/device.cpp | |||
| @@ -3,13 +3,13 @@ | |||
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | 5 | ||
| 6 | #include "audio_core/renderer/adsp/command_list_processor.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" |
| 7 | #include "audio_core/renderer/command/sink/device.h" | 7 | #include "audio_core/renderer/command/sink/device.h" |
| 8 | #include "audio_core/sink/sink.h" | 8 | #include "audio_core/sink/sink.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | 12 | void DeviceSinkCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, |
| 13 | std::string& string) { | 13 | std::string& string) { |
| 14 | string += fmt::format("DeviceSinkCommand\n\t{} session {} input_count {}\n\tinputs: ", | 14 | string += fmt::format("DeviceSinkCommand\n\t{} session {} input_count {}\n\tinputs: ", |
| 15 | std::string_view(name), session_id, input_count); | 15 | std::string_view(name), session_id, input_count); |
| @@ -19,7 +19,7 @@ void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& | |||
| 19 | string += "\n"; | 19 | string += "\n"; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { | 22 | void DeviceSinkCommand::Process(const AudioRenderer::CommandListProcessor& processor) { |
| 23 | constexpr s32 min = std::numeric_limits<s16>::min(); | 23 | constexpr s32 min = std::numeric_limits<s16>::min(); |
| 24 | constexpr s32 max = std::numeric_limits<s16>::max(); | 24 | constexpr s32 max = std::numeric_limits<s16>::max(); |
| 25 | 25 | ||
| @@ -51,8 +51,8 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 51 | } | 51 | } |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | bool DeviceSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { | 54 | bool DeviceSinkCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { |
| 55 | return true; | 55 | return true; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | } // namespace AudioCore::AudioRenderer | 58 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/command/sink/device.h b/src/audio_core/renderer/command/sink/device.h index 1099bcf8c..385b51ecc 100644 --- a/src/audio_core/renderer/command/sink/device.h +++ b/src/audio_core/renderer/command/sink/device.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "audio_core/renderer/command/icommand.h" | 10 | #include "audio_core/renderer/command/icommand.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::ADSP::AudioRenderer { |
| 14 | namespace ADSP { | ||
| 15 | class CommandListProcessor; | 14 | class CommandListProcessor; |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 17 | namespace AudioCore::Renderer { | ||
| 18 | |||
| 18 | /** | 19 | /** |
| 19 | * AudioRenderer command for sinking samples to an output device. | 20 | * AudioRenderer command for sinking samples to an output device. |
| 20 | */ | 21 | */ |
| @@ -25,14 +26,14 @@ struct DeviceSinkCommand : ICommand { | |||
| 25 | * @param processor - The CommandListProcessor processing this command. | 26 | * @param processor - The CommandListProcessor processing this command. |
| 26 | * @param string - The string to print into. | 27 | * @param string - The string to print into. |
| 27 | */ | 28 | */ |
| 28 | void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | 29 | void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Process this command. | 32 | * Process this command. |
| 32 | * | 33 | * |
| 33 | * @param processor - The CommandListProcessor processing this command. | 34 | * @param processor - The CommandListProcessor processing this command. |
| 34 | */ | 35 | */ |
| 35 | void Process(const ADSP::CommandListProcessor& processor) override; | 36 | void Process(const AudioRenderer::CommandListProcessor& processor) override; |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Verify this command's data is valid. | 39 | * Verify this command's data is valid. |
| @@ -40,7 +41,7 @@ struct DeviceSinkCommand : ICommand { | |||
| 40 | * @param processor - The CommandListProcessor processing this command. | 41 | * @param processor - The CommandListProcessor processing this command. |
| 41 | * @return True if the command is valid, otherwise false. | 42 | * @return True if the command is valid, otherwise false. |
| 42 | */ | 43 | */ |
| 43 | bool Verify(const ADSP::CommandListProcessor& processor) override; | 44 | bool Verify(const AudioRenderer::CommandListProcessor& processor) override; |
| 44 | 45 | ||
| 45 | /// Device name | 46 | /// Device name |
| 46 | char name[0x100]; | 47 | char name[0x100]; |
| @@ -54,4 +55,4 @@ struct DeviceSinkCommand : ICommand { | |||
| 54 | std::array<s16, MaxChannels> inputs; | 55 | std::array<s16, MaxChannels> inputs; |
| 55 | }; | 56 | }; |
| 56 | 57 | ||
| 57 | } // namespace AudioCore::AudioRenderer | 58 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/aux_.cpp b/src/audio_core/renderer/effect/aux_.cpp index 51e780ef1..1c1411eff 100644 --- a/src/audio_core/renderer/effect/aux_.cpp +++ b/src/audio_core/renderer/effect/aux_.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/aux_.h" | 4 | #include "audio_core/renderer/effect/aux_.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | 8 | void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, |
| 9 | const PoolMapper& pool_mapper) { | 9 | const PoolMapper& pool_mapper) { |
| @@ -90,4 +90,4 @@ CpuAddr AuxInfo::GetWorkbuffer(s32 index) { | |||
| 90 | return workbuffers[index].GetReference(true); | 90 | return workbuffers[index].GetReference(true); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | } // namespace AudioCore::AudioRenderer | 93 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/aux_.h b/src/audio_core/renderer/effect/aux_.h index 4d3d9e3d9..c5b3058da 100644 --- a/src/audio_core/renderer/effect/aux_.h +++ b/src/audio_core/renderer/effect/aux_.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | 9 | #include "audio_core/renderer/effect/effect_info_base.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * Auxiliary Buffer used for Aux commands. | 14 | * Auxiliary Buffer used for Aux commands. |
| 15 | * Send and return buffers are available (names from the game's perspective). | 15 | * Send and return buffers are available (names from the game's perspective). |
| @@ -120,4 +120,4 @@ public: | |||
| 120 | CpuAddr GetWorkbuffer(s32 index) override; | 120 | CpuAddr GetWorkbuffer(s32 index) override; |
| 121 | }; | 121 | }; |
| 122 | 122 | ||
| 123 | } // namespace AudioCore::AudioRenderer | 123 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/biquad_filter.cpp b/src/audio_core/renderer/effect/biquad_filter.cpp index a1efb3231..08161d840 100644 --- a/src/audio_core/renderer/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/effect/biquad_filter.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/biquad_filter.h" | 4 | #include "audio_core/renderer/effect/biquad_filter.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | 8 | void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, |
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { |
| @@ -49,4 +49,4 @@ void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {} | |||
| 49 | void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, | 49 | void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, |
| 50 | EffectResultState& dsp_state) {} | 50 | EffectResultState& dsp_state) {} |
| 51 | 51 | ||
| 52 | } // namespace AudioCore::AudioRenderer | 52 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/biquad_filter.h b/src/audio_core/renderer/effect/biquad_filter.h index f53fd5bab..5a22899ab 100644 --- a/src/audio_core/renderer/effect/biquad_filter.h +++ b/src/audio_core/renderer/effect/biquad_filter.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | 9 | #include "audio_core/renderer/effect/effect_info_base.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | 13 | ||
| 14 | class BiquadFilterInfo : public EffectInfoBase { | 14 | class BiquadFilterInfo : public EffectInfoBase { |
| 15 | public: | 15 | public: |
| @@ -76,4 +76,4 @@ public: | |||
| 76 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | 76 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; |
| 77 | }; | 77 | }; |
| 78 | 78 | ||
| 79 | } // namespace AudioCore::AudioRenderer | 79 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/buffer_mixer.cpp b/src/audio_core/renderer/effect/buffer_mixer.cpp index 9c8877f01..826e246ec 100644 --- a/src/audio_core/renderer/effect/buffer_mixer.cpp +++ b/src/audio_core/renderer/effect/buffer_mixer.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/buffer_mixer.h" | 4 | #include "audio_core/renderer/effect/buffer_mixer.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, | 8 | void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, |
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { |
| @@ -46,4 +46,4 @@ void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {} | |||
| 46 | void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, | 46 | void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, |
| 47 | EffectResultState& dsp_state) {} | 47 | EffectResultState& dsp_state) {} |
| 48 | 48 | ||
| 49 | } // namespace AudioCore::AudioRenderer | 49 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/buffer_mixer.h b/src/audio_core/renderer/effect/buffer_mixer.h index 23eed4a8b..0c01ef38d 100644 --- a/src/audio_core/renderer/effect/buffer_mixer.h +++ b/src/audio_core/renderer/effect/buffer_mixer.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | 9 | #include "audio_core/renderer/effect/effect_info_base.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | 13 | ||
| 14 | class BufferMixerInfo : public EffectInfoBase { | 14 | class BufferMixerInfo : public EffectInfoBase { |
| 15 | public: | 15 | public: |
| @@ -72,4 +72,4 @@ public: | |||
| 72 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | 72 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; |
| 73 | }; | 73 | }; |
| 74 | 74 | ||
| 75 | } // namespace AudioCore::AudioRenderer | 75 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/capture.cpp b/src/audio_core/renderer/effect/capture.cpp index 3f038efdb..dfa062a59 100644 --- a/src/audio_core/renderer/effect/capture.cpp +++ b/src/audio_core/renderer/effect/capture.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include "audio_core/renderer/effect/aux_.h" | 4 | #include "audio_core/renderer/effect/aux_.h" |
| 5 | #include "audio_core/renderer/effect/capture.h" | 5 | #include "audio_core/renderer/effect/capture.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | 9 | void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, |
| 10 | const PoolMapper& pool_mapper) { | 10 | const PoolMapper& pool_mapper) { |
| @@ -79,4 +79,4 @@ CpuAddr CaptureInfo::GetWorkbuffer(s32 index) { | |||
| 79 | return workbuffers[index].GetReference(true); | 79 | return workbuffers[index].GetReference(true); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | } // namespace AudioCore::AudioRenderer | 82 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/capture.h b/src/audio_core/renderer/effect/capture.h index 6fbed8e6b..cbe71e22a 100644 --- a/src/audio_core/renderer/effect/capture.h +++ b/src/audio_core/renderer/effect/capture.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | 9 | #include "audio_core/renderer/effect/effect_info_base.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | 13 | ||
| 14 | class CaptureInfo : public EffectInfoBase { | 14 | class CaptureInfo : public EffectInfoBase { |
| 15 | public: | 15 | public: |
| @@ -62,4 +62,4 @@ public: | |||
| 62 | CpuAddr GetWorkbuffer(s32 index) override; | 62 | CpuAddr GetWorkbuffer(s32 index) override; |
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| 65 | } // namespace AudioCore::AudioRenderer | 65 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/compressor.cpp b/src/audio_core/renderer/effect/compressor.cpp index 220ae02f9..fea0aefcf 100644 --- a/src/audio_core/renderer/effect/compressor.cpp +++ b/src/audio_core/renderer/effect/compressor.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/compressor.h" | 4 | #include "audio_core/renderer/effect/compressor.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, | 8 | void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, |
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} | 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} |
| @@ -37,4 +37,4 @@ CpuAddr CompressorInfo::GetWorkbuffer(s32 index) { | |||
| 37 | return GetSingleBuffer(index); | 37 | return GetSingleBuffer(index); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | } // namespace AudioCore::AudioRenderer | 40 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/compressor.h b/src/audio_core/renderer/effect/compressor.h index 019a5ae58..cda55c284 100644 --- a/src/audio_core/renderer/effect/compressor.h +++ b/src/audio_core/renderer/effect/compressor.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/fixed_point.h" | 11 | #include "common/fixed_point.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | 14 | ||
| 15 | class CompressorInfo : public EffectInfoBase { | 15 | class CompressorInfo : public EffectInfoBase { |
| 16 | public: | 16 | public: |
| @@ -103,4 +103,4 @@ public: | |||
| 103 | CpuAddr GetWorkbuffer(s32 index) override; | 103 | CpuAddr GetWorkbuffer(s32 index) override; |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | } // namespace AudioCore::AudioRenderer | 106 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/delay.cpp b/src/audio_core/renderer/effect/delay.cpp index d9853efd9..e038d4498 100644 --- a/src/audio_core/renderer/effect/delay.cpp +++ b/src/audio_core/renderer/effect/delay.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/delay.h" | 4 | #include "audio_core/renderer/effect/delay.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | 8 | void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, |
| 9 | const PoolMapper& pool_mapper) { | 9 | const PoolMapper& pool_mapper) { |
| @@ -90,4 +90,4 @@ CpuAddr DelayInfo::GetWorkbuffer(s32 index) { | |||
| 90 | return GetSingleBuffer(index); | 90 | return GetSingleBuffer(index); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | } // namespace AudioCore::AudioRenderer | 93 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/delay.h b/src/audio_core/renderer/effect/delay.h index accc42a06..47417fbc6 100644 --- a/src/audio_core/renderer/effect/delay.h +++ b/src/audio_core/renderer/effect/delay.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fixed_point.h" | 12 | #include "common/fixed_point.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::Renderer { |
| 15 | 15 | ||
| 16 | class DelayInfo : public EffectInfoBase { | 16 | class DelayInfo : public EffectInfoBase { |
| 17 | public: | 17 | public: |
| @@ -132,4 +132,4 @@ public: | |||
| 132 | CpuAddr GetWorkbuffer(s32 index) override; | 132 | CpuAddr GetWorkbuffer(s32 index) override; |
| 133 | }; | 133 | }; |
| 134 | 134 | ||
| 135 | } // namespace AudioCore::AudioRenderer | 135 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/effect_context.cpp b/src/audio_core/renderer/effect/effect_context.cpp index 74c7801c9..00f6d7822 100644 --- a/src/audio_core/renderer/effect/effect_context.cpp +++ b/src/audio_core/renderer/effect/effect_context.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/effect_context.h" | 4 | #include "audio_core/renderer/effect/effect_context.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, | 8 | void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, |
| 9 | std::span<EffectResultState> result_states_cpu_, | 9 | std::span<EffectResultState> result_states_cpu_, |
| @@ -38,4 +38,4 @@ void EffectContext::UpdateStateByDspShared() { | |||
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | } // namespace AudioCore::AudioRenderer | 41 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/effect_context.h b/src/audio_core/renderer/effect/effect_context.h index 8f6d6e7d8..8364c5521 100644 --- a/src/audio_core/renderer/effect/effect_context.h +++ b/src/audio_core/renderer/effect/effect_context.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/effect/effect_result_state.h" | 9 | #include "audio_core/renderer/effect/effect_result_state.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | 13 | ||
| 14 | class EffectContext { | 14 | class EffectContext { |
| 15 | public: | 15 | public: |
| @@ -72,4 +72,4 @@ private: | |||
| 72 | size_t dsp_state_count{}; | 72 | size_t dsp_state_count{}; |
| 73 | }; | 73 | }; |
| 74 | 74 | ||
| 75 | } // namespace AudioCore::AudioRenderer | 75 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h index dbdccf278..b49503409 100644 --- a/src/audio_core/renderer/effect/effect_info_base.h +++ b/src/audio_core/renderer/effect/effect_info_base.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include "audio_core/renderer/memory/pool_mapper.h" | 12 | #include "audio_core/renderer/memory/pool_mapper.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | 14 | ||
| 15 | namespace AudioCore::AudioRenderer { | 15 | namespace AudioCore::Renderer { |
| 16 | /** | 16 | /** |
| 17 | * Base of all effects. Holds various data and functions used for all derived effects. | 17 | * Base of all effects. Holds various data and functions used for all derived effects. |
| 18 | * Should not be used directly. | 18 | * Should not be used directly. |
| @@ -432,4 +432,4 @@ protected: | |||
| 432 | std::array<u8, sizeof(State)> state{}; | 432 | std::array<u8, sizeof(State)> state{}; |
| 433 | }; | 433 | }; |
| 434 | 434 | ||
| 435 | } // namespace AudioCore::AudioRenderer | 435 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/effect_reset.h b/src/audio_core/renderer/effect/effect_reset.h index 1ea67e334..c9e3b4b78 100644 --- a/src/audio_core/renderer/effect/effect_reset.h +++ b/src/audio_core/renderer/effect/effect_reset.h | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #include "audio_core/renderer/effect/reverb.h" | 14 | #include "audio_core/renderer/effect/reverb.h" |
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | 16 | ||
| 17 | namespace AudioCore::AudioRenderer { | 17 | namespace AudioCore::Renderer { |
| 18 | /** | 18 | /** |
| 19 | * Reset an effect, and create a new one of the given type. | 19 | * Reset an effect, and create a new one of the given type. |
| 20 | * | 20 | * |
| @@ -68,4 +68,4 @@ static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type) | |||
| 68 | } | 68 | } |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | } // namespace AudioCore::AudioRenderer | 71 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/effect_result_state.h b/src/audio_core/renderer/effect/effect_result_state.h index ae096ad69..f4d4b6086 100644 --- a/src/audio_core/renderer/effect/effect_result_state.h +++ b/src/audio_core/renderer/effect/effect_result_state.h | |||
| @@ -7,10 +7,10 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | struct EffectResultState { | 12 | struct EffectResultState { |
| 13 | std::array<u8, 0x80> state; | 13 | std::array<u8, 0x80> state; |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | } // namespace AudioCore::AudioRenderer | 16 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/i3dl2.cpp b/src/audio_core/renderer/effect/i3dl2.cpp index 960b29cfc..a3c324c1e 100644 --- a/src/audio_core/renderer/effect/i3dl2.cpp +++ b/src/audio_core/renderer/effect/i3dl2.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/i3dl2.h" | 4 | #include "audio_core/renderer/effect/i3dl2.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, | 8 | void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, |
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { |
| @@ -91,4 +91,4 @@ CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) { | |||
| 91 | return GetSingleBuffer(index); | 91 | return GetSingleBuffer(index); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | } // namespace AudioCore::AudioRenderer | 94 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/i3dl2.h b/src/audio_core/renderer/effect/i3dl2.h index 6e3ffd1d4..e0432b4ae 100644 --- a/src/audio_core/renderer/effect/i3dl2.h +++ b/src/audio_core/renderer/effect/i3dl2.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fixed_point.h" | 12 | #include "common/fixed_point.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::Renderer { |
| 15 | 15 | ||
| 16 | class I3dl2ReverbInfo : public EffectInfoBase { | 16 | class I3dl2ReverbInfo : public EffectInfoBase { |
| 17 | public: | 17 | public: |
| @@ -198,4 +198,4 @@ public: | |||
| 198 | CpuAddr GetWorkbuffer(s32 index) override; | 198 | CpuAddr GetWorkbuffer(s32 index) override; |
| 199 | }; | 199 | }; |
| 200 | 200 | ||
| 201 | } // namespace AudioCore::AudioRenderer | 201 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/light_limiter.cpp b/src/audio_core/renderer/effect/light_limiter.cpp index 1635a952d..9c8ea3c49 100644 --- a/src/audio_core/renderer/effect/light_limiter.cpp +++ b/src/audio_core/renderer/effect/light_limiter.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/light_limiter.h" | 4 | #include "audio_core/renderer/effect/light_limiter.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | 8 | void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, |
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { |
| @@ -78,4 +78,4 @@ CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) { | |||
| 78 | return GetSingleBuffer(index); | 78 | return GetSingleBuffer(index); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | } // namespace AudioCore::AudioRenderer | 81 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/light_limiter.h b/src/audio_core/renderer/effect/light_limiter.h index 338d67bbc..7f2ede405 100644 --- a/src/audio_core/renderer/effect/light_limiter.h +++ b/src/audio_core/renderer/effect/light_limiter.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fixed_point.h" | 12 | #include "common/fixed_point.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::Renderer { |
| 15 | 15 | ||
| 16 | class LightLimiterInfo : public EffectInfoBase { | 16 | class LightLimiterInfo : public EffectInfoBase { |
| 17 | public: | 17 | public: |
| @@ -135,4 +135,4 @@ public: | |||
| 135 | CpuAddr GetWorkbuffer(s32 index) override; | 135 | CpuAddr GetWorkbuffer(s32 index) override; |
| 136 | }; | 136 | }; |
| 137 | 137 | ||
| 138 | } // namespace AudioCore::AudioRenderer | 138 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/reverb.cpp b/src/audio_core/renderer/effect/reverb.cpp index 2d32383d0..4da72469a 100644 --- a/src/audio_core/renderer/effect/reverb.cpp +++ b/src/audio_core/renderer/effect/reverb.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/effect/reverb.h" | 4 | #include "audio_core/renderer/effect/reverb.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | 8 | void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, |
| 9 | const PoolMapper& pool_mapper) { | 9 | const PoolMapper& pool_mapper) { |
| @@ -90,4 +90,4 @@ CpuAddr ReverbInfo::GetWorkbuffer(s32 index) { | |||
| 90 | return GetSingleBuffer(index); | 90 | return GetSingleBuffer(index); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | } // namespace AudioCore::AudioRenderer | 93 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/effect/reverb.h b/src/audio_core/renderer/effect/reverb.h index 6cc345ef6..52a048da6 100644 --- a/src/audio_core/renderer/effect/reverb.h +++ b/src/audio_core/renderer/effect/reverb.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fixed_point.h" | 12 | #include "common/fixed_point.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::Renderer { |
| 15 | 15 | ||
| 16 | class ReverbInfo : public EffectInfoBase { | 16 | class ReverbInfo : public EffectInfoBase { |
| 17 | public: | 17 | public: |
| @@ -187,4 +187,4 @@ public: | |||
| 187 | CpuAddr GetWorkbuffer(s32 index) override; | 187 | CpuAddr GetWorkbuffer(s32 index) override; |
| 188 | }; | 188 | }; |
| 189 | 189 | ||
| 190 | } // namespace AudioCore::AudioRenderer | 190 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/memory/address_info.h b/src/audio_core/renderer/memory/address_info.h index bb5c930e1..c81ef1b8a 100644 --- a/src/audio_core/renderer/memory/address_info.h +++ b/src/audio_core/renderer/memory/address_info.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/memory/memory_pool_info.h" | 6 | #include "audio_core/renderer/memory/memory_pool_info.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | /** | 11 | /** |
| 12 | * Represents a region of mapped or unmapped memory. | 12 | * Represents a region of mapped or unmapped memory. |
| @@ -121,4 +121,4 @@ private: | |||
| 121 | CpuAddr dsp_address; | 121 | CpuAddr dsp_address; |
| 122 | }; | 122 | }; |
| 123 | 123 | ||
| 124 | } // namespace AudioCore::AudioRenderer | 124 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/memory/memory_pool_info.cpp b/src/audio_core/renderer/memory/memory_pool_info.cpp index 9b7824af1..03b44d5f3 100644 --- a/src/audio_core/renderer/memory/memory_pool_info.cpp +++ b/src/audio_core/renderer/memory/memory_pool_info.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/memory/memory_pool_info.h" | 4 | #include "audio_core/renderer/memory/memory_pool_info.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | CpuAddr MemoryPoolInfo::GetCpuAddress() const { | 8 | CpuAddr MemoryPoolInfo::GetCpuAddress() const { |
| 9 | return cpu_address; | 9 | return cpu_address; |
| @@ -58,4 +58,4 @@ bool MemoryPoolInfo::IsUsed() const { | |||
| 58 | return in_use; | 58 | return in_use; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | } // namespace AudioCore::AudioRenderer | 61 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/memory/memory_pool_info.h b/src/audio_core/renderer/memory/memory_pool_info.h index 80c571bc1..2f9c85184 100644 --- a/src/audio_core/renderer/memory/memory_pool_info.h +++ b/src/audio_core/renderer/memory/memory_pool_info.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "audio_core/common/common.h" | 8 | #include "audio_core/common/common.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | /** | 12 | /** |
| 13 | * CPU pools are mapped in user memory with the supplied process_handle (see PoolMapper). | 13 | * CPU pools are mapped in user memory with the supplied process_handle (see PoolMapper). |
| 14 | */ | 14 | */ |
| @@ -167,4 +167,4 @@ private: | |||
| 167 | bool in_use{}; | 167 | bool in_use{}; |
| 168 | }; | 168 | }; |
| 169 | 169 | ||
| 170 | } // namespace AudioCore::AudioRenderer | 170 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/memory/pool_mapper.cpp b/src/audio_core/renderer/memory/pool_mapper.cpp index 7fd2b5f47..999bb746b 100644 --- a/src/audio_core/renderer/memory/pool_mapper.cpp +++ b/src/audio_core/renderer/memory/pool_mapper.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "core/hle/kernel/k_process.h" | 6 | #include "core/hle/kernel/k_process.h" |
| 7 | #include "core/hle/kernel/svc.h" | 7 | #include "core/hle/kernel/svc.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | PoolMapper::PoolMapper(u32 process_handle_, bool force_map_) | 11 | PoolMapper::PoolMapper(u32 process_handle_, bool force_map_) |
| 12 | : process_handle{process_handle_}, force_map{force_map_} {} | 12 | : process_handle{process_handle_}, force_map{force_map_} {} |
| @@ -240,4 +240,4 @@ bool PoolMapper::InitializeSystemPool(MemoryPoolInfo& pool, const u8* memory, | |||
| 240 | } | 240 | } |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | } // namespace AudioCore::AudioRenderer | 243 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/memory/pool_mapper.h b/src/audio_core/renderer/memory/pool_mapper.h index 9a691da7a..95ae5d8ea 100644 --- a/src/audio_core/renderer/memory/pool_mapper.h +++ b/src/audio_core/renderer/memory/pool_mapper.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/hle/service/audio/errors.h" | 11 | #include "core/hle/service/audio/errors.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | class AddressInfo; | 14 | class AddressInfo; |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| @@ -176,4 +176,4 @@ private: | |||
| 176 | bool force_map; | 176 | bool force_map; |
| 177 | }; | 177 | }; |
| 178 | 178 | ||
| 179 | } // namespace AudioCore::AudioRenderer | 179 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp index 3a18ae7c2..c712610bb 100644 --- a/src/audio_core/renderer/mix/mix_context.cpp +++ b/src/audio_core/renderer/mix/mix_context.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include "audio_core/renderer/splitter/splitter_context.h" | 7 | #include "audio_core/renderer/splitter/splitter_context.h" |
| 8 | #include "common/polyfill_ranges.h" | 8 | #include "common/polyfill_ranges.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | void MixContext::Initialize(std::span<MixInfo*> sorted_mix_infos_, std::span<MixInfo> mix_infos_, | 12 | void MixContext::Initialize(std::span<MixInfo*> sorted_mix_infos_, std::span<MixInfo> mix_infos_, |
| 13 | const u32 count_, std::span<s32> effect_process_order_buffer_, | 13 | const u32 count_, std::span<s32> effect_process_order_buffer_, |
| @@ -139,4 +139,4 @@ EdgeMatrix& MixContext::GetEdgeMatrix() { | |||
| 139 | return edge_matrix; | 139 | return edge_matrix; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | } // namespace AudioCore::AudioRenderer | 142 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/mix/mix_context.h b/src/audio_core/renderer/mix/mix_context.h index bcd9637da..ce19ec8d6 100644 --- a/src/audio_core/renderer/mix/mix_context.h +++ b/src/audio_core/renderer/mix/mix_context.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "audio_core/renderer/nodes/node_states.h" | 10 | #include "audio_core/renderer/nodes/node_states.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | class SplitterContext; | 14 | class SplitterContext; |
| 15 | 15 | ||
| 16 | /* | 16 | /* |
| @@ -121,4 +121,4 @@ private: | |||
| 121 | EdgeMatrix edge_matrix{}; | 121 | EdgeMatrix edge_matrix{}; |
| 122 | }; | 122 | }; |
| 123 | 123 | ||
| 124 | } // namespace AudioCore::AudioRenderer | 124 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/mix/mix_info.cpp b/src/audio_core/renderer/mix/mix_info.cpp index cc18e57ee..5e44bde18 100644 --- a/src/audio_core/renderer/mix/mix_info.cpp +++ b/src/audio_core/renderer/mix/mix_info.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include "audio_core/renderer/nodes/edge_matrix.h" | 7 | #include "audio_core/renderer/nodes/edge_matrix.h" |
| 8 | #include "audio_core/renderer/splitter/splitter_context.h" | 8 | #include "audio_core/renderer/splitter/splitter_context.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior) | 12 | MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior) |
| 13 | : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_}, | 13 | : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_}, |
| @@ -117,4 +117,4 @@ bool MixInfo::HasAnyConnection() const { | |||
| 117 | return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId; | 117 | return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | } // namespace AudioCore::AudioRenderer | 120 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/mix/mix_info.h b/src/audio_core/renderer/mix/mix_info.h index b5fa4c0c7..7005daa4f 100644 --- a/src/audio_core/renderer/mix/mix_info.h +++ b/src/audio_core/renderer/mix/mix_info.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/common/common.h" | 9 | #include "audio_core/common/common.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | class EdgeMatrix; | 13 | class EdgeMatrix; |
| 14 | class SplitterContext; | 14 | class SplitterContext; |
| 15 | class EffectContext; | 15 | class EffectContext; |
| @@ -121,4 +121,4 @@ public: | |||
| 121 | const bool long_size_pre_delay_supported; | 121 | const bool long_size_pre_delay_supported; |
| 122 | }; | 122 | }; |
| 123 | 123 | ||
| 124 | } // namespace AudioCore::AudioRenderer | 124 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/nodes/bit_array.h b/src/audio_core/renderer/nodes/bit_array.h index b0d53cd51..d8a2d09d0 100644 --- a/src/audio_core/renderer/nodes/bit_array.h +++ b/src/audio_core/renderer/nodes/bit_array.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | /** | 11 | /** |
| 12 | * Represents an array of bits used for nodes and edges for the mixing graph. | 12 | * Represents an array of bits used for nodes and edges for the mixing graph. |
| 13 | */ | 13 | */ |
| @@ -22,4 +22,4 @@ struct BitArray { | |||
| 22 | u32 size{}; | 22 | u32 size{}; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | } // namespace AudioCore::AudioRenderer | 25 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/nodes/edge_matrix.cpp b/src/audio_core/renderer/nodes/edge_matrix.cpp index 5573f33b9..c28773b22 100644 --- a/src/audio_core/renderer/nodes/edge_matrix.cpp +++ b/src/audio_core/renderer/nodes/edge_matrix.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/nodes/edge_matrix.h" | 4 | #include "audio_core/renderer/nodes/edge_matrix.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void EdgeMatrix::Initialize([[maybe_unused]] std::span<u8> buffer, | 8 | void EdgeMatrix::Initialize([[maybe_unused]] std::span<u8> buffer, |
| 9 | [[maybe_unused]] const u64 node_buffer_size, const u32 count_) { | 9 | [[maybe_unused]] const u64 node_buffer_size, const u32 count_) { |
| @@ -35,4 +35,4 @@ u32 EdgeMatrix::GetNodeCount() const { | |||
| 35 | return count; | 35 | return count; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | } // namespace AudioCore::AudioRenderer | 38 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/nodes/edge_matrix.h b/src/audio_core/renderer/nodes/edge_matrix.h index 27a20e43e..0271c23b1 100644 --- a/src/audio_core/renderer/nodes/edge_matrix.h +++ b/src/audio_core/renderer/nodes/edge_matrix.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "common/alignment.h" | 9 | #include "common/alignment.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * An edge matrix, holding the connections for each node to every other node in the graph. | 14 | * An edge matrix, holding the connections for each node to every other node in the graph. |
| 15 | */ | 15 | */ |
| @@ -79,4 +79,4 @@ private: | |||
| 79 | u32 count; | 79 | u32 count; |
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | } // namespace AudioCore::AudioRenderer | 82 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp index b7a44a54c..028a58041 100644 --- a/src/audio_core/renderer/nodes/node_states.cpp +++ b/src/audio_core/renderer/nodes/node_states.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include "audio_core/renderer/nodes/node_states.h" | 4 | #include "audio_core/renderer/nodes/node_states.h" |
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | void NodeStates::Initialize(std::span<u8> buffer_, [[maybe_unused]] const u64 node_buffer_size, | 9 | void NodeStates::Initialize(std::span<u8> buffer_, [[maybe_unused]] const u64 node_buffer_size, |
| 10 | const u32 count) { | 10 | const u32 count) { |
| @@ -138,4 +138,4 @@ std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls( | |||
| 138 | return {results.rbegin(), result_pos}; | 138 | return {results.rbegin(), result_pos}; |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | } // namespace AudioCore::AudioRenderer | 141 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h index e768cd4b5..991a82841 100644 --- a/src/audio_core/renderer/nodes/node_states.h +++ b/src/audio_core/renderer/nodes/node_states.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | /** | 14 | /** |
| 15 | * Graph utility functions for sorting and getting results from the DAG. | 15 | * Graph utility functions for sorting and getting results from the DAG. |
| 16 | */ | 16 | */ |
| @@ -192,4 +192,4 @@ private: | |||
| 192 | Stack stack{}; | 192 | Stack stack{}; |
| 193 | }; | 193 | }; |
| 194 | 194 | ||
| 195 | } // namespace AudioCore::AudioRenderer | 195 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/detail_aspect.cpp b/src/audio_core/renderer/performance/detail_aspect.cpp index f6405937f..ef8b47cee 100644 --- a/src/audio_core/renderer/performance/detail_aspect.cpp +++ b/src/audio_core/renderer/performance/detail_aspect.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include "audio_core/renderer/command/command_generator.h" | 5 | #include "audio_core/renderer/command/command_generator.h" |
| 6 | #include "audio_core/renderer/performance/detail_aspect.h" | 6 | #include "audio_core/renderer/performance/detail_aspect.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | DetailAspect::DetailAspect(CommandGenerator& command_generator_, | 10 | DetailAspect::DetailAspect(CommandGenerator& command_generator_, |
| 11 | const PerformanceEntryType entry_type, const s32 node_id_, | 11 | const PerformanceEntryType entry_type, const s32 node_id_, |
| @@ -22,4 +22,4 @@ DetailAspect::DetailAspect(CommandGenerator& command_generator_, | |||
| 22 | } | 22 | } |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | } // namespace AudioCore::AudioRenderer | 25 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/detail_aspect.h b/src/audio_core/renderer/performance/detail_aspect.h index 736c331b9..0bd7f80c8 100644 --- a/src/audio_core/renderer/performance/detail_aspect.h +++ b/src/audio_core/renderer/performance/detail_aspect.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include "audio_core/renderer/performance/performance_manager.h" | 7 | #include "audio_core/renderer/performance/performance_manager.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | class CommandGenerator; | 11 | class CommandGenerator; |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| @@ -29,4 +29,4 @@ public: | |||
| 29 | s32 node_id; | 29 | s32 node_id; |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | } // namespace AudioCore::AudioRenderer | 32 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/entry_aspect.cpp b/src/audio_core/renderer/performance/entry_aspect.cpp index dd4165803..c9241a639 100644 --- a/src/audio_core/renderer/performance/entry_aspect.cpp +++ b/src/audio_core/renderer/performance/entry_aspect.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include "audio_core/renderer/command/command_generator.h" | 5 | #include "audio_core/renderer/command/command_generator.h" |
| 6 | #include "audio_core/renderer/performance/entry_aspect.h" | 6 | #include "audio_core/renderer/performance/entry_aspect.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | EntryAspect::EntryAspect(CommandGenerator& command_generator_, const PerformanceEntryType type, | 10 | EntryAspect::EntryAspect(CommandGenerator& command_generator_, const PerformanceEntryType type, |
| 11 | const s32 node_id_) | 11 | const s32 node_id_) |
| @@ -20,4 +20,4 @@ EntryAspect::EntryAspect(CommandGenerator& command_generator_, const Performance | |||
| 20 | } | 20 | } |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | } // namespace AudioCore::AudioRenderer | 23 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/entry_aspect.h b/src/audio_core/renderer/performance/entry_aspect.h index 14c9e3baf..f99287d68 100644 --- a/src/audio_core/renderer/performance/entry_aspect.h +++ b/src/audio_core/renderer/performance/entry_aspect.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include "audio_core/renderer/performance/performance_manager.h" | 7 | #include "audio_core/renderer/performance/performance_manager.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | class CommandGenerator; | 11 | class CommandGenerator; |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| @@ -28,4 +28,4 @@ public: | |||
| 28 | s32 node_id; | 28 | s32 node_id; |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | } // namespace AudioCore::AudioRenderer | 31 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/performance_detail.h b/src/audio_core/renderer/performance/performance_detail.h index f603b9026..2b0cf9422 100644 --- a/src/audio_core/renderer/performance/performance_detail.h +++ b/src/audio_core/renderer/performance/performance_detail.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/performance/performance_entry.h" | 6 | #include "audio_core/renderer/performance/performance_entry.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | enum class PerformanceDetailType : u8 { | 11 | enum class PerformanceDetailType : u8 { |
| 12 | Invalid, | 12 | Invalid, |
| @@ -47,4 +47,4 @@ struct PerformanceDetailVersion2 { | |||
| 47 | static_assert(sizeof(PerformanceDetailVersion2) == 0x18, | 47 | static_assert(sizeof(PerformanceDetailVersion2) == 0x18, |
| 48 | "PerformanceDetailVersion2 has the wrong size!"); | 48 | "PerformanceDetailVersion2 has the wrong size!"); |
| 49 | 49 | ||
| 50 | } // namespace AudioCore::AudioRenderer | 50 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/performance_entry.h b/src/audio_core/renderer/performance/performance_entry.h index d6b1158db..dbd6053a5 100644 --- a/src/audio_core/renderer/performance/performance_entry.h +++ b/src/audio_core/renderer/performance/performance_entry.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | enum class PerformanceEntryType : u8 { | 10 | enum class PerformanceEntryType : u8 { |
| 11 | Invalid, | 11 | Invalid, |
| @@ -34,4 +34,4 @@ struct PerformanceEntryVersion2 { | |||
| 34 | static_assert(sizeof(PerformanceEntryVersion2) == 0x18, | 34 | static_assert(sizeof(PerformanceEntryVersion2) == 0x18, |
| 35 | "PerformanceEntryVersion2 has the wrong size!"); | 35 | "PerformanceEntryVersion2 has the wrong size!"); |
| 36 | 36 | ||
| 37 | } // namespace AudioCore::AudioRenderer | 37 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/performance_entry_addresses.h b/src/audio_core/renderer/performance/performance_entry_addresses.h index e381d765c..51eee975f 100644 --- a/src/audio_core/renderer/performance/performance_entry_addresses.h +++ b/src/audio_core/renderer/performance/performance_entry_addresses.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "audio_core/common/common.h" | 6 | #include "audio_core/common/common.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | struct PerformanceEntryAddresses { | 10 | struct PerformanceEntryAddresses { |
| 11 | CpuAddr translated_address; | 11 | CpuAddr translated_address; |
| @@ -14,4 +14,4 @@ struct PerformanceEntryAddresses { | |||
| 14 | CpuAddr entry_processed_time_offset; | 14 | CpuAddr entry_processed_time_offset; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | } // namespace AudioCore::AudioRenderer | 17 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/performance_frame_header.h b/src/audio_core/renderer/performance/performance_frame_header.h index b1848284e..24e4989f8 100644 --- a/src/audio_core/renderer/performance/performance_frame_header.h +++ b/src/audio_core/renderer/performance/performance_frame_header.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | struct PerformanceFrameHeaderVersion1 { | 10 | struct PerformanceFrameHeaderVersion1 { |
| 11 | /* 0x00 */ u32 magic; // "PERF" | 11 | /* 0x00 */ u32 magic; // "PERF" |
| @@ -33,4 +33,4 @@ struct PerformanceFrameHeaderVersion2 { | |||
| 33 | static_assert(sizeof(PerformanceFrameHeaderVersion2) == 0x30, | 33 | static_assert(sizeof(PerformanceFrameHeaderVersion2) == 0x30, |
| 34 | "PerformanceFrameHeaderVersion2 has the wrong size!"); | 34 | "PerformanceFrameHeaderVersion2 has the wrong size!"); |
| 35 | 35 | ||
| 36 | } // namespace AudioCore::AudioRenderer | 36 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp index 8aa0f5ed0..ce736db71 100644 --- a/src/audio_core/renderer/performance/performance_manager.cpp +++ b/src/audio_core/renderer/performance/performance_manager.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/performance/performance_manager.h" | 6 | #include "audio_core/renderer/performance/performance_manager.h" |
| 7 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | void PerformanceManager::CreateImpl(const size_t version) { | 11 | void PerformanceManager::CreateImpl(const size_t version) { |
| 12 | switch (version) { | 12 | switch (version) { |
| @@ -643,4 +643,4 @@ void PerformanceManagerImpl<PerformanceVersion::Version2, PerformanceFrameHeader | |||
| 643 | target_node_id = target_node_id_; | 643 | target_node_id = target_node_id_; |
| 644 | } | 644 | } |
| 645 | 645 | ||
| 646 | } // namespace AudioCore::AudioRenderer | 646 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/performance/performance_manager.h b/src/audio_core/renderer/performance/performance_manager.h index b65caa9b6..ffd0fa1fb 100644 --- a/src/audio_core/renderer/performance/performance_manager.h +++ b/src/audio_core/renderer/performance/performance_manager.h | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #include "audio_core/renderer/performance/performance_frame_header.h" | 14 | #include "audio_core/renderer/performance/performance_frame_header.h" |
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | 16 | ||
| 17 | namespace AudioCore::AudioRenderer { | 17 | namespace AudioCore::Renderer { |
| 18 | class BehaviorInfo; | 18 | class BehaviorInfo; |
| 19 | class MemoryPoolInfo; | 19 | class MemoryPoolInfo; |
| 20 | 20 | ||
| @@ -272,4 +272,4 @@ private: | |||
| 272 | PerformanceVersion version{}; | 272 | PerformanceVersion version{}; |
| 273 | }; | 273 | }; |
| 274 | 274 | ||
| 275 | } // namespace AudioCore::AudioRenderer | 275 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp b/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp index d91f10402..0ede02b6b 100644 --- a/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp +++ b/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include "audio_core/renderer/sink/circular_buffer_sink_info.h" | 5 | #include "audio_core/renderer/sink/circular_buffer_sink_info.h" |
| 6 | #include "audio_core/renderer/upsampler/upsampler_manager.h" | 6 | #include "audio_core/renderer/upsampler/upsampler_manager.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore::AudioRenderer { | 8 | namespace AudioCore::Renderer { |
| 9 | 9 | ||
| 10 | CircularBufferSinkInfo::CircularBufferSinkInfo() { | 10 | CircularBufferSinkInfo::CircularBufferSinkInfo() { |
| 11 | state.fill(0); | 11 | state.fill(0); |
| @@ -73,4 +73,4 @@ void CircularBufferSinkInfo::UpdateForCommandGeneration() { | |||
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | } // namespace AudioCore::AudioRenderer | 76 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/circular_buffer_sink_info.h b/src/audio_core/renderer/sink/circular_buffer_sink_info.h index 3356213ea..d4e61d641 100644 --- a/src/audio_core/renderer/sink/circular_buffer_sink_info.h +++ b/src/audio_core/renderer/sink/circular_buffer_sink_info.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/sink/sink_info_base.h" | 6 | #include "audio_core/renderer/sink/sink_info_base.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Info for a circular buffer sink. | 11 | * Info for a circular buffer sink. |
| 12 | */ | 12 | */ |
| @@ -38,4 +38,4 @@ public: | |||
| 38 | static_assert(sizeof(CircularBufferSinkInfo) <= sizeof(SinkInfoBase), | 38 | static_assert(sizeof(CircularBufferSinkInfo) <= sizeof(SinkInfoBase), |
| 39 | "CircularBufferSinkInfo is too large!"); | 39 | "CircularBufferSinkInfo is too large!"); |
| 40 | 40 | ||
| 41 | } // namespace AudioCore::AudioRenderer | 41 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/device_sink_info.cpp b/src/audio_core/renderer/sink/device_sink_info.cpp index b7b3d6f1d..2de05e38e 100644 --- a/src/audio_core/renderer/sink/device_sink_info.cpp +++ b/src/audio_core/renderer/sink/device_sink_info.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include "audio_core/renderer/sink/device_sink_info.h" | 4 | #include "audio_core/renderer/sink/device_sink_info.h" |
| 5 | #include "audio_core/renderer/upsampler/upsampler_manager.h" | 5 | #include "audio_core/renderer/upsampler/upsampler_manager.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | DeviceSinkInfo::DeviceSinkInfo() { | 9 | DeviceSinkInfo::DeviceSinkInfo() { |
| 10 | state.fill(0); | 10 | state.fill(0); |
| @@ -54,4 +54,4 @@ void DeviceSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStatus& out_ | |||
| 54 | 54 | ||
| 55 | void DeviceSinkInfo::UpdateForCommandGeneration() {} | 55 | void DeviceSinkInfo::UpdateForCommandGeneration() {} |
| 56 | 56 | ||
| 57 | } // namespace AudioCore::AudioRenderer | 57 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/device_sink_info.h b/src/audio_core/renderer/sink/device_sink_info.h index a1c441454..7974ae820 100644 --- a/src/audio_core/renderer/sink/device_sink_info.h +++ b/src/audio_core/renderer/sink/device_sink_info.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/sink/sink_info_base.h" | 6 | #include "audio_core/renderer/sink/sink_info_base.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Info for a device sink. | 11 | * Info for a device sink. |
| 12 | */ | 12 | */ |
| @@ -37,4 +37,4 @@ public: | |||
| 37 | }; | 37 | }; |
| 38 | static_assert(sizeof(DeviceSinkInfo) <= sizeof(SinkInfoBase), "DeviceSinkInfo is too large!"); | 38 | static_assert(sizeof(DeviceSinkInfo) <= sizeof(SinkInfoBase), "DeviceSinkInfo is too large!"); |
| 39 | 39 | ||
| 40 | } // namespace AudioCore::AudioRenderer | 40 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/sink_context.cpp b/src/audio_core/renderer/sink/sink_context.cpp index 634bc1cf9..a4f9cac21 100644 --- a/src/audio_core/renderer/sink/sink_context.cpp +++ b/src/audio_core/renderer/sink/sink_context.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/sink/sink_context.h" | 4 | #include "audio_core/renderer/sink/sink_context.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | void SinkContext::Initialize(std::span<SinkInfoBase> sink_infos_, const u32 sink_count_) { | 8 | void SinkContext::Initialize(std::span<SinkInfoBase> sink_infos_, const u32 sink_count_) { |
| 9 | sink_infos = sink_infos_; | 9 | sink_infos = sink_infos_; |
| @@ -18,4 +18,4 @@ u32 SinkContext::GetCount() const { | |||
| 18 | return sink_count; | 18 | return sink_count; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | } // namespace AudioCore::AudioRenderer | 21 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/sink_context.h b/src/audio_core/renderer/sink/sink_context.h index 185572e29..66925b48e 100644 --- a/src/audio_core/renderer/sink/sink_context.h +++ b/src/audio_core/renderer/sink/sink_context.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "audio_core/renderer/sink/sink_info_base.h" | 8 | #include "audio_core/renderer/sink/sink_info_base.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | /** | 12 | /** |
| 13 | * Manages output sinks. | 13 | * Manages output sinks. |
| 14 | */ | 14 | */ |
| @@ -44,4 +44,4 @@ private: | |||
| 44 | u32 sink_count{}; | 44 | u32 sink_count{}; |
| 45 | }; | 45 | }; |
| 46 | 46 | ||
| 47 | } // namespace AudioCore::AudioRenderer | 47 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/sink_info_base.cpp b/src/audio_core/renderer/sink/sink_info_base.cpp index 4279beaa0..8a064f15a 100644 --- a/src/audio_core/renderer/sink/sink_info_base.cpp +++ b/src/audio_core/renderer/sink/sink_info_base.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include "audio_core/renderer/memory/pool_mapper.h" | 4 | #include "audio_core/renderer/memory/pool_mapper.h" |
| 5 | #include "audio_core/renderer/sink/sink_info_base.h" | 5 | #include "audio_core/renderer/sink/sink_info_base.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::AudioRenderer { | 7 | namespace AudioCore::Renderer { |
| 8 | 8 | ||
| 9 | void SinkInfoBase::CleanUp() { | 9 | void SinkInfoBase::CleanUp() { |
| 10 | type = Type::Invalid; | 10 | type = Type::Invalid; |
| @@ -48,4 +48,4 @@ u8* SinkInfoBase::GetParameter() { | |||
| 48 | return parameter.data(); | 48 | return parameter.data(); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | } // namespace AudioCore::AudioRenderer | 51 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/sink/sink_info_base.h b/src/audio_core/renderer/sink/sink_info_base.h index a1b855f20..e10d1cb38 100644 --- a/src/audio_core/renderer/sink/sink_info_base.h +++ b/src/audio_core/renderer/sink/sink_info_base.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fixed_point.h" | 12 | #include "common/fixed_point.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::AudioRenderer { | 14 | namespace AudioCore::Renderer { |
| 15 | struct UpsamplerInfo; | 15 | struct UpsamplerInfo; |
| 16 | class PoolMapper; | 16 | class PoolMapper; |
| 17 | 17 | ||
| @@ -174,4 +174,4 @@ protected: | |||
| 174 | parameter{}; | 174 | parameter{}; |
| 175 | }; | 175 | }; |
| 176 | 176 | ||
| 177 | } // namespace AudioCore::AudioRenderer | 177 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp index 7a23ba43f..686150ea6 100644 --- a/src/audio_core/renderer/splitter/splitter_context.cpp +++ b/src/audio_core/renderer/splitter/splitter_context.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include "audio_core/renderer/splitter/splitter_context.h" | 7 | #include "audio_core/renderer/splitter/splitter_context.h" |
| 8 | #include "common/alignment.h" | 8 | #include "common/alignment.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::AudioRenderer { | 10 | namespace AudioCore::Renderer { |
| 11 | 11 | ||
| 12 | SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id, | 12 | SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id, |
| 13 | const s32 destination_id) { | 13 | const s32 destination_id) { |
| @@ -214,4 +214,4 @@ u64 SplitterContext::CalcWorkBufferSize(const BehaviorInfo& behavior, | |||
| 214 | return size; | 214 | return size; |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | } // namespace AudioCore::AudioRenderer | 217 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h index 1a63db1d3..556e6dcc3 100644 --- a/src/audio_core/renderer/splitter/splitter_context.h +++ b/src/audio_core/renderer/splitter/splitter_context.h | |||
| @@ -13,7 +13,7 @@ namespace AudioCore { | |||
| 13 | struct AudioRendererParameterInternal; | 13 | struct AudioRendererParameterInternal; |
| 14 | class WorkbufferAllocator; | 14 | class WorkbufferAllocator; |
| 15 | 15 | ||
| 16 | namespace AudioRenderer { | 16 | namespace Renderer { |
| 17 | class BehaviorInfo; | 17 | class BehaviorInfo; |
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| @@ -185,5 +185,5 @@ private: | |||
| 185 | bool splitter_bug_fixed{}; | 185 | bool splitter_bug_fixed{}; |
| 186 | }; | 186 | }; |
| 187 | 187 | ||
| 188 | } // namespace AudioRenderer | 188 | } // namespace Renderer |
| 189 | } // namespace AudioCore | 189 | } // namespace AudioCore |
diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp index b27d44896..5ec37e48e 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/splitter/splitter_destinations_data.h" | 4 | #include "audio_core/renderer/splitter/splitter_destinations_data.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {} | 8 | SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {} |
| 9 | 9 | ||
| @@ -84,4 +84,4 @@ void SplitterDestinationData::SetNext(SplitterDestinationData* next_) { | |||
| 84 | next = next_; | 84 | next = next_; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | } // namespace AudioCore::AudioRenderer | 87 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.h b/src/audio_core/renderer/splitter/splitter_destinations_data.h index d55ce0ad3..90edfc667 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.h +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/common/common.h" | 9 | #include "audio_core/common/common.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * Represents a mixing node, can be connected to a previous and next destination forming a chain | 14 | * Represents a mixing node, can be connected to a previous and next destination forming a chain |
| 15 | * that a certain mix buffer will pass through to output. | 15 | * that a certain mix buffer will pass through to output. |
| @@ -132,4 +132,4 @@ private: | |||
| 132 | bool need_update{}; | 132 | bool need_update{}; |
| 133 | }; | 133 | }; |
| 134 | 134 | ||
| 135 | } // namespace AudioCore::AudioRenderer | 135 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/splitter/splitter_info.cpp b/src/audio_core/renderer/splitter/splitter_info.cpp index 1aee6720b..beb5b7f19 100644 --- a/src/audio_core/renderer/splitter/splitter_info.cpp +++ b/src/audio_core/renderer/splitter/splitter_info.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/splitter/splitter_info.h" | 4 | #include "audio_core/renderer/splitter/splitter_info.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {} | 8 | SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {} |
| 9 | 9 | ||
| @@ -76,4 +76,4 @@ void SplitterInfo::SetDestinations(SplitterDestinationData* destinations_) { | |||
| 76 | destinations = destinations_; | 76 | destinations = destinations_; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | } // namespace AudioCore::AudioRenderer | 79 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/splitter/splitter_info.h b/src/audio_core/renderer/splitter/splitter_info.h index b0ad01fe0..c1e4c2df1 100644 --- a/src/audio_core/renderer/splitter/splitter_info.h +++ b/src/audio_core/renderer/splitter/splitter_info.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/splitter/splitter_destinations_data.h" | 6 | #include "audio_core/renderer/splitter/splitter_destinations_data.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | /** | 10 | /** |
| 11 | * Represents a splitter, wraps multiple output destinations to split an input mix into. | 11 | * Represents a splitter, wraps multiple output destinations to split an input mix into. |
| 12 | */ | 12 | */ |
| @@ -104,4 +104,4 @@ private: | |||
| 104 | u32 channel_count{}; | 104 | u32 channel_count{}; |
| 105 | }; | 105 | }; |
| 106 | 106 | ||
| 107 | } // namespace AudioCore::AudioRenderer | 107 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index a23627472..d29754634 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp | |||
| @@ -4,12 +4,13 @@ | |||
| 4 | #include <chrono> | 4 | #include <chrono> |
| 5 | #include <span> | 5 | #include <span> |
| 6 | 6 | ||
| 7 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" | ||
| 8 | #include "audio_core/adsp/apps/audio_renderer/command_buffer.h" | ||
| 7 | #include "audio_core/audio_core.h" | 9 | #include "audio_core/audio_core.h" |
| 8 | #include "audio_core/common/audio_renderer_parameter.h" | 10 | #include "audio_core/common/audio_renderer_parameter.h" |
| 9 | #include "audio_core/common/common.h" | 11 | #include "audio_core/common/common.h" |
| 10 | #include "audio_core/common/feature_support.h" | 12 | #include "audio_core/common/feature_support.h" |
| 11 | #include "audio_core/common/workbuffer_allocator.h" | 13 | #include "audio_core/common/workbuffer_allocator.h" |
| 12 | #include "audio_core/renderer/adsp/adsp.h" | ||
| 13 | #include "audio_core/renderer/behavior/info_updater.h" | 14 | #include "audio_core/renderer/behavior/info_updater.h" |
| 14 | #include "audio_core/renderer/command/command_buffer.h" | 15 | #include "audio_core/renderer/command/command_buffer.h" |
| 15 | #include "audio_core/renderer/command/command_generator.h" | 16 | #include "audio_core/renderer/command/command_generator.h" |
| @@ -34,7 +35,7 @@ | |||
| 34 | #include "core/hle/kernel/k_transfer_memory.h" | 35 | #include "core/hle/kernel/k_transfer_memory.h" |
| 35 | #include "core/memory.h" | 36 | #include "core/memory.h" |
| 36 | 37 | ||
| 37 | namespace AudioCore::AudioRenderer { | 38 | namespace AudioCore::Renderer { |
| 38 | 39 | ||
| 39 | u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) { | 40 | u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) { |
| 40 | BehaviorInfo behavior; | 41 | BehaviorInfo behavior; |
| @@ -95,7 +96,8 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) { | |||
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) | 98 | System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) |
| 98 | : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} | 99 | : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()}, |
| 100 | adsp_rendered_event{adsp_rendered_event_} {} | ||
| 99 | 101 | ||
| 100 | Result System::Initialize(const AudioRendererParameterInternal& params, | 102 | Result System::Initialize(const AudioRendererParameterInternal& params, |
| 101 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, | 103 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, |
| @@ -443,7 +445,7 @@ void System::Stop() { | |||
| 443 | Result System::Update(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) { | 445 | Result System::Update(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) { |
| 444 | std::scoped_lock l{lock}; | 446 | std::scoped_lock l{lock}; |
| 445 | 447 | ||
| 446 | const auto start_time{core.CoreTiming().GetClockTicks()}; | 448 | const auto start_time{core.CoreTiming().GetGlobalTimeNs().count()}; |
| 447 | std::memset(output.data(), 0, output.size()); | 449 | std::memset(output.data(), 0, output.size()); |
| 448 | 450 | ||
| 449 | InfoUpdater info_updater(input, output, process_handle, behavior); | 451 | InfoUpdater info_updater(input, output, process_handle, behavior); |
| @@ -535,7 +537,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std: | |||
| 535 | adsp_rendered_event->Clear(); | 537 | adsp_rendered_event->Clear(); |
| 536 | num_times_updated++; | 538 | num_times_updated++; |
| 537 | 539 | ||
| 538 | const auto end_time{core.CoreTiming().GetClockTicks()}; | 540 | const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; |
| 539 | ticks_spent_updating += end_time - start_time; | 541 | ticks_spent_updating += end_time - start_time; |
| 540 | 542 | ||
| 541 | return ResultSuccess; | 543 | return ResultSuccess; |
| @@ -583,7 +585,7 @@ void System::SendCommandToDsp() { | |||
| 583 | if (initialized) { | 585 | if (initialized) { |
| 584 | if (active) { | 586 | if (active) { |
| 585 | terminate_event.Reset(); | 587 | terminate_event.Reset(); |
| 586 | const auto remaining_command_count{adsp.GetRemainCommandCount(session_id)}; | 588 | const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)}; |
| 587 | u64 command_size{0}; | 589 | u64 command_size{0}; |
| 588 | 590 | ||
| 589 | if (remaining_command_count) { | 591 | if (remaining_command_count) { |
| @@ -607,26 +609,18 @@ void System::SendCommandToDsp() { | |||
| 607 | time_limit_percent = 70.0f; | 609 | time_limit_percent = 70.0f; |
| 608 | } | 610 | } |
| 609 | 611 | ||
| 610 | ADSP::CommandBuffer command_buffer{ | 612 | auto time_limit{ |
| 611 | .buffer{translated_addr}, | 613 | static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * |
| 612 | .size{command_size}, | 614 | (static_cast<f32>(render_time_limit_percent) / 100.0f))}; |
| 613 | .time_limit{ | 615 | audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit, |
| 614 | static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * | 616 | applet_resource_user_id, reset_command_buffers); |
| 615 | (static_cast<f32>(render_time_limit_percent) / 100.0f))}, | ||
| 616 | .remaining_command_count{remaining_command_count}, | ||
| 617 | .reset_buffers{reset_command_buffers}, | ||
| 618 | .applet_resource_user_id{applet_resource_user_id}, | ||
| 619 | .render_time_taken{adsp.GetRenderTimeTaken(session_id)}, | ||
| 620 | }; | ||
| 621 | |||
| 622 | adsp.SendCommandBuffer(session_id, command_buffer); | ||
| 623 | reset_command_buffers = false; | 617 | reset_command_buffers = false; |
| 624 | command_buffer_size = command_size; | 618 | command_buffer_size = command_size; |
| 625 | if (remaining_command_count == 0) { | 619 | if (remaining_command_count == 0) { |
| 626 | adsp_rendered_event->Signal(); | 620 | adsp_rendered_event->Signal(); |
| 627 | } | 621 | } |
| 628 | } else { | 622 | } else { |
| 629 | adsp.ClearRemainCount(session_id); | 623 | audio_renderer.ClearRemainCommandCount(session_id); |
| 630 | terminate_event.Set(); | 624 | terminate_event.Set(); |
| 631 | } | 625 | } |
| 632 | } | 626 | } |
| @@ -635,7 +629,7 @@ void System::SendCommandToDsp() { | |||
| 635 | u64 System::GenerateCommand(std::span<u8> in_command_buffer, | 629 | u64 System::GenerateCommand(std::span<u8> in_command_buffer, |
| 636 | [[maybe_unused]] u64 command_buffer_size_) { | 630 | [[maybe_unused]] u64 command_buffer_size_) { |
| 637 | PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); | 631 | PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); |
| 638 | const auto start_time{core.CoreTiming().GetClockTicks()}; | 632 | const auto start_time{core.CoreTiming().GetGlobalTimeNs().count()}; |
| 639 | 633 | ||
| 640 | auto command_list_header{reinterpret_cast<CommandListHeader*>(in_command_buffer.data())}; | 634 | auto command_list_header{reinterpret_cast<CommandListHeader*>(in_command_buffer.data())}; |
| 641 | 635 | ||
| @@ -732,10 +726,10 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer, | |||
| 732 | effect_context.UpdateStateByDspShared(); | 726 | effect_context.UpdateStateByDspShared(); |
| 733 | } | 727 | } |
| 734 | 728 | ||
| 735 | const auto end_time{core.CoreTiming().GetClockTicks()}; | 729 | const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; |
| 736 | total_ticks_elapsed += end_time - start_time; | 730 | total_ticks_elapsed += end_time - start_time; |
| 737 | num_command_lists_generated++; | 731 | num_command_lists_generated++; |
| 738 | render_start_tick = adsp.GetRenderingStartTick(session_id); | 732 | render_start_tick = audio_renderer.GetRenderingStartTick(session_id); |
| 739 | frames_elapsed++; | 733 | frames_elapsed++; |
| 740 | 734 | ||
| 741 | return command_buffer.size; | 735 | return command_buffer.size; |
| @@ -778,7 +772,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time | |||
| 778 | while (i < command_buffer.count) { | 772 | while (i < command_buffer.count) { |
| 779 | const auto node_id{cmd->node_id}; | 773 | const auto node_id{cmd->node_id}; |
| 780 | const auto node_id_type{cmd->node_id >> 28}; | 774 | const auto node_id_type{cmd->node_id >> 28}; |
| 781 | const auto node_id_base{cmd->node_id & 0xFFF}; | 775 | const auto node_id_base{(cmd->node_id >> 16) & 0xFFF}; |
| 782 | 776 | ||
| 783 | // If the new estimated process time falls below the limit, we're done dropping. | 777 | // If the new estimated process time falls below the limit, we're done dropping. |
| 784 | if (estimated_process_time <= time_limit) { | 778 | if (estimated_process_time <= time_limit) { |
| @@ -819,4 +813,4 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time | |||
| 819 | return voices_dropped; | 813 | return voices_dropped; |
| 820 | } | 814 | } |
| 821 | 815 | ||
| 822 | } // namespace AudioCore::AudioRenderer | 816 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h index e328783b6..8a8341710 100644 --- a/src/audio_core/renderer/system.h +++ b/src/audio_core/renderer/system.h | |||
| @@ -34,12 +34,16 @@ class KTransferMemory; | |||
| 34 | 34 | ||
| 35 | namespace AudioCore { | 35 | namespace AudioCore { |
| 36 | struct AudioRendererParameterInternal; | 36 | struct AudioRendererParameterInternal; |
| 37 | |||
| 38 | namespace AudioRenderer { | ||
| 39 | class CommandBuffer; | ||
| 40 | namespace ADSP { | 37 | namespace ADSP { |
| 41 | class ADSP; | 38 | class ADSP; |
| 39 | namespace AudioRenderer { | ||
| 40 | class AudioRenderer; | ||
| 42 | } | 41 | } |
| 42 | } // namespace ADSP | ||
| 43 | |||
| 44 | namespace Renderer { | ||
| 45 | using namespace ::AudioCore::ADSP; | ||
| 46 | class CommandBuffer; | ||
| 43 | 47 | ||
| 44 | /** | 48 | /** |
| 45 | * Audio Renderer System, the main worker for audio rendering. | 49 | * Audio Renderer System, the main worker for audio rendering. |
| @@ -213,8 +217,8 @@ public: | |||
| 213 | private: | 217 | private: |
| 214 | /// Core system | 218 | /// Core system |
| 215 | Core::System& core; | 219 | Core::System& core; |
| 216 | /// Reference to the ADSP for communication | 220 | /// Reference to the ADSP's AudioRenderer for communication |
| 217 | ADSP::ADSP& adsp; | 221 | ::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer; |
| 218 | /// Is this system initialized? | 222 | /// Is this system initialized? |
| 219 | bool initialized{}; | 223 | bool initialized{}; |
| 220 | /// Is this system currently active? | 224 | /// Is this system currently active? |
| @@ -319,5 +323,5 @@ private: | |||
| 319 | f32 drop_voice_param{1.0f}; | 323 | f32 drop_voice_param{1.0f}; |
| 320 | }; | 324 | }; |
| 321 | 325 | ||
| 322 | } // namespace AudioRenderer | 326 | } // namespace Renderer |
| 323 | } // namespace AudioCore | 327 | } // namespace AudioCore |
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index 300ecdbf1..a0b8ef29e 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #include <chrono> | 4 | #include <chrono> |
| 5 | 5 | ||
| 6 | #include "audio_core/adsp/adsp.h" | ||
| 6 | #include "audio_core/audio_core.h" | 7 | #include "audio_core/audio_core.h" |
| 7 | #include "audio_core/renderer/adsp/adsp.h" | ||
| 8 | #include "audio_core/renderer/system_manager.h" | 8 | #include "audio_core/renderer/system_manager.h" |
| 9 | #include "common/microprofile.h" | 9 | #include "common/microprofile.h" |
| 10 | #include "common/thread.h" | 10 | #include "common/thread.h" |
| @@ -14,24 +14,21 @@ | |||
| 14 | MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager", | 14 | MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager", |
| 15 | MP_RGB(60, 19, 97)); | 15 | MP_RGB(60, 19, 97)); |
| 16 | 16 | ||
| 17 | namespace AudioCore::AudioRenderer { | 17 | namespace AudioCore::Renderer { |
| 18 | 18 | ||
| 19 | SystemManager::SystemManager(Core::System& core_) | 19 | SystemManager::SystemManager(Core::System& core_) |
| 20 | : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()} {} | 20 | : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {} |
| 21 | 21 | ||
| 22 | SystemManager::~SystemManager() { | 22 | SystemManager::~SystemManager() { |
| 23 | Stop(); | 23 | Stop(); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | bool SystemManager::InitializeUnsafe() { | 26 | void SystemManager::InitializeUnsafe() { |
| 27 | if (!active) { | 27 | if (!active) { |
| 28 | if (adsp.Start()) { | 28 | active = true; |
| 29 | active = true; | 29 | audio_renderer.Start(); |
| 30 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); | 30 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); |
| 31 | } | ||
| 32 | } | 31 | } |
| 33 | |||
| 34 | return adsp.GetState() == ADSP::State::Started; | ||
| 35 | } | 32 | } |
| 36 | 33 | ||
| 37 | void SystemManager::Stop() { | 34 | void SystemManager::Stop() { |
| @@ -41,7 +38,7 @@ void SystemManager::Stop() { | |||
| 41 | active = false; | 38 | active = false; |
| 42 | thread.request_stop(); | 39 | thread.request_stop(); |
| 43 | thread.join(); | 40 | thread.join(); |
| 44 | adsp.Stop(); | 41 | audio_renderer.Stop(); |
| 45 | } | 42 | } |
| 46 | 43 | ||
| 47 | bool SystemManager::Add(System& system_) { | 44 | bool SystemManager::Add(System& system_) { |
| @@ -55,10 +52,7 @@ bool SystemManager::Add(System& system_) { | |||
| 55 | { | 52 | { |
| 56 | std::scoped_lock l{mutex1}; | 53 | std::scoped_lock l{mutex1}; |
| 57 | if (systems.empty()) { | 54 | if (systems.empty()) { |
| 58 | if (!InitializeUnsafe()) { | 55 | InitializeUnsafe(); |
| 59 | LOG_ERROR(Service_Audio, "Failed to start the AudioRenderer SystemManager"); | ||
| 60 | return false; | ||
| 61 | } | ||
| 62 | } | 56 | } |
| 63 | } | 57 | } |
| 64 | 58 | ||
| @@ -100,9 +94,9 @@ void SystemManager::ThreadFunc(std::stop_token stop_token) { | |||
| 100 | } | 94 | } |
| 101 | } | 95 | } |
| 102 | 96 | ||
| 103 | adsp.Signal(); | 97 | audio_renderer.Signal(); |
| 104 | adsp.Wait(); | 98 | audio_renderer.Wait(); |
| 105 | } | 99 | } |
| 106 | } | 100 | } |
| 107 | 101 | ||
| 108 | } // namespace AudioCore::AudioRenderer | 102 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/system_manager.h b/src/audio_core/renderer/system_manager.h index 9681fd121..62e8e5f15 100644 --- a/src/audio_core/renderer/system_manager.h +++ b/src/audio_core/renderer/system_manager.h | |||
| @@ -18,11 +18,14 @@ struct EventType; | |||
| 18 | class System; | 18 | class System; |
| 19 | } // namespace Core | 19 | } // namespace Core |
| 20 | 20 | ||
| 21 | namespace AudioCore::AudioRenderer { | 21 | namespace AudioCore::ADSP { |
| 22 | namespace ADSP { | ||
| 23 | class ADSP; | 22 | class ADSP; |
| 24 | class AudioRenderer_Mailbox; | 23 | namespace AudioRenderer { |
| 25 | } // namespace ADSP | 24 | class AudioRenderer; |
| 25 | } // namespace AudioRenderer | ||
| 26 | } // namespace AudioCore::ADSP | ||
| 27 | |||
| 28 | namespace AudioCore::Renderer { | ||
| 26 | 29 | ||
| 27 | /** | 30 | /** |
| 28 | * Manages all audio renderers, responsible for triggering command list generation and signalling | 31 | * Manages all audio renderers, responsible for triggering command list generation and signalling |
| @@ -38,7 +41,7 @@ public: | |||
| 38 | * | 41 | * |
| 39 | * @return True if successfully initialized, otherwise false. | 42 | * @return True if successfully initialized, otherwise false. |
| 40 | */ | 43 | */ |
| 41 | bool InitializeUnsafe(); | 44 | void InitializeUnsafe(); |
| 42 | 45 | ||
| 43 | /** | 46 | /** |
| 44 | * Stop the system manager. | 47 | * Stop the system manager. |
| @@ -80,10 +83,8 @@ private: | |||
| 80 | std::mutex mutex2{}; | 83 | std::mutex mutex2{}; |
| 81 | /// Is the system manager thread active? | 84 | /// Is the system manager thread active? |
| 82 | std::atomic<bool> active{}; | 85 | std::atomic<bool> active{}; |
| 83 | /// Reference to the ADSP for communication | 86 | /// Reference to the ADSP's AudioRenderer for communication |
| 84 | ADSP::ADSP& adsp; | 87 | ::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer; |
| 85 | /// AudioRenderer mailbox for communication | ||
| 86 | ADSP::AudioRenderer_Mailbox* mailbox{}; | ||
| 87 | }; | 88 | }; |
| 88 | 89 | ||
| 89 | } // namespace AudioCore::AudioRenderer | 90 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/upsampler/upsampler_info.h b/src/audio_core/renderer/upsampler/upsampler_info.h index a43c15af3..85c87f137 100644 --- a/src/audio_core/renderer/upsampler/upsampler_info.h +++ b/src/audio_core/renderer/upsampler/upsampler_info.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/upsampler/upsampler_state.h" | 9 | #include "audio_core/renderer/upsampler/upsampler_state.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | class UpsamplerManager; | 13 | class UpsamplerManager; |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| @@ -32,4 +32,4 @@ struct UpsamplerInfo { | |||
| 32 | std::array<s16, MaxChannels> inputs{}; | 32 | std::array<s16, MaxChannels> inputs{}; |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | } // namespace AudioCore::AudioRenderer | 35 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.cpp b/src/audio_core/renderer/upsampler/upsampler_manager.cpp index 4c76a5066..ef740f6c9 100644 --- a/src/audio_core/renderer/upsampler/upsampler_manager.cpp +++ b/src/audio_core/renderer/upsampler/upsampler_manager.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "audio_core/renderer/upsampler/upsampler_manager.h" | 4 | #include "audio_core/renderer/upsampler/upsampler_manager.h" |
| 5 | 5 | ||
| 6 | namespace AudioCore::AudioRenderer { | 6 | namespace AudioCore::Renderer { |
| 7 | 7 | ||
| 8 | UpsamplerManager::UpsamplerManager(const u32 count_, std::span<UpsamplerInfo> infos_, | 8 | UpsamplerManager::UpsamplerManager(const u32 count_, std::span<UpsamplerInfo> infos_, |
| 9 | std::span<s32> workbuffer_) | 9 | std::span<s32> workbuffer_) |
| @@ -41,4 +41,4 @@ void UpsamplerManager::Free(UpsamplerInfo* info) { | |||
| 41 | info->enabled = false; | 41 | info->enabled = false; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | } // namespace AudioCore::AudioRenderer | 44 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.h b/src/audio_core/renderer/upsampler/upsampler_manager.h index 83c697c0c..263e5718b 100644 --- a/src/audio_core/renderer/upsampler/upsampler_manager.h +++ b/src/audio_core/renderer/upsampler/upsampler_manager.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/upsampler/upsampler_info.h" | 9 | #include "audio_core/renderer/upsampler/upsampler_info.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * Manages and has utility functions for upsampler infos. | 14 | * Manages and has utility functions for upsampler infos. |
| 15 | */ | 15 | */ |
| @@ -42,4 +42,4 @@ private: | |||
| 42 | std::mutex lock{}; | 42 | std::mutex lock{}; |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | } // namespace AudioCore::AudioRenderer | 45 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/upsampler/upsampler_state.h b/src/audio_core/renderer/upsampler/upsampler_state.h index 28cebe200..dc7b31d42 100644 --- a/src/audio_core/renderer/upsampler/upsampler_state.h +++ b/src/audio_core/renderer/upsampler/upsampler_state.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/fixed_point.h" | 9 | #include "common/fixed_point.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | /** | 12 | /** |
| 13 | * Upsampling state used by the AudioRenderer across calls. | 13 | * Upsampling state used by the AudioRenderer across calls. |
| 14 | */ | 14 | */ |
| @@ -37,4 +37,4 @@ struct UpsamplerState { | |||
| 37 | u8 sample_index; | 37 | u8 sample_index; |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | } // namespace AudioCore::AudioRenderer | 40 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/voice/voice_channel_resource.h b/src/audio_core/renderer/voice/voice_channel_resource.h index 26ab4ccce..4f19c2fcc 100644 --- a/src/audio_core/renderer/voice/voice_channel_resource.h +++ b/src/audio_core/renderer/voice/voice_channel_resource.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "audio_core/common/common.h" | 8 | #include "audio_core/common/common.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::AudioRenderer { | 11 | namespace AudioCore::Renderer { |
| 12 | /** | 12 | /** |
| 13 | * Represents one channel for mixing a voice. | 13 | * Represents one channel for mixing a voice. |
| 14 | */ | 14 | */ |
| @@ -35,4 +35,4 @@ public: | |||
| 35 | bool in_use{}; | 35 | bool in_use{}; |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | } // namespace AudioCore::AudioRenderer | 38 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp index 16a3e839d..c3644e38b 100644 --- a/src/audio_core/renderer/voice/voice_context.cpp +++ b/src/audio_core/renderer/voice/voice_context.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/voice/voice_context.h" | 6 | #include "audio_core/renderer/voice/voice_context.h" |
| 7 | #include "common/polyfill_ranges.h" | 7 | #include "common/polyfill_ranges.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | VoiceState& VoiceContext::GetDspSharedState(const u32 index) { | 11 | VoiceState& VoiceContext::GetDspSharedState(const u32 index) { |
| 12 | if (index >= dsp_states.size()) { | 12 | if (index >= dsp_states.size()) { |
| @@ -84,4 +84,4 @@ void VoiceContext::UpdateStateByDspShared() { | |||
| 84 | std::memcpy(cpu_states.data(), dsp_states.data(), voice_count * sizeof(VoiceState)); | 84 | std::memcpy(cpu_states.data(), dsp_states.data(), voice_count * sizeof(VoiceState)); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | } // namespace AudioCore::AudioRenderer | 87 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/voice/voice_context.h b/src/audio_core/renderer/voice/voice_context.h index 43b677154..138ab2773 100644 --- a/src/audio_core/renderer/voice/voice_context.h +++ b/src/audio_core/renderer/voice/voice_context.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "audio_core/renderer/voice/voice_state.h" | 10 | #include "audio_core/renderer/voice/voice_state.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::Renderer { |
| 14 | /** | 14 | /** |
| 15 | * Contains all voices, with utility functions for managing them. | 15 | * Contains all voices, with utility functions for managing them. |
| 16 | */ | 16 | */ |
| @@ -123,4 +123,4 @@ private: | |||
| 123 | u32 active_count{}; | 123 | u32 active_count{}; |
| 124 | }; | 124 | }; |
| 125 | 125 | ||
| 126 | } // namespace AudioCore::AudioRenderer | 126 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/voice/voice_info.cpp b/src/audio_core/renderer/voice/voice_info.cpp index c0bfb23fc..6239cfab7 100644 --- a/src/audio_core/renderer/voice/voice_info.cpp +++ b/src/audio_core/renderer/voice/voice_info.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "audio_core/renderer/voice/voice_info.h" | 6 | #include "audio_core/renderer/voice/voice_info.h" |
| 7 | #include "audio_core/renderer/voice/voice_state.h" | 7 | #include "audio_core/renderer/voice/voice_state.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::Renderer { |
| 10 | 10 | ||
| 11 | VoiceInfo::VoiceInfo() { | 11 | VoiceInfo::VoiceInfo() { |
| 12 | Initialize(); | 12 | Initialize(); |
| @@ -405,4 +405,4 @@ void VoiceInfo::ResetResources(VoiceContext& voice_context) const { | |||
| 405 | } | 405 | } |
| 406 | } | 406 | } |
| 407 | 407 | ||
| 408 | } // namespace AudioCore::AudioRenderer | 408 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/voice/voice_info.h b/src/audio_core/renderer/voice/voice_info.h index 3c5d3e04f..14a687dcb 100644 --- a/src/audio_core/renderer/voice/voice_info.h +++ b/src/audio_core/renderer/voice/voice_info.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include "audio_core/renderer/memory/address_info.h" | 12 | #include "audio_core/renderer/memory/address_info.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | 14 | ||
| 15 | namespace AudioCore::AudioRenderer { | 15 | namespace AudioCore::Renderer { |
| 16 | class PoolMapper; | 16 | class PoolMapper; |
| 17 | class VoiceContext; | 17 | class VoiceContext; |
| 18 | struct VoiceState; | 18 | struct VoiceState; |
| @@ -377,4 +377,4 @@ public: | |||
| 377 | u8 flush_buffer_count{}; | 377 | u8 flush_buffer_count{}; |
| 378 | }; | 378 | }; |
| 379 | 379 | ||
| 380 | } // namespace AudioCore::AudioRenderer | 380 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/renderer/voice/voice_state.h b/src/audio_core/renderer/voice/voice_state.h index ce947233f..c7aee167b 100644 --- a/src/audio_core/renderer/voice/voice_state.h +++ b/src/audio_core/renderer/voice/voice_state.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/fixed_point.h" | 10 | #include "common/fixed_point.h" |
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::Renderer { |
| 13 | /** | 13 | /** |
| 14 | * Holds a state for a voice. One is kept host-side, and one is used by the AudioRenderer, | 14 | * Holds a state for a voice. One is kept host-side, and one is used by the AudioRenderer, |
| 15 | * host-side is updated on the next iteration. | 15 | * host-side is updated on the next iteration. |
| @@ -67,4 +67,4 @@ struct VoiceState { | |||
| 67 | s32 loop_count; | 67 | s32 loop_count; |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | } // namespace AudioCore::AudioRenderer | 70 | } // namespace AudioCore::Renderer |
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 9a0801888..bbb598bc5 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "audio_core/sink/cubeb_sink.h" | 8 | #include "audio_core/sink/cubeb_sink.h" |
| 9 | #include "audio_core/sink/sink_stream.h" | 9 | #include "audio_core/sink/sink_stream.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/scope_exit.h" | ||
| 11 | #include "core/core.h" | 12 | #include "core/core.h" |
| 12 | 13 | ||
| 13 | #ifdef _WIN32 | 14 | #ifdef _WIN32 |
| @@ -332,25 +333,38 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) { | |||
| 332 | return device_list; | 333 | return device_list; |
| 333 | } | 334 | } |
| 334 | 335 | ||
| 335 | u32 GetCubebLatency() { | 336 | namespace { |
| 336 | cubeb* ctx; | 337 | static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) { |
| 338 | return TargetSampleCount; | ||
| 339 | } | ||
| 340 | static void TmpStateCallback(cubeb_stream*, void*, cubeb_state) {} | ||
| 341 | } // namespace | ||
| 342 | |||
| 343 | bool IsCubebSuitable() { | ||
| 344 | #if !defined(HAVE_CUBEB) | ||
| 345 | return false; | ||
| 346 | #else | ||
| 347 | cubeb* ctx{nullptr}; | ||
| 337 | 348 | ||
| 338 | #ifdef _WIN32 | 349 | #ifdef _WIN32 |
| 339 | auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); | 350 | auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); |
| 340 | #endif | 351 | #endif |
| 341 | 352 | ||
| 353 | // Init cubeb | ||
| 342 | if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { | 354 | if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { |
| 343 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | 355 | LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable."); |
| 344 | // Return a large latency so we choose SDL instead. | 356 | return false; |
| 345 | return 10000u; | ||
| 346 | } | 357 | } |
| 347 | 358 | ||
| 359 | SCOPE_EXIT({ cubeb_destroy(ctx); }); | ||
| 360 | |||
| 348 | #ifdef _WIN32 | 361 | #ifdef _WIN32 |
| 349 | if (SUCCEEDED(com_init_result)) { | 362 | if (SUCCEEDED(com_init_result)) { |
| 350 | CoUninitialize(); | 363 | CoUninitialize(); |
| 351 | } | 364 | } |
| 352 | #endif | 365 | #endif |
| 353 | 366 | ||
| 367 | // Get min latency | ||
| 354 | cubeb_stream_params params{}; | 368 | cubeb_stream_params params{}; |
| 355 | params.rate = TargetSampleRate; | 369 | params.rate = TargetSampleRate; |
| 356 | params.channels = 2; | 370 | params.channels = 2; |
| @@ -361,12 +375,27 @@ u32 GetCubebLatency() { | |||
| 361 | u32 latency{0}; | 375 | u32 latency{0}; |
| 362 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency); | 376 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency); |
| 363 | if (latency_error != CUBEB_OK) { | 377 | if (latency_error != CUBEB_OK) { |
| 364 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); | 378 | LOG_ERROR(Audio_Sink, "Cubeb could not get min latency, it is not suitable."); |
| 365 | latency = TargetSampleCount * 2; | 379 | return false; |
| 366 | } | 380 | } |
| 367 | latency = std::max(latency, TargetSampleCount * 2); | 381 | latency = std::max(latency, TargetSampleCount * 2); |
| 368 | cubeb_destroy(ctx); | 382 | |
| 369 | return latency; | 383 | // Test opening a device with standard parameters |
| 384 | cubeb_devid output_device{0}; | ||
| 385 | cubeb_devid input_device{0}; | ||
| 386 | std::string name{"Yuzu test"}; | ||
| 387 | cubeb_stream* stream{nullptr}; | ||
| 388 | |||
| 389 | if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, ¶ms, | ||
| 390 | latency, &TmpDataCallback, &TmpStateCallback, nullptr) != CUBEB_OK) { | ||
| 391 | LOG_CRITICAL(Audio_Sink, "Cubeb could not open a device, it is not suitable."); | ||
| 392 | return false; | ||
| 393 | } | ||
| 394 | |||
| 395 | cubeb_stream_stop(stream); | ||
| 396 | cubeb_stream_destroy(stream); | ||
| 397 | return true; | ||
| 398 | #endif | ||
| 370 | } | 399 | } |
| 371 | 400 | ||
| 372 | } // namespace AudioCore::Sink | 401 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h index 3302cb98d..f49a6fdaa 100644 --- a/src/audio_core/sink/cubeb_sink.h +++ b/src/audio_core/sink/cubeb_sink.h | |||
| @@ -97,10 +97,11 @@ private: | |||
| 97 | std::vector<std::string> ListCubebSinkDevices(bool capture); | 97 | std::vector<std::string> ListCubebSinkDevices(bool capture); |
| 98 | 98 | ||
| 99 | /** | 99 | /** |
| 100 | * Get the reported latency for this sink. | 100 | * Check if this backend is suitable for use. |
| 101 | * Checks if enabled, its latency, whether it opens successfully, etc. | ||
| 101 | * | 102 | * |
| 102 | * @return Minimum latency for this sink. | 103 | * @return True is this backend is suitable, false otherwise. |
| 103 | */ | 104 | */ |
| 104 | u32 GetCubebLatency(); | 105 | bool IsCubebSuitable(); |
| 105 | 106 | ||
| 106 | } // namespace AudioCore::Sink | 107 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index c1529d1f9..7b89151de 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "audio_core/sink/sdl2_sink.h" | 9 | #include "audio_core/sink/sdl2_sink.h" |
| 10 | #include "audio_core/sink/sink_stream.h" | 10 | #include "audio_core/sink/sink_stream.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/scope_exit.h" | ||
| 12 | #include "core/core.h" | 13 | #include "core/core.h" |
| 13 | 14 | ||
| 14 | namespace AudioCore::Sink { | 15 | namespace AudioCore::Sink { |
| @@ -84,6 +85,7 @@ public: | |||
| 84 | } | 85 | } |
| 85 | 86 | ||
| 86 | Stop(); | 87 | Stop(); |
| 88 | SDL_ClearQueuedAudio(device); | ||
| 87 | SDL_CloseAudioDevice(device); | 89 | SDL_CloseAudioDevice(device); |
| 88 | } | 90 | } |
| 89 | 91 | ||
| @@ -227,8 +229,42 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) { | |||
| 227 | return device_list; | 229 | return device_list; |
| 228 | } | 230 | } |
| 229 | 231 | ||
| 230 | u32 GetSDLLatency() { | 232 | bool IsSDLSuitable() { |
| 231 | return TargetSampleCount * 2; | 233 | #if !defined(HAVE_SDL2) |
| 234 | return false; | ||
| 235 | #else | ||
| 236 | // Check SDL can init | ||
| 237 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { | ||
| 238 | if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { | ||
| 239 | LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}", | ||
| 240 | SDL_GetError()); | ||
| 241 | return false; | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | // We can set any latency frequency we want with SDL, so no need to check that. | ||
| 246 | |||
| 247 | // Check we can open a device with standard parameters | ||
| 248 | SDL_AudioSpec spec; | ||
| 249 | spec.freq = TargetSampleRate; | ||
| 250 | spec.channels = 2u; | ||
| 251 | spec.format = AUDIO_S16SYS; | ||
| 252 | spec.samples = TargetSampleCount * 2; | ||
| 253 | spec.callback = nullptr; | ||
| 254 | spec.userdata = nullptr; | ||
| 255 | |||
| 256 | SDL_AudioSpec obtained; | ||
| 257 | auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false); | ||
| 258 | |||
| 259 | if (device == 0) { | ||
| 260 | LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}", | ||
| 261 | SDL_GetError()); | ||
| 262 | return false; | ||
| 263 | } | ||
| 264 | |||
| 265 | SDL_CloseAudioDevice(device); | ||
| 266 | return true; | ||
| 267 | #endif | ||
| 232 | } | 268 | } |
| 233 | 269 | ||
| 234 | } // namespace AudioCore::Sink | 270 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index 27ed1ab94..9211d2e97 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h | |||
| @@ -88,10 +88,11 @@ private: | |||
| 88 | std::vector<std::string> ListSDLSinkDevices(bool capture); | 88 | std::vector<std::string> ListSDLSinkDevices(bool capture); |
| 89 | 89 | ||
| 90 | /** | 90 | /** |
| 91 | * Get the reported latency for this sink. | 91 | * Check if this backend is suitable for use. |
| 92 | * Checks if enabled, its latency, whether it opens successfully, etc. | ||
| 92 | * | 93 | * |
| 93 | * @return Minimum latency for this sink. | 94 | * @return True is this backend is suitable, false otherwise. |
| 94 | */ | 95 | */ |
| 95 | u32 GetSDLLatency(); | 96 | bool IsSDLSuitable(); |
| 96 | 97 | ||
| 97 | } // namespace AudioCore::Sink | 98 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 027bfa517..7c9a4e3ac 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp | |||
| @@ -22,7 +22,7 @@ namespace { | |||
| 22 | struct SinkDetails { | 22 | struct SinkDetails { |
| 23 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); | 23 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); |
| 24 | using ListDevicesFn = std::vector<std::string> (*)(bool); | 24 | using ListDevicesFn = std::vector<std::string> (*)(bool); |
| 25 | using LatencyFn = u32 (*)(); | 25 | using SuitableFn = bool (*)(); |
| 26 | 26 | ||
| 27 | /// Name for this sink. | 27 | /// Name for this sink. |
| 28 | Settings::AudioEngine id; | 28 | Settings::AudioEngine id; |
| @@ -30,8 +30,8 @@ struct SinkDetails { | |||
| 30 | FactoryFn factory; | 30 | FactoryFn factory; |
| 31 | /// A method to call to list available devices. | 31 | /// A method to call to list available devices. |
| 32 | ListDevicesFn list_devices; | 32 | ListDevicesFn list_devices; |
| 33 | /// Method to get the latency of this backend. | 33 | /// Check whether this backend is suitable to be used. |
| 34 | LatencyFn latency; | 34 | SuitableFn is_suitable; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | // sink_details is ordered in terms of desirability, with the best choice at the top. | 37 | // sink_details is ordered in terms of desirability, with the best choice at the top. |
| @@ -43,7 +43,7 @@ constexpr SinkDetails sink_details[] = { | |||
| 43 | return std::make_unique<CubebSink>(device_id); | 43 | return std::make_unique<CubebSink>(device_id); |
| 44 | }, | 44 | }, |
| 45 | &ListCubebSinkDevices, | 45 | &ListCubebSinkDevices, |
| 46 | &GetCubebLatency, | 46 | &IsCubebSuitable, |
| 47 | }, | 47 | }, |
| 48 | #endif | 48 | #endif |
| 49 | #ifdef HAVE_SDL2 | 49 | #ifdef HAVE_SDL2 |
| @@ -53,14 +53,17 @@ constexpr SinkDetails sink_details[] = { | |||
| 53 | return std::make_unique<SDLSink>(device_id); | 53 | return std::make_unique<SDLSink>(device_id); |
| 54 | }, | 54 | }, |
| 55 | &ListSDLSinkDevices, | 55 | &ListSDLSinkDevices, |
| 56 | &GetSDLLatency, | 56 | &IsSDLSuitable, |
| 57 | }, | 57 | }, |
| 58 | #endif | 58 | #endif |
| 59 | SinkDetails{Settings::AudioEngine::Null, | 59 | SinkDetails{ |
| 60 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 60 | Settings::AudioEngine::Null, |
| 61 | return std::make_unique<NullSink>(device_id); | 61 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 62 | }, | 62 | return std::make_unique<NullSink>(device_id); |
| 63 | [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, | 63 | }, |
| 64 | [](bool capture) { return std::vector<std::string>{"null"}; }, | ||
| 65 | []() { return true; }, | ||
| 66 | }, | ||
| 64 | }; | 67 | }; |
| 65 | 68 | ||
| 66 | const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { | 69 | const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { |
| @@ -72,18 +75,22 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { | |||
| 72 | auto iter = find_backend(sink_id); | 75 | auto iter = find_backend(sink_id); |
| 73 | 76 | ||
| 74 | if (sink_id == Settings::AudioEngine::Auto) { | 77 | if (sink_id == Settings::AudioEngine::Auto) { |
| 75 | // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which | 78 | // Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking |
| 76 | // causes audio issues, in that case go with SDL. | 79 | // that the backend is available and suitable to use. |
| 77 | #if defined(HAVE_CUBEB) && defined(HAVE_SDL2) | 80 | for (auto& details : sink_details) { |
| 78 | iter = find_backend(Settings::AudioEngine::Cubeb); | 81 | if (details.is_suitable()) { |
| 79 | if (iter->latency() > TargetSampleCount * 3) { | 82 | iter = &details; |
| 80 | iter = find_backend(Settings::AudioEngine::Sdl2); | 83 | break; |
| 84 | } | ||
| 81 | } | 85 | } |
| 82 | #else | ||
| 83 | iter = std::begin(sink_details); | ||
| 84 | #endif | ||
| 85 | LOG_INFO(Service_Audio, "Auto-selecting the {} backend", | 86 | LOG_INFO(Service_Audio, "Auto-selecting the {} backend", |
| 86 | Settings::CanonicalizeEnum(iter->id)); | 87 | Settings::CanonicalizeEnum(iter->id)); |
| 88 | } else { | ||
| 89 | if (iter != std::end(sink_details) && !iter->is_suitable()) { | ||
| 90 | LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null", | ||
| 91 | Settings::CanonicalizeEnum(iter->id)); | ||
| 92 | iter = find_backend(Settings::AudioEngine::Null); | ||
| 93 | } | ||
| 87 | } | 94 | } |
| 88 | 95 | ||
| 89 | if (iter == std::end(sink_details)) { | 96 | if (iter == std::end(sink_details)) { |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bf97d9ba2..34877b461 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -151,6 +151,10 @@ add_library(common STATIC | |||
| 151 | zstd_compression.h | 151 | zstd_compression.h |
| 152 | ) | 152 | ) |
| 153 | 153 | ||
| 154 | if (YUZU_ENABLE_PORTABLE) | ||
| 155 | add_compile_definitions(YUZU_ENABLE_PORTABLE) | ||
| 156 | endif() | ||
| 157 | |||
| 154 | if (WIN32) | 158 | if (WIN32) |
| 155 | target_sources(common PRIVATE | 159 | target_sources(common PRIVATE |
| 156 | windows/timer_resolution.cpp | 160 | windows/timer_resolution.cpp |
| @@ -191,8 +195,6 @@ if (MSVC) | |||
| 191 | _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING | 195 | _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING |
| 192 | ) | 196 | ) |
| 193 | target_compile_options(common PRIVATE | 197 | target_compile_options(common PRIVATE |
| 194 | /W4 | ||
| 195 | |||
| 196 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data | 198 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data |
| 197 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 199 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 198 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | 200 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss |
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index d71cfacc6..dce219fcf 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp | |||
| @@ -88,8 +88,9 @@ public: | |||
| 88 | fs::path yuzu_path_config; | 88 | fs::path yuzu_path_config; |
| 89 | 89 | ||
| 90 | #ifdef _WIN32 | 90 | #ifdef _WIN32 |
| 91 | #ifdef YUZU_ENABLE_PORTABLE | ||
| 91 | yuzu_path = GetExeDirectory() / PORTABLE_DIR; | 92 | yuzu_path = GetExeDirectory() / PORTABLE_DIR; |
| 92 | 93 | #endif | |
| 93 | if (!IsDir(yuzu_path)) { | 94 | if (!IsDir(yuzu_path)) { |
| 94 | yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR; | 95 | yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR; |
| 95 | } | 96 | } |
| @@ -101,8 +102,9 @@ public: | |||
| 101 | yuzu_path_cache = yuzu_path / CACHE_DIR; | 102 | yuzu_path_cache = yuzu_path / CACHE_DIR; |
| 102 | yuzu_path_config = yuzu_path / CONFIG_DIR; | 103 | yuzu_path_config = yuzu_path / CONFIG_DIR; |
| 103 | #else | 104 | #else |
| 105 | #ifdef YUZU_ENABLE_PORTABLE | ||
| 104 | yuzu_path = GetCurrentDir() / PORTABLE_DIR; | 106 | yuzu_path = GetCurrentDir() / PORTABLE_DIR; |
| 105 | 107 | #endif | |
| 106 | if (Exists(yuzu_path) && IsDir(yuzu_path)) { | 108 | if (Exists(yuzu_path) && IsDir(yuzu_path)) { |
| 107 | yuzu_path_cache = yuzu_path / CACHE_DIR; | 109 | yuzu_path_cache = yuzu_path / CACHE_DIR; |
| 108 | yuzu_path_config = yuzu_path / CONFIG_DIR; | 110 | yuzu_path_config = yuzu_path / CONFIG_DIR; |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 16a58a750..4ecaf550b 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <version> | 4 | #include <version> |
| 5 | #include "common/settings_enums.h" | ||
| 5 | #if __cpp_lib_chrono >= 201907L | 6 | #if __cpp_lib_chrono >= 201907L |
| 6 | #include <chrono> | 7 | #include <chrono> |
| 7 | #include <exception> | 8 | #include <exception> |
| @@ -145,6 +146,10 @@ bool IsFastmemEnabled() { | |||
| 145 | return true; | 146 | return true; |
| 146 | } | 147 | } |
| 147 | 148 | ||
| 149 | bool IsDockedMode() { | ||
| 150 | return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; | ||
| 151 | } | ||
| 152 | |||
| 148 | float Volume() { | 153 | float Volume() { |
| 149 | if (values.audio_muted) { | 154 | if (values.audio_muted) { |
| 150 | return 0.0f; | 155 | return 0.0f; |
| @@ -154,6 +159,8 @@ float Volume() { | |||
| 154 | 159 | ||
| 155 | const char* TranslateCategory(Category category) { | 160 | const char* TranslateCategory(Category category) { |
| 156 | switch (category) { | 161 | switch (category) { |
| 162 | case Category::Android: | ||
| 163 | return "Android"; | ||
| 157 | case Category::Audio: | 164 | case Category::Audio: |
| 158 | return "Audio"; | 165 | return "Audio"; |
| 159 | case Category::Core: | 166 | case Category::Core: |
diff --git a/src/common/settings.h b/src/common/settings.h index 4407c1e6d..b15213bd7 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -379,7 +379,13 @@ struct Values { | |||
| 379 | 379 | ||
| 380 | Setting<s32> current_user{linkage, 0, "current_user", Category::System}; | 380 | Setting<s32> current_user{linkage, 0, "current_user", Category::System}; |
| 381 | 381 | ||
| 382 | SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System}; | 382 | SwitchableSetting<ConsoleMode> use_docked_mode{linkage, |
| 383 | ConsoleMode::Docked, | ||
| 384 | "use_docked_mode", | ||
| 385 | Category::System, | ||
| 386 | Specialization::Radio, | ||
| 387 | true, | ||
| 388 | true}; | ||
| 383 | 389 | ||
| 384 | // Controls | 390 | // Controls |
| 385 | InputSetting<std::array<PlayerInput, 10>> players; | 391 | InputSetting<std::array<PlayerInput, 10>> players; |
| @@ -519,6 +525,8 @@ bool IsGPULevelHigh(); | |||
| 519 | 525 | ||
| 520 | bool IsFastmemEnabled(); | 526 | bool IsFastmemEnabled(); |
| 521 | 527 | ||
| 528 | bool IsDockedMode(); | ||
| 529 | |||
| 522 | float Volume(); | 530 | float Volume(); |
| 523 | 531 | ||
| 524 | std::string GetTimeZoneString(TimeZone time_zone); | 532 | std::string GetTimeZoneString(TimeZone time_zone); |
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp index 137b65d5f..5960b78aa 100644 --- a/src/common/settings_common.cpp +++ b/src/common/settings_common.cpp | |||
| @@ -14,6 +14,7 @@ BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Categ | |||
| 14 | : label{name}, category{category_}, id{linkage.count}, save{save_}, | 14 | : label{name}, category{category_}, id{linkage.count}, save{save_}, |
| 15 | runtime_modifiable{runtime_modifiable_}, specialization{specialization_}, | 15 | runtime_modifiable{runtime_modifiable_}, specialization{specialization_}, |
| 16 | other_setting{other_setting_} { | 16 | other_setting{other_setting_} { |
| 17 | linkage.by_key.insert({name, this}); | ||
| 17 | linkage.by_category[category].push_back(this); | 18 | linkage.by_category[category].push_back(this); |
| 18 | linkage.count++; | 19 | linkage.count++; |
| 19 | } | 20 | } |
diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 2efb329b0..5b170dfd5 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | namespace Settings { | 12 | namespace Settings { |
| 13 | 13 | ||
| 14 | enum class Category : u32 { | 14 | enum class Category : u32 { |
| 15 | Android, | ||
| 15 | Audio, | 16 | Audio, |
| 16 | Core, | 17 | Core, |
| 17 | Cpu, | 18 | Cpu, |
| @@ -56,6 +57,7 @@ enum Specialization : u8 { | |||
| 56 | Scalar = 5, // Values are continuous | 57 | Scalar = 5, // Values are continuous |
| 57 | Countable = 6, // Can be stepped through | 58 | Countable = 6, // Can be stepped through |
| 58 | Paired = 7, // Another setting is associated with this setting | 59 | Paired = 7, // Another setting is associated with this setting |
| 60 | Radio = 8, // Setting should be presented in a radio group | ||
| 59 | 61 | ||
| 60 | Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage | 62 | Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage |
| 61 | }; | 63 | }; |
| @@ -67,6 +69,7 @@ public: | |||
| 67 | explicit Linkage(u32 initial_count = 0); | 69 | explicit Linkage(u32 initial_count = 0); |
| 68 | ~Linkage(); | 70 | ~Linkage(); |
| 69 | std::map<Category, std::vector<BasicSetting*>> by_category{}; | 71 | std::map<Category, std::vector<BasicSetting*>> by_category{}; |
| 72 | std::map<std::string, Settings::BasicSetting*> by_key{}; | ||
| 70 | std::vector<std::function<void()>> restore_functions{}; | 73 | std::vector<std::function<void()>> restore_functions{}; |
| 71 | u32 count; | 74 | u32 count; |
| 72 | }; | 75 | }; |
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index e7cb59ea5..815cafe15 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h | |||
| @@ -146,6 +146,8 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); | |||
| 146 | 146 | ||
| 147 | ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); | 147 | ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); |
| 148 | 148 | ||
| 149 | ENUM(ConsoleMode, Handheld, Docked); | ||
| 150 | |||
| 149 | template <typename Type> | 151 | template <typename Type> |
| 150 | inline std::string CanonicalizeEnum(Type id) { | 152 | inline std::string CanonicalizeEnum(Type id) { |
| 151 | const auto group = EnumMetadata<Type>::Canonicalizations(); | 153 | const auto group = EnumMetadata<Type>::Canonicalizations(); |
diff --git a/src/common/swap.h b/src/common/swap.h index 085baaf9a..fde343e45 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) { | |||
| 460 | return i & v.swap(); | 460 | return i & v.swap(); |
| 461 | } | 461 | } |
| 462 | 462 | ||
| 463 | template <typename S, typename T, typename F> | ||
| 464 | S operator&(const swap_struct_t<T, F> v, const S& i) { | ||
| 465 | return static_cast<S>(v.swap() & i); | ||
| 466 | } | ||
| 467 | |||
| 468 | // Comparison | 463 | // Comparison |
| 469 | template <typename S, typename T, typename F> | 464 | template <typename S, typename T, typename F> |
| 470 | bool operator<(const S& p, const swap_struct_t<T, F> v) { | 465 | bool operator<(const S& p, const swap_struct_t<T, F> v) { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 012648d69..c33910ade 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -584,13 +584,23 @@ add_library(core STATIC | |||
| 584 | hle/service/lm/lm.h | 584 | hle/service/lm/lm.h |
| 585 | hle/service/mig/mig.cpp | 585 | hle/service/mig/mig.cpp |
| 586 | hle/service/mig/mig.h | 586 | hle/service/mig/mig.h |
| 587 | hle/service/mii/types/char_info.cpp | ||
| 588 | hle/service/mii/types/char_info.h | ||
| 589 | hle/service/mii/types/core_data.cpp | ||
| 590 | hle/service/mii/types/core_data.h | ||
| 591 | hle/service/mii/types/raw_data.cpp | ||
| 592 | hle/service/mii/types/raw_data.h | ||
| 593 | hle/service/mii/types/store_data.cpp | ||
| 594 | hle/service/mii/types/store_data.h | ||
| 595 | hle/service/mii/types/ver3_store_data.cpp | ||
| 596 | hle/service/mii/types/ver3_store_data.h | ||
| 587 | hle/service/mii/mii.cpp | 597 | hle/service/mii/mii.cpp |
| 588 | hle/service/mii/mii.h | 598 | hle/service/mii/mii.h |
| 589 | hle/service/mii/mii_manager.cpp | 599 | hle/service/mii/mii_manager.cpp |
| 590 | hle/service/mii/mii_manager.h | 600 | hle/service/mii/mii_manager.h |
| 591 | hle/service/mii/raw_data.cpp | 601 | hle/service/mii/mii_result.h |
| 592 | hle/service/mii/raw_data.h | 602 | hle/service/mii/mii_types.h |
| 593 | hle/service/mii/types.h | 603 | hle/service/mii/mii_util.h |
| 594 | hle/service/mm/mm_u.cpp | 604 | hle/service/mm/mm_u.cpp |
| 595 | hle/service/mm/mm_u.h | 605 | hle/service/mm/mm_u.h |
| 596 | hle/service/mnpp/mnpp_app.cpp | 606 | hle/service/mnpp/mnpp_app.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 9c5246a56..2d6e61398 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -273,7 +273,8 @@ struct System::Impl { | |||
| 273 | time_manager.Initialize(); | 273 | time_manager.Initialize(); |
| 274 | 274 | ||
| 275 | is_powered_on = true; | 275 | is_powered_on = true; |
| 276 | exit_lock = false; | 276 | exit_locked = false; |
| 277 | exit_requested = false; | ||
| 277 | 278 | ||
| 278 | microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); | 279 | microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); |
| 279 | microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); | 280 | microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); |
| @@ -398,7 +399,8 @@ struct System::Impl { | |||
| 398 | } | 399 | } |
| 399 | 400 | ||
| 400 | is_powered_on = false; | 401 | is_powered_on = false; |
| 401 | exit_lock = false; | 402 | exit_locked = false; |
| 403 | exit_requested = false; | ||
| 402 | 404 | ||
| 403 | if (gpu_core != nullptr) { | 405 | if (gpu_core != nullptr) { |
| 404 | gpu_core->NotifyShutdown(); | 406 | gpu_core->NotifyShutdown(); |
| @@ -509,7 +511,8 @@ struct System::Impl { | |||
| 509 | 511 | ||
| 510 | CpuManager cpu_manager; | 512 | CpuManager cpu_manager; |
| 511 | std::atomic_bool is_powered_on{}; | 513 | std::atomic_bool is_powered_on{}; |
| 512 | bool exit_lock = false; | 514 | bool exit_locked = false; |
| 515 | bool exit_requested = false; | ||
| 513 | 516 | ||
| 514 | bool nvdec_active{}; | 517 | bool nvdec_active{}; |
| 515 | 518 | ||
| @@ -561,6 +564,8 @@ struct System::Impl { | |||
| 561 | 564 | ||
| 562 | std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> | 565 | std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> |
| 563 | gpu_dirty_memory_write_manager{}; | 566 | gpu_dirty_memory_write_manager{}; |
| 567 | |||
| 568 | std::deque<std::vector<u8>> user_channel; | ||
| 564 | }; | 569 | }; |
| 565 | 570 | ||
| 566 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 571 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| @@ -945,12 +950,20 @@ const Service::Time::TimeManager& System::GetTimeManager() const { | |||
| 945 | return impl->time_manager; | 950 | return impl->time_manager; |
| 946 | } | 951 | } |
| 947 | 952 | ||
| 948 | void System::SetExitLock(bool locked) { | 953 | void System::SetExitLocked(bool locked) { |
| 949 | impl->exit_lock = locked; | 954 | impl->exit_locked = locked; |
| 955 | } | ||
| 956 | |||
| 957 | bool System::GetExitLocked() const { | ||
| 958 | return impl->exit_locked; | ||
| 950 | } | 959 | } |
| 951 | 960 | ||
| 952 | bool System::GetExitLock() const { | 961 | void System::SetExitRequested(bool requested) { |
| 953 | return impl->exit_lock; | 962 | impl->exit_requested = requested; |
| 963 | } | ||
| 964 | |||
| 965 | bool System::GetExitRequested() const { | ||
| 966 | return impl->exit_requested; | ||
| 954 | } | 967 | } |
| 955 | 968 | ||
| 956 | void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { | 969 | void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { |
| @@ -1027,6 +1040,10 @@ void System::ExecuteProgram(std::size_t program_index) { | |||
| 1027 | } | 1040 | } |
| 1028 | } | 1041 | } |
| 1029 | 1042 | ||
| 1043 | std::deque<std::vector<u8>>& System::GetUserChannel() { | ||
| 1044 | return impl->user_channel; | ||
| 1045 | } | ||
| 1046 | |||
| 1030 | void System::RegisterExitCallback(ExitCallback&& callback) { | 1047 | void System::RegisterExitCallback(ExitCallback&& callback) { |
| 1031 | impl->exit_callback = std::move(callback); | 1048 | impl->exit_callback = std::move(callback); |
| 1032 | } | 1049 | } |
diff --git a/src/core/core.h b/src/core/core.h index c70ea1965..fba312125 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include <deque> | ||
| 7 | #include <functional> | 8 | #include <functional> |
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| @@ -412,8 +413,11 @@ public: | |||
| 412 | /// Gets an immutable reference to the Room Network. | 413 | /// Gets an immutable reference to the Room Network. |
| 413 | [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; | 414 | [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; |
| 414 | 415 | ||
| 415 | void SetExitLock(bool locked); | 416 | void SetExitLocked(bool locked); |
| 416 | [[nodiscard]] bool GetExitLock() const; | 417 | bool GetExitLocked() const; |
| 418 | |||
| 419 | void SetExitRequested(bool requested); | ||
| 420 | bool GetExitRequested() const; | ||
| 417 | 421 | ||
| 418 | void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); | 422 | void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); |
| 419 | [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; | 423 | [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; |
| @@ -456,6 +460,12 @@ public: | |||
| 456 | */ | 460 | */ |
| 457 | void ExecuteProgram(std::size_t program_index); | 461 | void ExecuteProgram(std::size_t program_index); |
| 458 | 462 | ||
| 463 | /** | ||
| 464 | * Gets a reference to the user channel stack. | ||
| 465 | * It is used to transfer data between programs. | ||
| 466 | */ | ||
| 467 | [[nodiscard]] std::deque<std::vector<u8>>& GetUserChannel(); | ||
| 468 | |||
| 459 | /// Type used for the frontend to designate a callback for System to exit the application. | 469 | /// Type used for the frontend to designate a callback for System to exit the application. |
| 460 | using ExitCallback = std::function<void()>; | 470 | using ExitCallback = std::function<void()>; |
| 461 | 471 | ||
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 4ff2c50e5..e13c5cdc7 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -35,7 +35,6 @@ namespace Core::Crypto { | |||
| 35 | namespace { | 35 | namespace { |
| 36 | 36 | ||
| 37 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; | 37 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; |
| 38 | constexpr u64 FULL_TICKET_SIZE = 0x400; | ||
| 39 | 38 | ||
| 40 | using Common::AsArray; | 39 | using Common::AsArray; |
| 41 | 40 | ||
| @@ -156,6 +155,10 @@ u64 GetSignatureTypePaddingSize(SignatureType type) { | |||
| 156 | UNREACHABLE(); | 155 | UNREACHABLE(); |
| 157 | } | 156 | } |
| 158 | 157 | ||
| 158 | bool Ticket::IsValid() const { | ||
| 159 | return !std::holds_alternative<std::monostate>(data); | ||
| 160 | } | ||
| 161 | |||
| 159 | SignatureType Ticket::GetSignatureType() const { | 162 | SignatureType Ticket::GetSignatureType() const { |
| 160 | if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { | 163 | if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { |
| 161 | return ticket->sig_type; | 164 | return ticket->sig_type; |
| @@ -210,6 +213,54 @@ Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& righ | |||
| 210 | return Ticket{out}; | 213 | return Ticket{out}; |
| 211 | } | 214 | } |
| 212 | 215 | ||
| 216 | Ticket Ticket::Read(const FileSys::VirtualFile& file) { | ||
| 217 | // Attempt to read up to the largest ticket size, and make sure we read at least a signature | ||
| 218 | // type. | ||
| 219 | std::array<u8, sizeof(RSA4096Ticket)> raw_data{}; | ||
| 220 | auto read_size = file->Read(raw_data.data(), raw_data.size(), 0); | ||
| 221 | if (read_size < sizeof(SignatureType)) { | ||
| 222 | LOG_WARNING(Crypto, "Attempted to read ticket file with invalid size {}.", read_size); | ||
| 223 | return Ticket{std::monostate()}; | ||
| 224 | } | ||
| 225 | return Read(std::span{raw_data}); | ||
| 226 | } | ||
| 227 | |||
| 228 | Ticket Ticket::Read(std::span<const u8> raw_data) { | ||
| 229 | // Some tools read only 0x180 bytes of ticket data instead of 0x2C0, so | ||
| 230 | // just make sure we have at least the bare minimum of data to work with. | ||
| 231 | SignatureType sig_type; | ||
| 232 | if (raw_data.size() < sizeof(SignatureType)) { | ||
| 233 | LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid size {}.", | ||
| 234 | raw_data.size()); | ||
| 235 | return Ticket{std::monostate()}; | ||
| 236 | } | ||
| 237 | std::memcpy(&sig_type, raw_data.data(), sizeof(sig_type)); | ||
| 238 | |||
| 239 | switch (sig_type) { | ||
| 240 | case SignatureType::RSA_4096_SHA1: | ||
| 241 | case SignatureType::RSA_4096_SHA256: { | ||
| 242 | RSA4096Ticket ticket{}; | ||
| 243 | std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); | ||
| 244 | return Ticket{ticket}; | ||
| 245 | } | ||
| 246 | case SignatureType::RSA_2048_SHA1: | ||
| 247 | case SignatureType::RSA_2048_SHA256: { | ||
| 248 | RSA2048Ticket ticket{}; | ||
| 249 | std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); | ||
| 250 | return Ticket{ticket}; | ||
| 251 | } | ||
| 252 | case SignatureType::ECDSA_SHA1: | ||
| 253 | case SignatureType::ECDSA_SHA256: { | ||
| 254 | ECDSATicket ticket{}; | ||
| 255 | std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); | ||
| 256 | return Ticket{ticket}; | ||
| 257 | } | ||
| 258 | default: | ||
| 259 | LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid type {}.", sig_type); | ||
| 260 | return Ticket{std::monostate()}; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 213 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { | 264 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { |
| 214 | Key128 out{}; | 265 | Key128 out{}; |
| 215 | 266 | ||
| @@ -290,9 +341,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { | |||
| 290 | } | 341 | } |
| 291 | } | 342 | } |
| 292 | 343 | ||
| 293 | RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { | 344 | void KeyManager::DeriveETicketRSAKey() { |
| 294 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { | 345 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { |
| 295 | return {}; | 346 | return; |
| 296 | } | 347 | } |
| 297 | 348 | ||
| 298 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); | 349 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); |
| @@ -304,12 +355,12 @@ RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { | |||
| 304 | rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, | 355 | rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, |
| 305 | extended_dec.data(), Op::Decrypt); | 356 | extended_dec.data(), Op::Decrypt); |
| 306 | 357 | ||
| 307 | RSAKeyPair<2048> rsa_key{}; | 358 | std::memcpy(eticket_rsa_keypair.decryption_key.data(), extended_dec.data(), |
| 308 | std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); | 359 | eticket_rsa_keypair.decryption_key.size()); |
| 309 | std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); | 360 | std::memcpy(eticket_rsa_keypair.modulus.data(), extended_dec.data() + 0x100, |
| 310 | std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); | 361 | eticket_rsa_keypair.modulus.size()); |
| 311 | 362 | std::memcpy(eticket_rsa_keypair.exponent.data(), extended_dec.data() + 0x200, | |
| 312 | return rsa_key; | 363 | eticket_rsa_keypair.exponent.size()); |
| 313 | } | 364 | } |
| 314 | 365 | ||
| 315 | Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { | 366 | Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { |
| @@ -447,10 +498,12 @@ std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) { | |||
| 447 | for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { | 498 | for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { |
| 448 | if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && | 499 | if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && |
| 449 | buffer[offset + 3] == 0x0) { | 500 | buffer[offset + 3] == 0x0) { |
| 450 | out.emplace_back(); | 501 | // NOTE: Assumes ticket blob will only contain RSA-2048 tickets. |
| 451 | auto& next = out.back(); | 502 | auto ticket = Ticket::Read(std::span{buffer.data() + offset, sizeof(RSA2048Ticket)}); |
| 452 | std::memcpy(&next, buffer.data() + offset, sizeof(Ticket)); | 503 | offset += sizeof(RSA2048Ticket); |
| 453 | offset += FULL_TICKET_SIZE; | 504 | if (ticket.IsValid()) { |
| 505 | out.push_back(ticket); | ||
| 506 | } | ||
| 454 | } | 507 | } |
| 455 | } | 508 | } |
| 456 | 509 | ||
| @@ -503,25 +556,36 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { | |||
| 503 | return offset; | 556 | return offset; |
| 504 | } | 557 | } |
| 505 | 558 | ||
| 506 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | 559 | std::optional<Key128> KeyManager::ParseTicketTitleKey(const Ticket& ticket) { |
| 507 | const RSAKeyPair<2048>& key) { | 560 | if (!ticket.IsValid()) { |
| 561 | LOG_WARNING(Crypto, "Attempted to parse title key of invalid ticket."); | ||
| 562 | return std::nullopt; | ||
| 563 | } | ||
| 564 | |||
| 565 | if (ticket.GetData().rights_id == Key128{}) { | ||
| 566 | LOG_WARNING(Crypto, "Attempted to parse title key of ticket with no rights ID."); | ||
| 567 | return std::nullopt; | ||
| 568 | } | ||
| 569 | |||
| 508 | const auto issuer = ticket.GetData().issuer; | 570 | const auto issuer = ticket.GetData().issuer; |
| 509 | if (IsAllZeroArray(issuer)) { | 571 | if (IsAllZeroArray(issuer)) { |
| 572 | LOG_WARNING(Crypto, "Attempted to parse title key of ticket with invalid issuer."); | ||
| 510 | return std::nullopt; | 573 | return std::nullopt; |
| 511 | } | 574 | } |
| 575 | |||
| 512 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { | 576 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { |
| 513 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); | 577 | LOG_WARNING(Crypto, "Parsing ticket with non-standard certificate authority."); |
| 514 | } | 578 | } |
| 515 | 579 | ||
| 516 | Key128 rights_id = ticket.GetData().rights_id; | 580 | if (ticket.GetData().type == TitleKeyType::Common) { |
| 517 | 581 | return ticket.GetData().title_key_common; | |
| 518 | if (rights_id == Key128{}) { | ||
| 519 | return std::nullopt; | ||
| 520 | } | 582 | } |
| 521 | 583 | ||
| 522 | if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), | 584 | if (eticket_rsa_keypair == RSAKeyPair<2048>{}) { |
| 523 | ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { | 585 | LOG_WARNING( |
| 524 | return std::make_pair(rights_id, ticket.GetData().title_key_common); | 586 | Crypto, |
| 587 | "Skipping personalized ticket title key parsing due to missing ETicket RSA key-pair."); | ||
| 588 | return std::nullopt; | ||
| 525 | } | 589 | } |
| 526 | 590 | ||
| 527 | mbedtls_mpi D; // RSA Private Exponent | 591 | mbedtls_mpi D; // RSA Private Exponent |
| @@ -534,9 +598,12 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 534 | mbedtls_mpi_init(&S); | 598 | mbedtls_mpi_init(&S); |
| 535 | mbedtls_mpi_init(&M); | 599 | mbedtls_mpi_init(&M); |
| 536 | 600 | ||
| 537 | mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); | 601 | const auto& title_key_block = ticket.GetData().title_key_block; |
| 538 | mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); | 602 | mbedtls_mpi_read_binary(&D, eticket_rsa_keypair.decryption_key.data(), |
| 539 | mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100); | 603 | eticket_rsa_keypair.decryption_key.size()); |
| 604 | mbedtls_mpi_read_binary(&N, eticket_rsa_keypair.modulus.data(), | ||
| 605 | eticket_rsa_keypair.modulus.size()); | ||
| 606 | mbedtls_mpi_read_binary(&S, title_key_block.data(), title_key_block.size()); | ||
| 540 | 607 | ||
| 541 | mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); | 608 | mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); |
| 542 | 609 | ||
| @@ -564,8 +631,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 564 | 631 | ||
| 565 | Key128 key_temp{}; | 632 | Key128 key_temp{}; |
| 566 | std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); | 633 | std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); |
| 567 | 634 | return key_temp; | |
| 568 | return std::make_pair(rights_id, key_temp); | ||
| 569 | } | 635 | } |
| 570 | 636 | ||
| 571 | KeyManager::KeyManager() { | 637 | KeyManager::KeyManager() { |
| @@ -669,6 +735,14 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti | |||
| 669 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); | 735 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); |
| 670 | } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { | 736 | } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { |
| 671 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); | 737 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); |
| 738 | } else if (out[0].compare(0, 19, "eticket_rsa_keypair") == 0) { | ||
| 739 | const auto key_data = Common::HexStringToArray<528>(out[1]); | ||
| 740 | std::memcpy(eticket_rsa_keypair.decryption_key.data(), key_data.data(), | ||
| 741 | eticket_rsa_keypair.decryption_key.size()); | ||
| 742 | std::memcpy(eticket_rsa_keypair.modulus.data(), key_data.data() + 0x100, | ||
| 743 | eticket_rsa_keypair.modulus.size()); | ||
| 744 | std::memcpy(eticket_rsa_keypair.exponent.data(), key_data.data() + 0x200, | ||
| 745 | eticket_rsa_keypair.exponent.size()); | ||
| 672 | } else { | 746 | } else { |
| 673 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { | 747 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { |
| 674 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { | 748 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { |
| @@ -1110,56 +1184,38 @@ void KeyManager::DeriveETicket(PartitionDataManager& data, | |||
| 1110 | 1184 | ||
| 1111 | eticket_extended_kek = data.GetETicketExtendedKek(); | 1185 | eticket_extended_kek = data.GetETicketExtendedKek(); |
| 1112 | WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); | 1186 | WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); |
| 1187 | DeriveETicketRSAKey(); | ||
| 1113 | PopulateTickets(); | 1188 | PopulateTickets(); |
| 1114 | } | 1189 | } |
| 1115 | 1190 | ||
| 1116 | void KeyManager::PopulateTickets() { | 1191 | void KeyManager::PopulateTickets() { |
| 1117 | const auto rsa_key = GetETicketRSAKey(); | 1192 | if (ticket_databases_loaded) { |
| 1118 | |||
| 1119 | if (rsa_key == RSAKeyPair<2048>{}) { | ||
| 1120 | return; | 1193 | return; |
| 1121 | } | 1194 | } |
| 1195 | ticket_databases_loaded = true; | ||
| 1122 | 1196 | ||
| 1123 | if (!common_tickets.empty() && !personal_tickets.empty()) { | 1197 | std::vector<Ticket> tickets; |
| 1124 | return; | ||
| 1125 | } | ||
| 1126 | 1198 | ||
| 1127 | const auto system_save_e1_path = | 1199 | const auto system_save_e1_path = |
| 1128 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; | 1200 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; |
| 1129 | 1201 | if (Common::FS::Exists(system_save_e1_path)) { | |
| 1130 | const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, | 1202 | const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, |
| 1131 | Common::FS::FileType::BinaryFile}; | 1203 | Common::FS::FileType::BinaryFile}; |
| 1204 | const auto blob1 = GetTicketblob(save_e1); | ||
| 1205 | tickets.insert(tickets.end(), blob1.begin(), blob1.end()); | ||
| 1206 | } | ||
| 1132 | 1207 | ||
| 1133 | const auto system_save_e2_path = | 1208 | const auto system_save_e2_path = |
| 1134 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; | 1209 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; |
| 1210 | if (Common::FS::Exists(system_save_e2_path)) { | ||
| 1211 | const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, | ||
| 1212 | Common::FS::FileType::BinaryFile}; | ||
| 1213 | const auto blob2 = GetTicketblob(save_e2); | ||
| 1214 | tickets.insert(tickets.end(), blob2.begin(), blob2.end()); | ||
| 1215 | } | ||
| 1135 | 1216 | ||
| 1136 | const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, | 1217 | for (const auto& ticket : tickets) { |
| 1137 | Common::FS::FileType::BinaryFile}; | 1218 | AddTicket(ticket); |
| 1138 | |||
| 1139 | const auto blob2 = GetTicketblob(save_e2); | ||
| 1140 | auto res = GetTicketblob(save_e1); | ||
| 1141 | |||
| 1142 | const auto idx = res.size(); | ||
| 1143 | res.insert(res.end(), blob2.begin(), blob2.end()); | ||
| 1144 | |||
| 1145 | for (std::size_t i = 0; i < res.size(); ++i) { | ||
| 1146 | const auto common = i < idx; | ||
| 1147 | const auto pair = ParseTicket(res[i], rsa_key); | ||
| 1148 | if (!pair) { | ||
| 1149 | continue; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | const auto& [rid, key] = *pair; | ||
| 1153 | u128 rights_id; | ||
| 1154 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | ||
| 1155 | |||
| 1156 | if (common) { | ||
| 1157 | common_tickets[rights_id] = res[i]; | ||
| 1158 | } else { | ||
| 1159 | personal_tickets[rights_id] = res[i]; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 1163 | } | 1219 | } |
| 1164 | } | 1220 | } |
| 1165 | 1221 | ||
| @@ -1291,41 +1347,33 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const { | |||
| 1291 | return personal_tickets; | 1347 | return personal_tickets; |
| 1292 | } | 1348 | } |
| 1293 | 1349 | ||
| 1294 | bool KeyManager::AddTicketCommon(Ticket raw) { | 1350 | bool KeyManager::AddTicket(const Ticket& ticket) { |
| 1295 | const auto rsa_key = GetETicketRSAKey(); | 1351 | if (!ticket.IsValid()) { |
| 1296 | if (rsa_key == RSAKeyPair<2048>{}) { | 1352 | LOG_WARNING(Crypto, "Attempted to add invalid ticket."); |
| 1297 | return false; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | const auto pair = ParseTicket(raw, rsa_key); | ||
| 1301 | if (!pair) { | ||
| 1302 | return false; | 1353 | return false; |
| 1303 | } | 1354 | } |
| 1304 | 1355 | ||
| 1305 | const auto& [rid, key] = *pair; | 1356 | const auto& rid = ticket.GetData().rights_id; |
| 1306 | u128 rights_id; | 1357 | u128 rights_id; |
| 1307 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | 1358 | std::memcpy(rights_id.data(), rid.data(), rid.size()); |
| 1308 | common_tickets[rights_id] = raw; | 1359 | if (ticket.GetData().type == Core::Crypto::TitleKeyType::Common) { |
| 1309 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | 1360 | common_tickets[rights_id] = ticket; |
| 1310 | return true; | 1361 | } else { |
| 1311 | } | 1362 | personal_tickets[rights_id] = ticket; |
| 1363 | } | ||
| 1312 | 1364 | ||
| 1313 | bool KeyManager::AddTicketPersonalized(Ticket raw) { | 1365 | if (HasKey(S128KeyType::Titlekey, rights_id[1], rights_id[0])) { |
| 1314 | const auto rsa_key = GetETicketRSAKey(); | 1366 | LOG_DEBUG(Crypto, |
| 1315 | if (rsa_key == RSAKeyPair<2048>{}) { | 1367 | "Skipping parsing title key from ticket for known rights ID {:016X}{:016X}.", |
| 1316 | return false; | 1368 | rights_id[1], rights_id[0]); |
| 1369 | return true; | ||
| 1317 | } | 1370 | } |
| 1318 | 1371 | ||
| 1319 | const auto pair = ParseTicket(raw, rsa_key); | 1372 | const auto key = ParseTicketTitleKey(ticket); |
| 1320 | if (!pair) { | 1373 | if (!key) { |
| 1321 | return false; | 1374 | return false; |
| 1322 | } | 1375 | } |
| 1323 | 1376 | SetKey(S128KeyType::Titlekey, key.value(), rights_id[1], rights_id[0]); | |
| 1324 | const auto& [rid, key] = *pair; | ||
| 1325 | u128 rights_id; | ||
| 1326 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | ||
| 1327 | common_tickets[rights_id] = raw; | ||
| 1328 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 1329 | return true; | 1377 | return true; |
| 1330 | } | 1378 | } |
| 1331 | } // namespace Core::Crypto | 1379 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 8c864503b..2250eccec 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <filesystem> | 7 | #include <filesystem> |
| 8 | #include <map> | 8 | #include <map> |
| 9 | #include <optional> | 9 | #include <optional> |
| 10 | #include <span> | ||
| 10 | #include <string> | 11 | #include <string> |
| 11 | 12 | ||
| 12 | #include <variant> | 13 | #include <variant> |
| @@ -29,8 +30,6 @@ enum class ResultStatus : u16; | |||
| 29 | 30 | ||
| 30 | namespace Core::Crypto { | 31 | namespace Core::Crypto { |
| 31 | 32 | ||
| 32 | constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180; | ||
| 33 | |||
| 34 | using Key128 = std::array<u8, 0x10>; | 33 | using Key128 = std::array<u8, 0x10>; |
| 35 | using Key256 = std::array<u8, 0x20>; | 34 | using Key256 = std::array<u8, 0x20>; |
| 36 | using SHA256Hash = std::array<u8, 0x20>; | 35 | using SHA256Hash = std::array<u8, 0x20>; |
| @@ -82,6 +81,7 @@ struct RSA4096Ticket { | |||
| 82 | INSERT_PADDING_BYTES(0x3C); | 81 | INSERT_PADDING_BYTES(0x3C); |
| 83 | TicketData data; | 82 | TicketData data; |
| 84 | }; | 83 | }; |
| 84 | static_assert(sizeof(RSA4096Ticket) == 0x500, "RSA4096Ticket has incorrect size."); | ||
| 85 | 85 | ||
| 86 | struct RSA2048Ticket { | 86 | struct RSA2048Ticket { |
| 87 | SignatureType sig_type; | 87 | SignatureType sig_type; |
| @@ -89,6 +89,7 @@ struct RSA2048Ticket { | |||
| 89 | INSERT_PADDING_BYTES(0x3C); | 89 | INSERT_PADDING_BYTES(0x3C); |
| 90 | TicketData data; | 90 | TicketData data; |
| 91 | }; | 91 | }; |
| 92 | static_assert(sizeof(RSA2048Ticket) == 0x400, "RSA2048Ticket has incorrect size."); | ||
| 92 | 93 | ||
| 93 | struct ECDSATicket { | 94 | struct ECDSATicket { |
| 94 | SignatureType sig_type; | 95 | SignatureType sig_type; |
| @@ -96,16 +97,41 @@ struct ECDSATicket { | |||
| 96 | INSERT_PADDING_BYTES(0x40); | 97 | INSERT_PADDING_BYTES(0x40); |
| 97 | TicketData data; | 98 | TicketData data; |
| 98 | }; | 99 | }; |
| 100 | static_assert(sizeof(ECDSATicket) == 0x340, "ECDSATicket has incorrect size."); | ||
| 99 | 101 | ||
| 100 | struct Ticket { | 102 | struct Ticket { |
| 101 | std::variant<RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; | 103 | std::variant<std::monostate, RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; |
| 102 | 104 | ||
| 103 | SignatureType GetSignatureType() const; | 105 | [[nodiscard]] bool IsValid() const; |
| 104 | TicketData& GetData(); | 106 | [[nodiscard]] SignatureType GetSignatureType() const; |
| 105 | const TicketData& GetData() const; | 107 | [[nodiscard]] TicketData& GetData(); |
| 106 | u64 GetSize() const; | 108 | [[nodiscard]] const TicketData& GetData() const; |
| 107 | 109 | [[nodiscard]] u64 GetSize() const; | |
| 110 | |||
| 111 | /** | ||
| 112 | * Synthesizes a common ticket given a title key and rights ID. | ||
| 113 | * | ||
| 114 | * @param title_key Title key to store in the ticket. | ||
| 115 | * @param rights_id Rights ID the ticket is for. | ||
| 116 | * @return The synthesized common ticket. | ||
| 117 | */ | ||
| 108 | static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id); | 118 | static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id); |
| 119 | |||
| 120 | /** | ||
| 121 | * Reads a ticket from a file. | ||
| 122 | * | ||
| 123 | * @param file File to read the ticket from. | ||
| 124 | * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false. | ||
| 125 | */ | ||
| 126 | static Ticket Read(const FileSys::VirtualFile& file); | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Reads a ticket from a memory buffer. | ||
| 130 | * | ||
| 131 | * @param raw_data Buffer to read the ticket from. | ||
| 132 | * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false. | ||
| 133 | */ | ||
| 134 | static Ticket Read(std::span<const u8> raw_data); | ||
| 109 | }; | 135 | }; |
| 110 | 136 | ||
| 111 | static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); | 137 | static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); |
| @@ -264,8 +290,7 @@ public: | |||
| 264 | const std::map<u128, Ticket>& GetCommonTickets() const; | 290 | const std::map<u128, Ticket>& GetCommonTickets() const; |
| 265 | const std::map<u128, Ticket>& GetPersonalizedTickets() const; | 291 | const std::map<u128, Ticket>& GetPersonalizedTickets() const; |
| 266 | 292 | ||
| 267 | bool AddTicketCommon(Ticket raw); | 293 | bool AddTicket(const Ticket& ticket); |
| 268 | bool AddTicketPersonalized(Ticket raw); | ||
| 269 | 294 | ||
| 270 | void ReloadKeys(); | 295 | void ReloadKeys(); |
| 271 | bool AreKeysLoaded() const; | 296 | bool AreKeysLoaded() const; |
| @@ -279,10 +304,12 @@ private: | |||
| 279 | // Map from rights ID to ticket | 304 | // Map from rights ID to ticket |
| 280 | std::map<u128, Ticket> common_tickets; | 305 | std::map<u128, Ticket> common_tickets; |
| 281 | std::map<u128, Ticket> personal_tickets; | 306 | std::map<u128, Ticket> personal_tickets; |
| 307 | bool ticket_databases_loaded = false; | ||
| 282 | 308 | ||
| 283 | std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; | 309 | std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; |
| 284 | std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; | 310 | std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; |
| 285 | std::array<u8, 576> eticket_extended_kek{}; | 311 | std::array<u8, 576> eticket_extended_kek{}; |
| 312 | RSAKeyPair<2048> eticket_rsa_keypair{}; | ||
| 286 | 313 | ||
| 287 | bool dev_mode; | 314 | bool dev_mode; |
| 288 | void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); | 315 | void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); |
| @@ -293,10 +320,13 @@ private: | |||
| 293 | 320 | ||
| 294 | void DeriveGeneralPurposeKeys(std::size_t crypto_revision); | 321 | void DeriveGeneralPurposeKeys(std::size_t crypto_revision); |
| 295 | 322 | ||
| 296 | RSAKeyPair<2048> GetETicketRSAKey() const; | 323 | void DeriveETicketRSAKey(); |
| 297 | 324 | ||
| 298 | void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); | 325 | void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); |
| 299 | void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); | 326 | void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); |
| 327 | |||
| 328 | /// Parses the title key section of a ticket. | ||
| 329 | std::optional<Key128> ParseTicketTitleKey(const Ticket& ticket); | ||
| 300 | }; | 330 | }; |
| 301 | 331 | ||
| 302 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); | 332 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); |
| @@ -311,9 +341,4 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke | |||
| 311 | 341 | ||
| 312 | std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save); | 342 | std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save); |
| 313 | 343 | ||
| 314 | // Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority | ||
| 315 | // (offset 0x140-0x144 is zero) | ||
| 316 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | ||
| 317 | const RSAKeyPair<2048>& eticket_extended_key); | ||
| 318 | |||
| 319 | } // namespace Core::Crypto | 344 | } // namespace Core::Crypto |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 44e6852fe..7d2f0abb8 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -22,6 +22,10 @@ | |||
| 22 | 22 | ||
| 23 | namespace FileSys { | 23 | namespace FileSys { |
| 24 | 24 | ||
| 25 | static u8 MasterKeyIdForKeyGeneration(u8 key_generation) { | ||
| 26 | return std::max<u8>(key_generation, 1) - 1; | ||
| 27 | } | ||
| 28 | |||
| 25 | NCA::NCA(VirtualFile file_, const NCA* base_nca) | 29 | NCA::NCA(VirtualFile file_, const NCA* base_nca) |
| 26 | : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { | 30 | : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { |
| 27 | if (file == nullptr) { | 31 | if (file == nullptr) { |
| @@ -41,12 +45,17 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) | |||
| 41 | return; | 45 | return; |
| 42 | } | 46 | } |
| 43 | 47 | ||
| 48 | // Ensure we have the proper key area keys to continue. | ||
| 49 | const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration()); | ||
| 50 | if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) { | ||
| 51 | status = Loader::ResultStatus::ErrorMissingKeyAreaKey; | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | |||
| 44 | RightsId rights_id{}; | 55 | RightsId rights_id{}; |
| 45 | reader->GetRightsId(rights_id.data(), rights_id.size()); | 56 | reader->GetRightsId(rights_id.data(), rights_id.size()); |
| 46 | if (rights_id != RightsId{}) { | 57 | if (rights_id != RightsId{}) { |
| 47 | // External decryption key required; provide it here. | 58 | // External decryption key required; provide it here. |
| 48 | const auto key_generation = std::max<s32>(reader->GetKeyGeneration(), 1) - 1; | ||
| 49 | |||
| 50 | u128 rights_id_u128; | 59 | u128 rights_id_u128; |
| 51 | std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id)); | 60 | std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id)); |
| 52 | 61 | ||
| @@ -57,12 +66,12 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) | |||
| 57 | return; | 66 | return; |
| 58 | } | 67 | } |
| 59 | 68 | ||
| 60 | if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, key_generation)) { | 69 | if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { |
| 61 | status = Loader::ResultStatus::ErrorMissingTitlekek; | 70 | status = Loader::ResultStatus::ErrorMissingTitlekek; |
| 62 | return; | 71 | return; |
| 63 | } | 72 | } |
| 64 | 73 | ||
| 65 | auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, key_generation); | 74 | auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id); |
| 66 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB); | 75 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB); |
| 67 | cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), | 76 | cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), |
| 68 | Core::Crypto::Op::Decrypt); | 77 | Core::Crypto::Op::Decrypt); |
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 52c78020c..f4a774675 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp | |||
| @@ -45,6 +45,10 @@ CNMT::CNMT(CNMTHeader header_, OptionalHeader opt_header_, | |||
| 45 | 45 | ||
| 46 | CNMT::~CNMT() = default; | 46 | CNMT::~CNMT() = default; |
| 47 | 47 | ||
| 48 | const CNMTHeader& CNMT::GetHeader() const { | ||
| 49 | return header; | ||
| 50 | } | ||
| 51 | |||
| 48 | u64 CNMT::GetTitleID() const { | 52 | u64 CNMT::GetTitleID() const { |
| 49 | return header.title_id; | 53 | return header.title_id; |
| 50 | } | 54 | } |
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index c59ece010..68e463b5f 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h | |||
| @@ -89,6 +89,7 @@ public: | |||
| 89 | std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_); | 89 | std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_); |
| 90 | ~CNMT(); | 90 | ~CNMT(); |
| 91 | 91 | ||
| 92 | const CNMTHeader& GetHeader() const; | ||
| 92 | u64 GetTitleID() const; | 93 | u64 GetTitleID() const; |
| 93 | u32 GetTitleVersion() const; | 94 | u32 GetTitleVersion() const; |
| 94 | TitleType GetType() const; | 95 | TitleType GetType() const; |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index f70adab82..e33b00d89 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/fs/path_util.h" | 9 | #include "common/fs/path_util.h" |
| 10 | #include "common/hex_util.h" | 10 | #include "common/hex_util.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/scope_exit.h" | ||
| 12 | #include "core/crypto/key_manager.h" | 13 | #include "core/crypto/key_manager.h" |
| 13 | #include "core/file_sys/card_image.h" | 14 | #include "core/file_sys/card_image.h" |
| 14 | #include "core/file_sys/common_funcs.h" | 15 | #include "core/file_sys/common_funcs.h" |
| @@ -625,7 +626,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex | |||
| 625 | nca->GetTitleId() != title_id) { | 626 | nca->GetTitleId() != title_id) { |
| 626 | // Create fake cnmt for patch to multiprogram application | 627 | // Create fake cnmt for patch to multiprogram application |
| 627 | const auto sub_nca_result = | 628 | const auto sub_nca_result = |
| 628 | InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy); | 629 | InstallEntry(*nca, cnmt.GetHeader(), record, overwrite_if_exists, copy); |
| 629 | if (sub_nca_result != InstallResult::Success) { | 630 | if (sub_nca_result != InstallResult::Success) { |
| 630 | return sub_nca_result; | 631 | return sub_nca_result; |
| 631 | } | 632 | } |
| @@ -672,6 +673,31 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, | |||
| 672 | return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); | 673 | return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); |
| 673 | } | 674 | } |
| 674 | 675 | ||
| 676 | InstallResult RegisteredCache::InstallEntry(const NCA& nca, const CNMTHeader& base_header, | ||
| 677 | const ContentRecord& base_record, | ||
| 678 | bool overwrite_if_exists, const VfsCopyFunction& copy) { | ||
| 679 | const CNMTHeader header{ | ||
| 680 | .title_id = nca.GetTitleId(), | ||
| 681 | .title_version = base_header.title_version, | ||
| 682 | .type = base_header.type, | ||
| 683 | .reserved = {}, | ||
| 684 | .table_offset = 0x10, | ||
| 685 | .number_content_entries = 1, | ||
| 686 | .number_meta_entries = 0, | ||
| 687 | .attributes = 0, | ||
| 688 | .reserved2 = {}, | ||
| 689 | .is_committed = 0, | ||
| 690 | .required_download_system_version = 0, | ||
| 691 | .reserved3 = {}, | ||
| 692 | }; | ||
| 693 | const OptionalHeader opt_header{0, 0}; | ||
| 694 | const CNMT new_cnmt(header, opt_header, {base_record}, {}); | ||
| 695 | if (!RawInstallYuzuMeta(new_cnmt)) { | ||
| 696 | return InstallResult::ErrorMetaFailed; | ||
| 697 | } | ||
| 698 | return RawInstallNCA(nca, copy, overwrite_if_exists, base_record.nca_id); | ||
| 699 | } | ||
| 700 | |||
| 675 | bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { | 701 | bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { |
| 676 | bool removed_data = false; | 702 | bool removed_data = false; |
| 677 | 703 | ||
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index bd7f53eaf..64815a845 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -24,6 +24,7 @@ enum class NCAContentType : u8; | |||
| 24 | enum class TitleType : u8; | 24 | enum class TitleType : u8; |
| 25 | 25 | ||
| 26 | struct ContentRecord; | 26 | struct ContentRecord; |
| 27 | struct CNMTHeader; | ||
| 27 | struct MetaRecord; | 28 | struct MetaRecord; |
| 28 | class RegisteredCache; | 29 | class RegisteredCache; |
| 29 | 30 | ||
| @@ -169,6 +170,10 @@ public: | |||
| 169 | InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, | 170 | InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, |
| 170 | const VfsCopyFunction& copy = &VfsRawCopy); | 171 | const VfsCopyFunction& copy = &VfsRawCopy); |
| 171 | 172 | ||
| 173 | InstallResult InstallEntry(const NCA& nca, const CNMTHeader& base_header, | ||
| 174 | const ContentRecord& base_record, bool overwrite_if_exists = false, | ||
| 175 | const VfsCopyFunction& copy = &VfsRawCopy); | ||
| 176 | |||
| 172 | // Removes an existing entry based on title id | 177 | // Removes an existing entry based on title id |
| 173 | bool RemoveExistingEntry(u64 title_id) const; | 178 | bool RemoveExistingEntry(u64 title_id) const; |
| 174 | 179 | ||
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index e1e89ce2d..68e8ec22f 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -164,24 +164,6 @@ VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType titl | |||
| 164 | return nullptr; | 164 | return nullptr; |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const { | ||
| 168 | if (extracted) | ||
| 169 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 170 | std::vector<Core::Crypto::Key128> out; | ||
| 171 | for (const auto& ticket_file : ticket_files) { | ||
| 172 | if (ticket_file == nullptr || | ||
| 173 | ticket_file->GetSize() < | ||
| 174 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 175 | continue; | ||
| 176 | } | ||
| 177 | |||
| 178 | out.emplace_back(); | ||
| 179 | ticket_file->Read(out.back().data(), out.back().size(), | ||
| 180 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 181 | } | ||
| 182 | return out; | ||
| 183 | } | ||
| 184 | |||
| 185 | std::vector<VirtualFile> NSP::GetFiles() const { | 167 | std::vector<VirtualFile> NSP::GetFiles() const { |
| 186 | return pfs->GetFiles(); | 168 | return pfs->GetFiles(); |
| 187 | } | 169 | } |
| @@ -208,22 +190,11 @@ void NSP::SetTicketKeys(const std::vector<VirtualFile>& files) { | |||
| 208 | continue; | 190 | continue; |
| 209 | } | 191 | } |
| 210 | 192 | ||
| 211 | if (ticket_file->GetSize() < | 193 | auto ticket = Core::Crypto::Ticket::Read(ticket_file); |
| 212 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | 194 | if (!keys.AddTicket(ticket)) { |
| 195 | LOG_WARNING(Common_Filesystem, "Could not load NSP ticket {}", ticket_file->GetName()); | ||
| 213 | continue; | 196 | continue; |
| 214 | } | 197 | } |
| 215 | |||
| 216 | Core::Crypto::Key128 key{}; | ||
| 217 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 218 | |||
| 219 | // We get the name without the extension in order to create the rights ID. | ||
| 220 | std::string name_only(ticket_file->GetName()); | ||
| 221 | name_only.erase(name_only.size() - 4); | ||
| 222 | |||
| 223 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 224 | u128 rights_id; | ||
| 225 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 226 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 227 | } | 198 | } |
| 228 | } | 199 | } |
| 229 | 200 | ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 27f97c725..915bffca9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -53,7 +53,6 @@ public: | |||
| 53 | TitleType title_type = TitleType::Application) const; | 53 | TitleType title_type = TitleType::Application) const; |
| 54 | VirtualFile GetNCAFile(u64 title_id, ContentRecordType type, | 54 | VirtualFile GetNCAFile(u64 title_id, ContentRecordType type, |
| 55 | TitleType title_type = TitleType::Application) const; | 55 | TitleType title_type = TitleType::Application) const; |
| 56 | std::vector<Core::Crypto::Key128> GetTitlekey() const; | ||
| 57 | 56 | ||
| 58 | std::vector<VirtualFile> GetFiles() const override; | 57 | std::vector<VirtualFile> GetFiles() const override; |
| 59 | 58 | ||
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 3300d4f79..27755cb58 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "common/settings.h" | ||
| 7 | #include "common/settings_enums.h" | ||
| 6 | #include "core/frontend/applets/controller.h" | 8 | #include "core/frontend/applets/controller.h" |
| 7 | #include "core/hid/emulated_controller.h" | 9 | #include "core/hid/emulated_controller.h" |
| 8 | #include "core/hid/hid_core.h" | 10 | #include "core/hid/hid_core.h" |
| @@ -62,7 +64,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac | |||
| 62 | controller->Connect(true); | 64 | controller->Connect(true); |
| 63 | } | 65 | } |
| 64 | } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && | 66 | } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && |
| 65 | !Settings::values.use_docked_mode.GetValue()) { | 67 | !Settings::IsDockedMode()) { |
| 66 | // We should *never* reach here under any normal circumstances. | 68 | // We should *never* reach here under any normal circumstances. |
| 67 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | 69 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); |
| 68 | controller->Connect(true); | 70 | controller->Connect(true); |
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index b4081fc39..2590b20da 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 8 | #include "common/settings_enums.h" | ||
| 8 | #include "core/frontend/framebuffer_layout.h" | 9 | #include "core/frontend/framebuffer_layout.h" |
| 9 | 10 | ||
| 10 | namespace Layout { | 11 | namespace Layout { |
| @@ -49,7 +50,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { | |||
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) { | 52 | FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) { |
| 52 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | 53 | const bool is_docked = Settings::IsDockedMode(); |
| 53 | const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width; | 54 | const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width; |
| 54 | const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height; | 55 | const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height; |
| 55 | 56 | ||
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp index 90e4e8fb0..e7da7a21d 100644 --- a/src/core/hle/kernel/k_capabilities.cpp +++ b/src/core/hle/kernel/k_capabilities.cpp | |||
| @@ -156,7 +156,6 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { | |||
| 156 | const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; | 156 | const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; |
| 157 | const size_t num_pages = 1; | 157 | const size_t num_pages = 1; |
| 158 | const size_t size = num_pages * PageSize; | 158 | const size_t size = num_pages * PageSize; |
| 159 | R_UNLESS(num_pages != 0, ResultInvalidSize); | ||
| 160 | R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress); | 159 | R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress); |
| 161 | R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); | 160 | R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); |
| 162 | 161 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 8d057b3a8..f9c4f9678 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <cinttypes> | 6 | #include <cinttypes> |
| 7 | #include <cstring> | 7 | #include <cstring> |
| 8 | #include "common/settings.h" | 8 | #include "common/settings.h" |
| 9 | #include "common/settings_enums.h" | ||
| 9 | #include "core/core.h" | 10 | #include "core/core.h" |
| 10 | #include "core/file_sys/control_metadata.h" | 11 | #include "core/file_sys/control_metadata.h" |
| 11 | #include "core/file_sys/patch_manager.h" | 12 | #include "core/file_sys/patch_manager.h" |
| @@ -45,7 +46,7 @@ constexpr Result ResultNoMessages{ErrorModule::AM, 3}; | |||
| 45 | constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; | 46 | constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; |
| 46 | 47 | ||
| 47 | enum class LaunchParameterKind : u32 { | 48 | enum class LaunchParameterKind : u32 { |
| 48 | ApplicationSpecific = 1, | 49 | UserChannel = 1, |
| 49 | AccountPreselectedUser = 2, | 50 | AccountPreselectedUser = 2, |
| 50 | }; | 51 | }; |
| 51 | 52 | ||
| @@ -340,7 +341,7 @@ void ISelfController::Exit(HLERequestContext& ctx) { | |||
| 340 | void ISelfController::LockExit(HLERequestContext& ctx) { | 341 | void ISelfController::LockExit(HLERequestContext& ctx) { |
| 341 | LOG_DEBUG(Service_AM, "called"); | 342 | LOG_DEBUG(Service_AM, "called"); |
| 342 | 343 | ||
| 343 | system.SetExitLock(true); | 344 | system.SetExitLocked(true); |
| 344 | 345 | ||
| 345 | IPC::ResponseBuilder rb{ctx, 2}; | 346 | IPC::ResponseBuilder rb{ctx, 2}; |
| 346 | rb.Push(ResultSuccess); | 347 | rb.Push(ResultSuccess); |
| @@ -349,10 +350,14 @@ void ISelfController::LockExit(HLERequestContext& ctx) { | |||
| 349 | void ISelfController::UnlockExit(HLERequestContext& ctx) { | 350 | void ISelfController::UnlockExit(HLERequestContext& ctx) { |
| 350 | LOG_DEBUG(Service_AM, "called"); | 351 | LOG_DEBUG(Service_AM, "called"); |
| 351 | 352 | ||
| 352 | system.SetExitLock(false); | 353 | system.SetExitLocked(false); |
| 353 | 354 | ||
| 354 | IPC::ResponseBuilder rb{ctx, 2}; | 355 | IPC::ResponseBuilder rb{ctx, 2}; |
| 355 | rb.Push(ResultSuccess); | 356 | rb.Push(ResultSuccess); |
| 357 | |||
| 358 | if (system.GetExitRequested()) { | ||
| 359 | system.Exit(); | ||
| 360 | } | ||
| 356 | } | 361 | } |
| 357 | 362 | ||
| 358 | void ISelfController::EnterFatalSection(HLERequestContext& ctx) { | 363 | void ISelfController::EnterFatalSection(HLERequestContext& ctx) { |
| @@ -833,7 +838,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) { | |||
| 833 | IPC::ResponseBuilder rb{ctx, 4}; | 838 | IPC::ResponseBuilder rb{ctx, 4}; |
| 834 | rb.Push(ResultSuccess); | 839 | rb.Push(ResultSuccess); |
| 835 | 840 | ||
| 836 | if (Settings::values.use_docked_mode.GetValue()) { | 841 | if (Settings::IsDockedMode()) { |
| 837 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); | 842 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); |
| 838 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); | 843 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); |
| 839 | } else { | 844 | } else { |
| @@ -921,7 +926,7 @@ void IStorage::Open(HLERequestContext& ctx) { | |||
| 921 | } | 926 | } |
| 922 | 927 | ||
| 923 | void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { | 928 | void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { |
| 924 | const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()}; | 929 | const bool use_docked_mode{Settings::IsDockedMode()}; |
| 925 | LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); | 930 | LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); |
| 926 | 931 | ||
| 927 | IPC::ResponseBuilder rb{ctx, 3}; | 932 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -1513,27 +1518,26 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { | |||
| 1513 | IPC::RequestParser rp{ctx}; | 1518 | IPC::RequestParser rp{ctx}; |
| 1514 | const auto kind = rp.PopEnum<LaunchParameterKind>(); | 1519 | const auto kind = rp.PopEnum<LaunchParameterKind>(); |
| 1515 | 1520 | ||
| 1516 | LOG_DEBUG(Service_AM, "called, kind={:08X}", kind); | 1521 | LOG_INFO(Service_AM, "called, kind={:08X}", kind); |
| 1517 | 1522 | ||
| 1518 | if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) { | 1523 | if (kind == LaunchParameterKind::UserChannel) { |
| 1519 | const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) { | 1524 | auto channel = system.GetUserChannel(); |
| 1520 | return system.GetFileSystemController().GetBCATDirectory(tid); | 1525 | if (channel.empty()) { |
| 1521 | }); | 1526 | LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); |
| 1522 | const auto build_id_full = system.GetApplicationProcessBuildID(); | 1527 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1523 | u64 build_id{}; | 1528 | rb.Push(AM::ResultNoDataInChannel); |
| 1524 | std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); | ||
| 1525 | |||
| 1526 | auto data = | ||
| 1527 | backend->GetLaunchParameter({system.GetApplicationProcessProgramID(), build_id}); | ||
| 1528 | if (data.has_value()) { | ||
| 1529 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 1530 | rb.Push(ResultSuccess); | ||
| 1531 | rb.PushIpcInterface<IStorage>(system, std::move(*data)); | ||
| 1532 | launch_popped_application_specific = true; | ||
| 1533 | return; | 1529 | return; |
| 1534 | } | 1530 | } |
| 1531 | |||
| 1532 | auto data = channel.back(); | ||
| 1533 | channel.pop_back(); | ||
| 1534 | |||
| 1535 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 1536 | rb.Push(ResultSuccess); | ||
| 1537 | rb.PushIpcInterface<IStorage>(system, std::move(data)); | ||
| 1535 | } else if (kind == LaunchParameterKind::AccountPreselectedUser && | 1538 | } else if (kind == LaunchParameterKind::AccountPreselectedUser && |
| 1536 | !launch_popped_account_preselect) { | 1539 | !launch_popped_account_preselect) { |
| 1540 | // TODO: Verify this is hw-accurate | ||
| 1537 | LaunchParameterAccountPreselectedUser params{}; | 1541 | LaunchParameterAccountPreselectedUser params{}; |
| 1538 | 1542 | ||
| 1539 | params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; | 1543 | params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; |
| @@ -1545,7 +1549,6 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { | |||
| 1545 | params.current_user = *uuid; | 1549 | params.current_user = *uuid; |
| 1546 | 1550 | ||
| 1547 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 1551 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 1548 | |||
| 1549 | rb.Push(ResultSuccess); | 1552 | rb.Push(ResultSuccess); |
| 1550 | 1553 | ||
| 1551 | std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); | 1554 | std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); |
| @@ -1553,12 +1556,11 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { | |||
| 1553 | 1556 | ||
| 1554 | rb.PushIpcInterface<IStorage>(system, std::move(buffer)); | 1557 | rb.PushIpcInterface<IStorage>(system, std::move(buffer)); |
| 1555 | launch_popped_account_preselect = true; | 1558 | launch_popped_account_preselect = true; |
| 1556 | return; | 1559 | } else { |
| 1560 | LOG_ERROR(Service_AM, "Unknown launch parameter kind."); | ||
| 1561 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1562 | rb.Push(AM::ResultNoDataInChannel); | ||
| 1557 | } | 1563 | } |
| 1558 | |||
| 1559 | LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); | ||
| 1560 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1561 | rb.Push(AM::ResultNoDataInChannel); | ||
| 1562 | } | 1564 | } |
| 1563 | 1565 | ||
| 1564 | void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { | 1566 | void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { |
| @@ -1850,14 +1852,22 @@ void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) { | |||
| 1850 | } | 1852 | } |
| 1851 | 1853 | ||
| 1852 | void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) { | 1854 | void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) { |
| 1853 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 1855 | LOG_DEBUG(Service_AM, "called"); |
| 1856 | |||
| 1857 | system.GetUserChannel().clear(); | ||
| 1854 | 1858 | ||
| 1855 | IPC::ResponseBuilder rb{ctx, 2}; | 1859 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1856 | rb.Push(ResultSuccess); | 1860 | rb.Push(ResultSuccess); |
| 1857 | } | 1861 | } |
| 1858 | 1862 | ||
| 1859 | void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) { | 1863 | void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) { |
| 1860 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 1864 | LOG_DEBUG(Service_AM, "called"); |
| 1865 | |||
| 1866 | IPC::RequestParser rp{ctx}; | ||
| 1867 | const auto storage = rp.PopIpcInterface<IStorage>().lock(); | ||
| 1868 | if (storage) { | ||
| 1869 | system.GetUserChannel().push_back(storage->GetData()); | ||
| 1870 | } | ||
| 1861 | 1871 | ||
| 1862 | IPC::ResponseBuilder rb{ctx, 2}; | 1872 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1863 | rb.Push(ResultSuccess); | 1873 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index d68998f04..f75a665b2 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -339,7 +339,6 @@ private: | |||
| 339 | 339 | ||
| 340 | KernelHelpers::ServiceContext service_context; | 340 | KernelHelpers::ServiceContext service_context; |
| 341 | 341 | ||
| 342 | bool launch_popped_application_specific = false; | ||
| 343 | bool launch_popped_account_preselect = false; | 342 | bool launch_popped_account_preselect = false; |
| 344 | s32 previous_program_index{-1}; | 343 | s32 previous_program_index{-1}; |
| 345 | Kernel::KEvent* gpu_error_detected_event; | 344 | Kernel::KEvent* gpu_error_detected_event; |
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp index d1f652c09..350a90818 100644 --- a/src/core/hle/service/am/applets/applet_mii_edit.cpp +++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp | |||
| @@ -85,15 +85,18 @@ void MiiEdit::Execute() { | |||
| 85 | break; | 85 | break; |
| 86 | case MiiEditAppletMode::CreateMii: | 86 | case MiiEditAppletMode::CreateMii: |
| 87 | case MiiEditAppletMode::EditMii: { | 87 | case MiiEditAppletMode::EditMii: { |
| 88 | Service::Mii::MiiManager mii_manager; | 88 | Mii::CharInfo char_info{}; |
| 89 | Mii::StoreData store_data{}; | ||
| 90 | store_data.BuildBase(Mii::Gender::Male); | ||
| 91 | char_info.SetFromStoreData(store_data); | ||
| 89 | 92 | ||
| 90 | const MiiEditCharInfo char_info{ | 93 | const MiiEditCharInfo edit_char_info{ |
| 91 | .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii | 94 | .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii |
| 92 | ? applet_input_v4.char_info.mii_info | 95 | ? applet_input_v4.char_info.mii_info |
| 93 | : mii_manager.BuildDefault(0)}, | 96 | : char_info}, |
| 94 | }; | 97 | }; |
| 95 | 98 | ||
| 96 | MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info); | 99 | MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info); |
| 97 | break; | 100 | break; |
| 98 | } | 101 | } |
| 99 | default: | 102 | default: |
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h index 4705d019f..f3d764073 100644 --- a/src/core/hle/service/am/applets/applet_mii_edit_types.h +++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h | |||
| @@ -7,7 +7,8 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/service/mii/types.h" | 10 | #include "common/uuid.h" |
| 11 | #include "core/hle/service/mii/types/char_info.h" | ||
| 11 | 12 | ||
| 12 | namespace Service::AM::Applets { | 13 | namespace Service::AM::Applets { |
| 13 | 14 | ||
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 227fdd0cf..4f1aa5cc2 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| 10 | #include "common/settings_enums.h" | ||
| 10 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 11 | #include "core/hle/service/apm/apm_controller.h" | 12 | #include "core/hle/service/apm/apm_controller.h" |
| 12 | 13 | ||
| @@ -67,8 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { | |||
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | PerformanceMode Controller::GetCurrentPerformanceMode() const { | 70 | PerformanceMode Controller::GetCurrentPerformanceMode() const { |
| 70 | return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost | 71 | return Settings::IsDockedMode() ? PerformanceMode::Boost : PerformanceMode::Normal; |
| 71 | : PerformanceMode::Normal; | ||
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { | 74 | PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { |
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 526a39130..56fee4591 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -220,7 +220,7 @@ AudInU::AudInU(Core::System& system_) | |||
| 220 | AudInU::~AudInU() = default; | 220 | AudInU::~AudInU() = default; |
| 221 | 221 | ||
| 222 | void AudInU::ListAudioIns(HLERequestContext& ctx) { | 222 | void AudInU::ListAudioIns(HLERequestContext& ctx) { |
| 223 | using namespace AudioCore::AudioRenderer; | 223 | using namespace AudioCore::Renderer; |
| 224 | 224 | ||
| 225 | LOG_DEBUG(Service_Audio, "called"); | 225 | LOG_DEBUG(Service_Audio, "called"); |
| 226 | 226 | ||
| @@ -240,7 +240,7 @@ void AudInU::ListAudioIns(HLERequestContext& ctx) { | |||
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | void AudInU::ListAudioInsAutoFiltered(HLERequestContext& ctx) { | 242 | void AudInU::ListAudioInsAutoFiltered(HLERequestContext& ctx) { |
| 243 | using namespace AudioCore::AudioRenderer; | 243 | using namespace AudioCore::Renderer; |
| 244 | 244 | ||
| 245 | LOG_DEBUG(Service_Audio, "called"); | 245 | LOG_DEBUG(Service_Audio, "called"); |
| 246 | 246 | ||
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 23f84a29f..ca683d72c 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -228,7 +228,7 @@ AudOutU::AudOutU(Core::System& system_) | |||
| 228 | AudOutU::~AudOutU() = default; | 228 | AudOutU::~AudOutU() = default; |
| 229 | 229 | ||
| 230 | void AudOutU::ListAudioOuts(HLERequestContext& ctx) { | 230 | void AudOutU::ListAudioOuts(HLERequestContext& ctx) { |
| 231 | using namespace AudioCore::AudioRenderer; | 231 | using namespace AudioCore::Renderer; |
| 232 | 232 | ||
| 233 | std::scoped_lock l{impl->mutex}; | 233 | std::scoped_lock l{impl->mutex}; |
| 234 | 234 | ||
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index b723b65c8..2f09cade5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -26,7 +26,7 @@ | |||
| 26 | #include "core/hle/service/ipc_helpers.h" | 26 | #include "core/hle/service/ipc_helpers.h" |
| 27 | #include "core/memory.h" | 27 | #include "core/memory.h" |
| 28 | 28 | ||
| 29 | using namespace AudioCore::AudioRenderer; | 29 | using namespace AudioCore::Renderer; |
| 30 | 30 | ||
| 31 | namespace Service::Audio { | 31 | namespace Service::Audio { |
| 32 | 32 | ||
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index d8e9c8719..3d7993a16 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h | |||
| @@ -28,7 +28,7 @@ private: | |||
| 28 | void GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx); | 28 | void GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx); |
| 29 | 29 | ||
| 30 | KernelHelpers::ServiceContext service_context; | 30 | KernelHelpers::ServiceContext service_context; |
| 31 | std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; | 31 | std::unique_ptr<AudioCore::Renderer::Manager> impl; |
| 32 | u32 num_audio_devices{0}; | 32 | u32 num_audio_devices{0}; |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index fa77007f3..1557e6088 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -174,7 +174,7 @@ public: | |||
| 174 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, | 174 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, |
| 175 | {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, | 175 | {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, |
| 176 | {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, | 176 | {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, |
| 177 | {9, nullptr, "DecodeInterleavedForMultiStream"}, | 177 | {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, |
| 178 | }; | 178 | }; |
| 179 | // clang-format on | 179 | // clang-format on |
| 180 | 180 | ||
| @@ -206,6 +206,16 @@ private: | |||
| 206 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); | 206 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { | ||
| 210 | LOG_DEBUG(Audio, "called"); | ||
| 211 | |||
| 212 | IPC::RequestParser rp{ctx}; | ||
| 213 | const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext | ||
| 214 | : OpusDecoderState::ExtraBehavior::None; | ||
| 215 | |||
| 216 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); | ||
| 217 | } | ||
| 218 | |||
| 209 | OpusDecoderState decoder_state; | 219 | OpusDecoderState decoder_state; |
| 210 | }; | 220 | }; |
| 211 | 221 | ||
| @@ -257,6 +267,10 @@ void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { | |||
| 257 | GetWorkBufferSize(ctx); | 267 | GetWorkBufferSize(ctx); |
| 258 | } | 268 | } |
| 259 | 269 | ||
| 270 | void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { | ||
| 271 | GetWorkBufferSizeEx(ctx); | ||
| 272 | } | ||
| 273 | |||
| 260 | void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { | 274 | void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { |
| 261 | OpusMultiStreamParametersEx param; | 275 | OpusMultiStreamParametersEx param; |
| 262 | std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); | 276 | std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); |
| @@ -354,6 +368,40 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { | |||
| 354 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | 368 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); |
| 355 | } | 369 | } |
| 356 | 370 | ||
| 371 | void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { | ||
| 372 | OpusMultiStreamParametersEx params; | ||
| 373 | std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); | ||
| 374 | |||
| 375 | const auto& sample_rate = params.sample_rate; | ||
| 376 | const auto& channel_count = params.channel_count; | ||
| 377 | |||
| 378 | LOG_INFO( | ||
| 379 | Audio, | ||
| 380 | "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", | ||
| 381 | sample_rate, channel_count, params.number_streams, params.number_stereo_streams); | ||
| 382 | |||
| 383 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||
| 384 | sample_rate == 12000 || sample_rate == 8000, | ||
| 385 | "Invalid sample rate"); | ||
| 386 | |||
| 387 | int error = 0; | ||
| 388 | OpusDecoderPtr decoder{opus_multistream_decoder_create( | ||
| 389 | sample_rate, static_cast<int>(channel_count), params.number_streams, | ||
| 390 | params.number_stereo_streams, params.channel_mappings.data(), &error)}; | ||
| 391 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 392 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 393 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 394 | // TODO(ogniK): Use correct error code | ||
| 395 | rb.Push(ResultUnknown); | ||
| 396 | return; | ||
| 397 | } | ||
| 398 | |||
| 399 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 400 | rb.Push(ResultSuccess); | ||
| 401 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | ||
| 402 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | ||
| 403 | } | ||
| 404 | |||
| 357 | HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { | 405 | HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { |
| 358 | static const FunctionInfo functions[] = { | 406 | static const FunctionInfo functions[] = { |
| 359 | {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, | 407 | {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, |
| @@ -362,9 +410,10 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { | |||
| 362 | {3, nullptr, "GetWorkBufferSizeForMultiStream"}, | 410 | {3, nullptr, "GetWorkBufferSizeForMultiStream"}, |
| 363 | {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, | 411 | {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, |
| 364 | {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, | 412 | {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, |
| 365 | {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, | 413 | {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, |
| 414 | "OpenHardwareOpusDecoderForMultiStreamEx"}, | ||
| 366 | {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, | 415 | {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, |
| 367 | {8, nullptr, "GetWorkBufferSizeExEx"}, | 416 | {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, |
| 368 | {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, | 417 | {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, |
| 369 | }; | 418 | }; |
| 370 | RegisterHandlers(functions); | 419 | RegisterHandlers(functions); |
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index ece65c02c..90867bf74 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h | |||
| @@ -18,8 +18,10 @@ struct OpusMultiStreamParametersEx { | |||
| 18 | u32 number_stereo_streams; | 18 | u32 number_stereo_streams; |
| 19 | u32 use_large_frame_size; | 19 | u32 use_large_frame_size; |
| 20 | u32 padding; | 20 | u32 padding; |
| 21 | std::array<u32, 64> channel_mappings; | 21 | std::array<u8, 0x100> channel_mappings; |
| 22 | }; | 22 | }; |
| 23 | static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, | ||
| 24 | "OpusMultiStreamParametersEx has incorrect size"); | ||
| 23 | 25 | ||
| 24 | class HwOpus final : public ServiceFramework<HwOpus> { | 26 | class HwOpus final : public ServiceFramework<HwOpus> { |
| 25 | public: | 27 | public: |
| @@ -29,8 +31,10 @@ public: | |||
| 29 | private: | 31 | private: |
| 30 | void OpenHardwareOpusDecoder(HLERequestContext& ctx); | 32 | void OpenHardwareOpusDecoder(HLERequestContext& ctx); |
| 31 | void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); | 33 | void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); |
| 34 | void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); | ||
| 32 | void GetWorkBufferSize(HLERequestContext& ctx); | 35 | void GetWorkBufferSize(HLERequestContext& ctx); |
| 33 | void GetWorkBufferSizeEx(HLERequestContext& ctx); | 36 | void GetWorkBufferSizeEx(HLERequestContext& ctx); |
| 37 | void GetWorkBufferSizeExEx(HLERequestContext& ctx); | ||
| 34 | void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); | 38 | void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); |
| 35 | }; | 39 | }; |
| 36 | 40 | ||
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 446f46b3c..9eaae4c4b 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -122,20 +122,18 @@ private: | |||
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | void ImportTicket(HLERequestContext& ctx) { | 124 | void ImportTicket(HLERequestContext& ctx) { |
| 125 | const auto ticket = ctx.ReadBuffer(); | 125 | const auto raw_ticket = ctx.ReadBuffer(); |
| 126 | [[maybe_unused]] const auto cert = ctx.ReadBuffer(1); | 126 | [[maybe_unused]] const auto cert = ctx.ReadBuffer(1); |
| 127 | 127 | ||
| 128 | if (ticket.size() < sizeof(Core::Crypto::Ticket)) { | 128 | if (raw_ticket.size() < sizeof(Core::Crypto::Ticket)) { |
| 129 | LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); | 129 | LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); |
| 130 | IPC::ResponseBuilder rb{ctx, 2}; | 130 | IPC::ResponseBuilder rb{ctx, 2}; |
| 131 | rb.Push(ERROR_INVALID_ARGUMENT); | 131 | rb.Push(ERROR_INVALID_ARGUMENT); |
| 132 | return; | 132 | return; |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | Core::Crypto::Ticket raw{}; | 135 | Core::Crypto::Ticket ticket = Core::Crypto::Ticket::Read(raw_ticket); |
| 136 | std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket)); | 136 | if (!keys.AddTicket(ticket)) { |
| 137 | |||
| 138 | if (!keys.AddTicketPersonalized(raw)) { | ||
| 139 | LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); | 137 | LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); |
| 140 | IPC::ResponseBuilder rb{ctx, 2}; | 138 | IPC::ResponseBuilder rb{ctx, 2}; |
| 141 | rb.Push(ERROR_INVALID_ARGUMENT); | 139 | rb.Push(ERROR_INVALID_ARGUMENT); |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 03432f7cb..63eecd42b 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -331,7 +331,7 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() | |||
| 331 | }; | 331 | }; |
| 332 | 332 | ||
| 333 | // Hack: There is no touch in docked but games still allow it | 333 | // Hack: There is no touch in docked but games still allow it |
| 334 | if (Settings::values.use_docked_mode.GetValue()) { | 334 | if (Settings::IsDockedMode()) { |
| 335 | gesture.points[id] = { | 335 | gesture.points[id] = { |
| 336 | .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), | 336 | .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), |
| 337 | .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), | 337 | .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 28818c813..3b349b4c4 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -1518,7 +1518,7 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller | |||
| 1518 | return false; | 1518 | return false; |
| 1519 | } | 1519 | } |
| 1520 | // Handheld shouldn't be supported in docked mode | 1520 | // Handheld shouldn't be supported in docked mode |
| 1521 | if (Settings::values.use_docked_mode.GetValue()) { | 1521 | if (Settings::IsDockedMode()) { |
| 1522 | return false; | 1522 | return false; |
| 1523 | } | 1523 | } |
| 1524 | 1524 | ||
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 65c11a2f3..3b83c5ed7 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp | |||
| @@ -7,17 +7,16 @@ | |||
| 7 | #include "core/hle/service/ipc_helpers.h" | 7 | #include "core/hle/service/ipc_helpers.h" |
| 8 | #include "core/hle/service/mii/mii.h" | 8 | #include "core/hle/service/mii/mii.h" |
| 9 | #include "core/hle/service/mii/mii_manager.h" | 9 | #include "core/hle/service/mii/mii_manager.h" |
| 10 | #include "core/hle/service/mii/mii_result.h" | ||
| 10 | #include "core/hle/service/server_manager.h" | 11 | #include "core/hle/service/server_manager.h" |
| 11 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 12 | 13 | ||
| 13 | namespace Service::Mii { | 14 | namespace Service::Mii { |
| 14 | 15 | ||
| 15 | constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; | ||
| 16 | |||
| 17 | class IDatabaseService final : public ServiceFramework<IDatabaseService> { | 16 | class IDatabaseService final : public ServiceFramework<IDatabaseService> { |
| 18 | public: | 17 | public: |
| 19 | explicit IDatabaseService(Core::System& system_) | 18 | explicit IDatabaseService(Core::System& system_, bool is_system_) |
| 20 | : ServiceFramework{system_, "IDatabaseService"} { | 19 | : ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} { |
| 21 | // clang-format off | 20 | // clang-format off |
| 22 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 23 | {0, &IDatabaseService::IsUpdated, "IsUpdated"}, | 22 | {0, &IDatabaseService::IsUpdated, "IsUpdated"}, |
| @@ -54,34 +53,27 @@ public: | |||
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | private: | 55 | private: |
| 57 | template <typename T> | ||
| 58 | std::vector<u8> SerializeArray(const std::vector<T>& values) { | ||
| 59 | std::vector<u8> out(values.size() * sizeof(T)); | ||
| 60 | std::size_t offset{}; | ||
| 61 | for (const auto& value : values) { | ||
| 62 | std::memcpy(out.data() + offset, &value, sizeof(T)); | ||
| 63 | offset += sizeof(T); | ||
| 64 | } | ||
| 65 | return out; | ||
| 66 | } | ||
| 67 | |||
| 68 | void IsUpdated(HLERequestContext& ctx) { | 56 | void IsUpdated(HLERequestContext& ctx) { |
| 69 | IPC::RequestParser rp{ctx}; | 57 | IPC::RequestParser rp{ctx}; |
| 70 | const auto source_flag{rp.PopRaw<SourceFlag>()}; | 58 | const auto source_flag{rp.PopRaw<SourceFlag>()}; |
| 71 | 59 | ||
| 72 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); | 60 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); |
| 73 | 61 | ||
| 62 | const bool is_updated = manager.IsUpdated(metadata, source_flag); | ||
| 63 | |||
| 74 | IPC::ResponseBuilder rb{ctx, 3}; | 64 | IPC::ResponseBuilder rb{ctx, 3}; |
| 75 | rb.Push(ResultSuccess); | 65 | rb.Push(ResultSuccess); |
| 76 | rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter)); | 66 | rb.Push<u8>(is_updated); |
| 77 | } | 67 | } |
| 78 | 68 | ||
| 79 | void IsFullDatabase(HLERequestContext& ctx) { | 69 | void IsFullDatabase(HLERequestContext& ctx) { |
| 80 | LOG_DEBUG(Service_Mii, "called"); | 70 | LOG_DEBUG(Service_Mii, "called"); |
| 81 | 71 | ||
| 72 | const bool is_full_database = manager.IsFullDatabase(); | ||
| 73 | |||
| 82 | IPC::ResponseBuilder rb{ctx, 3}; | 74 | IPC::ResponseBuilder rb{ctx, 3}; |
| 83 | rb.Push(ResultSuccess); | 75 | rb.Push(ResultSuccess); |
| 84 | rb.Push(manager.IsFullDatabase()); | 76 | rb.Push<u8>(is_full_database); |
| 85 | } | 77 | } |
| 86 | 78 | ||
| 87 | void GetCount(HLERequestContext& ctx) { | 79 | void GetCount(HLERequestContext& ctx) { |
| @@ -90,57 +82,63 @@ private: | |||
| 90 | 82 | ||
| 91 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); | 83 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); |
| 92 | 84 | ||
| 85 | const u32 mii_count = manager.GetCount(metadata, source_flag); | ||
| 86 | |||
| 93 | IPC::ResponseBuilder rb{ctx, 3}; | 87 | IPC::ResponseBuilder rb{ctx, 3}; |
| 94 | rb.Push(ResultSuccess); | 88 | rb.Push(ResultSuccess); |
| 95 | rb.Push<u32>(manager.GetCount(source_flag)); | 89 | rb.Push(mii_count); |
| 96 | } | 90 | } |
| 97 | 91 | ||
| 98 | void Get(HLERequestContext& ctx) { | 92 | void Get(HLERequestContext& ctx) { |
| 99 | IPC::RequestParser rp{ctx}; | 93 | IPC::RequestParser rp{ctx}; |
| 100 | const auto source_flag{rp.PopRaw<SourceFlag>()}; | 94 | const auto source_flag{rp.PopRaw<SourceFlag>()}; |
| 95 | const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()}; | ||
| 101 | 96 | ||
| 102 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); | 97 | LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size); |
| 98 | |||
| 99 | u32 mii_count{}; | ||
| 100 | std::vector<CharInfoElement> char_info_elements(output_size); | ||
| 101 | Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag); | ||
| 103 | 102 | ||
| 104 | const auto default_miis{manager.GetDefault(source_flag)}; | 103 | if (mii_count != 0) { |
| 105 | if (default_miis.size() > 0) { | 104 | ctx.WriteBuffer(char_info_elements); |
| 106 | ctx.WriteBuffer(SerializeArray(default_miis)); | ||
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | IPC::ResponseBuilder rb{ctx, 3}; | 107 | IPC::ResponseBuilder rb{ctx, 3}; |
| 110 | rb.Push(ResultSuccess); | 108 | rb.Push(result); |
| 111 | rb.Push<u32>(static_cast<u32>(default_miis.size())); | 109 | rb.Push(mii_count); |
| 112 | } | 110 | } |
| 113 | 111 | ||
| 114 | void Get1(HLERequestContext& ctx) { | 112 | void Get1(HLERequestContext& ctx) { |
| 115 | IPC::RequestParser rp{ctx}; | 113 | IPC::RequestParser rp{ctx}; |
| 116 | const auto source_flag{rp.PopRaw<SourceFlag>()}; | 114 | const auto source_flag{rp.PopRaw<SourceFlag>()}; |
| 115 | const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()}; | ||
| 117 | 116 | ||
| 118 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); | 117 | LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size); |
| 119 | 118 | ||
| 120 | const auto default_miis{manager.GetDefault(source_flag)}; | 119 | u32 mii_count{}; |
| 120 | std::vector<CharInfo> char_info(output_size); | ||
| 121 | Result result = manager.Get(metadata, char_info, mii_count, source_flag); | ||
| 121 | 122 | ||
| 122 | std::vector<CharInfo> values; | 123 | if (mii_count != 0) { |
| 123 | for (const auto& element : default_miis) { | 124 | ctx.WriteBuffer(char_info); |
| 124 | values.emplace_back(element.info); | ||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | ctx.WriteBuffer(SerializeArray(values)); | ||
| 128 | |||
| 129 | IPC::ResponseBuilder rb{ctx, 3}; | 127 | IPC::ResponseBuilder rb{ctx, 3}; |
| 130 | rb.Push(ResultSuccess); | 128 | rb.Push(result); |
| 131 | rb.Push<u32>(static_cast<u32>(default_miis.size())); | 129 | rb.Push(mii_count); |
| 132 | } | 130 | } |
| 133 | 131 | ||
| 134 | void UpdateLatest(HLERequestContext& ctx) { | 132 | void UpdateLatest(HLERequestContext& ctx) { |
| 135 | IPC::RequestParser rp{ctx}; | 133 | IPC::RequestParser rp{ctx}; |
| 136 | const auto info{rp.PopRaw<CharInfo>()}; | 134 | const auto char_info{rp.PopRaw<CharInfo>()}; |
| 137 | const auto source_flag{rp.PopRaw<SourceFlag>()}; | 135 | const auto source_flag{rp.PopRaw<SourceFlag>()}; |
| 138 | 136 | ||
| 139 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); | 137 | LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); |
| 140 | 138 | ||
| 141 | CharInfo new_char_info{}; | 139 | CharInfo new_char_info{}; |
| 142 | const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)}; | 140 | const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag); |
| 143 | if (result != ResultSuccess) { | 141 | if (result.IsFailure()) { |
| 144 | IPC::ResponseBuilder rb{ctx, 2}; | 142 | IPC::ResponseBuilder rb{ctx, 2}; |
| 145 | rb.Push(result); | 143 | rb.Push(result); |
| 146 | return; | 144 | return; |
| @@ -153,7 +151,6 @@ private: | |||
| 153 | 151 | ||
| 154 | void BuildRandom(HLERequestContext& ctx) { | 152 | void BuildRandom(HLERequestContext& ctx) { |
| 155 | IPC::RequestParser rp{ctx}; | 153 | IPC::RequestParser rp{ctx}; |
| 156 | |||
| 157 | const auto age{rp.PopRaw<Age>()}; | 154 | const auto age{rp.PopRaw<Age>()}; |
| 158 | const auto gender{rp.PopRaw<Gender>()}; | 155 | const auto gender{rp.PopRaw<Gender>()}; |
| 159 | const auto race{rp.PopRaw<Race>()}; | 156 | const auto race{rp.PopRaw<Race>()}; |
| @@ -162,47 +159,48 @@ private: | |||
| 162 | 159 | ||
| 163 | if (age > Age::All) { | 160 | if (age > Age::All) { |
| 164 | IPC::ResponseBuilder rb{ctx, 2}; | 161 | IPC::ResponseBuilder rb{ctx, 2}; |
| 165 | rb.Push(ERROR_INVALID_ARGUMENT); | 162 | rb.Push(ResultInvalidArgument); |
| 166 | LOG_ERROR(Service_Mii, "invalid age={}", age); | ||
| 167 | return; | 163 | return; |
| 168 | } | 164 | } |
| 169 | 165 | ||
| 170 | if (gender > Gender::All) { | 166 | if (gender > Gender::All) { |
| 171 | IPC::ResponseBuilder rb{ctx, 2}; | 167 | IPC::ResponseBuilder rb{ctx, 2}; |
| 172 | rb.Push(ERROR_INVALID_ARGUMENT); | 168 | rb.Push(ResultInvalidArgument); |
| 173 | LOG_ERROR(Service_Mii, "invalid gender={}", gender); | ||
| 174 | return; | 169 | return; |
| 175 | } | 170 | } |
| 176 | 171 | ||
| 177 | if (race > Race::All) { | 172 | if (race > Race::All) { |
| 178 | IPC::ResponseBuilder rb{ctx, 2}; | 173 | IPC::ResponseBuilder rb{ctx, 2}; |
| 179 | rb.Push(ERROR_INVALID_ARGUMENT); | 174 | rb.Push(ResultInvalidArgument); |
| 180 | LOG_ERROR(Service_Mii, "invalid race={}", race); | ||
| 181 | return; | 175 | return; |
| 182 | } | 176 | } |
| 183 | 177 | ||
| 178 | CharInfo char_info{}; | ||
| 179 | manager.BuildRandom(char_info, age, gender, race); | ||
| 180 | |||
| 184 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; | 181 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; |
| 185 | rb.Push(ResultSuccess); | 182 | rb.Push(ResultSuccess); |
| 186 | rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race)); | 183 | rb.PushRaw<CharInfo>(char_info); |
| 187 | } | 184 | } |
| 188 | 185 | ||
| 189 | void BuildDefault(HLERequestContext& ctx) { | 186 | void BuildDefault(HLERequestContext& ctx) { |
| 190 | IPC::RequestParser rp{ctx}; | 187 | IPC::RequestParser rp{ctx}; |
| 191 | const auto index{rp.Pop<u32>()}; | 188 | const auto index{rp.Pop<u32>()}; |
| 192 | 189 | ||
| 193 | LOG_DEBUG(Service_Mii, "called with index={}", index); | 190 | LOG_INFO(Service_Mii, "called with index={}", index); |
| 194 | 191 | ||
| 195 | if (index > 5) { | 192 | if (index > 5) { |
| 196 | LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}", | ||
| 197 | index); | ||
| 198 | IPC::ResponseBuilder rb{ctx, 2}; | 193 | IPC::ResponseBuilder rb{ctx, 2}; |
| 199 | rb.Push(ERROR_INVALID_ARGUMENT); | 194 | rb.Push(ResultInvalidArgument); |
| 200 | return; | 195 | return; |
| 201 | } | 196 | } |
| 202 | 197 | ||
| 198 | CharInfo char_info{}; | ||
| 199 | manager.BuildDefault(char_info, index); | ||
| 200 | |||
| 203 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; | 201 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; |
| 204 | rb.Push(ResultSuccess); | 202 | rb.Push(ResultSuccess); |
| 205 | rb.PushRaw<CharInfo>(manager.BuildDefault(index)); | 203 | rb.PushRaw<CharInfo>(char_info); |
| 206 | } | 204 | } |
| 207 | 205 | ||
| 208 | void GetIndex(HLERequestContext& ctx) { | 206 | void GetIndex(HLERequestContext& ctx) { |
| @@ -211,19 +209,21 @@ private: | |||
| 211 | 209 | ||
| 212 | LOG_DEBUG(Service_Mii, "called"); | 210 | LOG_DEBUG(Service_Mii, "called"); |
| 213 | 211 | ||
| 214 | u32 index{}; | 212 | s32 index{}; |
| 213 | const auto result = manager.GetIndex(metadata, info, index); | ||
| 214 | |||
| 215 | IPC::ResponseBuilder rb{ctx, 3}; | 215 | IPC::ResponseBuilder rb{ctx, 3}; |
| 216 | rb.Push(manager.GetIndex(info, index)); | 216 | rb.Push(result); |
| 217 | rb.Push(index); | 217 | rb.Push(index); |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | void SetInterfaceVersion(HLERequestContext& ctx) { | 220 | void SetInterfaceVersion(HLERequestContext& ctx) { |
| 221 | IPC::RequestParser rp{ctx}; | 221 | IPC::RequestParser rp{ctx}; |
| 222 | current_interface_version = rp.PopRaw<u32>(); | 222 | const auto interface_version{rp.PopRaw<u32>()}; |
| 223 | 223 | ||
| 224 | LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version); | 224 | LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version); |
| 225 | 225 | ||
| 226 | UNIMPLEMENTED_IF(current_interface_version != 1); | 226 | manager.SetInterfaceVersion(metadata, interface_version); |
| 227 | 227 | ||
| 228 | IPC::ResponseBuilder rb{ctx, 2}; | 228 | IPC::ResponseBuilder rb{ctx, 2}; |
| 229 | rb.Push(ResultSuccess); | 229 | rb.Push(ResultSuccess); |
| @@ -231,30 +231,27 @@ private: | |||
| 231 | 231 | ||
| 232 | void Convert(HLERequestContext& ctx) { | 232 | void Convert(HLERequestContext& ctx) { |
| 233 | IPC::RequestParser rp{ctx}; | 233 | IPC::RequestParser rp{ctx}; |
| 234 | |||
| 235 | const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; | 234 | const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; |
| 236 | 235 | ||
| 237 | LOG_INFO(Service_Mii, "called"); | 236 | LOG_INFO(Service_Mii, "called"); |
| 238 | 237 | ||
| 238 | CharInfo char_info{}; | ||
| 239 | manager.ConvertV3ToCharInfo(char_info, mii_v3); | ||
| 240 | |||
| 239 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; | 241 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; |
| 240 | rb.Push(ResultSuccess); | 242 | rb.Push(ResultSuccess); |
| 241 | rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3)); | 243 | rb.PushRaw<CharInfo>(char_info); |
| 242 | } | ||
| 243 | |||
| 244 | constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { | ||
| 245 | return current_interface_version >= interface_version; | ||
| 246 | } | 244 | } |
| 247 | 245 | ||
| 248 | MiiManager manager; | 246 | MiiManager manager{}; |
| 249 | 247 | DatabaseSessionMetadata metadata{}; | |
| 250 | u32 current_interface_version{}; | 248 | bool is_system{}; |
| 251 | u64 current_update_counter{}; | ||
| 252 | }; | 249 | }; |
| 253 | 250 | ||
| 254 | class MiiDBModule final : public ServiceFramework<MiiDBModule> { | 251 | class MiiDBModule final : public ServiceFramework<MiiDBModule> { |
| 255 | public: | 252 | public: |
| 256 | explicit MiiDBModule(Core::System& system_, const char* name_) | 253 | explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_) |
| 257 | : ServiceFramework{system_, name_} { | 254 | : ServiceFramework{system_, name_}, is_system{is_system_} { |
| 258 | // clang-format off | 255 | // clang-format off |
| 259 | static const FunctionInfo functions[] = { | 256 | static const FunctionInfo functions[] = { |
| 260 | {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, | 257 | {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, |
| @@ -268,10 +265,12 @@ private: | |||
| 268 | void GetDatabaseService(HLERequestContext& ctx) { | 265 | void GetDatabaseService(HLERequestContext& ctx) { |
| 269 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 266 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 270 | rb.Push(ResultSuccess); | 267 | rb.Push(ResultSuccess); |
| 271 | rb.PushIpcInterface<IDatabaseService>(system); | 268 | rb.PushIpcInterface<IDatabaseService>(system, is_system); |
| 272 | 269 | ||
| 273 | LOG_DEBUG(Service_Mii, "called"); | 270 | LOG_DEBUG(Service_Mii, "called"); |
| 274 | } | 271 | } |
| 272 | |||
| 273 | bool is_system{}; | ||
| 275 | }; | 274 | }; |
| 276 | 275 | ||
| 277 | class MiiImg final : public ServiceFramework<MiiImg> { | 276 | class MiiImg final : public ServiceFramework<MiiImg> { |
| @@ -303,8 +302,10 @@ public: | |||
| 303 | void LoopProcess(Core::System& system) { | 302 | void LoopProcess(Core::System& system) { |
| 304 | auto server_manager = std::make_unique<ServerManager>(system); | 303 | auto server_manager = std::make_unique<ServerManager>(system); |
| 305 | 304 | ||
| 306 | server_manager->RegisterNamedService("mii:e", std::make_shared<MiiDBModule>(system, "mii:e")); | 305 | server_manager->RegisterNamedService("mii:e", |
| 307 | server_manager->RegisterNamedService("mii:u", std::make_shared<MiiDBModule>(system, "mii:u")); | 306 | std::make_shared<MiiDBModule>(system, "mii:e", true)); |
| 307 | server_manager->RegisterNamedService("mii:u", | ||
| 308 | std::make_shared<MiiDBModule>(system, "mii:u", false)); | ||
| 308 | server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); | 309 | server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); |
| 309 | ServerManager::RunServer(std::move(server_manager)); | 310 | ServerManager::RunServer(std::move(server_manager)); |
| 310 | } | 311 | } |
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 46125d473..292d63777 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp | |||
| @@ -10,386 +10,24 @@ | |||
| 10 | 10 | ||
| 11 | #include "core/hle/service/acc/profile_manager.h" | 11 | #include "core/hle/service/acc/profile_manager.h" |
| 12 | #include "core/hle/service/mii/mii_manager.h" | 12 | #include "core/hle/service/mii/mii_manager.h" |
| 13 | #include "core/hle/service/mii/raw_data.h" | 13 | #include "core/hle/service/mii/mii_result.h" |
| 14 | #include "core/hle/service/mii/mii_util.h" | ||
| 15 | #include "core/hle/service/mii/types/core_data.h" | ||
| 16 | #include "core/hle/service/mii/types/raw_data.h" | ||
| 14 | 17 | ||
| 15 | namespace Service::Mii { | 18 | namespace Service::Mii { |
| 16 | |||
| 17 | namespace { | ||
| 18 | |||
| 19 | constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; | ||
| 20 | |||
| 21 | constexpr std::size_t BaseMiiCount{2}; | ||
| 22 | constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; | 19 | constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; |
| 23 | 20 | ||
| 24 | constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; | 21 | MiiManager::MiiManager() {} |
| 25 | constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7}; | ||
| 26 | constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13}; | ||
| 27 | constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23}; | ||
| 28 | constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0}; | ||
| 29 | constexpr std::array<u8, 62> EyeRotateLookup{ | ||
| 30 | {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, | ||
| 31 | 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, | ||
| 32 | 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, | ||
| 33 | 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}}; | ||
| 34 | constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, | ||
| 35 | 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06, | ||
| 36 | 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}}; | ||
| 37 | |||
| 38 | template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> | ||
| 39 | std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { | ||
| 40 | std::array<T, DestArraySize> out{}; | ||
| 41 | std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize)); | ||
| 42 | return out; | ||
| 43 | } | ||
| 44 | |||
| 45 | CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { | ||
| 46 | MiiStoreBitFields bf; | ||
| 47 | std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); | ||
| 48 | |||
| 49 | return { | ||
| 50 | .uuid = data.data.uuid, | ||
| 51 | .name = ResizeArray<char16_t, 10, 11>(data.data.name), | ||
| 52 | .font_region = static_cast<u8>(bf.font_region.Value()), | ||
| 53 | .favorite_color = static_cast<u8>(bf.favorite_color.Value()), | ||
| 54 | .gender = static_cast<u8>(bf.gender.Value()), | ||
| 55 | .height = static_cast<u8>(bf.height.Value()), | ||
| 56 | .build = static_cast<u8>(bf.build.Value()), | ||
| 57 | .type = static_cast<u8>(bf.type.Value()), | ||
| 58 | .region_move = static_cast<u8>(bf.region_move.Value()), | ||
| 59 | .faceline_type = static_cast<u8>(bf.faceline_type.Value()), | ||
| 60 | .faceline_color = static_cast<u8>(bf.faceline_color.Value()), | ||
| 61 | .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()), | ||
| 62 | .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()), | ||
| 63 | .hair_type = static_cast<u8>(bf.hair_type.Value()), | ||
| 64 | .hair_color = static_cast<u8>(bf.hair_color.Value()), | ||
| 65 | .hair_flip = static_cast<u8>(bf.hair_flip.Value()), | ||
| 66 | .eye_type = static_cast<u8>(bf.eye_type.Value()), | ||
| 67 | .eye_color = static_cast<u8>(bf.eye_color.Value()), | ||
| 68 | .eye_scale = static_cast<u8>(bf.eye_scale.Value()), | ||
| 69 | .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()), | ||
| 70 | .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()), | ||
| 71 | .eye_x = static_cast<u8>(bf.eye_x.Value()), | ||
| 72 | .eye_y = static_cast<u8>(bf.eye_y.Value()), | ||
| 73 | .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()), | ||
| 74 | .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()), | ||
| 75 | .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()), | ||
| 76 | .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()), | ||
| 77 | .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()), | ||
| 78 | .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()), | ||
| 79 | .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3), | ||
| 80 | .nose_type = static_cast<u8>(bf.nose_type.Value()), | ||
| 81 | .nose_scale = static_cast<u8>(bf.nose_scale.Value()), | ||
| 82 | .nose_y = static_cast<u8>(bf.nose_y.Value()), | ||
| 83 | .mouth_type = static_cast<u8>(bf.mouth_type.Value()), | ||
| 84 | .mouth_color = static_cast<u8>(bf.mouth_color.Value()), | ||
| 85 | .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()), | ||
| 86 | .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()), | ||
| 87 | .mouth_y = static_cast<u8>(bf.mouth_y.Value()), | ||
| 88 | .beard_color = static_cast<u8>(bf.beard_color.Value()), | ||
| 89 | .beard_type = static_cast<u8>(bf.beard_type.Value()), | ||
| 90 | .mustache_type = static_cast<u8>(bf.mustache_type.Value()), | ||
| 91 | .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()), | ||
| 92 | .mustache_y = static_cast<u8>(bf.mustache_y.Value()), | ||
| 93 | .glasses_type = static_cast<u8>(bf.glasses_type.Value()), | ||
| 94 | .glasses_color = static_cast<u8>(bf.glasses_color.Value()), | ||
| 95 | .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()), | ||
| 96 | .glasses_y = static_cast<u8>(bf.glasses_y.Value()), | ||
| 97 | .mole_type = static_cast<u8>(bf.mole_type.Value()), | ||
| 98 | .mole_scale = static_cast<u8>(bf.mole_scale.Value()), | ||
| 99 | .mole_x = static_cast<u8>(bf.mole_x.Value()), | ||
| 100 | .mole_y = static_cast<u8>(bf.mole_y.Value()), | ||
| 101 | .padding = 0, | ||
| 102 | }; | ||
| 103 | } | ||
| 104 | |||
| 105 | u16 GenerateCrc16(const void* data, std::size_t size) { | ||
| 106 | s32 crc{}; | ||
| 107 | for (std::size_t i = 0; i < size; i++) { | ||
| 108 | crc ^= static_cast<const u8*>(data)[i] << 8; | ||
| 109 | for (std::size_t j = 0; j < 8; j++) { | ||
| 110 | crc <<= 1; | ||
| 111 | if ((crc & 0x10000) != 0) { | ||
| 112 | crc = (crc ^ 0x1021) & 0xFFFF; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | return Common::swap16(static_cast<u16>(crc)); | ||
| 117 | } | ||
| 118 | |||
| 119 | template <typename T> | ||
| 120 | T GetRandomValue(T min, T max) { | ||
| 121 | std::random_device device; | ||
| 122 | std::mt19937 gen(device()); | ||
| 123 | std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max)); | ||
| 124 | return static_cast<T>(distribution(gen)); | ||
| 125 | } | ||
| 126 | |||
| 127 | template <typename T> | ||
| 128 | T GetRandomValue(T max) { | ||
| 129 | return GetRandomValue<T>({}, max); | ||
| 130 | } | ||
| 131 | |||
| 132 | MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { | ||
| 133 | MiiStoreBitFields bf{}; | ||
| 134 | |||
| 135 | if (gender == Gender::All) { | ||
| 136 | gender = GetRandomValue<Gender>(Gender::Maximum); | ||
| 137 | } | ||
| 138 | |||
| 139 | bf.gender.Assign(gender); | ||
| 140 | bf.favorite_color.Assign(GetRandomValue<u8>(11)); | ||
| 141 | bf.region_move.Assign(0); | ||
| 142 | bf.font_region.Assign(FontRegion::Standard); | ||
| 143 | bf.type.Assign(0); | ||
| 144 | bf.height.Assign(64); | ||
| 145 | bf.build.Assign(64); | ||
| 146 | |||
| 147 | if (age == Age::All) { | ||
| 148 | const auto temp{GetRandomValue<int>(10)}; | ||
| 149 | if (temp >= 8) { | ||
| 150 | age = Age::Old; | ||
| 151 | } else if (temp >= 4) { | ||
| 152 | age = Age::Normal; | ||
| 153 | } else { | ||
| 154 | age = Age::Young; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | if (race == Race::All) { | ||
| 159 | const auto temp{GetRandomValue<int>(10)}; | ||
| 160 | if (temp >= 8) { | ||
| 161 | race = Race::Black; | ||
| 162 | } else if (temp >= 4) { | ||
| 163 | race = Race::White; | ||
| 164 | } else { | ||
| 165 | race = Race::Asian; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | u32 axis_y{}; | ||
| 170 | if (gender == Gender::Female && age == Age::Young) { | ||
| 171 | axis_y = GetRandomValue<u32>(3); | ||
| 172 | } | ||
| 173 | |||
| 174 | const std::size_t index{3 * static_cast<std::size_t>(age) + | ||
| 175 | 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)}; | ||
| 176 | |||
| 177 | const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)}; | ||
| 178 | const auto faceline_color_info{RawData::RandomMiiFacelineColor.at( | ||
| 179 | 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))}; | ||
| 180 | const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; | ||
| 181 | const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; | ||
| 182 | const auto hair_type_info{RawData::RandomMiiHairType.at(index)}; | ||
| 183 | const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) + | ||
| 184 | static_cast<std::size_t>(age))}; | ||
| 185 | const auto eye_type_info{RawData::RandomMiiEyeType.at(index)}; | ||
| 186 | const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))}; | ||
| 187 | const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)}; | ||
| 188 | const auto nose_type_info{RawData::RandomMiiNoseType.at(index)}; | ||
| 189 | const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)}; | ||
| 190 | const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))}; | ||
| 191 | |||
| 192 | bf.faceline_type.Assign( | ||
| 193 | faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]); | ||
| 194 | bf.faceline_color.Assign( | ||
| 195 | faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]); | ||
| 196 | bf.faceline_wrinkle.Assign( | ||
| 197 | faceline_wrinkle_info | ||
| 198 | .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]); | ||
| 199 | bf.faceline_makeup.Assign( | ||
| 200 | faceline_makeup_info | ||
| 201 | .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]); | ||
| 202 | |||
| 203 | bf.hair_type.Assign( | ||
| 204 | hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]); | ||
| 205 | bf.hair_color.Assign( | ||
| 206 | HairColorLookup[hair_color_info | ||
| 207 | .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]); | ||
| 208 | bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum)); | ||
| 209 | |||
| 210 | bf.eye_type.Assign( | ||
| 211 | eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]); | ||
| 212 | |||
| 213 | const auto eye_rotate_1{gender != Gender::Male ? 4 : 2}; | ||
| 214 | const auto eye_rotate_2{gender != Gender::Male ? 3 : 4}; | ||
| 215 | const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; | ||
| 216 | const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]}; | ||
| 217 | |||
| 218 | bf.eye_color.Assign( | ||
| 219 | EyeColorLookup[eye_color_info | ||
| 220 | .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]); | ||
| 221 | bf.eye_scale.Assign(4); | ||
| 222 | bf.eye_aspect.Assign(3); | ||
| 223 | bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate); | ||
| 224 | bf.eye_x.Assign(2); | ||
| 225 | bf.eye_y.Assign(axis_y + 12); | ||
| 226 | |||
| 227 | bf.eyebrow_type.Assign( | ||
| 228 | eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); | ||
| 229 | |||
| 230 | const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; | ||
| 231 | const auto eyebrow_y{race == Race::Asian ? 9 : 10}; | ||
| 232 | const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6}; | ||
| 233 | const auto eyebrow_rotate{ | ||
| 234 | 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]}; | ||
| 235 | |||
| 236 | bf.eyebrow_color.Assign(bf.hair_color); | ||
| 237 | bf.eyebrow_scale.Assign(4); | ||
| 238 | bf.eyebrow_aspect.Assign(3); | ||
| 239 | bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate); | ||
| 240 | bf.eyebrow_x.Assign(2); | ||
| 241 | bf.eyebrow_y.Assign(axis_y + eyebrow_y); | ||
| 242 | |||
| 243 | const auto nose_scale{gender == Gender::Female ? 3 : 4}; | ||
| 244 | |||
| 245 | bf.nose_type.Assign( | ||
| 246 | nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]); | ||
| 247 | bf.nose_scale.Assign(nose_scale); | ||
| 248 | bf.nose_y.Assign(axis_y + 9); | ||
| 249 | |||
| 250 | const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0}; | ||
| 251 | |||
| 252 | bf.mouth_type.Assign( | ||
| 253 | mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]); | ||
| 254 | bf.mouth_color.Assign(MouthColorLookup[mouth_color]); | ||
| 255 | bf.mouth_scale.Assign(4); | ||
| 256 | bf.mouth_aspect.Assign(3); | ||
| 257 | bf.mouth_y.Assign(axis_y + 13); | ||
| 258 | |||
| 259 | bf.beard_color.Assign(bf.hair_color); | ||
| 260 | bf.mustache_scale.Assign(4); | ||
| 261 | |||
| 262 | if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) { | ||
| 263 | const auto mustache_and_beard_flag{ | ||
| 264 | GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)}; | ||
| 265 | |||
| 266 | auto beard_type{BeardType::None}; | ||
| 267 | auto mustache_type{MustacheType::None}; | ||
| 268 | |||
| 269 | if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) == | ||
| 270 | BeardAndMustacheFlag::Beard) { | ||
| 271 | beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5); | ||
| 272 | } | ||
| 273 | |||
| 274 | if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == | ||
| 275 | BeardAndMustacheFlag::Mustache) { | ||
| 276 | mustache_type = | ||
| 277 | GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5); | ||
| 278 | } | ||
| 279 | |||
| 280 | bf.mustache_type.Assign(mustache_type); | ||
| 281 | bf.beard_type.Assign(beard_type); | ||
| 282 | bf.mustache_y.Assign(10); | ||
| 283 | } else { | ||
| 284 | bf.mustache_type.Assign(MustacheType::None); | ||
| 285 | bf.beard_type.Assign(BeardType::None); | ||
| 286 | bf.mustache_y.Assign(axis_y + 10); | ||
| 287 | } | ||
| 288 | |||
| 289 | const auto glasses_type_start{GetRandomValue<std::size_t>(100)}; | ||
| 290 | u8 glasses_type{}; | ||
| 291 | while (glasses_type_start < glasses_type_info.values[glasses_type]) { | ||
| 292 | if (++glasses_type >= glasses_type_info.values_count) { | ||
| 293 | ASSERT(false); | ||
| 294 | break; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | bf.glasses_type.Assign(glasses_type); | ||
| 299 | bf.glasses_color.Assign(GlassesColorLookup[0]); | ||
| 300 | bf.glasses_scale.Assign(4); | ||
| 301 | bf.glasses_y.Assign(axis_y + 10); | ||
| 302 | |||
| 303 | bf.mole_type.Assign(0); | ||
| 304 | bf.mole_scale.Assign(4); | ||
| 305 | bf.mole_x.Assign(2); | ||
| 306 | bf.mole_y.Assign(20); | ||
| 307 | |||
| 308 | return {DefaultMiiName, bf, user_id}; | ||
| 309 | } | ||
| 310 | |||
| 311 | MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { | ||
| 312 | MiiStoreBitFields bf{}; | ||
| 313 | |||
| 314 | bf.font_region.Assign(info.font_region); | ||
| 315 | bf.favorite_color.Assign(info.favorite_color); | ||
| 316 | bf.gender.Assign(info.gender); | ||
| 317 | bf.height.Assign(info.height); | ||
| 318 | bf.build.Assign(info.weight); | ||
| 319 | bf.type.Assign(info.type); | ||
| 320 | bf.region_move.Assign(info.region); | ||
| 321 | bf.faceline_type.Assign(info.face_type); | ||
| 322 | bf.faceline_color.Assign(info.face_color); | ||
| 323 | bf.faceline_wrinkle.Assign(info.face_wrinkle); | ||
| 324 | bf.faceline_makeup.Assign(info.face_makeup); | ||
| 325 | bf.hair_type.Assign(info.hair_type); | ||
| 326 | bf.hair_color.Assign(HairColorLookup[info.hair_color]); | ||
| 327 | bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip)); | ||
| 328 | bf.eye_type.Assign(info.eye_type); | ||
| 329 | bf.eye_color.Assign(EyeColorLookup[info.eye_color]); | ||
| 330 | bf.eye_scale.Assign(info.eye_scale); | ||
| 331 | bf.eye_aspect.Assign(info.eye_aspect); | ||
| 332 | bf.eye_rotate.Assign(info.eye_rotate); | ||
| 333 | bf.eye_x.Assign(info.eye_x); | ||
| 334 | bf.eye_y.Assign(info.eye_y); | ||
| 335 | bf.eyebrow_type.Assign(info.eyebrow_type); | ||
| 336 | bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]); | ||
| 337 | bf.eyebrow_scale.Assign(info.eyebrow_scale); | ||
| 338 | bf.eyebrow_aspect.Assign(info.eyebrow_aspect); | ||
| 339 | bf.eyebrow_rotate.Assign(info.eyebrow_rotate); | ||
| 340 | bf.eyebrow_x.Assign(info.eyebrow_x); | ||
| 341 | bf.eyebrow_y.Assign(info.eyebrow_y - 3); | ||
| 342 | bf.nose_type.Assign(info.nose_type); | ||
| 343 | bf.nose_scale.Assign(info.nose_scale); | ||
| 344 | bf.nose_y.Assign(info.nose_y); | ||
| 345 | bf.mouth_type.Assign(info.mouth_type); | ||
| 346 | bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]); | ||
| 347 | bf.mouth_scale.Assign(info.mouth_scale); | ||
| 348 | bf.mouth_aspect.Assign(info.mouth_aspect); | ||
| 349 | bf.mouth_y.Assign(info.mouth_y); | ||
| 350 | bf.beard_color.Assign(HairColorLookup[info.beard_color]); | ||
| 351 | bf.beard_type.Assign(static_cast<BeardType>(info.beard_type)); | ||
| 352 | bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type)); | ||
| 353 | bf.mustache_scale.Assign(info.mustache_scale); | ||
| 354 | bf.mustache_y.Assign(info.mustache_y); | ||
| 355 | bf.glasses_type.Assign(info.glasses_type); | ||
| 356 | bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]); | ||
| 357 | bf.glasses_scale.Assign(info.glasses_scale); | ||
| 358 | bf.glasses_y.Assign(info.glasses_y); | ||
| 359 | bf.mole_type.Assign(info.mole_type); | ||
| 360 | bf.mole_scale.Assign(info.mole_scale); | ||
| 361 | bf.mole_x.Assign(info.mole_x); | ||
| 362 | bf.mole_y.Assign(info.mole_y); | ||
| 363 | |||
| 364 | return {DefaultMiiName, bf, user_id}; | ||
| 365 | } | ||
| 366 | |||
| 367 | } // namespace | ||
| 368 | |||
| 369 | MiiStoreData::MiiStoreData() = default; | ||
| 370 | |||
| 371 | MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, | ||
| 372 | const Common::UUID& user_id) { | ||
| 373 | data.name = name; | ||
| 374 | data.uuid = Common::UUID::MakeRandomRFC4122V4(); | ||
| 375 | |||
| 376 | std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); | ||
| 377 | data_crc = GenerateCrc16(data.data.data(), sizeof(data)); | ||
| 378 | device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID)); | ||
| 379 | } | ||
| 380 | 22 | ||
| 381 | MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {} | 23 | bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { |
| 382 | |||
| 383 | bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) { | ||
| 384 | if ((source_flag & SourceFlag::Database) == SourceFlag::None) { | 24 | if ((source_flag & SourceFlag::Database) == SourceFlag::None) { |
| 385 | return false; | 25 | return false; |
| 386 | } | 26 | } |
| 387 | 27 | ||
| 388 | const bool result{current_update_counter != update_counter}; | 28 | const auto metadata_update_counter = metadata.update_counter; |
| 389 | 29 | metadata.update_counter = update_counter; | |
| 390 | current_update_counter = update_counter; | 30 | return metadata_update_counter != update_counter; |
| 391 | |||
| 392 | return result; | ||
| 393 | } | 31 | } |
| 394 | 32 | ||
| 395 | bool MiiManager::IsFullDatabase() const { | 33 | bool MiiManager::IsFullDatabase() const { |
| @@ -397,301 +35,138 @@ bool MiiManager::IsFullDatabase() const { | |||
| 397 | return false; | 35 | return false; |
| 398 | } | 36 | } |
| 399 | 37 | ||
| 400 | u32 MiiManager::GetCount(SourceFlag source_flag) const { | 38 | u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { |
| 401 | std::size_t count{}; | 39 | u32 mii_count{}; |
| 40 | if ((source_flag & SourceFlag::Default) != SourceFlag::None) { | ||
| 41 | mii_count += DefaultMiiCount; | ||
| 42 | } | ||
| 402 | if ((source_flag & SourceFlag::Database) != SourceFlag::None) { | 43 | if ((source_flag & SourceFlag::Database) != SourceFlag::None) { |
| 403 | // TODO(bunnei): We don't implement the Mii database, but when we do, update this | 44 | // TODO(bunnei): We don't implement the Mii database, but when we do, update this |
| 404 | count += 0; | ||
| 405 | } | 45 | } |
| 406 | if ((source_flag & SourceFlag::Default) != SourceFlag::None) { | 46 | return mii_count; |
| 407 | count += (DefaultMiiCount - BaseMiiCount); | ||
| 408 | } | ||
| 409 | return static_cast<u32>(count); | ||
| 410 | } | 47 | } |
| 411 | 48 | ||
| 412 | Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) { | 49 | Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info, |
| 50 | const CharInfo& char_info, SourceFlag source_flag) { | ||
| 413 | if ((source_flag & SourceFlag::Database) == SourceFlag::None) { | 51 | if ((source_flag & SourceFlag::Database) == SourceFlag::None) { |
| 414 | return ERROR_CANNOT_FIND_ENTRY; | 52 | return ResultNotFound; |
| 415 | } | 53 | } |
| 416 | 54 | ||
| 417 | // TODO(bunnei): We don't implement the Mii database, so we can't have an entry | 55 | // TODO(bunnei): We don't implement the Mii database, so we can't have an entry |
| 418 | return ERROR_CANNOT_FIND_ENTRY; | 56 | return ResultNotFound; |
| 419 | } | 57 | } |
| 420 | 58 | ||
| 421 | CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { | 59 | void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const { |
| 422 | return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); | 60 | StoreData store_data{}; |
| 61 | store_data.BuildDefault(index); | ||
| 62 | out_char_info.SetFromStoreData(store_data); | ||
| 423 | } | 63 | } |
| 424 | 64 | ||
| 425 | CharInfo MiiManager::BuildDefault(std::size_t index) { | 65 | void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const { |
| 426 | return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); | 66 | StoreData store_data{}; |
| 67 | store_data.BuildBase(gender); | ||
| 68 | out_char_info.SetFromStoreData(store_data); | ||
| 427 | } | 69 | } |
| 428 | 70 | ||
| 429 | CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { | 71 | void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const { |
| 430 | Service::Mii::MiiManager manager; | 72 | StoreData store_data{}; |
| 431 | auto mii = manager.BuildDefault(0); | 73 | store_data.BuildRandom(age, gender, race); |
| 432 | 74 | out_char_info.SetFromStoreData(store_data); | |
| 433 | if (!ValidateV3Info(mii_v3)) { | ||
| 434 | return mii; | ||
| 435 | } | ||
| 436 | |||
| 437 | // TODO: We are ignoring a bunch of data from the mii_v3 | ||
| 438 | |||
| 439 | mii.gender = static_cast<u8>(mii_v3.mii_information.gender); | ||
| 440 | mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color); | ||
| 441 | mii.height = mii_v3.height; | ||
| 442 | mii.build = mii_v3.build; | ||
| 443 | |||
| 444 | // Copy name until string terminator | ||
| 445 | mii.name = {}; | ||
| 446 | for (std::size_t index = 0; index < mii.name.size() - 1; index++) { | ||
| 447 | mii.name[index] = mii_v3.mii_name[index]; | ||
| 448 | if (mii.name[index] == 0) { | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | mii.font_region = mii_v3.region_information.character_set; | ||
| 454 | |||
| 455 | mii.faceline_type = mii_v3.appearance_bits1.face_shape; | ||
| 456 | mii.faceline_color = mii_v3.appearance_bits1.skin_color; | ||
| 457 | mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles; | ||
| 458 | mii.faceline_make = mii_v3.appearance_bits2.makeup; | ||
| 459 | |||
| 460 | mii.hair_type = mii_v3.hair_style; | ||
| 461 | mii.hair_color = mii_v3.appearance_bits3.hair_color; | ||
| 462 | mii.hair_flip = mii_v3.appearance_bits3.flip_hair; | ||
| 463 | |||
| 464 | mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type); | ||
| 465 | mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color); | ||
| 466 | mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale); | ||
| 467 | mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch); | ||
| 468 | mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation); | ||
| 469 | mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing); | ||
| 470 | mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position); | ||
| 471 | |||
| 472 | mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style); | ||
| 473 | mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color); | ||
| 474 | mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale); | ||
| 475 | mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale); | ||
| 476 | mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation); | ||
| 477 | mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing); | ||
| 478 | mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position); | ||
| 479 | |||
| 480 | mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type); | ||
| 481 | mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale); | ||
| 482 | mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position); | ||
| 483 | |||
| 484 | mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type); | ||
| 485 | mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color); | ||
| 486 | mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale); | ||
| 487 | mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch); | ||
| 488 | mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position); | ||
| 489 | |||
| 490 | mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type); | ||
| 491 | mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale); | ||
| 492 | mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position); | ||
| 493 | |||
| 494 | mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type); | ||
| 495 | mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color); | ||
| 496 | |||
| 497 | mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type); | ||
| 498 | mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color); | ||
| 499 | mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale); | ||
| 500 | mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position); | ||
| 501 | |||
| 502 | mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled); | ||
| 503 | mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale); | ||
| 504 | mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position); | ||
| 505 | mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position); | ||
| 506 | |||
| 507 | // TODO: Validate mii data | ||
| 508 | |||
| 509 | return mii; | ||
| 510 | } | 75 | } |
| 511 | 76 | ||
| 512 | Ver3StoreData MiiManager::BuildFromStoreData(const CharInfo& mii) const { | 77 | void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const { |
| 513 | Service::Mii::MiiManager manager; | 78 | StoreData store_data{}; |
| 514 | Ver3StoreData mii_v3{}; | 79 | mii_v3.BuildToStoreData(store_data); |
| 515 | 80 | out_char_info.SetFromStoreData(store_data); | |
| 516 | // TODO: We are ignoring a bunch of data from the mii_v3 | 81 | } |
| 517 | |||
| 518 | mii_v3.version = 1; | ||
| 519 | mii_v3.mii_information.gender.Assign(mii.gender); | ||
| 520 | mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); | ||
| 521 | mii_v3.height = mii.height; | ||
| 522 | mii_v3.build = mii.build; | ||
| 523 | 82 | ||
| 524 | // Copy name until string terminator | 83 | Result MiiManager::Get(const DatabaseSessionMetadata& metadata, |
| 525 | mii_v3.mii_name = {}; | 84 | std::span<CharInfoElement> out_elements, u32& out_count, |
| 526 | for (std::size_t index = 0; index < mii.name.size() - 1; index++) { | 85 | SourceFlag source_flag) { |
| 527 | mii_v3.mii_name[index] = mii.name[index]; | 86 | if ((source_flag & SourceFlag::Database) == SourceFlag::None) { |
| 528 | if (mii_v3.mii_name[index] == 0) { | 87 | return BuildDefault(out_elements, out_count, source_flag); |
| 529 | break; | ||
| 530 | } | ||
| 531 | } | 88 | } |
| 532 | 89 | ||
| 533 | mii_v3.region_information.character_set.Assign(mii.font_region); | 90 | // TODO(bunnei): We don't implement the Mii database, so we can't have an entry |
| 534 | 91 | ||
| 535 | mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); | 92 | // Include default Mii at the end of the list |
| 536 | mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); | 93 | return BuildDefault(out_elements, out_count, source_flag); |
| 537 | mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); | 94 | } |
| 538 | 95 | ||
| 539 | mii_v3.hair_style = mii.hair_type; | 96 | Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, |
| 540 | mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); | 97 | u32& out_count, SourceFlag source_flag) { |
| 98 | if ((source_flag & SourceFlag::Database) == SourceFlag::None) { | ||
| 99 | return BuildDefault(out_char_info, out_count, source_flag); | ||
| 100 | } | ||
| 541 | 101 | ||
| 542 | mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); | 102 | // TODO(bunnei): We don't implement the Mii database, so we can't have an entry |
| 543 | mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); | ||
| 544 | mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); | ||
| 545 | mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); | ||
| 546 | mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); | ||
| 547 | mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); | ||
| 548 | 103 | ||
| 549 | mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); | 104 | // Include default Mii at the end of the list |
| 550 | mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); | 105 | return BuildDefault(out_char_info, out_count, source_flag); |
| 551 | mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); | 106 | } |
| 552 | mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); | ||
| 553 | mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); | ||
| 554 | mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); | ||
| 555 | 107 | ||
| 556 | mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); | 108 | Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, |
| 557 | mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); | 109 | SourceFlag source_flag) { |
| 558 | mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); | 110 | if ((source_flag & SourceFlag::Default) == SourceFlag::None) { |
| 111 | return ResultSuccess; | ||
| 112 | } | ||
| 559 | 113 | ||
| 560 | mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); | 114 | StoreData store_data{}; |
| 561 | mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); | ||
| 562 | mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); | ||
| 563 | mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); | ||
| 564 | 115 | ||
| 565 | mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); | 116 | for (std::size_t index = 0; index < DefaultMiiCount; ++index) { |
| 566 | mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); | 117 | if (out_elements.size() <= static_cast<std::size_t>(out_count)) { |
| 567 | mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); | 118 | return ResultInvalidArgumentSize; |
| 119 | } | ||
| 568 | 120 | ||
| 569 | mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); | 121 | store_data.BuildDefault(static_cast<u32>(index)); |
| 570 | 122 | ||
| 571 | mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); | 123 | out_elements[out_count].source = Source::Default; |
| 572 | mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); | 124 | out_elements[out_count].char_info.SetFromStoreData(store_data); |
| 125 | out_count++; | ||
| 126 | } | ||
| 573 | 127 | ||
| 574 | mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); | 128 | return ResultSuccess; |
| 575 | mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); | 129 | } |
| 576 | mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); | ||
| 577 | mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); | ||
| 578 | 130 | ||
| 579 | // These types are converted to V3 from a table | 131 | Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, |
| 580 | mii_v3.appearance_bits1.skin_color.Assign(Ver3FacelineColorTable[mii.faceline_color]); | 132 | SourceFlag source_flag) { |
| 581 | mii_v3.appearance_bits3.hair_color.Assign(Ver3HairColorTable[mii.hair_color]); | 133 | if ((source_flag & SourceFlag::Default) == SourceFlag::None) { |
| 582 | mii_v3.appearance_bits4.eye_color.Assign(Ver3EyeColorTable[mii.eye_color]); | 134 | return ResultSuccess; |
| 583 | mii_v3.appearance_bits5.eyebrow_color.Assign(Ver3HairColorTable[mii.eyebrow_color]); | 135 | } |
| 584 | mii_v3.appearance_bits7.mouth_color.Assign(Ver3MouthlineColorTable[mii.mouth_color]); | ||
| 585 | mii_v3.appearance_bits9.facial_hair_color.Assign(Ver3HairColorTable[mii.beard_color]); | ||
| 586 | mii_v3.appearance_bits10.glasses_color.Assign(Ver3GlassColorTable[mii.glasses_color]); | ||
| 587 | mii_v3.appearance_bits10.glasses_type.Assign(Ver3GlassTypeTable[mii.glasses_type]); | ||
| 588 | 136 | ||
| 589 | mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16)); | 137 | StoreData store_data{}; |
| 590 | 138 | ||
| 591 | // TODO: Validate mii_v3 data | 139 | for (std::size_t index = 0; index < DefaultMiiCount; ++index) { |
| 140 | if (out_char_info.size() <= static_cast<std::size_t>(out_count)) { | ||
| 141 | return ResultInvalidArgumentSize; | ||
| 142 | } | ||
| 592 | 143 | ||
| 593 | return mii_v3; | 144 | store_data.BuildDefault(static_cast<u32>(index)); |
| 594 | } | ||
| 595 | 145 | ||
| 596 | NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { | 146 | out_char_info[out_count].SetFromStoreData(store_data); |
| 597 | return { | 147 | out_count++; |
| 598 | .faceline_color = static_cast<u8>(mii.faceline_color & 0xf), | 148 | } |
| 599 | .hair_color = static_cast<u8>(mii.hair_color & 0x7f), | ||
| 600 | .eye_color = static_cast<u8>(mii.eyebrow_color & 0x7f), | ||
| 601 | .eyebrow_color = static_cast<u8>(mii.eyebrow_color & 0x7f), | ||
| 602 | .mouth_color = static_cast<u8>(mii.mouth_color & 0x7f), | ||
| 603 | .beard_color = static_cast<u8>(mii.beard_color & 0x7f), | ||
| 604 | .glass_color = static_cast<u8>(mii.glasses_color & 0x7f), | ||
| 605 | .glass_type = static_cast<u8>(mii.glasses_type & 0x1f), | ||
| 606 | }; | ||
| 607 | } | ||
| 608 | 149 | ||
| 609 | bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { | 150 | return ResultSuccess; |
| 610 | bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; | ||
| 611 | |||
| 612 | is_valid = is_valid && (mii_v3.mii_name[0] != 0); | ||
| 613 | |||
| 614 | is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); | ||
| 615 | is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); | ||
| 616 | is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); | ||
| 617 | is_valid = is_valid && (mii_v3.height < 128); | ||
| 618 | is_valid = is_valid && (mii_v3.build < 128); | ||
| 619 | |||
| 620 | is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); | ||
| 621 | is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); | ||
| 622 | is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); | ||
| 623 | is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); | ||
| 624 | |||
| 625 | is_valid = is_valid && (mii_v3.hair_style < 132); | ||
| 626 | is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); | ||
| 627 | |||
| 628 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); | ||
| 629 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); | ||
| 630 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); | ||
| 631 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); | ||
| 632 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); | ||
| 633 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); | ||
| 634 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); | ||
| 635 | |||
| 636 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); | ||
| 637 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); | ||
| 638 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); | ||
| 639 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); | ||
| 640 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); | ||
| 641 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); | ||
| 642 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); | ||
| 643 | |||
| 644 | is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); | ||
| 645 | is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); | ||
| 646 | is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); | ||
| 647 | |||
| 648 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); | ||
| 649 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); | ||
| 650 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); | ||
| 651 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); | ||
| 652 | is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); | ||
| 653 | |||
| 654 | is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); | ||
| 655 | is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); | ||
| 656 | is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); | ||
| 657 | |||
| 658 | is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); | ||
| 659 | is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); | ||
| 660 | |||
| 661 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); | ||
| 662 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); | ||
| 663 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); | ||
| 664 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); | ||
| 665 | |||
| 666 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); | ||
| 667 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); | ||
| 668 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); | ||
| 669 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); | ||
| 670 | |||
| 671 | return is_valid; | ||
| 672 | } | 151 | } |
| 673 | 152 | ||
| 674 | std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) { | 153 | Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, |
| 675 | std::vector<MiiInfoElement> result; | 154 | s32& out_index) { |
| 676 | 155 | ||
| 677 | if ((source_flag & SourceFlag::Default) == SourceFlag::None) { | 156 | if (char_info.Verify() != ValidationResult::NoErrors) { |
| 678 | return result; | 157 | return ResultInvalidCharInfo; |
| 679 | } | 158 | } |
| 680 | 159 | ||
| 681 | for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { | ||
| 682 | result.emplace_back(BuildDefault(index), Source::Default); | ||
| 683 | } | ||
| 684 | |||
| 685 | return result; | ||
| 686 | } | ||
| 687 | |||
| 688 | Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { | ||
| 689 | constexpr u32 INVALID_INDEX{0xFFFFFFFF}; | 160 | constexpr u32 INVALID_INDEX{0xFFFFFFFF}; |
| 690 | 161 | ||
| 691 | index = INVALID_INDEX; | 162 | out_index = INVALID_INDEX; |
| 692 | 163 | ||
| 693 | // TODO(bunnei): We don't implement the Mii database, so we can't have an index | 164 | // TODO(bunnei): We don't implement the Mii database, so we can't have an index |
| 694 | return ERROR_CANNOT_FIND_ENTRY; | 165 | return ResultNotFound; |
| 166 | } | ||
| 167 | |||
| 168 | void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) { | ||
| 169 | metadata.interface_version = version; | ||
| 695 | } | 170 | } |
| 696 | 171 | ||
| 697 | } // namespace Service::Mii | 172 | } // namespace Service::Mii |
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 45c2be3c8..a2e7a6d73 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h | |||
| @@ -6,7 +6,10 @@ | |||
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | 7 | ||
| 8 | #include "core/hle/result.h" | 8 | #include "core/hle/result.h" |
| 9 | #include "core/hle/service/mii/types.h" | 9 | #include "core/hle/service/mii/mii_types.h" |
| 10 | #include "core/hle/service/mii/types/char_info.h" | ||
| 11 | #include "core/hle/service/mii/types/store_data.h" | ||
| 12 | #include "core/hle/service/mii/types/ver3_store_data.h" | ||
| 10 | 13 | ||
| 11 | namespace Service::Mii { | 14 | namespace Service::Mii { |
| 12 | 15 | ||
| @@ -16,25 +19,30 @@ class MiiManager { | |||
| 16 | public: | 19 | public: |
| 17 | MiiManager(); | 20 | MiiManager(); |
| 18 | 21 | ||
| 19 | bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); | 22 | bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; |
| 20 | bool IsFullDatabase() const; | ||
| 21 | u32 GetCount(SourceFlag source_flag) const; | ||
| 22 | Result UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag); | ||
| 23 | CharInfo BuildRandom(Age age, Gender gender, Race race); | ||
| 24 | CharInfo BuildDefault(std::size_t index); | ||
| 25 | CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; | ||
| 26 | bool ValidateV3Info(const Ver3StoreData& mii_v3) const; | ||
| 27 | std::vector<MiiInfoElement> GetDefault(SourceFlag source_flag); | ||
| 28 | Result GetIndex(const CharInfo& info, u32& index); | ||
| 29 | |||
| 30 | // This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData | ||
| 31 | Ver3StoreData BuildFromStoreData(const CharInfo& mii) const; | ||
| 32 | 23 | ||
| 33 | // This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData | 24 | bool IsFullDatabase() const; |
| 34 | NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const; | 25 | u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; |
| 26 | Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info, | ||
| 27 | const CharInfo& char_info, SourceFlag source_flag); | ||
| 28 | Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements, | ||
| 29 | u32& out_count, SourceFlag source_flag); | ||
| 30 | Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, | ||
| 31 | u32& out_count, SourceFlag source_flag); | ||
| 32 | void BuildDefault(CharInfo& out_char_info, u32 index) const; | ||
| 33 | void BuildBase(CharInfo& out_char_info, Gender gender) const; | ||
| 34 | void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const; | ||
| 35 | void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const; | ||
| 36 | std::vector<CharInfoElement> GetDefault(SourceFlag source_flag); | ||
| 37 | Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, | ||
| 38 | s32& out_index); | ||
| 39 | void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version); | ||
| 35 | 40 | ||
| 36 | private: | 41 | private: |
| 37 | const Common::UUID user_id{}; | 42 | Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, |
| 43 | SourceFlag source_flag); | ||
| 44 | Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag); | ||
| 45 | |||
| 38 | u64 update_counter{}; | 46 | u64 update_counter{}; |
| 39 | }; | 47 | }; |
| 40 | 48 | ||
diff --git a/src/core/hle/service/mii/mii_result.h b/src/core/hle/service/mii/mii_result.h new file mode 100644 index 000000000..021cb76da --- /dev/null +++ b/src/core/hle/service/mii/mii_result.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Service::Mii { | ||
| 9 | |||
| 10 | constexpr Result ResultInvalidArgument{ErrorModule::Mii, 1}; | ||
| 11 | constexpr Result ResultInvalidArgumentSize{ErrorModule::Mii, 2}; | ||
| 12 | constexpr Result ResultNotUpdated{ErrorModule::Mii, 3}; | ||
| 13 | constexpr Result ResultNotFound{ErrorModule::Mii, 4}; | ||
| 14 | constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5}; | ||
| 15 | constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100}; | ||
| 16 | constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109}; | ||
| 17 | constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202}; | ||
| 18 | constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203}; | ||
| 19 | |||
| 20 | }; // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h new file mode 100644 index 000000000..95476f745 --- /dev/null +++ b/src/core/hle/service/mii/mii_types.h | |||
| @@ -0,0 +1,694 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <type_traits> | ||
| 8 | |||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/uuid.h" | ||
| 13 | |||
| 14 | namespace Service::Mii { | ||
| 15 | |||
| 16 | constexpr u8 MaxHeight = 127; | ||
| 17 | constexpr u8 MaxBuild = 127; | ||
| 18 | constexpr u8 MaxType = 1; | ||
| 19 | constexpr u8 MaxRegionMove = 3; | ||
| 20 | constexpr u8 MaxEyeScale = 7; | ||
| 21 | constexpr u8 MaxEyeAspect = 6; | ||
| 22 | constexpr u8 MaxEyeRotate = 7; | ||
| 23 | constexpr u8 MaxEyeX = 12; | ||
| 24 | constexpr u8 MaxEyeY = 18; | ||
| 25 | constexpr u8 MaxEyebrowScale = 8; | ||
| 26 | constexpr u8 MaxEyebrowAspect = 6; | ||
| 27 | constexpr u8 MaxEyebrowRotate = 11; | ||
| 28 | constexpr u8 MaxEyebrowX = 12; | ||
| 29 | constexpr u8 MaxEyebrowY = 18; | ||
| 30 | constexpr u8 MaxNoseScale = 8; | ||
| 31 | constexpr u8 MaxNoseY = 18; | ||
| 32 | constexpr u8 MaxMouthScale = 8; | ||
| 33 | constexpr u8 MaxMoutAspect = 6; | ||
| 34 | constexpr u8 MaxMouthY = 18; | ||
| 35 | constexpr u8 MaxMustacheScale = 8; | ||
| 36 | constexpr u8 MasMustacheY = 16; | ||
| 37 | constexpr u8 MaxGlassScale = 7; | ||
| 38 | constexpr u8 MaxGlassY = 20; | ||
| 39 | constexpr u8 MaxMoleScale = 8; | ||
| 40 | constexpr u8 MaxMoleX = 16; | ||
| 41 | constexpr u8 MaxMoleY = 30; | ||
| 42 | constexpr u8 MaxVer3CommonColor = 7; | ||
| 43 | constexpr u8 MaxVer3GlassType = 8; | ||
| 44 | |||
| 45 | enum class Age : u8 { | ||
| 46 | Young, | ||
| 47 | Normal, | ||
| 48 | Old, | ||
| 49 | All, // Default | ||
| 50 | |||
| 51 | Max = All, | ||
| 52 | }; | ||
| 53 | |||
| 54 | enum class Gender : u8 { | ||
| 55 | Male, | ||
| 56 | Female, | ||
| 57 | All, // Default | ||
| 58 | |||
| 59 | Max = Female, | ||
| 60 | }; | ||
| 61 | |||
| 62 | enum class Race : u8 { | ||
| 63 | Black, | ||
| 64 | White, | ||
| 65 | Asian, | ||
| 66 | All, // Default | ||
| 67 | |||
| 68 | Max = All, | ||
| 69 | }; | ||
| 70 | |||
| 71 | enum class HairType : u8 { | ||
| 72 | NormalLong, // Default | ||
| 73 | NormalShort, | ||
| 74 | NormalMedium, | ||
| 75 | NormalExtraLong, | ||
| 76 | NormalLongBottom, | ||
| 77 | NormalTwoPeaks, | ||
| 78 | PartingLong, | ||
| 79 | FrontLock, | ||
| 80 | PartingShort, | ||
| 81 | PartingExtraLongCurved, | ||
| 82 | PartingExtraLong, | ||
| 83 | PartingMiddleLong, | ||
| 84 | PartingSquared, | ||
| 85 | PartingLongBottom, | ||
| 86 | PeaksTop, | ||
| 87 | PeaksSquared, | ||
| 88 | PartingPeaks, | ||
| 89 | PeaksLongBottom, | ||
| 90 | Peaks, | ||
| 91 | PeaksRounded, | ||
| 92 | PeaksSide, | ||
| 93 | PeaksMedium, | ||
| 94 | PeaksLong, | ||
| 95 | PeaksRoundedLong, | ||
| 96 | PartingFrontPeaks, | ||
| 97 | PartingLongFront, | ||
| 98 | PartingLongRounded, | ||
| 99 | PartingFrontPeaksLong, | ||
| 100 | PartingExtraLongRounded, | ||
| 101 | LongRounded, | ||
| 102 | NormalUnknown1, | ||
| 103 | NormalUnknown2, | ||
| 104 | NormalUnknown3, | ||
| 105 | NormalUnknown4, | ||
| 106 | NormalUnknown5, | ||
| 107 | NormalUnknown6, | ||
| 108 | DreadLocks, | ||
| 109 | PlatedMats, | ||
| 110 | Caps, | ||
| 111 | Afro, | ||
| 112 | PlatedMatsLong, | ||
| 113 | Beanie, | ||
| 114 | Short, | ||
| 115 | ShortTopLongSide, | ||
| 116 | ShortUnknown1, | ||
| 117 | ShortUnknown2, | ||
| 118 | MilitaryParting, | ||
| 119 | Military, | ||
| 120 | ShortUnknown3, | ||
| 121 | ShortUnknown4, | ||
| 122 | ShortUnknown5, | ||
| 123 | ShortUnknown6, | ||
| 124 | NoneTop, | ||
| 125 | None, | ||
| 126 | LongUnknown1, | ||
| 127 | LongUnknown2, | ||
| 128 | LongUnknown3, | ||
| 129 | LongUnknown4, | ||
| 130 | LongUnknown5, | ||
| 131 | LongUnknown6, | ||
| 132 | LongUnknown7, | ||
| 133 | LongUnknown8, | ||
| 134 | LongUnknown9, | ||
| 135 | LongUnknown10, | ||
| 136 | LongUnknown11, | ||
| 137 | LongUnknown12, | ||
| 138 | LongUnknown13, | ||
| 139 | LongUnknown14, | ||
| 140 | LongUnknown15, | ||
| 141 | LongUnknown16, | ||
| 142 | LongUnknown17, | ||
| 143 | LongUnknown18, | ||
| 144 | LongUnknown19, | ||
| 145 | LongUnknown20, | ||
| 146 | LongUnknown21, | ||
| 147 | LongUnknown22, | ||
| 148 | LongUnknown23, | ||
| 149 | LongUnknown24, | ||
| 150 | LongUnknown25, | ||
| 151 | LongUnknown26, | ||
| 152 | LongUnknown27, | ||
| 153 | LongUnknown28, | ||
| 154 | LongUnknown29, | ||
| 155 | LongUnknown30, | ||
| 156 | LongUnknown31, | ||
| 157 | LongUnknown32, | ||
| 158 | LongUnknown33, | ||
| 159 | LongUnknown34, | ||
| 160 | LongUnknown35, | ||
| 161 | LongUnknown36, | ||
| 162 | LongUnknown37, | ||
| 163 | LongUnknown38, | ||
| 164 | LongUnknown39, | ||
| 165 | LongUnknown40, | ||
| 166 | LongUnknown41, | ||
| 167 | LongUnknown42, | ||
| 168 | LongUnknown43, | ||
| 169 | LongUnknown44, | ||
| 170 | LongUnknown45, | ||
| 171 | LongUnknown46, | ||
| 172 | LongUnknown47, | ||
| 173 | LongUnknown48, | ||
| 174 | LongUnknown49, | ||
| 175 | LongUnknown50, | ||
| 176 | LongUnknown51, | ||
| 177 | LongUnknown52, | ||
| 178 | LongUnknown53, | ||
| 179 | LongUnknown54, | ||
| 180 | LongUnknown55, | ||
| 181 | LongUnknown56, | ||
| 182 | LongUnknown57, | ||
| 183 | LongUnknown58, | ||
| 184 | LongUnknown59, | ||
| 185 | LongUnknown60, | ||
| 186 | LongUnknown61, | ||
| 187 | LongUnknown62, | ||
| 188 | LongUnknown63, | ||
| 189 | LongUnknown64, | ||
| 190 | LongUnknown65, | ||
| 191 | LongUnknown66, | ||
| 192 | TwoMediumFrontStrandsOneLongBackPonyTail, | ||
| 193 | TwoFrontStrandsLongBackPonyTail, | ||
| 194 | PartingFrontTwoLongBackPonyTails, | ||
| 195 | TwoFrontStrandsOneLongBackPonyTail, | ||
| 196 | LongBackPonyTail, | ||
| 197 | LongFrontTwoLongBackPonyTails, | ||
| 198 | StrandsTwoShortSidedPonyTails, | ||
| 199 | TwoMediumSidedPonyTails, | ||
| 200 | ShortFrontTwoBackPonyTails, | ||
| 201 | TwoShortSidedPonyTails, | ||
| 202 | TwoLongSidedPonyTails, | ||
| 203 | LongFrontTwoBackPonyTails, | ||
| 204 | |||
| 205 | Max = LongFrontTwoBackPonyTails, | ||
| 206 | }; | ||
| 207 | |||
| 208 | enum class MoleType : u8 { | ||
| 209 | None, // Default | ||
| 210 | OneDot, | ||
| 211 | |||
| 212 | Max = OneDot, | ||
| 213 | }; | ||
| 214 | |||
| 215 | enum class HairFlip : u8 { | ||
| 216 | Left, // Default | ||
| 217 | Right, | ||
| 218 | |||
| 219 | Max = Right, | ||
| 220 | }; | ||
| 221 | |||
| 222 | enum class CommonColor : u8 { | ||
| 223 | // For simplicity common colors aren't listed | ||
| 224 | Max = 99, | ||
| 225 | Count = 100, | ||
| 226 | }; | ||
| 227 | |||
| 228 | enum class FavoriteColor : u8 { | ||
| 229 | Red, // Default | ||
| 230 | Orange, | ||
| 231 | Yellow, | ||
| 232 | LimeGreen, | ||
| 233 | Green, | ||
| 234 | Blue, | ||
| 235 | LightBlue, | ||
| 236 | Pink, | ||
| 237 | Purple, | ||
| 238 | Brown, | ||
| 239 | White, | ||
| 240 | Black, | ||
| 241 | |||
| 242 | Max = Black, | ||
| 243 | }; | ||
| 244 | |||
| 245 | enum class EyeType : u8 { | ||
| 246 | Normal, // Default | ||
| 247 | NormalLash, | ||
| 248 | WhiteLash, | ||
| 249 | WhiteNoBottom, | ||
| 250 | OvalAngledWhite, | ||
| 251 | AngryWhite, | ||
| 252 | DotLashType1, | ||
| 253 | Line, | ||
| 254 | DotLine, | ||
| 255 | OvalWhite, | ||
| 256 | RoundedWhite, | ||
| 257 | NormalShadow, | ||
| 258 | CircleWhite, | ||
| 259 | Circle, | ||
| 260 | CircleWhiteStroke, | ||
| 261 | NormalOvalNoBottom, | ||
| 262 | NormalOvalLarge, | ||
| 263 | NormalRoundedNoBottom, | ||
| 264 | SmallLash, | ||
| 265 | Small, | ||
| 266 | TwoSmall, | ||
| 267 | NormalLongLash, | ||
| 268 | WhiteTwoLashes, | ||
| 269 | WhiteThreeLashes, | ||
| 270 | DotAngry, | ||
| 271 | DotAngled, | ||
| 272 | Oval, | ||
| 273 | SmallWhite, | ||
| 274 | WhiteAngledNoBottom, | ||
| 275 | WhiteAngledNoLeft, | ||
| 276 | SmallWhiteTwoLashes, | ||
| 277 | LeafWhiteLash, | ||
| 278 | WhiteLargeNoBottom, | ||
| 279 | Dot, | ||
| 280 | DotLashType2, | ||
| 281 | DotThreeLashes, | ||
| 282 | WhiteOvalTop, | ||
| 283 | WhiteOvalBottom, | ||
| 284 | WhiteOvalBottomFlat, | ||
| 285 | WhiteOvalTwoLashes, | ||
| 286 | WhiteOvalThreeLashes, | ||
| 287 | WhiteOvalNoBottomTwoLashes, | ||
| 288 | DotWhite, | ||
| 289 | WhiteOvalTopFlat, | ||
| 290 | WhiteThinLeaf, | ||
| 291 | StarThreeLashes, | ||
| 292 | LineTwoLashes, | ||
| 293 | CrowsFeet, | ||
| 294 | WhiteNoBottomFlat, | ||
| 295 | WhiteNoBottomRounded, | ||
| 296 | WhiteSmallBottomLine, | ||
| 297 | WhiteNoBottomLash, | ||
| 298 | WhiteNoPartialBottomLash, | ||
| 299 | WhiteOvalBottomLine, | ||
| 300 | WhiteNoBottomLashTopLine, | ||
| 301 | WhiteNoPartialBottomTwoLashes, | ||
| 302 | NormalTopLine, | ||
| 303 | WhiteOvalLash, | ||
| 304 | RoundTired, | ||
| 305 | WhiteLarge, | ||
| 306 | |||
| 307 | Max = WhiteLarge, | ||
| 308 | }; | ||
| 309 | |||
| 310 | enum class MouthType : u8 { | ||
| 311 | Neutral, // Default | ||
| 312 | NeutralLips, | ||
| 313 | Smile, | ||
| 314 | SmileStroke, | ||
| 315 | SmileTeeth, | ||
| 316 | LipsSmall, | ||
| 317 | LipsLarge, | ||
| 318 | Wave, | ||
| 319 | WaveAngrySmall, | ||
| 320 | NeutralStrokeLarge, | ||
| 321 | TeethSurprised, | ||
| 322 | LipsExtraLarge, | ||
| 323 | LipsUp, | ||
| 324 | NeutralDown, | ||
| 325 | Surprised, | ||
| 326 | TeethMiddle, | ||
| 327 | NeutralStroke, | ||
| 328 | LipsExtraSmall, | ||
| 329 | Malicious, | ||
| 330 | LipsDual, | ||
| 331 | NeutralComma, | ||
| 332 | NeutralUp, | ||
| 333 | TeethLarge, | ||
| 334 | WaveAngry, | ||
| 335 | LipsSexy, | ||
| 336 | SmileInverted, | ||
| 337 | LipsSexyOutline, | ||
| 338 | SmileRounded, | ||
| 339 | LipsTeeth, | ||
| 340 | NeutralOpen, | ||
| 341 | TeethRounded, | ||
| 342 | WaveAngrySmallInverted, | ||
| 343 | NeutralCommaInverted, | ||
| 344 | TeethFull, | ||
| 345 | SmileDownLine, | ||
| 346 | Kiss, | ||
| 347 | |||
| 348 | Max = Kiss, | ||
| 349 | }; | ||
| 350 | |||
| 351 | enum class FontRegion : u8 { | ||
| 352 | Standard, // Default | ||
| 353 | China, | ||
| 354 | Korea, | ||
| 355 | Taiwan, | ||
| 356 | |||
| 357 | Max = Taiwan, | ||
| 358 | }; | ||
| 359 | |||
| 360 | enum class FacelineType : u8 { | ||
| 361 | Sharp, // Default | ||
| 362 | Rounded, | ||
| 363 | SharpRounded, | ||
| 364 | SharpRoundedSmall, | ||
| 365 | Large, | ||
| 366 | LargeRounded, | ||
| 367 | SharpSmall, | ||
| 368 | Flat, | ||
| 369 | Bump, | ||
| 370 | Angular, | ||
| 371 | FlatRounded, | ||
| 372 | AngularSmall, | ||
| 373 | |||
| 374 | Max = AngularSmall, | ||
| 375 | }; | ||
| 376 | |||
| 377 | enum class FacelineColor : u8 { | ||
| 378 | Beige, // Default | ||
| 379 | WarmBeige, | ||
| 380 | Natural, | ||
| 381 | Honey, | ||
| 382 | Chestnut, | ||
| 383 | Porcelain, | ||
| 384 | Ivory, | ||
| 385 | WarmIvory, | ||
| 386 | Almond, | ||
| 387 | Espresso, | ||
| 388 | |||
| 389 | Max = Espresso, | ||
| 390 | Count = Max + 1, | ||
| 391 | }; | ||
| 392 | |||
| 393 | enum class FacelineWrinkle : u8 { | ||
| 394 | None, // Default | ||
| 395 | TearTroughs, | ||
| 396 | FacialPain, | ||
| 397 | Cheeks, | ||
| 398 | Folds, | ||
| 399 | UnderTheEyes, | ||
| 400 | SplitChin, | ||
| 401 | Chin, | ||
| 402 | BrowDroop, | ||
| 403 | MouthFrown, | ||
| 404 | CrowsFeet, | ||
| 405 | FoldsCrowsFrown, | ||
| 406 | |||
| 407 | Max = FoldsCrowsFrown, | ||
| 408 | }; | ||
| 409 | |||
| 410 | enum class FacelineMake : u8 { | ||
| 411 | None, // Default | ||
| 412 | CheekPorcelain, | ||
| 413 | CheekNatural, | ||
| 414 | EyeShadowBlue, | ||
| 415 | CheekBlushPorcelain, | ||
| 416 | CheekBlushNatural, | ||
| 417 | CheekPorcelainEyeShadowBlue, | ||
| 418 | CheekPorcelainEyeShadowNatural, | ||
| 419 | CheekBlushPorcelainEyeShadowEspresso, | ||
| 420 | Freckles, | ||
| 421 | LionsManeBeard, | ||
| 422 | StubbleBeard, | ||
| 423 | |||
| 424 | Max = StubbleBeard, | ||
| 425 | }; | ||
| 426 | |||
| 427 | enum class EyebrowType : u8 { | ||
| 428 | FlatAngledLarge, // Default | ||
| 429 | LowArchRoundedThin, | ||
| 430 | SoftAngledLarge, | ||
| 431 | MediumArchRoundedThin, | ||
| 432 | RoundedMedium, | ||
| 433 | LowArchMedium, | ||
| 434 | RoundedThin, | ||
| 435 | UpThin, | ||
| 436 | MediumArchRoundedMedium, | ||
| 437 | RoundedLarge, | ||
| 438 | UpLarge, | ||
| 439 | FlatAngledLargeInverted, | ||
| 440 | MediumArchFlat, | ||
| 441 | AngledThin, | ||
| 442 | HorizontalLarge, | ||
| 443 | HighArchFlat, | ||
| 444 | Flat, | ||
| 445 | MediumArchLarge, | ||
| 446 | LowArchThin, | ||
| 447 | RoundedThinInverted, | ||
| 448 | HighArchLarge, | ||
| 449 | Hairy, | ||
| 450 | Dotted, | ||
| 451 | None, | ||
| 452 | |||
| 453 | Max = None, | ||
| 454 | }; | ||
| 455 | |||
| 456 | enum class NoseType : u8 { | ||
| 457 | Normal, // Default | ||
| 458 | Rounded, | ||
| 459 | Dot, | ||
| 460 | Arrow, | ||
| 461 | Roman, | ||
| 462 | Triangle, | ||
| 463 | Button, | ||
| 464 | RoundedInverted, | ||
| 465 | Potato, | ||
| 466 | Grecian, | ||
| 467 | Snub, | ||
| 468 | Aquiline, | ||
| 469 | ArrowLeft, | ||
| 470 | RoundedLarge, | ||
| 471 | Hooked, | ||
| 472 | Fat, | ||
| 473 | Droopy, | ||
| 474 | ArrowLarge, | ||
| 475 | |||
| 476 | Max = ArrowLarge, | ||
| 477 | }; | ||
| 478 | |||
| 479 | enum class BeardType : u8 { | ||
| 480 | None, | ||
| 481 | Goatee, | ||
| 482 | GoateeLong, | ||
| 483 | LionsManeLong, | ||
| 484 | LionsMane, | ||
| 485 | Full, | ||
| 486 | |||
| 487 | Min = Goatee, | ||
| 488 | Max = Full, | ||
| 489 | }; | ||
| 490 | |||
| 491 | enum class MustacheType : u8 { | ||
| 492 | None, | ||
| 493 | Walrus, | ||
| 494 | Pencil, | ||
| 495 | Horseshoe, | ||
| 496 | Normal, | ||
| 497 | Toothbrush, | ||
| 498 | |||
| 499 | Min = Walrus, | ||
| 500 | Max = Toothbrush, | ||
| 501 | }; | ||
| 502 | |||
| 503 | enum class GlassType : u8 { | ||
| 504 | None, | ||
| 505 | Oval, | ||
| 506 | Wayfarer, | ||
| 507 | Rectangle, | ||
| 508 | TopRimless, | ||
| 509 | Rounded, | ||
| 510 | Oversized, | ||
| 511 | CatEye, | ||
| 512 | Square, | ||
| 513 | BottomRimless, | ||
| 514 | SemiOpaqueRounded, | ||
| 515 | SemiOpaqueCatEye, | ||
| 516 | SemiOpaqueOval, | ||
| 517 | SemiOpaqueRectangle, | ||
| 518 | SemiOpaqueAviator, | ||
| 519 | OpaqueRounded, | ||
| 520 | OpaqueCatEye, | ||
| 521 | OpaqueOval, | ||
| 522 | OpaqueRectangle, | ||
| 523 | OpaqueAviator, | ||
| 524 | |||
| 525 | Max = OpaqueAviator, | ||
| 526 | Count = Max + 1, | ||
| 527 | }; | ||
| 528 | |||
| 529 | enum class BeardAndMustacheFlag : u32 { | ||
| 530 | Beard = 1, | ||
| 531 | Mustache, | ||
| 532 | All = Beard | Mustache, | ||
| 533 | }; | ||
| 534 | DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); | ||
| 535 | |||
| 536 | enum class Source : u32 { | ||
| 537 | Database = 0, | ||
| 538 | Default = 1, | ||
| 539 | Account = 2, | ||
| 540 | Friend = 3, | ||
| 541 | }; | ||
| 542 | |||
| 543 | enum class SourceFlag : u32 { | ||
| 544 | None = 0, | ||
| 545 | Database = 1 << 0, | ||
| 546 | Default = 1 << 1, | ||
| 547 | }; | ||
| 548 | DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); | ||
| 549 | |||
| 550 | enum class ValidationResult : u32 { | ||
| 551 | NoErrors = 0x0, | ||
| 552 | InvalidBeardColor = 0x1, | ||
| 553 | InvalidBeardType = 0x2, | ||
| 554 | InvalidBuild = 0x3, | ||
| 555 | InvalidEyeAspect = 0x4, | ||
| 556 | InvalidEyeColor = 0x5, | ||
| 557 | InvalidEyeRotate = 0x6, | ||
| 558 | InvalidEyeScale = 0x7, | ||
| 559 | InvalidEyeType = 0x8, | ||
| 560 | InvalidEyeX = 0x9, | ||
| 561 | InvalidEyeY = 0xa, | ||
| 562 | InvalidEyebrowAspect = 0xb, | ||
| 563 | InvalidEyebrowColor = 0xc, | ||
| 564 | InvalidEyebrowRotate = 0xd, | ||
| 565 | InvalidEyebrowScale = 0xe, | ||
| 566 | InvalidEyebrowType = 0xf, | ||
| 567 | InvalidEyebrowX = 0x10, | ||
| 568 | InvalidEyebrowY = 0x11, | ||
| 569 | InvalidFacelineColor = 0x12, | ||
| 570 | InvalidFacelineMake = 0x13, | ||
| 571 | InvalidFacelineWrinkle = 0x14, | ||
| 572 | InvalidFacelineType = 0x15, | ||
| 573 | InvalidColor = 0x16, | ||
| 574 | InvalidFont = 0x17, | ||
| 575 | InvalidGender = 0x18, | ||
| 576 | InvalidGlassColor = 0x19, | ||
| 577 | InvalidGlassScale = 0x1a, | ||
| 578 | InvalidGlassType = 0x1b, | ||
| 579 | InvalidGlassY = 0x1c, | ||
| 580 | InvalidHairColor = 0x1d, | ||
| 581 | InvalidHairFlip = 0x1e, | ||
| 582 | InvalidHairType = 0x1f, | ||
| 583 | InvalidHeight = 0x20, | ||
| 584 | InvalidMoleScale = 0x21, | ||
| 585 | InvalidMoleType = 0x22, | ||
| 586 | InvalidMoleX = 0x23, | ||
| 587 | InvalidMoleY = 0x24, | ||
| 588 | InvalidMouthAspect = 0x25, | ||
| 589 | InvalidMouthColor = 0x26, | ||
| 590 | InvalidMouthScale = 0x27, | ||
| 591 | InvalidMouthType = 0x28, | ||
| 592 | InvalidMouthY = 0x29, | ||
| 593 | InvalidMustacheScale = 0x2a, | ||
| 594 | InvalidMustacheType = 0x2b, | ||
| 595 | InvalidMustacheY = 0x2c, | ||
| 596 | InvalidNoseScale = 0x2e, | ||
| 597 | InvalidNoseType = 0x2f, | ||
| 598 | InvalidNoseY = 0x30, | ||
| 599 | InvalidRegionMove = 0x31, | ||
| 600 | InvalidCreateId = 0x32, | ||
| 601 | InvalidName = 0x33, | ||
| 602 | InvalidType = 0x35, | ||
| 603 | }; | ||
| 604 | |||
| 605 | struct Nickname { | ||
| 606 | static constexpr std::size_t MaxNameSize = 10; | ||
| 607 | std::array<char16_t, MaxNameSize> data; | ||
| 608 | |||
| 609 | // Checks for null, non-zero terminated or dirty strings | ||
| 610 | bool IsValid() const { | ||
| 611 | if (data[0] == 0) { | ||
| 612 | return false; | ||
| 613 | } | ||
| 614 | |||
| 615 | if (data[MaxNameSize] != 0) { | ||
| 616 | return false; | ||
| 617 | } | ||
| 618 | std::size_t index = 1; | ||
| 619 | while (data[index] != 0) { | ||
| 620 | index++; | ||
| 621 | } | ||
| 622 | while (index < MaxNameSize && data[index] == 0) { | ||
| 623 | index++; | ||
| 624 | } | ||
| 625 | return index == MaxNameSize; | ||
| 626 | } | ||
| 627 | }; | ||
| 628 | static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size"); | ||
| 629 | |||
| 630 | struct DefaultMii { | ||
| 631 | u32 face_type{}; | ||
| 632 | u32 face_color{}; | ||
| 633 | u32 face_wrinkle{}; | ||
| 634 | u32 face_makeup{}; | ||
| 635 | u32 hair_type{}; | ||
| 636 | u32 hair_color{}; | ||
| 637 | u32 hair_flip{}; | ||
| 638 | u32 eye_type{}; | ||
| 639 | u32 eye_color{}; | ||
| 640 | u32 eye_scale{}; | ||
| 641 | u32 eye_aspect{}; | ||
| 642 | u32 eye_rotate{}; | ||
| 643 | u32 eye_x{}; | ||
| 644 | u32 eye_y{}; | ||
| 645 | u32 eyebrow_type{}; | ||
| 646 | u32 eyebrow_color{}; | ||
| 647 | u32 eyebrow_scale{}; | ||
| 648 | u32 eyebrow_aspect{}; | ||
| 649 | u32 eyebrow_rotate{}; | ||
| 650 | u32 eyebrow_x{}; | ||
| 651 | u32 eyebrow_y{}; | ||
| 652 | u32 nose_type{}; | ||
| 653 | u32 nose_scale{}; | ||
| 654 | u32 nose_y{}; | ||
| 655 | u32 mouth_type{}; | ||
| 656 | u32 mouth_color{}; | ||
| 657 | u32 mouth_scale{}; | ||
| 658 | u32 mouth_aspect{}; | ||
| 659 | u32 mouth_y{}; | ||
| 660 | u32 mustache_type{}; | ||
| 661 | u32 beard_type{}; | ||
| 662 | u32 beard_color{}; | ||
| 663 | u32 mustache_scale{}; | ||
| 664 | u32 mustache_y{}; | ||
| 665 | u32 glasses_type{}; | ||
| 666 | u32 glasses_color{}; | ||
| 667 | u32 glasses_scale{}; | ||
| 668 | u32 glasses_y{}; | ||
| 669 | u32 mole_type{}; | ||
| 670 | u32 mole_scale{}; | ||
| 671 | u32 mole_x{}; | ||
| 672 | u32 mole_y{}; | ||
| 673 | u32 height{}; | ||
| 674 | u32 weight{}; | ||
| 675 | u32 gender{}; | ||
| 676 | u32 favorite_color{}; | ||
| 677 | u32 region_move{}; | ||
| 678 | u32 font_region{}; | ||
| 679 | u32 type{}; | ||
| 680 | Nickname nickname; | ||
| 681 | }; | ||
| 682 | static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii has incorrect size."); | ||
| 683 | |||
| 684 | struct DatabaseSessionMetadata { | ||
| 685 | u32 interface_version; | ||
| 686 | u32 magic; | ||
| 687 | u64 update_counter; | ||
| 688 | |||
| 689 | bool IsInterfaceVersionSupported(u32 version) const { | ||
| 690 | return version <= interface_version; | ||
| 691 | } | ||
| 692 | }; | ||
| 693 | |||
| 694 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h new file mode 100644 index 000000000..ddb544c23 --- /dev/null +++ b/src/core/hle/service/mii/mii_util.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <random> | ||
| 7 | #include <span> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/swap.h" | ||
| 11 | #include "common/uuid.h" | ||
| 12 | #include "core/hle/service/mii/mii_types.h" | ||
| 13 | |||
| 14 | namespace Service::Mii { | ||
| 15 | class MiiUtil { | ||
| 16 | public: | ||
| 17 | static u16 CalculateCrc16(const void* data, std::size_t size) { | ||
| 18 | s32 crc{}; | ||
| 19 | for (std::size_t i = 0; i < size; i++) { | ||
| 20 | crc ^= static_cast<const u8*>(data)[i] << 8; | ||
| 21 | for (std::size_t j = 0; j < 8; j++) { | ||
| 22 | crc <<= 1; | ||
| 23 | if ((crc & 0x10000) != 0) { | ||
| 24 | crc = (crc ^ 0x1021) & 0xFFFF; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | return Common::swap16(static_cast<u16>(crc)); | ||
| 29 | } | ||
| 30 | |||
| 31 | static Common::UUID MakeCreateId() { | ||
| 32 | return Common::UUID::MakeRandomRFC4122V4(); | ||
| 33 | } | ||
| 34 | |||
| 35 | static Common::UUID GetDeviceId() { | ||
| 36 | // This should be nn::settings::detail::GetMiiAuthorId() | ||
| 37 | return Common::UUID::MakeDefault(); | ||
| 38 | } | ||
| 39 | |||
| 40 | template <typename T> | ||
| 41 | static T GetRandomValue(T min, T max) { | ||
| 42 | std::random_device device; | ||
| 43 | std::mt19937 gen(device()); | ||
| 44 | std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), | ||
| 45 | static_cast<u64>(max)); | ||
| 46 | return static_cast<T>(distribution(gen)); | ||
| 47 | } | ||
| 48 | |||
| 49 | template <typename T> | ||
| 50 | static T GetRandomValue(T max) { | ||
| 51 | return GetRandomValue<T>({}, max); | ||
| 52 | } | ||
| 53 | |||
| 54 | static bool IsFontRegionValid(FontRegion font, std::span<const char16_t> text) { | ||
| 55 | // TODO: This function needs to check against the font tables | ||
| 56 | return true; | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h deleted file mode 100644 index c2bec68d4..000000000 --- a/src/core/hle/service/mii/raw_data.h +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "core/hle/service/mii/types.h" | ||
| 9 | |||
| 10 | namespace Service::Mii::RawData { | ||
| 11 | |||
| 12 | extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii; | ||
| 13 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline; | ||
| 14 | extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor; | ||
| 15 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle; | ||
| 16 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup; | ||
| 17 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType; | ||
| 18 | extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor; | ||
| 19 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType; | ||
| 20 | extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor; | ||
| 21 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType; | ||
| 22 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType; | ||
| 23 | extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType; | ||
| 24 | extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType; | ||
| 25 | |||
| 26 | } // namespace Service::Mii::RawData | ||
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h deleted file mode 100644 index c48d08d79..000000000 --- a/src/core/hle/service/mii/types.h +++ /dev/null | |||
| @@ -1,553 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <type_traits> | ||
| 8 | |||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/uuid.h" | ||
| 13 | |||
| 14 | namespace Service::Mii { | ||
| 15 | |||
| 16 | enum class Age : u32 { | ||
| 17 | Young, | ||
| 18 | Normal, | ||
| 19 | Old, | ||
| 20 | All, | ||
| 21 | }; | ||
| 22 | |||
| 23 | enum class BeardType : u32 { | ||
| 24 | None, | ||
| 25 | Beard1, | ||
| 26 | Beard2, | ||
| 27 | Beard3, | ||
| 28 | Beard4, | ||
| 29 | Beard5, | ||
| 30 | }; | ||
| 31 | |||
| 32 | enum class BeardAndMustacheFlag : u32 { | ||
| 33 | Beard = 1, | ||
| 34 | Mustache, | ||
| 35 | All = Beard | Mustache, | ||
| 36 | }; | ||
| 37 | DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); | ||
| 38 | |||
| 39 | enum class FontRegion : u32 { | ||
| 40 | Standard, | ||
| 41 | China, | ||
| 42 | Korea, | ||
| 43 | Taiwan, | ||
| 44 | }; | ||
| 45 | |||
| 46 | enum class Gender : u32 { | ||
| 47 | Male, | ||
| 48 | Female, | ||
| 49 | All, | ||
| 50 | Maximum = Female, | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum class HairFlip : u32 { | ||
| 54 | Left, | ||
| 55 | Right, | ||
| 56 | Maximum = Right, | ||
| 57 | }; | ||
| 58 | |||
| 59 | enum class MustacheType : u32 { | ||
| 60 | None, | ||
| 61 | Mustache1, | ||
| 62 | Mustache2, | ||
| 63 | Mustache3, | ||
| 64 | Mustache4, | ||
| 65 | Mustache5, | ||
| 66 | }; | ||
| 67 | |||
| 68 | enum class Race : u32 { | ||
| 69 | Black, | ||
| 70 | White, | ||
| 71 | Asian, | ||
| 72 | All, | ||
| 73 | }; | ||
| 74 | |||
| 75 | enum class Source : u32 { | ||
| 76 | Database = 0, | ||
| 77 | Default = 1, | ||
| 78 | Account = 2, | ||
| 79 | Friend = 3, | ||
| 80 | }; | ||
| 81 | |||
| 82 | enum class SourceFlag : u32 { | ||
| 83 | None = 0, | ||
| 84 | Database = 1 << 0, | ||
| 85 | Default = 1 << 1, | ||
| 86 | }; | ||
| 87 | DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); | ||
| 88 | |||
| 89 | // nn::mii::CharInfo | ||
| 90 | struct CharInfo { | ||
| 91 | Common::UUID uuid; | ||
| 92 | std::array<char16_t, 11> name; | ||
| 93 | u8 font_region; | ||
| 94 | u8 favorite_color; | ||
| 95 | u8 gender; | ||
| 96 | u8 height; | ||
| 97 | u8 build; | ||
| 98 | u8 type; | ||
| 99 | u8 region_move; | ||
| 100 | u8 faceline_type; | ||
| 101 | u8 faceline_color; | ||
| 102 | u8 faceline_wrinkle; | ||
| 103 | u8 faceline_make; | ||
| 104 | u8 hair_type; | ||
| 105 | u8 hair_color; | ||
| 106 | u8 hair_flip; | ||
| 107 | u8 eye_type; | ||
| 108 | u8 eye_color; | ||
| 109 | u8 eye_scale; | ||
| 110 | u8 eye_aspect; | ||
| 111 | u8 eye_rotate; | ||
| 112 | u8 eye_x; | ||
| 113 | u8 eye_y; | ||
| 114 | u8 eyebrow_type; | ||
| 115 | u8 eyebrow_color; | ||
| 116 | u8 eyebrow_scale; | ||
| 117 | u8 eyebrow_aspect; | ||
| 118 | u8 eyebrow_rotate; | ||
| 119 | u8 eyebrow_x; | ||
| 120 | u8 eyebrow_y; | ||
| 121 | u8 nose_type; | ||
| 122 | u8 nose_scale; | ||
| 123 | u8 nose_y; | ||
| 124 | u8 mouth_type; | ||
| 125 | u8 mouth_color; | ||
| 126 | u8 mouth_scale; | ||
| 127 | u8 mouth_aspect; | ||
| 128 | u8 mouth_y; | ||
| 129 | u8 beard_color; | ||
| 130 | u8 beard_type; | ||
| 131 | u8 mustache_type; | ||
| 132 | u8 mustache_scale; | ||
| 133 | u8 mustache_y; | ||
| 134 | u8 glasses_type; | ||
| 135 | u8 glasses_color; | ||
| 136 | u8 glasses_scale; | ||
| 137 | u8 glasses_y; | ||
| 138 | u8 mole_type; | ||
| 139 | u8 mole_scale; | ||
| 140 | u8 mole_x; | ||
| 141 | u8 mole_y; | ||
| 142 | u8 padding; | ||
| 143 | }; | ||
| 144 | static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); | ||
| 145 | static_assert(std::has_unique_object_representations_v<CharInfo>, | ||
| 146 | "All bits of CharInfo must contribute to its value."); | ||
| 147 | |||
| 148 | #pragma pack(push, 4) | ||
| 149 | |||
| 150 | struct MiiInfoElement { | ||
| 151 | MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {} | ||
| 152 | |||
| 153 | CharInfo info{}; | ||
| 154 | Source source{}; | ||
| 155 | }; | ||
| 156 | static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); | ||
| 157 | |||
| 158 | struct MiiStoreBitFields { | ||
| 159 | union { | ||
| 160 | u32 word_0{}; | ||
| 161 | |||
| 162 | BitField<0, 8, u32> hair_type; | ||
| 163 | BitField<8, 7, u32> height; | ||
| 164 | BitField<15, 1, u32> mole_type; | ||
| 165 | BitField<16, 7, u32> build; | ||
| 166 | BitField<23, 1, HairFlip> hair_flip; | ||
| 167 | BitField<24, 7, u32> hair_color; | ||
| 168 | BitField<31, 1, u32> type; | ||
| 169 | }; | ||
| 170 | |||
| 171 | union { | ||
| 172 | u32 word_1{}; | ||
| 173 | |||
| 174 | BitField<0, 7, u32> eye_color; | ||
| 175 | BitField<7, 1, Gender> gender; | ||
| 176 | BitField<8, 7, u32> eyebrow_color; | ||
| 177 | BitField<16, 7, u32> mouth_color; | ||
| 178 | BitField<24, 7, u32> beard_color; | ||
| 179 | }; | ||
| 180 | |||
| 181 | union { | ||
| 182 | u32 word_2{}; | ||
| 183 | |||
| 184 | BitField<0, 7, u32> glasses_color; | ||
| 185 | BitField<8, 6, u32> eye_type; | ||
| 186 | BitField<14, 2, u32> region_move; | ||
| 187 | BitField<16, 6, u32> mouth_type; | ||
| 188 | BitField<22, 2, FontRegion> font_region; | ||
| 189 | BitField<24, 5, u32> eye_y; | ||
| 190 | BitField<29, 3, u32> glasses_scale; | ||
| 191 | }; | ||
| 192 | |||
| 193 | union { | ||
| 194 | u32 word_3{}; | ||
| 195 | |||
| 196 | BitField<0, 5, u32> eyebrow_type; | ||
| 197 | BitField<5, 3, MustacheType> mustache_type; | ||
| 198 | BitField<8, 5, u32> nose_type; | ||
| 199 | BitField<13, 3, BeardType> beard_type; | ||
| 200 | BitField<16, 5, u32> nose_y; | ||
| 201 | BitField<21, 3, u32> mouth_aspect; | ||
| 202 | BitField<24, 5, u32> mouth_y; | ||
| 203 | BitField<29, 3, u32> eyebrow_aspect; | ||
| 204 | }; | ||
| 205 | |||
| 206 | union { | ||
| 207 | u32 word_4{}; | ||
| 208 | |||
| 209 | BitField<0, 5, u32> mustache_y; | ||
| 210 | BitField<5, 3, u32> eye_rotate; | ||
| 211 | BitField<8, 5, u32> glasses_y; | ||
| 212 | BitField<13, 3, u32> eye_aspect; | ||
| 213 | BitField<16, 5, u32> mole_x; | ||
| 214 | BitField<21, 3, u32> eye_scale; | ||
| 215 | BitField<24, 5, u32> mole_y; | ||
| 216 | }; | ||
| 217 | |||
| 218 | union { | ||
| 219 | u32 word_5{}; | ||
| 220 | |||
| 221 | BitField<0, 5, u32> glasses_type; | ||
| 222 | BitField<8, 4, u32> favorite_color; | ||
| 223 | BitField<12, 4, u32> faceline_type; | ||
| 224 | BitField<16, 4, u32> faceline_color; | ||
| 225 | BitField<20, 4, u32> faceline_wrinkle; | ||
| 226 | BitField<24, 4, u32> faceline_makeup; | ||
| 227 | BitField<28, 4, u32> eye_x; | ||
| 228 | }; | ||
| 229 | |||
| 230 | union { | ||
| 231 | u32 word_6{}; | ||
| 232 | |||
| 233 | BitField<0, 4, u32> eyebrow_scale; | ||
| 234 | BitField<4, 4, u32> eyebrow_rotate; | ||
| 235 | BitField<8, 4, u32> eyebrow_x; | ||
| 236 | BitField<12, 4, u32> eyebrow_y; | ||
| 237 | BitField<16, 4, u32> nose_scale; | ||
| 238 | BitField<20, 4, u32> mouth_scale; | ||
| 239 | BitField<24, 4, u32> mustache_scale; | ||
| 240 | BitField<28, 4, u32> mole_scale; | ||
| 241 | }; | ||
| 242 | }; | ||
| 243 | static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size."); | ||
| 244 | static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, | ||
| 245 | "MiiStoreBitFields is not trivially copyable."); | ||
| 246 | |||
| 247 | // This is nn::mii::Ver3StoreData | ||
| 248 | // Based on citra HLE::Applets::MiiData and PretendoNetwork. | ||
| 249 | // https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48 | ||
| 250 | // https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 | ||
| 251 | struct Ver3StoreData { | ||
| 252 | u8 version; | ||
| 253 | union { | ||
| 254 | u8 raw; | ||
| 255 | |||
| 256 | BitField<0, 1, u8> allow_copying; | ||
| 257 | BitField<1, 1, u8> profanity_flag; | ||
| 258 | BitField<2, 2, u8> region_lock; | ||
| 259 | BitField<4, 2, u8> character_set; | ||
| 260 | } region_information; | ||
| 261 | u16_be mii_id; | ||
| 262 | u64_be system_id; | ||
| 263 | u32_be specialness_and_creation_date; | ||
| 264 | std::array<u8, 0x6> creator_mac; | ||
| 265 | u16_be padding; | ||
| 266 | union { | ||
| 267 | u16 raw; | ||
| 268 | |||
| 269 | BitField<0, 1, u16> gender; | ||
| 270 | BitField<1, 4, u16> birth_month; | ||
| 271 | BitField<5, 5, u16> birth_day; | ||
| 272 | BitField<10, 4, u16> favorite_color; | ||
| 273 | BitField<14, 1, u16> favorite; | ||
| 274 | } mii_information; | ||
| 275 | std::array<char16_t, 0xA> mii_name; | ||
| 276 | u8 height; | ||
| 277 | u8 build; | ||
| 278 | union { | ||
| 279 | u8 raw; | ||
| 280 | |||
| 281 | BitField<0, 1, u8> disable_sharing; | ||
| 282 | BitField<1, 4, u8> face_shape; | ||
| 283 | BitField<5, 3, u8> skin_color; | ||
| 284 | } appearance_bits1; | ||
| 285 | union { | ||
| 286 | u8 raw; | ||
| 287 | |||
| 288 | BitField<0, 4, u8> wrinkles; | ||
| 289 | BitField<4, 4, u8> makeup; | ||
| 290 | } appearance_bits2; | ||
| 291 | u8 hair_style; | ||
| 292 | union { | ||
| 293 | u8 raw; | ||
| 294 | |||
| 295 | BitField<0, 3, u8> hair_color; | ||
| 296 | BitField<3, 1, u8> flip_hair; | ||
| 297 | } appearance_bits3; | ||
| 298 | union { | ||
| 299 | u32 raw; | ||
| 300 | |||
| 301 | BitField<0, 6, u32> eye_type; | ||
| 302 | BitField<6, 3, u32> eye_color; | ||
| 303 | BitField<9, 4, u32> eye_scale; | ||
| 304 | BitField<13, 3, u32> eye_vertical_stretch; | ||
| 305 | BitField<16, 5, u32> eye_rotation; | ||
| 306 | BitField<21, 4, u32> eye_spacing; | ||
| 307 | BitField<25, 5, u32> eye_y_position; | ||
| 308 | } appearance_bits4; | ||
| 309 | union { | ||
| 310 | u32 raw; | ||
| 311 | |||
| 312 | BitField<0, 5, u32> eyebrow_style; | ||
| 313 | BitField<5, 3, u32> eyebrow_color; | ||
| 314 | BitField<8, 4, u32> eyebrow_scale; | ||
| 315 | BitField<12, 3, u32> eyebrow_yscale; | ||
| 316 | BitField<16, 4, u32> eyebrow_rotation; | ||
| 317 | BitField<21, 4, u32> eyebrow_spacing; | ||
| 318 | BitField<25, 5, u32> eyebrow_y_position; | ||
| 319 | } appearance_bits5; | ||
| 320 | union { | ||
| 321 | u16 raw; | ||
| 322 | |||
| 323 | BitField<0, 5, u16> nose_type; | ||
| 324 | BitField<5, 4, u16> nose_scale; | ||
| 325 | BitField<9, 5, u16> nose_y_position; | ||
| 326 | } appearance_bits6; | ||
| 327 | union { | ||
| 328 | u16 raw; | ||
| 329 | |||
| 330 | BitField<0, 6, u16> mouth_type; | ||
| 331 | BitField<6, 3, u16> mouth_color; | ||
| 332 | BitField<9, 4, u16> mouth_scale; | ||
| 333 | BitField<13, 3, u16> mouth_horizontal_stretch; | ||
| 334 | } appearance_bits7; | ||
| 335 | union { | ||
| 336 | u8 raw; | ||
| 337 | |||
| 338 | BitField<0, 5, u8> mouth_y_position; | ||
| 339 | BitField<5, 3, u8> mustache_type; | ||
| 340 | } appearance_bits8; | ||
| 341 | u8 allow_copying; | ||
| 342 | union { | ||
| 343 | u16 raw; | ||
| 344 | |||
| 345 | BitField<0, 3, u16> bear_type; | ||
| 346 | BitField<3, 3, u16> facial_hair_color; | ||
| 347 | BitField<6, 4, u16> mustache_scale; | ||
| 348 | BitField<10, 5, u16> mustache_y_position; | ||
| 349 | } appearance_bits9; | ||
| 350 | union { | ||
| 351 | u16 raw; | ||
| 352 | |||
| 353 | BitField<0, 4, u16> glasses_type; | ||
| 354 | BitField<4, 3, u16> glasses_color; | ||
| 355 | BitField<7, 4, u16> glasses_scale; | ||
| 356 | BitField<11, 5, u16> glasses_y_position; | ||
| 357 | } appearance_bits10; | ||
| 358 | union { | ||
| 359 | u16 raw; | ||
| 360 | |||
| 361 | BitField<0, 1, u16> mole_enabled; | ||
| 362 | BitField<1, 4, u16> mole_scale; | ||
| 363 | BitField<5, 5, u16> mole_x_position; | ||
| 364 | BitField<10, 5, u16> mole_y_position; | ||
| 365 | } appearance_bits11; | ||
| 366 | |||
| 367 | std::array<u16_le, 0xA> author_name; | ||
| 368 | INSERT_PADDING_BYTES(0x2); | ||
| 369 | u16_be crc; | ||
| 370 | }; | ||
| 371 | static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); | ||
| 372 | |||
| 373 | struct NfpStoreDataExtension { | ||
| 374 | u8 faceline_color; | ||
| 375 | u8 hair_color; | ||
| 376 | u8 eye_color; | ||
| 377 | u8 eyebrow_color; | ||
| 378 | u8 mouth_color; | ||
| 379 | u8 beard_color; | ||
| 380 | u8 glass_color; | ||
| 381 | u8 glass_type; | ||
| 382 | }; | ||
| 383 | static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size"); | ||
| 384 | |||
| 385 | constexpr std::array<u8, 0x10> Ver3FacelineColorTable{ | ||
| 386 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, | ||
| 387 | }; | ||
| 388 | |||
| 389 | constexpr std::array<u8, 100> Ver3HairColorTable{ | ||
| 390 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0, | ||
| 391 | 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, | ||
| 392 | 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4, | ||
| 393 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, | ||
| 394 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7, | ||
| 395 | 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4, | ||
| 396 | }; | ||
| 397 | |||
| 398 | constexpr std::array<u8, 100> Ver3EyeColorTable{ | ||
| 399 | 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4, | ||
| 400 | 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, | ||
| 401 | 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4, | ||
| 402 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, | ||
| 403 | 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, | ||
| 404 | 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, | ||
| 405 | }; | ||
| 406 | |||
| 407 | constexpr std::array<u8, 100> Ver3MouthlineColorTable{ | ||
| 408 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, | ||
| 409 | 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, | ||
| 410 | 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, | ||
| 411 | 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, | ||
| 412 | 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, | ||
| 413 | 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3, | ||
| 414 | }; | ||
| 415 | |||
| 416 | constexpr std::array<u8, 100> Ver3GlassColorTable{ | ||
| 417 | 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3, | ||
| 418 | 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, | ||
| 419 | 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, | ||
| 420 | 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5, | ||
| 421 | 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4, | ||
| 422 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, | ||
| 423 | }; | ||
| 424 | |||
| 425 | constexpr std::array<u8, 20> Ver3GlassTypeTable{ | ||
| 426 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, | ||
| 427 | 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7, | ||
| 428 | }; | ||
| 429 | |||
| 430 | struct MiiStoreData { | ||
| 431 | using Name = std::array<char16_t, 10>; | ||
| 432 | |||
| 433 | MiiStoreData(); | ||
| 434 | MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, | ||
| 435 | const Common::UUID& user_id); | ||
| 436 | |||
| 437 | // This corresponds to the above structure MiiStoreBitFields. I did it like this because the | ||
| 438 | // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is | ||
| 439 | // not suitable for our uses. | ||
| 440 | struct { | ||
| 441 | std::array<u8, 0x1C> data{}; | ||
| 442 | static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); | ||
| 443 | |||
| 444 | Name name{}; | ||
| 445 | Common::UUID uuid{}; | ||
| 446 | } data; | ||
| 447 | |||
| 448 | u16 data_crc{}; | ||
| 449 | u16 device_crc{}; | ||
| 450 | }; | ||
| 451 | static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); | ||
| 452 | |||
| 453 | struct MiiStoreDataElement { | ||
| 454 | MiiStoreData data{}; | ||
| 455 | Source source{}; | ||
| 456 | }; | ||
| 457 | static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); | ||
| 458 | |||
| 459 | struct MiiDatabase { | ||
| 460 | u32 magic{}; // 'NFDB' | ||
| 461 | std::array<MiiStoreData, 0x64> miis{}; | ||
| 462 | INSERT_PADDING_BYTES(1); | ||
| 463 | u8 count{}; | ||
| 464 | u16 crc{}; | ||
| 465 | }; | ||
| 466 | static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); | ||
| 467 | |||
| 468 | struct RandomMiiValues { | ||
| 469 | std::array<u8, 0xbc> values{}; | ||
| 470 | }; | ||
| 471 | static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); | ||
| 472 | |||
| 473 | struct RandomMiiData4 { | ||
| 474 | Gender gender{}; | ||
| 475 | Age age{}; | ||
| 476 | Race race{}; | ||
| 477 | u32 values_count{}; | ||
| 478 | std::array<u32, 47> values{}; | ||
| 479 | }; | ||
| 480 | static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); | ||
| 481 | |||
| 482 | struct RandomMiiData3 { | ||
| 483 | u32 arg_1; | ||
| 484 | u32 arg_2; | ||
| 485 | u32 values_count; | ||
| 486 | std::array<u32, 47> values{}; | ||
| 487 | }; | ||
| 488 | static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); | ||
| 489 | |||
| 490 | struct RandomMiiData2 { | ||
| 491 | u32 arg_1; | ||
| 492 | u32 values_count; | ||
| 493 | std::array<u32, 47> values{}; | ||
| 494 | }; | ||
| 495 | static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); | ||
| 496 | |||
| 497 | struct DefaultMii { | ||
| 498 | u32 face_type{}; | ||
| 499 | u32 face_color{}; | ||
| 500 | u32 face_wrinkle{}; | ||
| 501 | u32 face_makeup{}; | ||
| 502 | u32 hair_type{}; | ||
| 503 | u32 hair_color{}; | ||
| 504 | u32 hair_flip{}; | ||
| 505 | u32 eye_type{}; | ||
| 506 | u32 eye_color{}; | ||
| 507 | u32 eye_scale{}; | ||
| 508 | u32 eye_aspect{}; | ||
| 509 | u32 eye_rotate{}; | ||
| 510 | u32 eye_x{}; | ||
| 511 | u32 eye_y{}; | ||
| 512 | u32 eyebrow_type{}; | ||
| 513 | u32 eyebrow_color{}; | ||
| 514 | u32 eyebrow_scale{}; | ||
| 515 | u32 eyebrow_aspect{}; | ||
| 516 | u32 eyebrow_rotate{}; | ||
| 517 | u32 eyebrow_x{}; | ||
| 518 | u32 eyebrow_y{}; | ||
| 519 | u32 nose_type{}; | ||
| 520 | u32 nose_scale{}; | ||
| 521 | u32 nose_y{}; | ||
| 522 | u32 mouth_type{}; | ||
| 523 | u32 mouth_color{}; | ||
| 524 | u32 mouth_scale{}; | ||
| 525 | u32 mouth_aspect{}; | ||
| 526 | u32 mouth_y{}; | ||
| 527 | u32 mustache_type{}; | ||
| 528 | u32 beard_type{}; | ||
| 529 | u32 beard_color{}; | ||
| 530 | u32 mustache_scale{}; | ||
| 531 | u32 mustache_y{}; | ||
| 532 | u32 glasses_type{}; | ||
| 533 | u32 glasses_color{}; | ||
| 534 | u32 glasses_scale{}; | ||
| 535 | u32 glasses_y{}; | ||
| 536 | u32 mole_type{}; | ||
| 537 | u32 mole_scale{}; | ||
| 538 | u32 mole_x{}; | ||
| 539 | u32 mole_y{}; | ||
| 540 | u32 height{}; | ||
| 541 | u32 weight{}; | ||
| 542 | Gender gender{}; | ||
| 543 | u32 favorite_color{}; | ||
| 544 | u32 region{}; | ||
| 545 | FontRegion font_region{}; | ||
| 546 | u32 type{}; | ||
| 547 | INSERT_PADDING_WORDS(5); | ||
| 548 | }; | ||
| 549 | static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); | ||
| 550 | |||
| 551 | #pragma pack(pop) | ||
| 552 | |||
| 553 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp new file mode 100644 index 000000000..bb948c628 --- /dev/null +++ b/src/core/hle/service/mii/types/char_info.cpp | |||
| @@ -0,0 +1,482 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/mii/types/char_info.h" | ||
| 5 | #include "core/hle/service/mii/types/store_data.h" | ||
| 6 | |||
| 7 | namespace Service::Mii { | ||
| 8 | |||
| 9 | void CharInfo::SetFromStoreData(const StoreData& store_data) { | ||
| 10 | name = store_data.GetNickname(); | ||
| 11 | null_terminator = '\0'; | ||
| 12 | create_id = store_data.GetCreateId(); | ||
| 13 | font_region = store_data.GetFontRegion(); | ||
| 14 | favorite_color = store_data.GetFavoriteColor(); | ||
| 15 | gender = store_data.GetGender(); | ||
| 16 | height = store_data.GetHeight(); | ||
| 17 | build = store_data.GetBuild(); | ||
| 18 | type = store_data.GetType(); | ||
| 19 | region_move = store_data.GetRegionMove(); | ||
| 20 | faceline_type = store_data.GetFacelineType(); | ||
| 21 | faceline_color = store_data.GetFacelineColor(); | ||
| 22 | faceline_wrinkle = store_data.GetFacelineWrinkle(); | ||
| 23 | faceline_make = store_data.GetFacelineMake(); | ||
| 24 | hair_type = store_data.GetHairType(); | ||
| 25 | hair_color = store_data.GetHairColor(); | ||
| 26 | hair_flip = store_data.GetHairFlip(); | ||
| 27 | eye_type = store_data.GetEyeType(); | ||
| 28 | eye_color = store_data.GetEyeColor(); | ||
| 29 | eye_scale = store_data.GetEyeScale(); | ||
| 30 | eye_aspect = store_data.GetEyeAspect(); | ||
| 31 | eye_rotate = store_data.GetEyeRotate(); | ||
| 32 | eye_x = store_data.GetEyeX(); | ||
| 33 | eye_y = store_data.GetEyeY(); | ||
| 34 | eyebrow_type = store_data.GetEyebrowType(); | ||
| 35 | eyebrow_color = store_data.GetEyebrowColor(); | ||
| 36 | eyebrow_scale = store_data.GetEyebrowScale(); | ||
| 37 | eyebrow_aspect = store_data.GetEyebrowAspect(); | ||
| 38 | eyebrow_rotate = store_data.GetEyebrowRotate(); | ||
| 39 | eyebrow_x = store_data.GetEyebrowX(); | ||
| 40 | eyebrow_y = store_data.GetEyebrowY(); | ||
| 41 | nose_type = store_data.GetNoseType(); | ||
| 42 | nose_scale = store_data.GetNoseScale(); | ||
| 43 | nose_y = store_data.GetNoseY(); | ||
| 44 | mouth_type = store_data.GetMouthType(); | ||
| 45 | mouth_color = store_data.GetMouthColor(); | ||
| 46 | mouth_scale = store_data.GetMouthScale(); | ||
| 47 | mouth_aspect = store_data.GetMouthAspect(); | ||
| 48 | mouth_y = store_data.GetMouthY(); | ||
| 49 | beard_color = store_data.GetBeardColor(); | ||
| 50 | beard_type = store_data.GetBeardType(); | ||
| 51 | mustache_type = store_data.GetMustacheType(); | ||
| 52 | mustache_scale = store_data.GetMustacheScale(); | ||
| 53 | mustache_y = store_data.GetMustacheY(); | ||
| 54 | glass_type = store_data.GetGlassType(); | ||
| 55 | glass_color = store_data.GetGlassColor(); | ||
| 56 | glass_scale = store_data.GetGlassScale(); | ||
| 57 | glass_y = store_data.GetGlassY(); | ||
| 58 | mole_type = store_data.GetMoleType(); | ||
| 59 | mole_scale = store_data.GetMoleScale(); | ||
| 60 | mole_x = store_data.GetMoleX(); | ||
| 61 | mole_y = store_data.GetMoleY(); | ||
| 62 | padding = '\0'; | ||
| 63 | } | ||
| 64 | |||
| 65 | ValidationResult CharInfo::Verify() const { | ||
| 66 | if (!create_id.IsValid()) { | ||
| 67 | return ValidationResult::InvalidCreateId; | ||
| 68 | } | ||
| 69 | if (!name.IsValid()) { | ||
| 70 | return ValidationResult::InvalidName; | ||
| 71 | } | ||
| 72 | if (font_region > FontRegion::Max) { | ||
| 73 | return ValidationResult::InvalidFont; | ||
| 74 | } | ||
| 75 | if (favorite_color > FavoriteColor::Max) { | ||
| 76 | return ValidationResult::InvalidColor; | ||
| 77 | } | ||
| 78 | if (gender > Gender::Max) { | ||
| 79 | return ValidationResult::InvalidGender; | ||
| 80 | } | ||
| 81 | if (height > MaxHeight) { | ||
| 82 | return ValidationResult::InvalidHeight; | ||
| 83 | } | ||
| 84 | if (build > MaxBuild) { | ||
| 85 | return ValidationResult::InvalidBuild; | ||
| 86 | } | ||
| 87 | if (type > MaxType) { | ||
| 88 | return ValidationResult::InvalidType; | ||
| 89 | } | ||
| 90 | if (region_move > MaxRegionMove) { | ||
| 91 | return ValidationResult::InvalidRegionMove; | ||
| 92 | } | ||
| 93 | if (faceline_type > FacelineType::Max) { | ||
| 94 | return ValidationResult::InvalidFacelineType; | ||
| 95 | } | ||
| 96 | if (faceline_color > FacelineColor::Max) { | ||
| 97 | return ValidationResult::InvalidFacelineColor; | ||
| 98 | } | ||
| 99 | if (faceline_wrinkle > FacelineWrinkle::Max) { | ||
| 100 | return ValidationResult::InvalidFacelineWrinkle; | ||
| 101 | } | ||
| 102 | if (faceline_make > FacelineMake::Max) { | ||
| 103 | return ValidationResult::InvalidFacelineMake; | ||
| 104 | } | ||
| 105 | if (hair_type > HairType::Max) { | ||
| 106 | return ValidationResult::InvalidHairType; | ||
| 107 | } | ||
| 108 | if (hair_color > CommonColor::Max) { | ||
| 109 | return ValidationResult::InvalidHairColor; | ||
| 110 | } | ||
| 111 | if (hair_flip > HairFlip::Max) { | ||
| 112 | return ValidationResult::InvalidHairFlip; | ||
| 113 | } | ||
| 114 | if (eye_type > EyeType::Max) { | ||
| 115 | return ValidationResult::InvalidEyeType; | ||
| 116 | } | ||
| 117 | if (eye_color > CommonColor::Max) { | ||
| 118 | return ValidationResult::InvalidEyeColor; | ||
| 119 | } | ||
| 120 | if (eye_scale > MaxEyeScale) { | ||
| 121 | return ValidationResult::InvalidEyeScale; | ||
| 122 | } | ||
| 123 | if (eye_aspect > MaxEyeAspect) { | ||
| 124 | return ValidationResult::InvalidEyeAspect; | ||
| 125 | } | ||
| 126 | if (eye_rotate > MaxEyeX) { | ||
| 127 | return ValidationResult::InvalidEyeRotate; | ||
| 128 | } | ||
| 129 | if (eye_x > MaxEyeX) { | ||
| 130 | return ValidationResult::InvalidEyeX; | ||
| 131 | } | ||
| 132 | if (eye_y > MaxEyeY) { | ||
| 133 | return ValidationResult::InvalidEyeY; | ||
| 134 | } | ||
| 135 | if (eyebrow_type > EyebrowType::Max) { | ||
| 136 | return ValidationResult::InvalidEyebrowType; | ||
| 137 | } | ||
| 138 | if (eyebrow_color > CommonColor::Max) { | ||
| 139 | return ValidationResult::InvalidEyebrowColor; | ||
| 140 | } | ||
| 141 | if (eyebrow_scale > MaxEyebrowScale) { | ||
| 142 | return ValidationResult::InvalidEyebrowScale; | ||
| 143 | } | ||
| 144 | if (eyebrow_aspect > MaxEyebrowAspect) { | ||
| 145 | return ValidationResult::InvalidEyebrowAspect; | ||
| 146 | } | ||
| 147 | if (eyebrow_rotate > MaxEyebrowRotate) { | ||
| 148 | return ValidationResult::InvalidEyebrowRotate; | ||
| 149 | } | ||
| 150 | if (eyebrow_x > MaxEyebrowX) { | ||
| 151 | return ValidationResult::InvalidEyebrowX; | ||
| 152 | } | ||
| 153 | if (eyebrow_y > MaxEyebrowY) { | ||
| 154 | return ValidationResult::InvalidEyebrowY; | ||
| 155 | } | ||
| 156 | if (nose_type > NoseType::Max) { | ||
| 157 | return ValidationResult::InvalidNoseType; | ||
| 158 | } | ||
| 159 | if (nose_scale > MaxNoseScale) { | ||
| 160 | return ValidationResult::InvalidNoseScale; | ||
| 161 | } | ||
| 162 | if (nose_y > MaxNoseY) { | ||
| 163 | return ValidationResult::InvalidNoseY; | ||
| 164 | } | ||
| 165 | if (mouth_type > MouthType::Max) { | ||
| 166 | return ValidationResult::InvalidMouthType; | ||
| 167 | } | ||
| 168 | if (mouth_color > CommonColor::Max) { | ||
| 169 | return ValidationResult::InvalidMouthColor; | ||
| 170 | } | ||
| 171 | if (mouth_scale > MaxMouthScale) { | ||
| 172 | return ValidationResult::InvalidMouthScale; | ||
| 173 | } | ||
| 174 | if (mouth_aspect > MaxMoutAspect) { | ||
| 175 | return ValidationResult::InvalidMouthAspect; | ||
| 176 | } | ||
| 177 | if (mouth_y > MaxMouthY) { | ||
| 178 | return ValidationResult::InvalidMoleY; | ||
| 179 | } | ||
| 180 | if (beard_color > CommonColor::Max) { | ||
| 181 | return ValidationResult::InvalidBeardColor; | ||
| 182 | } | ||
| 183 | if (beard_type > BeardType::Max) { | ||
| 184 | return ValidationResult::InvalidBeardType; | ||
| 185 | } | ||
| 186 | if (mustache_type > MustacheType::Max) { | ||
| 187 | return ValidationResult::InvalidMustacheType; | ||
| 188 | } | ||
| 189 | if (mustache_scale > MaxMustacheScale) { | ||
| 190 | return ValidationResult::InvalidMustacheScale; | ||
| 191 | } | ||
| 192 | if (mustache_y > MasMustacheY) { | ||
| 193 | return ValidationResult::InvalidMustacheY; | ||
| 194 | } | ||
| 195 | if (glass_type > GlassType::Max) { | ||
| 196 | return ValidationResult::InvalidGlassType; | ||
| 197 | } | ||
| 198 | if (glass_color > CommonColor::Max) { | ||
| 199 | return ValidationResult::InvalidGlassColor; | ||
| 200 | } | ||
| 201 | if (glass_scale > MaxGlassScale) { | ||
| 202 | return ValidationResult::InvalidGlassScale; | ||
| 203 | } | ||
| 204 | if (glass_y > MaxGlassY) { | ||
| 205 | return ValidationResult::InvalidGlassY; | ||
| 206 | } | ||
| 207 | if (mole_type > MoleType::Max) { | ||
| 208 | return ValidationResult::InvalidMoleType; | ||
| 209 | } | ||
| 210 | if (mole_scale > MaxMoleScale) { | ||
| 211 | return ValidationResult::InvalidMoleScale; | ||
| 212 | } | ||
| 213 | if (mole_x > MaxMoleX) { | ||
| 214 | return ValidationResult::InvalidMoleX; | ||
| 215 | } | ||
| 216 | if (mole_y > MaxMoleY) { | ||
| 217 | return ValidationResult::InvalidMoleY; | ||
| 218 | } | ||
| 219 | return ValidationResult::NoErrors; | ||
| 220 | } | ||
| 221 | |||
| 222 | Common::UUID CharInfo::GetCreateId() const { | ||
| 223 | return create_id; | ||
| 224 | } | ||
| 225 | |||
| 226 | Nickname CharInfo::GetNickname() const { | ||
| 227 | return name; | ||
| 228 | } | ||
| 229 | |||
| 230 | FontRegion CharInfo::GetFontRegion() const { | ||
| 231 | return font_region; | ||
| 232 | } | ||
| 233 | |||
| 234 | FavoriteColor CharInfo::GetFavoriteColor() const { | ||
| 235 | return favorite_color; | ||
| 236 | } | ||
| 237 | |||
| 238 | Gender CharInfo::GetGender() const { | ||
| 239 | return gender; | ||
| 240 | } | ||
| 241 | |||
| 242 | u8 CharInfo::GetHeight() const { | ||
| 243 | return height; | ||
| 244 | } | ||
| 245 | |||
| 246 | u8 CharInfo::GetBuild() const { | ||
| 247 | return build; | ||
| 248 | } | ||
| 249 | |||
| 250 | u8 CharInfo::GetType() const { | ||
| 251 | return type; | ||
| 252 | } | ||
| 253 | |||
| 254 | u8 CharInfo::GetRegionMove() const { | ||
| 255 | return region_move; | ||
| 256 | } | ||
| 257 | |||
| 258 | FacelineType CharInfo::GetFacelineType() const { | ||
| 259 | return faceline_type; | ||
| 260 | } | ||
| 261 | |||
| 262 | FacelineColor CharInfo::GetFacelineColor() const { | ||
| 263 | return faceline_color; | ||
| 264 | } | ||
| 265 | |||
| 266 | FacelineWrinkle CharInfo::GetFacelineWrinkle() const { | ||
| 267 | return faceline_wrinkle; | ||
| 268 | } | ||
| 269 | |||
| 270 | FacelineMake CharInfo::GetFacelineMake() const { | ||
| 271 | return faceline_make; | ||
| 272 | } | ||
| 273 | |||
| 274 | HairType CharInfo::GetHairType() const { | ||
| 275 | return hair_type; | ||
| 276 | } | ||
| 277 | |||
| 278 | CommonColor CharInfo::GetHairColor() const { | ||
| 279 | return hair_color; | ||
| 280 | } | ||
| 281 | |||
| 282 | HairFlip CharInfo::GetHairFlip() const { | ||
| 283 | return hair_flip; | ||
| 284 | } | ||
| 285 | |||
| 286 | EyeType CharInfo::GetEyeType() const { | ||
| 287 | return eye_type; | ||
| 288 | } | ||
| 289 | |||
| 290 | CommonColor CharInfo::GetEyeColor() const { | ||
| 291 | return eye_color; | ||
| 292 | } | ||
| 293 | |||
| 294 | u8 CharInfo::GetEyeScale() const { | ||
| 295 | return eye_scale; | ||
| 296 | } | ||
| 297 | |||
| 298 | u8 CharInfo::GetEyeAspect() const { | ||
| 299 | return eye_aspect; | ||
| 300 | } | ||
| 301 | |||
| 302 | u8 CharInfo::GetEyeRotate() const { | ||
| 303 | return eye_rotate; | ||
| 304 | } | ||
| 305 | |||
| 306 | u8 CharInfo::GetEyeX() const { | ||
| 307 | return eye_x; | ||
| 308 | } | ||
| 309 | |||
| 310 | u8 CharInfo::GetEyeY() const { | ||
| 311 | return eye_y; | ||
| 312 | } | ||
| 313 | |||
| 314 | EyebrowType CharInfo::GetEyebrowType() const { | ||
| 315 | return eyebrow_type; | ||
| 316 | } | ||
| 317 | |||
| 318 | CommonColor CharInfo::GetEyebrowColor() const { | ||
| 319 | return eyebrow_color; | ||
| 320 | } | ||
| 321 | |||
| 322 | u8 CharInfo::GetEyebrowScale() const { | ||
| 323 | return eyebrow_scale; | ||
| 324 | } | ||
| 325 | |||
| 326 | u8 CharInfo::GetEyebrowAspect() const { | ||
| 327 | return eyebrow_aspect; | ||
| 328 | } | ||
| 329 | |||
| 330 | u8 CharInfo::GetEyebrowRotate() const { | ||
| 331 | return eyebrow_rotate; | ||
| 332 | } | ||
| 333 | |||
| 334 | u8 CharInfo::GetEyebrowX() const { | ||
| 335 | return eyebrow_x; | ||
| 336 | } | ||
| 337 | |||
| 338 | u8 CharInfo::GetEyebrowY() const { | ||
| 339 | return eyebrow_y; | ||
| 340 | } | ||
| 341 | |||
| 342 | NoseType CharInfo::GetNoseType() const { | ||
| 343 | return nose_type; | ||
| 344 | } | ||
| 345 | |||
| 346 | u8 CharInfo::GetNoseScale() const { | ||
| 347 | return nose_scale; | ||
| 348 | } | ||
| 349 | |||
| 350 | u8 CharInfo::GetNoseY() const { | ||
| 351 | return nose_y; | ||
| 352 | } | ||
| 353 | |||
| 354 | MouthType CharInfo::GetMouthType() const { | ||
| 355 | return mouth_type; | ||
| 356 | } | ||
| 357 | |||
| 358 | CommonColor CharInfo::GetMouthColor() const { | ||
| 359 | return mouth_color; | ||
| 360 | } | ||
| 361 | |||
| 362 | u8 CharInfo::GetMouthScale() const { | ||
| 363 | return mouth_scale; | ||
| 364 | } | ||
| 365 | |||
| 366 | u8 CharInfo::GetMouthAspect() const { | ||
| 367 | return mouth_aspect; | ||
| 368 | } | ||
| 369 | |||
| 370 | u8 CharInfo::GetMouthY() const { | ||
| 371 | return mouth_y; | ||
| 372 | } | ||
| 373 | |||
| 374 | CommonColor CharInfo::GetBeardColor() const { | ||
| 375 | return beard_color; | ||
| 376 | } | ||
| 377 | |||
| 378 | BeardType CharInfo::GetBeardType() const { | ||
| 379 | return beard_type; | ||
| 380 | } | ||
| 381 | |||
| 382 | MustacheType CharInfo::GetMustacheType() const { | ||
| 383 | return mustache_type; | ||
| 384 | } | ||
| 385 | |||
| 386 | u8 CharInfo::GetMustacheScale() const { | ||
| 387 | return mustache_scale; | ||
| 388 | } | ||
| 389 | |||
| 390 | u8 CharInfo::GetMustacheY() const { | ||
| 391 | return mustache_y; | ||
| 392 | } | ||
| 393 | |||
| 394 | GlassType CharInfo::GetGlassType() const { | ||
| 395 | return glass_type; | ||
| 396 | } | ||
| 397 | |||
| 398 | CommonColor CharInfo::GetGlassColor() const { | ||
| 399 | return glass_color; | ||
| 400 | } | ||
| 401 | |||
| 402 | u8 CharInfo::GetGlassScale() const { | ||
| 403 | return glass_scale; | ||
| 404 | } | ||
| 405 | |||
| 406 | u8 CharInfo::GetGlassY() const { | ||
| 407 | return glass_y; | ||
| 408 | } | ||
| 409 | |||
| 410 | MoleType CharInfo::GetMoleType() const { | ||
| 411 | return mole_type; | ||
| 412 | } | ||
| 413 | |||
| 414 | u8 CharInfo::GetMoleScale() const { | ||
| 415 | return mole_scale; | ||
| 416 | } | ||
| 417 | |||
| 418 | u8 CharInfo::GetMoleX() const { | ||
| 419 | return mole_x; | ||
| 420 | } | ||
| 421 | |||
| 422 | u8 CharInfo::GetMoleY() const { | ||
| 423 | return mole_y; | ||
| 424 | } | ||
| 425 | |||
| 426 | bool CharInfo::operator==(const CharInfo& info) { | ||
| 427 | bool is_identical = info.Verify() == ValidationResult::NoErrors; | ||
| 428 | is_identical &= name.data == info.GetNickname().data; | ||
| 429 | is_identical &= create_id == info.GetCreateId(); | ||
| 430 | is_identical &= font_region == info.GetFontRegion(); | ||
| 431 | is_identical &= favorite_color == info.GetFavoriteColor(); | ||
| 432 | is_identical &= gender == info.GetGender(); | ||
| 433 | is_identical &= height == info.GetHeight(); | ||
| 434 | is_identical &= build == info.GetBuild(); | ||
| 435 | is_identical &= type == info.GetType(); | ||
| 436 | is_identical &= region_move == info.GetRegionMove(); | ||
| 437 | is_identical &= faceline_type == info.GetFacelineType(); | ||
| 438 | is_identical &= faceline_color == info.GetFacelineColor(); | ||
| 439 | is_identical &= faceline_wrinkle == info.GetFacelineWrinkle(); | ||
| 440 | is_identical &= faceline_make == info.GetFacelineMake(); | ||
| 441 | is_identical &= hair_type == info.GetHairType(); | ||
| 442 | is_identical &= hair_color == info.GetHairColor(); | ||
| 443 | is_identical &= hair_flip == info.GetHairFlip(); | ||
| 444 | is_identical &= eye_type == info.GetEyeType(); | ||
| 445 | is_identical &= eye_color == info.GetEyeColor(); | ||
| 446 | is_identical &= eye_scale == info.GetEyeScale(); | ||
| 447 | is_identical &= eye_aspect == info.GetEyeAspect(); | ||
| 448 | is_identical &= eye_rotate == info.GetEyeRotate(); | ||
| 449 | is_identical &= eye_x == info.GetEyeX(); | ||
| 450 | is_identical &= eye_y == info.GetEyeY(); | ||
| 451 | is_identical &= eyebrow_type == info.GetEyebrowType(); | ||
| 452 | is_identical &= eyebrow_color == info.GetEyebrowColor(); | ||
| 453 | is_identical &= eyebrow_scale == info.GetEyebrowScale(); | ||
| 454 | is_identical &= eyebrow_aspect == info.GetEyebrowAspect(); | ||
| 455 | is_identical &= eyebrow_rotate == info.GetEyebrowRotate(); | ||
| 456 | is_identical &= eyebrow_x == info.GetEyebrowX(); | ||
| 457 | is_identical &= eyebrow_y == info.GetEyebrowY(); | ||
| 458 | is_identical &= nose_type == info.GetNoseType(); | ||
| 459 | is_identical &= nose_scale == info.GetNoseScale(); | ||
| 460 | is_identical &= nose_y == info.GetNoseY(); | ||
| 461 | is_identical &= mouth_type == info.GetMouthType(); | ||
| 462 | is_identical &= mouth_color == info.GetMouthColor(); | ||
| 463 | is_identical &= mouth_scale == info.GetMouthScale(); | ||
| 464 | is_identical &= mouth_aspect == info.GetMouthAspect(); | ||
| 465 | is_identical &= mouth_y == info.GetMouthY(); | ||
| 466 | is_identical &= beard_color == info.GetBeardColor(); | ||
| 467 | is_identical &= beard_type == info.GetBeardType(); | ||
| 468 | is_identical &= mustache_type == info.GetMustacheType(); | ||
| 469 | is_identical &= mustache_scale == info.GetMustacheScale(); | ||
| 470 | is_identical &= mustache_y == info.GetMustacheY(); | ||
| 471 | is_identical &= glass_type == info.GetGlassType(); | ||
| 472 | is_identical &= glass_color == info.GetGlassColor(); | ||
| 473 | is_identical &= glass_scale == info.GetGlassScale(); | ||
| 474 | is_identical &= glass_y == info.GetGlassY(); | ||
| 475 | is_identical &= mole_type == info.GetMoleType(); | ||
| 476 | is_identical &= mole_scale == info.GetMoleScale(); | ||
| 477 | is_identical &= mole_x == info.GetMoleX(); | ||
| 478 | is_identical &= mole_y == info.GetMoleY(); | ||
| 479 | return is_identical; | ||
| 480 | } | ||
| 481 | |||
| 482 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h new file mode 100644 index 000000000..d069b221f --- /dev/null +++ b/src/core/hle/service/mii/types/char_info.h | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/mii/mii_types.h" | ||
| 7 | |||
| 8 | namespace Service::Mii { | ||
| 9 | class StoreData; | ||
| 10 | |||
| 11 | // This is nn::mii::detail::CharInfoRaw | ||
| 12 | class CharInfo { | ||
| 13 | public: | ||
| 14 | void SetFromStoreData(const StoreData& store_data_raw); | ||
| 15 | |||
| 16 | ValidationResult Verify() const; | ||
| 17 | |||
| 18 | Common::UUID GetCreateId() const; | ||
| 19 | Nickname GetNickname() const; | ||
| 20 | FontRegion GetFontRegion() const; | ||
| 21 | FavoriteColor GetFavoriteColor() const; | ||
| 22 | Gender GetGender() const; | ||
| 23 | u8 GetHeight() const; | ||
| 24 | u8 GetBuild() const; | ||
| 25 | u8 GetType() const; | ||
| 26 | u8 GetRegionMove() const; | ||
| 27 | FacelineType GetFacelineType() const; | ||
| 28 | FacelineColor GetFacelineColor() const; | ||
| 29 | FacelineWrinkle GetFacelineWrinkle() const; | ||
| 30 | FacelineMake GetFacelineMake() const; | ||
| 31 | HairType GetHairType() const; | ||
| 32 | CommonColor GetHairColor() const; | ||
| 33 | HairFlip GetHairFlip() const; | ||
| 34 | EyeType GetEyeType() const; | ||
| 35 | CommonColor GetEyeColor() const; | ||
| 36 | u8 GetEyeScale() const; | ||
| 37 | u8 GetEyeAspect() const; | ||
| 38 | u8 GetEyeRotate() const; | ||
| 39 | u8 GetEyeX() const; | ||
| 40 | u8 GetEyeY() const; | ||
| 41 | EyebrowType GetEyebrowType() const; | ||
| 42 | CommonColor GetEyebrowColor() const; | ||
| 43 | u8 GetEyebrowScale() const; | ||
| 44 | u8 GetEyebrowAspect() const; | ||
| 45 | u8 GetEyebrowRotate() const; | ||
| 46 | u8 GetEyebrowX() const; | ||
| 47 | u8 GetEyebrowY() const; | ||
| 48 | NoseType GetNoseType() const; | ||
| 49 | u8 GetNoseScale() const; | ||
| 50 | u8 GetNoseY() const; | ||
| 51 | MouthType GetMouthType() const; | ||
| 52 | CommonColor GetMouthColor() const; | ||
| 53 | u8 GetMouthScale() const; | ||
| 54 | u8 GetMouthAspect() const; | ||
| 55 | u8 GetMouthY() const; | ||
| 56 | CommonColor GetBeardColor() const; | ||
| 57 | BeardType GetBeardType() const; | ||
| 58 | MustacheType GetMustacheType() const; | ||
| 59 | u8 GetMustacheScale() const; | ||
| 60 | u8 GetMustacheY() const; | ||
| 61 | GlassType GetGlassType() const; | ||
| 62 | CommonColor GetGlassColor() const; | ||
| 63 | u8 GetGlassScale() const; | ||
| 64 | u8 GetGlassY() const; | ||
| 65 | MoleType GetMoleType() const; | ||
| 66 | u8 GetMoleScale() const; | ||
| 67 | u8 GetMoleX() const; | ||
| 68 | u8 GetMoleY() const; | ||
| 69 | |||
| 70 | bool operator==(const CharInfo& info); | ||
| 71 | |||
| 72 | private: | ||
| 73 | Common::UUID create_id; | ||
| 74 | Nickname name; | ||
| 75 | u16 null_terminator; | ||
| 76 | FontRegion font_region; | ||
| 77 | FavoriteColor favorite_color; | ||
| 78 | Gender gender; | ||
| 79 | u8 height; | ||
| 80 | u8 build; | ||
| 81 | u8 type; | ||
| 82 | u8 region_move; | ||
| 83 | FacelineType faceline_type; | ||
| 84 | FacelineColor faceline_color; | ||
| 85 | FacelineWrinkle faceline_wrinkle; | ||
| 86 | FacelineMake faceline_make; | ||
| 87 | HairType hair_type; | ||
| 88 | CommonColor hair_color; | ||
| 89 | HairFlip hair_flip; | ||
| 90 | EyeType eye_type; | ||
| 91 | CommonColor eye_color; | ||
| 92 | u8 eye_scale; | ||
| 93 | u8 eye_aspect; | ||
| 94 | u8 eye_rotate; | ||
| 95 | u8 eye_x; | ||
| 96 | u8 eye_y; | ||
| 97 | EyebrowType eyebrow_type; | ||
| 98 | CommonColor eyebrow_color; | ||
| 99 | u8 eyebrow_scale; | ||
| 100 | u8 eyebrow_aspect; | ||
| 101 | u8 eyebrow_rotate; | ||
| 102 | u8 eyebrow_x; | ||
| 103 | u8 eyebrow_y; | ||
| 104 | NoseType nose_type; | ||
| 105 | u8 nose_scale; | ||
| 106 | u8 nose_y; | ||
| 107 | MouthType mouth_type; | ||
| 108 | CommonColor mouth_color; | ||
| 109 | u8 mouth_scale; | ||
| 110 | u8 mouth_aspect; | ||
| 111 | u8 mouth_y; | ||
| 112 | CommonColor beard_color; | ||
| 113 | BeardType beard_type; | ||
| 114 | MustacheType mustache_type; | ||
| 115 | u8 mustache_scale; | ||
| 116 | u8 mustache_y; | ||
| 117 | GlassType glass_type; | ||
| 118 | CommonColor glass_color; | ||
| 119 | u8 glass_scale; | ||
| 120 | u8 glass_y; | ||
| 121 | MoleType mole_type; | ||
| 122 | u8 mole_scale; | ||
| 123 | u8 mole_x; | ||
| 124 | u8 mole_y; | ||
| 125 | u8 padding; | ||
| 126 | }; | ||
| 127 | static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); | ||
| 128 | static_assert(std::has_unique_object_representations_v<CharInfo>, | ||
| 129 | "All bits of CharInfo must contribute to its value."); | ||
| 130 | |||
| 131 | struct CharInfoElement { | ||
| 132 | CharInfo char_info{}; | ||
| 133 | Source source{}; | ||
| 134 | }; | ||
| 135 | static_assert(sizeof(CharInfoElement) == 0x5c, "CharInfoElement has incorrect size."); | ||
| 136 | |||
| 137 | }; // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp new file mode 100644 index 000000000..659288b51 --- /dev/null +++ b/src/core/hle/service/mii/types/core_data.cpp | |||
| @@ -0,0 +1,601 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/assert.h" | ||
| 5 | #include "core/hle/service/mii/mii_util.h" | ||
| 6 | #include "core/hle/service/mii/types/core_data.h" | ||
| 7 | #include "core/hle/service/mii/types/raw_data.h" | ||
| 8 | |||
| 9 | namespace Service::Mii { | ||
| 10 | |||
| 11 | void CoreData::SetDefault() { | ||
| 12 | data = {}; | ||
| 13 | name = GetDefaultNickname(); | ||
| 14 | } | ||
| 15 | |||
| 16 | void CoreData::BuildRandom(Age age, Gender gender, Race race) { | ||
| 17 | if (gender == Gender::All) { | ||
| 18 | gender = MiiUtil::GetRandomValue(Gender::Max); | ||
| 19 | } | ||
| 20 | |||
| 21 | if (age == Age::All) { | ||
| 22 | const auto random{MiiUtil::GetRandomValue<int>(10)}; | ||
| 23 | if (random >= 8) { | ||
| 24 | age = Age::Old; | ||
| 25 | } else if (random >= 4) { | ||
| 26 | age = Age::Normal; | ||
| 27 | } else { | ||
| 28 | age = Age::Young; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | if (race == Race::All) { | ||
| 33 | const auto random{MiiUtil::GetRandomValue<int>(10)}; | ||
| 34 | if (random >= 8) { | ||
| 35 | race = Race::Black; | ||
| 36 | } else if (random >= 4) { | ||
| 37 | race = Race::White; | ||
| 38 | } else { | ||
| 39 | race = Race::Asian; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | SetGender(gender); | ||
| 44 | SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max)); | ||
| 45 | SetRegionMove(0); | ||
| 46 | SetFontRegion(FontRegion::Standard); | ||
| 47 | SetType(0); | ||
| 48 | SetHeight(64); | ||
| 49 | SetBuild(64); | ||
| 50 | |||
| 51 | u32 axis_y{}; | ||
| 52 | if (gender == Gender::Female && age == Age::Young) { | ||
| 53 | axis_y = MiiUtil::GetRandomValue<u32>(3); | ||
| 54 | } | ||
| 55 | |||
| 56 | const std::size_t index{3 * static_cast<std::size_t>(age) + | ||
| 57 | 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)}; | ||
| 58 | |||
| 59 | const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)}; | ||
| 60 | const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at( | ||
| 61 | 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))}; | ||
| 62 | const auto& faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; | ||
| 63 | const auto& faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; | ||
| 64 | const auto& hair_type_info{RawData::RandomMiiHairType.at(index)}; | ||
| 65 | const auto& hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) + | ||
| 66 | static_cast<std::size_t>(age))}; | ||
| 67 | const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)}; | ||
| 68 | const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))}; | ||
| 69 | const auto& eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)}; | ||
| 70 | const auto& nose_type_info{RawData::RandomMiiNoseType.at(index)}; | ||
| 71 | const auto& mouth_type_info{RawData::RandomMiiMouthType.at(index)}; | ||
| 72 | const auto& glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))}; | ||
| 73 | |||
| 74 | data.faceline_type.Assign( | ||
| 75 | faceline_type_info | ||
| 76 | .values[MiiUtil::GetRandomValue<std::size_t>(faceline_type_info.values_count)]); | ||
| 77 | data.faceline_color.Assign( | ||
| 78 | faceline_color_info | ||
| 79 | .values[MiiUtil::GetRandomValue<std::size_t>(faceline_color_info.values_count)]); | ||
| 80 | data.faceline_wrinkle.Assign( | ||
| 81 | faceline_wrinkle_info | ||
| 82 | .values[MiiUtil::GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]); | ||
| 83 | data.faceline_makeup.Assign( | ||
| 84 | faceline_makeup_info | ||
| 85 | .values[MiiUtil::GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]); | ||
| 86 | |||
| 87 | data.hair_type.Assign( | ||
| 88 | hair_type_info.values[MiiUtil::GetRandomValue<std::size_t>(hair_type_info.values_count)]); | ||
| 89 | SetHairColor(RawData::GetHairColorFromVer3( | ||
| 90 | hair_color_info | ||
| 91 | .values[MiiUtil::GetRandomValue<std::size_t>(hair_color_info.values_count)])); | ||
| 92 | SetHairFlip(MiiUtil::GetRandomValue(HairFlip::Max)); | ||
| 93 | |||
| 94 | data.eye_type.Assign( | ||
| 95 | eye_type_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_type_info.values_count)]); | ||
| 96 | |||
| 97 | const auto eye_rotate_1{gender != Gender::Male ? 4 : 2}; | ||
| 98 | const auto eye_rotate_2{gender != Gender::Male ? 3 : 4}; | ||
| 99 | const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; | ||
| 100 | const auto eye_rotate{32 - RawData::EyeRotateLookup[data.eye_type]}; | ||
| 101 | |||
| 102 | SetEyeColor(RawData::GetEyeColorFromVer3( | ||
| 103 | eye_color_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_color_info.values_count)])); | ||
| 104 | SetEyeScale(4); | ||
| 105 | SetEyeAspect(3); | ||
| 106 | SetEyeRotate(static_cast<u8>(eye_rotate_offset - eye_rotate)); | ||
| 107 | SetEyeX(2); | ||
| 108 | SetEyeY(static_cast<u8>(axis_y + 12)); | ||
| 109 | |||
| 110 | data.eyebrow_type.Assign( | ||
| 111 | eyebrow_type_info | ||
| 112 | .values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); | ||
| 113 | |||
| 114 | const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; | ||
| 115 | const auto eyebrow_y{race == Race::Asian ? 9 : 10}; | ||
| 116 | const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6}; | ||
| 117 | const auto eyebrow_rotate{ | ||
| 118 | 32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]}; | ||
| 119 | |||
| 120 | SetEyebrowColor(GetHairColor()); | ||
| 121 | SetEyebrowScale(4); | ||
| 122 | SetEyebrowAspect(3); | ||
| 123 | SetEyebrowRotate(static_cast<u8>(eyebrow_rotate_offset - eyebrow_rotate)); | ||
| 124 | SetEyebrowX(2); | ||
| 125 | SetEyebrowY(static_cast<u8>(axis_y + eyebrow_y)); | ||
| 126 | |||
| 127 | data.nose_type.Assign( | ||
| 128 | nose_type_info.values[MiiUtil::GetRandomValue<std::size_t>(nose_type_info.values_count)]); | ||
| 129 | SetNoseScale(gender == Gender::Female ? 3 : 4); | ||
| 130 | SetNoseY(static_cast<u8>(axis_y + 9)); | ||
| 131 | |||
| 132 | const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue<int>(4) : 0}; | ||
| 133 | |||
| 134 | data.mouth_type.Assign( | ||
| 135 | mouth_type_info.values[MiiUtil::GetRandomValue<std::size_t>(mouth_type_info.values_count)]); | ||
| 136 | SetMouthColor(RawData::GetMouthColorFromVer3(mouth_color)); | ||
| 137 | SetMouthScale(4); | ||
| 138 | SetMouthAspect(3); | ||
| 139 | SetMouthY(static_cast<u8>(axis_y + 13)); | ||
| 140 | |||
| 141 | SetBeardColor(GetHairColor()); | ||
| 142 | SetMustacheScale(4); | ||
| 143 | |||
| 144 | if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue<int>(10) < 2) { | ||
| 145 | const auto mustache_and_beard_flag{MiiUtil::GetRandomValue(BeardAndMustacheFlag::All)}; | ||
| 146 | |||
| 147 | auto beard_type{BeardType::None}; | ||
| 148 | auto mustache_type{MustacheType::None}; | ||
| 149 | |||
| 150 | if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) == | ||
| 151 | BeardAndMustacheFlag::Beard) { | ||
| 152 | beard_type = MiiUtil::GetRandomValue(BeardType::Min, BeardType::Max); | ||
| 153 | } | ||
| 154 | |||
| 155 | if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == | ||
| 156 | BeardAndMustacheFlag::Mustache) { | ||
| 157 | mustache_type = MiiUtil::GetRandomValue(MustacheType::Min, MustacheType::Max); | ||
| 158 | } | ||
| 159 | |||
| 160 | SetMustacheType(mustache_type); | ||
| 161 | SetBeardType(beard_type); | ||
| 162 | SetMustacheY(10); | ||
| 163 | } else { | ||
| 164 | SetMustacheType(MustacheType::None); | ||
| 165 | SetBeardType(BeardType::None); | ||
| 166 | SetMustacheY(static_cast<u8>(axis_y + 10)); | ||
| 167 | } | ||
| 168 | |||
| 169 | const auto glasses_type_start{MiiUtil::GetRandomValue<std::size_t>(100)}; | ||
| 170 | u8 glasses_type{}; | ||
| 171 | while (glasses_type_start < glasses_type_info.values[glasses_type]) { | ||
| 172 | if (++glasses_type >= glasses_type_info.values_count) { | ||
| 173 | ASSERT(false); | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | SetGlassType(static_cast<GlassType>(glasses_type)); | ||
| 179 | SetGlassColor(RawData::GetGlassColorFromVer3(0)); | ||
| 180 | SetGlassScale(4); | ||
| 181 | |||
| 182 | SetMoleType(MoleType::None); | ||
| 183 | SetMoleScale(4); | ||
| 184 | SetMoleX(2); | ||
| 185 | SetMoleY(20); | ||
| 186 | } | ||
| 187 | |||
| 188 | u32 CoreData::IsValid() const { | ||
| 189 | // TODO: Complete this | ||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | void CoreData::SetFontRegion(FontRegion value) { | ||
| 194 | data.font_region.Assign(static_cast<u32>(value)); | ||
| 195 | } | ||
| 196 | |||
| 197 | void CoreData::SetFavoriteColor(FavoriteColor value) { | ||
| 198 | data.favorite_color.Assign(static_cast<u32>(value)); | ||
| 199 | } | ||
| 200 | |||
| 201 | void CoreData::SetGender(Gender value) { | ||
| 202 | data.gender.Assign(static_cast<u32>(value)); | ||
| 203 | } | ||
| 204 | |||
| 205 | void CoreData::SetHeight(u8 value) { | ||
| 206 | data.height.Assign(value); | ||
| 207 | } | ||
| 208 | |||
| 209 | void CoreData::SetBuild(u8 value) { | ||
| 210 | data.build.Assign(value); | ||
| 211 | } | ||
| 212 | |||
| 213 | void CoreData::SetType(u8 value) { | ||
| 214 | data.type.Assign(value); | ||
| 215 | } | ||
| 216 | |||
| 217 | void CoreData::SetRegionMove(u8 value) { | ||
| 218 | data.region_move.Assign(value); | ||
| 219 | } | ||
| 220 | |||
| 221 | void CoreData::SetFacelineType(FacelineType value) { | ||
| 222 | data.faceline_type.Assign(static_cast<u32>(value)); | ||
| 223 | } | ||
| 224 | |||
| 225 | void CoreData::SetFacelineColor(FacelineColor value) { | ||
| 226 | data.faceline_color.Assign(static_cast<u32>(value)); | ||
| 227 | } | ||
| 228 | |||
| 229 | void CoreData::SetFacelineWrinkle(FacelineWrinkle value) { | ||
| 230 | data.faceline_wrinkle.Assign(static_cast<u32>(value)); | ||
| 231 | } | ||
| 232 | |||
| 233 | void CoreData::SetFacelineMake(FacelineMake value) { | ||
| 234 | data.faceline_makeup.Assign(static_cast<u32>(value)); | ||
| 235 | } | ||
| 236 | |||
| 237 | void CoreData::SetHairType(HairType value) { | ||
| 238 | data.hair_type.Assign(static_cast<u32>(value)); | ||
| 239 | } | ||
| 240 | |||
| 241 | void CoreData::SetHairColor(CommonColor value) { | ||
| 242 | data.hair_color.Assign(static_cast<u32>(value)); | ||
| 243 | } | ||
| 244 | |||
| 245 | void CoreData::SetHairFlip(HairFlip value) { | ||
| 246 | data.hair_flip.Assign(static_cast<u32>(value)); | ||
| 247 | } | ||
| 248 | |||
| 249 | void CoreData::SetEyeType(EyeType value) { | ||
| 250 | data.eye_type.Assign(static_cast<u32>(value)); | ||
| 251 | } | ||
| 252 | |||
| 253 | void CoreData::SetEyeColor(CommonColor value) { | ||
| 254 | data.eye_color.Assign(static_cast<u32>(value)); | ||
| 255 | } | ||
| 256 | |||
| 257 | void CoreData::SetEyeScale(u8 value) { | ||
| 258 | data.eye_scale.Assign(value); | ||
| 259 | } | ||
| 260 | |||
| 261 | void CoreData::SetEyeAspect(u8 value) { | ||
| 262 | data.eye_aspect.Assign(value); | ||
| 263 | } | ||
| 264 | |||
| 265 | void CoreData::SetEyeRotate(u8 value) { | ||
| 266 | data.eye_rotate.Assign(value); | ||
| 267 | } | ||
| 268 | |||
| 269 | void CoreData::SetEyeX(u8 value) { | ||
| 270 | data.eye_x.Assign(value); | ||
| 271 | } | ||
| 272 | |||
| 273 | void CoreData::SetEyeY(u8 value) { | ||
| 274 | data.eye_y.Assign(value); | ||
| 275 | } | ||
| 276 | |||
| 277 | void CoreData::SetEyebrowType(EyebrowType value) { | ||
| 278 | data.eyebrow_type.Assign(static_cast<u32>(value)); | ||
| 279 | } | ||
| 280 | |||
| 281 | void CoreData::SetEyebrowColor(CommonColor value) { | ||
| 282 | data.eyebrow_color.Assign(static_cast<u32>(value)); | ||
| 283 | } | ||
| 284 | |||
| 285 | void CoreData::SetEyebrowScale(u8 value) { | ||
| 286 | data.eyebrow_scale.Assign(value); | ||
| 287 | } | ||
| 288 | |||
| 289 | void CoreData::SetEyebrowAspect(u8 value) { | ||
| 290 | data.eyebrow_aspect.Assign(value); | ||
| 291 | } | ||
| 292 | |||
| 293 | void CoreData::SetEyebrowRotate(u8 value) { | ||
| 294 | data.eyebrow_rotate.Assign(value); | ||
| 295 | } | ||
| 296 | |||
| 297 | void CoreData::SetEyebrowX(u8 value) { | ||
| 298 | data.eyebrow_x.Assign(value); | ||
| 299 | } | ||
| 300 | |||
| 301 | void CoreData::SetEyebrowY(u8 value) { | ||
| 302 | data.eyebrow_y.Assign(value); | ||
| 303 | } | ||
| 304 | |||
| 305 | void CoreData::SetNoseType(NoseType value) { | ||
| 306 | data.nose_type.Assign(static_cast<u32>(value)); | ||
| 307 | } | ||
| 308 | |||
| 309 | void CoreData::SetNoseScale(u8 value) { | ||
| 310 | data.nose_scale.Assign(value); | ||
| 311 | } | ||
| 312 | |||
| 313 | void CoreData::SetNoseY(u8 value) { | ||
| 314 | data.nose_y.Assign(value); | ||
| 315 | } | ||
| 316 | |||
| 317 | void CoreData::SetMouthType(u8 value) { | ||
| 318 | data.mouth_type.Assign(value); | ||
| 319 | } | ||
| 320 | |||
| 321 | void CoreData::SetMouthColor(CommonColor value) { | ||
| 322 | data.mouth_color.Assign(static_cast<u32>(value)); | ||
| 323 | } | ||
| 324 | |||
| 325 | void CoreData::SetMouthScale(u8 value) { | ||
| 326 | data.mouth_scale.Assign(value); | ||
| 327 | } | ||
| 328 | |||
| 329 | void CoreData::SetMouthAspect(u8 value) { | ||
| 330 | data.mouth_aspect.Assign(value); | ||
| 331 | } | ||
| 332 | |||
| 333 | void CoreData::SetMouthY(u8 value) { | ||
| 334 | data.mouth_y.Assign(value); | ||
| 335 | } | ||
| 336 | |||
| 337 | void CoreData::SetBeardColor(CommonColor value) { | ||
| 338 | data.beard_color.Assign(static_cast<u32>(value)); | ||
| 339 | } | ||
| 340 | |||
| 341 | void CoreData::SetBeardType(BeardType value) { | ||
| 342 | data.beard_type.Assign(static_cast<u32>(value)); | ||
| 343 | } | ||
| 344 | |||
| 345 | void CoreData::SetMustacheType(MustacheType value) { | ||
| 346 | data.mustache_type.Assign(static_cast<u32>(value)); | ||
| 347 | } | ||
| 348 | |||
| 349 | void CoreData::SetMustacheScale(u8 value) { | ||
| 350 | data.mustache_scale.Assign(value); | ||
| 351 | } | ||
| 352 | |||
| 353 | void CoreData::SetMustacheY(u8 value) { | ||
| 354 | data.mustache_y.Assign(value); | ||
| 355 | } | ||
| 356 | |||
| 357 | void CoreData::SetGlassType(GlassType value) { | ||
| 358 | data.glasses_type.Assign(static_cast<u32>(value)); | ||
| 359 | } | ||
| 360 | |||
| 361 | void CoreData::SetGlassColor(CommonColor value) { | ||
| 362 | data.glasses_color.Assign(static_cast<u32>(value)); | ||
| 363 | } | ||
| 364 | |||
| 365 | void CoreData::SetGlassScale(u8 value) { | ||
| 366 | data.glasses_scale.Assign(value); | ||
| 367 | } | ||
| 368 | |||
| 369 | void CoreData::SetGlassY(u8 value) { | ||
| 370 | data.glasses_y.Assign(value); | ||
| 371 | } | ||
| 372 | |||
| 373 | void CoreData::SetMoleType(MoleType value) { | ||
| 374 | data.mole_type.Assign(static_cast<u32>(value)); | ||
| 375 | } | ||
| 376 | |||
| 377 | void CoreData::SetMoleScale(u8 value) { | ||
| 378 | data.mole_scale.Assign(value); | ||
| 379 | } | ||
| 380 | |||
| 381 | void CoreData::SetMoleX(u8 value) { | ||
| 382 | data.mole_x.Assign(value); | ||
| 383 | } | ||
| 384 | |||
| 385 | void CoreData::SetMoleY(u8 value) { | ||
| 386 | data.mole_y.Assign(value); | ||
| 387 | } | ||
| 388 | |||
| 389 | void CoreData::SetNickname(Nickname nickname) { | ||
| 390 | name = nickname; | ||
| 391 | } | ||
| 392 | |||
| 393 | FontRegion CoreData::GetFontRegion() const { | ||
| 394 | return static_cast<FontRegion>(data.font_region.Value()); | ||
| 395 | } | ||
| 396 | |||
| 397 | FavoriteColor CoreData::GetFavoriteColor() const { | ||
| 398 | return static_cast<FavoriteColor>(data.favorite_color.Value()); | ||
| 399 | } | ||
| 400 | |||
| 401 | Gender CoreData::GetGender() const { | ||
| 402 | return static_cast<Gender>(data.gender.Value()); | ||
| 403 | } | ||
| 404 | |||
| 405 | u8 CoreData::GetHeight() const { | ||
| 406 | return static_cast<u8>(data.height.Value()); | ||
| 407 | } | ||
| 408 | |||
| 409 | u8 CoreData::GetBuild() const { | ||
| 410 | return static_cast<u8>(data.build.Value()); | ||
| 411 | } | ||
| 412 | |||
| 413 | u8 CoreData::GetType() const { | ||
| 414 | return static_cast<u8>(data.type.Value()); | ||
| 415 | } | ||
| 416 | |||
| 417 | u8 CoreData::GetRegionMove() const { | ||
| 418 | return static_cast<u8>(data.region_move.Value()); | ||
| 419 | } | ||
| 420 | |||
| 421 | FacelineType CoreData::GetFacelineType() const { | ||
| 422 | return static_cast<FacelineType>(data.faceline_type.Value()); | ||
| 423 | } | ||
| 424 | |||
| 425 | FacelineColor CoreData::GetFacelineColor() const { | ||
| 426 | return static_cast<FacelineColor>(data.faceline_color.Value()); | ||
| 427 | } | ||
| 428 | |||
| 429 | FacelineWrinkle CoreData::GetFacelineWrinkle() const { | ||
| 430 | return static_cast<FacelineWrinkle>(data.faceline_wrinkle.Value()); | ||
| 431 | } | ||
| 432 | |||
| 433 | FacelineMake CoreData::GetFacelineMake() const { | ||
| 434 | return static_cast<FacelineMake>(data.faceline_makeup.Value()); | ||
| 435 | } | ||
| 436 | |||
| 437 | HairType CoreData::GetHairType() const { | ||
| 438 | return static_cast<HairType>(data.hair_type.Value()); | ||
| 439 | } | ||
| 440 | |||
| 441 | CommonColor CoreData::GetHairColor() const { | ||
| 442 | return static_cast<CommonColor>(data.hair_color.Value()); | ||
| 443 | } | ||
| 444 | |||
| 445 | HairFlip CoreData::GetHairFlip() const { | ||
| 446 | return static_cast<HairFlip>(data.hair_flip.Value()); | ||
| 447 | } | ||
| 448 | |||
| 449 | EyeType CoreData::GetEyeType() const { | ||
| 450 | return static_cast<EyeType>(data.eye_type.Value()); | ||
| 451 | } | ||
| 452 | |||
| 453 | CommonColor CoreData::GetEyeColor() const { | ||
| 454 | return static_cast<CommonColor>(data.eye_color.Value()); | ||
| 455 | } | ||
| 456 | |||
| 457 | u8 CoreData::GetEyeScale() const { | ||
| 458 | return static_cast<u8>(data.eye_scale.Value()); | ||
| 459 | } | ||
| 460 | |||
| 461 | u8 CoreData::GetEyeAspect() const { | ||
| 462 | return static_cast<u8>(data.eye_aspect.Value()); | ||
| 463 | } | ||
| 464 | |||
| 465 | u8 CoreData::GetEyeRotate() const { | ||
| 466 | return static_cast<u8>(data.eye_rotate.Value()); | ||
| 467 | } | ||
| 468 | |||
| 469 | u8 CoreData::GetEyeX() const { | ||
| 470 | return static_cast<u8>(data.eye_x.Value()); | ||
| 471 | } | ||
| 472 | |||
| 473 | u8 CoreData::GetEyeY() const { | ||
| 474 | return static_cast<u8>(data.eye_y.Value()); | ||
| 475 | } | ||
| 476 | |||
| 477 | EyebrowType CoreData::GetEyebrowType() const { | ||
| 478 | return static_cast<EyebrowType>(data.eyebrow_type.Value()); | ||
| 479 | } | ||
| 480 | |||
| 481 | CommonColor CoreData::GetEyebrowColor() const { | ||
| 482 | return static_cast<CommonColor>(data.eyebrow_color.Value()); | ||
| 483 | } | ||
| 484 | |||
| 485 | u8 CoreData::GetEyebrowScale() const { | ||
| 486 | return static_cast<u8>(data.eyebrow_scale.Value()); | ||
| 487 | } | ||
| 488 | |||
| 489 | u8 CoreData::GetEyebrowAspect() const { | ||
| 490 | return static_cast<u8>(data.eyebrow_aspect.Value()); | ||
| 491 | } | ||
| 492 | |||
| 493 | u8 CoreData::GetEyebrowRotate() const { | ||
| 494 | return static_cast<u8>(data.eyebrow_rotate.Value()); | ||
| 495 | } | ||
| 496 | |||
| 497 | u8 CoreData::GetEyebrowX() const { | ||
| 498 | return static_cast<u8>(data.eyebrow_x.Value()); | ||
| 499 | } | ||
| 500 | |||
| 501 | u8 CoreData::GetEyebrowY() const { | ||
| 502 | return static_cast<u8>(data.eyebrow_y.Value()); | ||
| 503 | } | ||
| 504 | |||
| 505 | NoseType CoreData::GetNoseType() const { | ||
| 506 | return static_cast<NoseType>(data.nose_type.Value()); | ||
| 507 | } | ||
| 508 | |||
| 509 | u8 CoreData::GetNoseScale() const { | ||
| 510 | return static_cast<u8>(data.nose_scale.Value()); | ||
| 511 | } | ||
| 512 | |||
| 513 | u8 CoreData::GetNoseY() const { | ||
| 514 | return static_cast<u8>(data.nose_y.Value()); | ||
| 515 | } | ||
| 516 | |||
| 517 | MouthType CoreData::GetMouthType() const { | ||
| 518 | return static_cast<MouthType>(data.mouth_type.Value()); | ||
| 519 | } | ||
| 520 | |||
| 521 | CommonColor CoreData::GetMouthColor() const { | ||
| 522 | return static_cast<CommonColor>(data.mouth_color.Value()); | ||
| 523 | } | ||
| 524 | |||
| 525 | u8 CoreData::GetMouthScale() const { | ||
| 526 | return static_cast<u8>(data.mouth_scale.Value()); | ||
| 527 | } | ||
| 528 | |||
| 529 | u8 CoreData::GetMouthAspect() const { | ||
| 530 | return static_cast<u8>(data.mouth_aspect.Value()); | ||
| 531 | } | ||
| 532 | |||
| 533 | u8 CoreData::GetMouthY() const { | ||
| 534 | return static_cast<u8>(data.mouth_y.Value()); | ||
| 535 | } | ||
| 536 | |||
| 537 | CommonColor CoreData::GetBeardColor() const { | ||
| 538 | return static_cast<CommonColor>(data.beard_color.Value()); | ||
| 539 | } | ||
| 540 | |||
| 541 | BeardType CoreData::GetBeardType() const { | ||
| 542 | return static_cast<BeardType>(data.beard_type.Value()); | ||
| 543 | } | ||
| 544 | |||
| 545 | MustacheType CoreData::GetMustacheType() const { | ||
| 546 | return static_cast<MustacheType>(data.mustache_type.Value()); | ||
| 547 | } | ||
| 548 | |||
| 549 | u8 CoreData::GetMustacheScale() const { | ||
| 550 | return static_cast<u8>(data.mustache_scale.Value()); | ||
| 551 | } | ||
| 552 | |||
| 553 | u8 CoreData::GetMustacheY() const { | ||
| 554 | return static_cast<u8>(data.mustache_y.Value()); | ||
| 555 | } | ||
| 556 | |||
| 557 | GlassType CoreData::GetGlassType() const { | ||
| 558 | return static_cast<GlassType>(data.glasses_type.Value()); | ||
| 559 | } | ||
| 560 | |||
| 561 | CommonColor CoreData::GetGlassColor() const { | ||
| 562 | return static_cast<CommonColor>(data.glasses_color.Value()); | ||
| 563 | } | ||
| 564 | |||
| 565 | u8 CoreData::GetGlassScale() const { | ||
| 566 | return static_cast<u8>(data.glasses_scale.Value()); | ||
| 567 | } | ||
| 568 | |||
| 569 | u8 CoreData::GetGlassY() const { | ||
| 570 | return static_cast<u8>(data.glasses_y.Value()); | ||
| 571 | } | ||
| 572 | |||
| 573 | MoleType CoreData::GetMoleType() const { | ||
| 574 | return static_cast<MoleType>(data.mole_type.Value()); | ||
| 575 | } | ||
| 576 | |||
| 577 | u8 CoreData::GetMoleScale() const { | ||
| 578 | return static_cast<u8>(data.mole_scale.Value()); | ||
| 579 | } | ||
| 580 | |||
| 581 | u8 CoreData::GetMoleX() const { | ||
| 582 | return static_cast<u8>(data.mole_x.Value()); | ||
| 583 | } | ||
| 584 | |||
| 585 | u8 CoreData::GetMoleY() const { | ||
| 586 | return static_cast<u8>(data.mole_y.Value()); | ||
| 587 | } | ||
| 588 | |||
| 589 | Nickname CoreData::GetNickname() const { | ||
| 590 | return name; | ||
| 591 | } | ||
| 592 | |||
| 593 | Nickname CoreData::GetDefaultNickname() const { | ||
| 594 | return {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}; | ||
| 595 | } | ||
| 596 | |||
| 597 | Nickname CoreData::GetInvalidNickname() const { | ||
| 598 | return {u'?', u'?', u'?'}; | ||
| 599 | } | ||
| 600 | |||
| 601 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h new file mode 100644 index 000000000..cebcd2ee4 --- /dev/null +++ b/src/core/hle/service/mii/types/core_data.h | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/mii/mii_types.h" | ||
| 7 | |||
| 8 | namespace Service::Mii { | ||
| 9 | |||
| 10 | struct StoreDataBitFields { | ||
| 11 | union { | ||
| 12 | u32 word_0{}; | ||
| 13 | |||
| 14 | BitField<0, 8, u32> hair_type; | ||
| 15 | BitField<8, 7, u32> height; | ||
| 16 | BitField<15, 1, u32> mole_type; | ||
| 17 | BitField<16, 7, u32> build; | ||
| 18 | BitField<23, 1, u32> hair_flip; | ||
| 19 | BitField<24, 7, u32> hair_color; | ||
| 20 | BitField<31, 1, u32> type; | ||
| 21 | }; | ||
| 22 | |||
| 23 | union { | ||
| 24 | u32 word_1{}; | ||
| 25 | |||
| 26 | BitField<0, 7, u32> eye_color; | ||
| 27 | BitField<7, 1, u32> gender; | ||
| 28 | BitField<8, 7, u32> eyebrow_color; | ||
| 29 | BitField<16, 7, u32> mouth_color; | ||
| 30 | BitField<24, 7, u32> beard_color; | ||
| 31 | }; | ||
| 32 | |||
| 33 | union { | ||
| 34 | u32 word_2{}; | ||
| 35 | |||
| 36 | BitField<0, 7, u32> glasses_color; | ||
| 37 | BitField<8, 6, u32> eye_type; | ||
| 38 | BitField<14, 2, u32> region_move; | ||
| 39 | BitField<16, 6, u32> mouth_type; | ||
| 40 | BitField<22, 2, u32> font_region; | ||
| 41 | BitField<24, 5, u32> eye_y; | ||
| 42 | BitField<29, 3, u32> glasses_scale; | ||
| 43 | }; | ||
| 44 | |||
| 45 | union { | ||
| 46 | u32 word_3{}; | ||
| 47 | |||
| 48 | BitField<0, 5, u32> eyebrow_type; | ||
| 49 | BitField<5, 3, u32> mustache_type; | ||
| 50 | BitField<8, 5, u32> nose_type; | ||
| 51 | BitField<13, 3, u32> beard_type; | ||
| 52 | BitField<16, 5, u32> nose_y; | ||
| 53 | BitField<21, 3, u32> mouth_aspect; | ||
| 54 | BitField<24, 5, u32> mouth_y; | ||
| 55 | BitField<29, 3, u32> eyebrow_aspect; | ||
| 56 | }; | ||
| 57 | |||
| 58 | union { | ||
| 59 | u32 word_4{}; | ||
| 60 | |||
| 61 | BitField<0, 5, u32> mustache_y; | ||
| 62 | BitField<5, 3, u32> eye_rotate; | ||
| 63 | BitField<8, 5, u32> glasses_y; | ||
| 64 | BitField<13, 3, u32> eye_aspect; | ||
| 65 | BitField<16, 5, u32> mole_x; | ||
| 66 | BitField<21, 3, u32> eye_scale; | ||
| 67 | BitField<24, 5, u32> mole_y; | ||
| 68 | }; | ||
| 69 | |||
| 70 | union { | ||
| 71 | u32 word_5{}; | ||
| 72 | |||
| 73 | BitField<0, 5, u32> glasses_type; | ||
| 74 | BitField<8, 4, u32> favorite_color; | ||
| 75 | BitField<12, 4, u32> faceline_type; | ||
| 76 | BitField<16, 4, u32> faceline_color; | ||
| 77 | BitField<20, 4, u32> faceline_wrinkle; | ||
| 78 | BitField<24, 4, u32> faceline_makeup; | ||
| 79 | BitField<28, 4, u32> eye_x; | ||
| 80 | }; | ||
| 81 | |||
| 82 | union { | ||
| 83 | u32 word_6{}; | ||
| 84 | |||
| 85 | BitField<0, 4, u32> eyebrow_scale; | ||
| 86 | BitField<4, 4, u32> eyebrow_rotate; | ||
| 87 | BitField<8, 4, u32> eyebrow_x; | ||
| 88 | BitField<12, 4, u32> eyebrow_y; | ||
| 89 | BitField<16, 4, u32> nose_scale; | ||
| 90 | BitField<20, 4, u32> mouth_scale; | ||
| 91 | BitField<24, 4, u32> mustache_scale; | ||
| 92 | BitField<28, 4, u32> mole_scale; | ||
| 93 | }; | ||
| 94 | }; | ||
| 95 | static_assert(sizeof(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorrect size."); | ||
| 96 | static_assert(std::is_trivially_copyable_v<StoreDataBitFields>, | ||
| 97 | "StoreDataBitFields is not trivially copyable."); | ||
| 98 | |||
| 99 | class CoreData { | ||
| 100 | public: | ||
| 101 | void SetDefault(); | ||
| 102 | void BuildRandom(Age age, Gender gender, Race race); | ||
| 103 | |||
| 104 | u32 IsValid() const; | ||
| 105 | |||
| 106 | void SetFontRegion(FontRegion value); | ||
| 107 | void SetFavoriteColor(FavoriteColor value); | ||
| 108 | void SetGender(Gender value); | ||
| 109 | void SetHeight(u8 value); | ||
| 110 | void SetBuild(u8 value); | ||
| 111 | void SetType(u8 value); | ||
| 112 | void SetRegionMove(u8 value); | ||
| 113 | void SetFacelineType(FacelineType value); | ||
| 114 | void SetFacelineColor(FacelineColor value); | ||
| 115 | void SetFacelineWrinkle(FacelineWrinkle value); | ||
| 116 | void SetFacelineMake(FacelineMake value); | ||
| 117 | void SetHairType(HairType value); | ||
| 118 | void SetHairColor(CommonColor value); | ||
| 119 | void SetHairFlip(HairFlip value); | ||
| 120 | void SetEyeType(EyeType value); | ||
| 121 | void SetEyeColor(CommonColor value); | ||
| 122 | void SetEyeScale(u8 value); | ||
| 123 | void SetEyeAspect(u8 value); | ||
| 124 | void SetEyeRotate(u8 value); | ||
| 125 | void SetEyeX(u8 value); | ||
| 126 | void SetEyeY(u8 value); | ||
| 127 | void SetEyebrowType(EyebrowType value); | ||
| 128 | void SetEyebrowColor(CommonColor value); | ||
| 129 | void SetEyebrowScale(u8 value); | ||
| 130 | void SetEyebrowAspect(u8 value); | ||
| 131 | void SetEyebrowRotate(u8 value); | ||
| 132 | void SetEyebrowX(u8 value); | ||
| 133 | void SetEyebrowY(u8 value); | ||
| 134 | void SetNoseType(NoseType value); | ||
| 135 | void SetNoseScale(u8 value); | ||
| 136 | void SetNoseY(u8 value); | ||
| 137 | void SetMouthType(u8 value); | ||
| 138 | void SetMouthColor(CommonColor value); | ||
| 139 | void SetMouthScale(u8 value); | ||
| 140 | void SetMouthAspect(u8 value); | ||
| 141 | void SetMouthY(u8 value); | ||
| 142 | void SetBeardColor(CommonColor value); | ||
| 143 | void SetBeardType(BeardType value); | ||
| 144 | void SetMustacheType(MustacheType value); | ||
| 145 | void SetMustacheScale(u8 value); | ||
| 146 | void SetMustacheY(u8 value); | ||
| 147 | void SetGlassType(GlassType value); | ||
| 148 | void SetGlassColor(CommonColor value); | ||
| 149 | void SetGlassScale(u8 value); | ||
| 150 | void SetGlassY(u8 value); | ||
| 151 | void SetMoleType(MoleType value); | ||
| 152 | void SetMoleScale(u8 value); | ||
| 153 | void SetMoleX(u8 value); | ||
| 154 | void SetMoleY(u8 value); | ||
| 155 | void SetNickname(Nickname nickname); | ||
| 156 | |||
| 157 | FontRegion GetFontRegion() const; | ||
| 158 | FavoriteColor GetFavoriteColor() const; | ||
| 159 | Gender GetGender() const; | ||
| 160 | u8 GetHeight() const; | ||
| 161 | u8 GetBuild() const; | ||
| 162 | u8 GetType() const; | ||
| 163 | u8 GetRegionMove() const; | ||
| 164 | FacelineType GetFacelineType() const; | ||
| 165 | FacelineColor GetFacelineColor() const; | ||
| 166 | FacelineWrinkle GetFacelineWrinkle() const; | ||
| 167 | FacelineMake GetFacelineMake() const; | ||
| 168 | HairType GetHairType() const; | ||
| 169 | CommonColor GetHairColor() const; | ||
| 170 | HairFlip GetHairFlip() const; | ||
| 171 | EyeType GetEyeType() const; | ||
| 172 | CommonColor GetEyeColor() const; | ||
| 173 | u8 GetEyeScale() const; | ||
| 174 | u8 GetEyeAspect() const; | ||
| 175 | u8 GetEyeRotate() const; | ||
| 176 | u8 GetEyeX() const; | ||
| 177 | u8 GetEyeY() const; | ||
| 178 | EyebrowType GetEyebrowType() const; | ||
| 179 | CommonColor GetEyebrowColor() const; | ||
| 180 | u8 GetEyebrowScale() const; | ||
| 181 | u8 GetEyebrowAspect() const; | ||
| 182 | u8 GetEyebrowRotate() const; | ||
| 183 | u8 GetEyebrowX() const; | ||
| 184 | u8 GetEyebrowY() const; | ||
| 185 | NoseType GetNoseType() const; | ||
| 186 | u8 GetNoseScale() const; | ||
| 187 | u8 GetNoseY() const; | ||
| 188 | MouthType GetMouthType() const; | ||
| 189 | CommonColor GetMouthColor() const; | ||
| 190 | u8 GetMouthScale() const; | ||
| 191 | u8 GetMouthAspect() const; | ||
| 192 | u8 GetMouthY() const; | ||
| 193 | CommonColor GetBeardColor() const; | ||
| 194 | BeardType GetBeardType() const; | ||
| 195 | MustacheType GetMustacheType() const; | ||
| 196 | u8 GetMustacheScale() const; | ||
| 197 | u8 GetMustacheY() const; | ||
| 198 | GlassType GetGlassType() const; | ||
| 199 | CommonColor GetGlassColor() const; | ||
| 200 | u8 GetGlassScale() const; | ||
| 201 | u8 GetGlassY() const; | ||
| 202 | MoleType GetMoleType() const; | ||
| 203 | u8 GetMoleScale() const; | ||
| 204 | u8 GetMoleX() const; | ||
| 205 | u8 GetMoleY() const; | ||
| 206 | Nickname GetNickname() const; | ||
| 207 | Nickname GetDefaultNickname() const; | ||
| 208 | Nickname GetInvalidNickname() const; | ||
| 209 | |||
| 210 | private: | ||
| 211 | StoreDataBitFields data{}; | ||
| 212 | Nickname name{}; | ||
| 213 | }; | ||
| 214 | static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size."); | ||
| 215 | |||
| 216 | }; // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/types/raw_data.cpp index 1442280c8..5143abcc8 100644 --- a/src/core/hle/service/mii/raw_data.cpp +++ b/src/core/hle/service/mii/types/raw_data.cpp | |||
| @@ -1,11 +1,88 @@ | |||
| 1 | // SPDX-FileCopyrightText: Ryujinx Team and Contributors | 1 | // SPDX-FileCopyrightText: Ryujinx Team and Contributors |
| 2 | // SPDX-License-Identifier: MIT | 2 | // SPDX-License-Identifier: MIT |
| 3 | 3 | ||
| 4 | #include "core/hle/service/mii/raw_data.h" | 4 | #include "core/hle/service/mii/types/raw_data.h" |
| 5 | 5 | ||
| 6 | namespace Service::Mii::RawData { | 6 | namespace Service::Mii::RawData { |
| 7 | 7 | ||
| 8 | const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | 8 | constexpr std::array<u8, static_cast<u8>(FacelineColor::Count)> FromVer3FacelineColorTable{ |
| 9 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, | ||
| 10 | }; | ||
| 11 | |||
| 12 | constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3HairColorTable{ | ||
| 13 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0, | ||
| 14 | 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, | ||
| 15 | 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4, | ||
| 16 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, | ||
| 17 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7, | ||
| 18 | 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4, | ||
| 19 | }; | ||
| 20 | |||
| 21 | constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3EyeColorTable{ | ||
| 22 | 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4, | ||
| 23 | 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, | ||
| 24 | 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4, | ||
| 25 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, | ||
| 26 | 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, | ||
| 27 | 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, | ||
| 28 | }; | ||
| 29 | |||
| 30 | constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3MouthlineColorTable{ | ||
| 31 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, | ||
| 32 | 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, | ||
| 33 | 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, | ||
| 34 | 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, | ||
| 35 | 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, | ||
| 36 | 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3, | ||
| 37 | }; | ||
| 38 | |||
| 39 | constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3GlassColorTable{ | ||
| 40 | 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3, | ||
| 41 | 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, | ||
| 42 | 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, | ||
| 43 | 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5, | ||
| 44 | 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4, | ||
| 45 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, | ||
| 46 | }; | ||
| 47 | |||
| 48 | constexpr std::array<u8, static_cast<u8>(GlassType::Count)> FromVer3GlassTypeTable{ | ||
| 49 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, | ||
| 50 | 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7, | ||
| 51 | }; | ||
| 52 | |||
| 53 | constexpr std::array<u8, 8> Ver3FacelineColorTable{ | ||
| 54 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, | ||
| 55 | }; | ||
| 56 | |||
| 57 | constexpr std::array<u8, 8> Ver3HairColorTable{ | ||
| 58 | 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, | ||
| 59 | }; | ||
| 60 | |||
| 61 | constexpr std::array<u8, 6> Ver3EyeColorTable{ | ||
| 62 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, | ||
| 63 | }; | ||
| 64 | |||
| 65 | constexpr std::array<u8, 5> Ver3MouthColorTable{ | ||
| 66 | 0x13, 0x14, 0x15, 0x16, 0x17, | ||
| 67 | }; | ||
| 68 | |||
| 69 | constexpr std::array<u8, 7> Ver3GlassColorTable{ | ||
| 70 | 0x8, 0xe, 0xf, 0x10, 0x11, 0x12, 0x0, | ||
| 71 | }; | ||
| 72 | |||
| 73 | const std::array<u8, 62> EyeRotateLookup{ | ||
| 74 | 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, | ||
| 75 | 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, | ||
| 76 | 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, | ||
| 77 | 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, | ||
| 78 | }; | ||
| 79 | |||
| 80 | const std::array<u8, 24> EyebrowRotateLookup{ | ||
| 81 | 0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x06, 0x08, | ||
| 82 | 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05, | ||
| 83 | }; | ||
| 84 | |||
| 85 | const std::array<Service::Mii::DefaultMii, 2> BaseMii{ | ||
| 9 | Service::Mii::DefaultMii{ | 86 | Service::Mii::DefaultMii{ |
| 10 | .face_type = 0, | 87 | .face_type = 0, |
| 11 | .face_color = 0, | 88 | .face_color = 0, |
| @@ -51,11 +128,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 51 | .mole_y = 20, | 128 | .mole_y = 20, |
| 52 | .height = 64, | 129 | .height = 64, |
| 53 | .weight = 64, | 130 | .weight = 64, |
| 54 | .gender = Gender::Male, | 131 | .gender = 0, |
| 55 | .favorite_color = 0, | 132 | .favorite_color = 0, |
| 56 | .region = 0, | 133 | .region_move = 0, |
| 57 | .font_region = FontRegion::Standard, | 134 | .font_region = 0, |
| 58 | .type = 0, | 135 | .type = 0, |
| 136 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 59 | }, | 137 | }, |
| 60 | Service::Mii::DefaultMii{ | 138 | Service::Mii::DefaultMii{ |
| 61 | .face_type = 0, | 139 | .face_type = 0, |
| @@ -102,12 +180,16 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 102 | .mole_y = 20, | 180 | .mole_y = 20, |
| 103 | .height = 64, | 181 | .height = 64, |
| 104 | .weight = 64, | 182 | .weight = 64, |
| 105 | .gender = Gender::Female, | 183 | .gender = 1, |
| 106 | .favorite_color = 0, | 184 | .favorite_color = 0, |
| 107 | .region = 0, | 185 | .region_move = 0, |
| 108 | .font_region = FontRegion::Standard, | 186 | .font_region = 0, |
| 109 | .type = 0, | 187 | .type = 0, |
| 188 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 110 | }, | 189 | }, |
| 190 | }; | ||
| 191 | |||
| 192 | const std::array<Service::Mii::DefaultMii, 6> DefaultMii{ | ||
| 111 | Service::Mii::DefaultMii{ | 193 | Service::Mii::DefaultMii{ |
| 112 | .face_type = 0, | 194 | .face_type = 0, |
| 113 | .face_color = 4, | 195 | .face_color = 4, |
| @@ -153,11 +235,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 153 | .mole_y = 20, | 235 | .mole_y = 20, |
| 154 | .height = 64, | 236 | .height = 64, |
| 155 | .weight = 64, | 237 | .weight = 64, |
| 156 | .gender = Gender::Male, | 238 | .gender = 0, |
| 157 | .favorite_color = 4, | 239 | .favorite_color = 4, |
| 158 | .region = 0, | 240 | .region_move = 0, |
| 159 | .font_region = FontRegion::Standard, | 241 | .font_region = 0, |
| 160 | .type = 0, | 242 | .type = 0, |
| 243 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 161 | }, | 244 | }, |
| 162 | Service::Mii::DefaultMii{ | 245 | Service::Mii::DefaultMii{ |
| 163 | .face_type = 0, | 246 | .face_type = 0, |
| @@ -204,11 +287,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 204 | .mole_y = 20, | 287 | .mole_y = 20, |
| 205 | .height = 64, | 288 | .height = 64, |
| 206 | .weight = 64, | 289 | .weight = 64, |
| 207 | .gender = Gender::Male, | 290 | .gender = 0, |
| 208 | .favorite_color = 5, | 291 | .favorite_color = 5, |
| 209 | .region = 0, | 292 | .region_move = 0, |
| 210 | .font_region = FontRegion::Standard, | 293 | .font_region = 0, |
| 211 | .type = 0, | 294 | .type = 0, |
| 295 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 212 | }, | 296 | }, |
| 213 | Service::Mii::DefaultMii{ | 297 | Service::Mii::DefaultMii{ |
| 214 | .face_type = 0, | 298 | .face_type = 0, |
| @@ -255,11 +339,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 255 | .mole_y = 20, | 339 | .mole_y = 20, |
| 256 | .height = 64, | 340 | .height = 64, |
| 257 | .weight = 64, | 341 | .weight = 64, |
| 258 | .gender = Gender::Male, | 342 | .gender = 0, |
| 259 | .favorite_color = 0, | 343 | .favorite_color = 0, |
| 260 | .region = 0, | 344 | .region_move = 0, |
| 261 | .font_region = FontRegion::Standard, | 345 | .font_region = 0, |
| 262 | .type = 0, | 346 | .type = 0, |
| 347 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 263 | }, | 348 | }, |
| 264 | Service::Mii::DefaultMii{ | 349 | Service::Mii::DefaultMii{ |
| 265 | .face_type = 0, | 350 | .face_type = 0, |
| @@ -306,11 +391,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 306 | .mole_y = 20, | 391 | .mole_y = 20, |
| 307 | .height = 64, | 392 | .height = 64, |
| 308 | .weight = 64, | 393 | .weight = 64, |
| 309 | .gender = Gender::Female, | 394 | .gender = 1, |
| 310 | .favorite_color = 2, | 395 | .favorite_color = 2, |
| 311 | .region = 0, | 396 | .region_move = 0, |
| 312 | .font_region = FontRegion::Standard, | 397 | .font_region = 0, |
| 313 | .type = 0, | 398 | .type = 0, |
| 399 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 314 | }, | 400 | }, |
| 315 | Service::Mii::DefaultMii{ | 401 | Service::Mii::DefaultMii{ |
| 316 | .face_type = 0, | 402 | .face_type = 0, |
| @@ -357,11 +443,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 357 | .mole_y = 20, | 443 | .mole_y = 20, |
| 358 | .height = 64, | 444 | .height = 64, |
| 359 | .weight = 64, | 445 | .weight = 64, |
| 360 | .gender = Gender::Female, | 446 | .gender = 1, |
| 361 | .favorite_color = 6, | 447 | .favorite_color = 6, |
| 362 | .region = 0, | 448 | .region_move = 0, |
| 363 | .font_region = FontRegion::Standard, | 449 | .font_region = 0, |
| 364 | .type = 0, | 450 | .type = 0, |
| 451 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 365 | }, | 452 | }, |
| 366 | Service::Mii::DefaultMii{ | 453 | Service::Mii::DefaultMii{ |
| 367 | .face_type = 0, | 454 | .face_type = 0, |
| @@ -408,176 +495,177 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ | |||
| 408 | .mole_y = 20, | 495 | .mole_y = 20, |
| 409 | .height = 64, | 496 | .height = 64, |
| 410 | .weight = 64, | 497 | .weight = 64, |
| 411 | .gender = Gender::Female, | 498 | .gender = 1, |
| 412 | .favorite_color = 7, | 499 | .favorite_color = 7, |
| 413 | .region = 0, | 500 | .region_move = 0, |
| 414 | .font_region = FontRegion::Standard, | 501 | .font_region = 0, |
| 415 | .type = 0, | 502 | .type = 0, |
| 503 | .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, | ||
| 416 | }, | 504 | }, |
| 417 | 505 | ||
| 418 | }; | 506 | }; |
| 419 | 507 | ||
| 420 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline{ | 508 | const std::array<RandomMiiData4, 18> RandomMiiFaceline{ |
| 421 | Service::Mii::RandomMiiData4{ | 509 | RandomMiiData4{ |
| 422 | .gender = Gender::Male, | 510 | .gender = static_cast<u32>(Gender::Male), |
| 423 | .age = Age::Young, | 511 | .age = static_cast<u32>(Age::Young), |
| 424 | .race = Race::Black, | 512 | .race = static_cast<u32>(Race::Black), |
| 425 | .values_count = 10, | 513 | .values_count = 10, |
| 426 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, | 514 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, |
| 427 | }, | 515 | }, |
| 428 | Service::Mii::RandomMiiData4{ | 516 | RandomMiiData4{ |
| 429 | .gender = Gender::Male, | 517 | .gender = static_cast<u32>(Gender::Male), |
| 430 | .age = Age::Normal, | 518 | .age = static_cast<u32>(Age::Normal), |
| 431 | .race = Race::Black, | 519 | .race = static_cast<u32>(Race::Black), |
| 432 | .values_count = 10, | 520 | .values_count = 10, |
| 433 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, | 521 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, |
| 434 | }, | 522 | }, |
| 435 | Service::Mii::RandomMiiData4{ | 523 | RandomMiiData4{ |
| 436 | .gender = Gender::Male, | 524 | .gender = static_cast<u32>(Gender::Male), |
| 437 | .age = Age::Old, | 525 | .age = static_cast<u32>(Age::Old), |
| 438 | .race = Race::Black, | 526 | .race = static_cast<u32>(Race::Black), |
| 439 | .values_count = 10, | 527 | .values_count = 10, |
| 440 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, | 528 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, |
| 441 | }, | 529 | }, |
| 442 | Service::Mii::RandomMiiData4{ | 530 | RandomMiiData4{ |
| 443 | .gender = Gender::Male, | 531 | .gender = static_cast<u32>(Gender::Male), |
| 444 | .age = Age::Young, | 532 | .age = static_cast<u32>(Age::Young), |
| 445 | .race = Race::White, | 533 | .race = static_cast<u32>(Race::White), |
| 446 | .values_count = 12, | 534 | .values_count = 12, |
| 447 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, | 535 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, |
| 448 | }, | 536 | }, |
| 449 | Service::Mii::RandomMiiData4{ | 537 | RandomMiiData4{ |
| 450 | .gender = Gender::Male, | 538 | .gender = static_cast<u32>(Gender::Male), |
| 451 | .age = Age::Normal, | 539 | .age = static_cast<u32>(Age::Normal), |
| 452 | .race = Race::White, | 540 | .race = static_cast<u32>(Race::White), |
| 453 | .values_count = 13, | 541 | .values_count = 13, |
| 454 | .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, | 542 | .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, |
| 455 | }, | 543 | }, |
| 456 | Service::Mii::RandomMiiData4{ | 544 | RandomMiiData4{ |
| 457 | .gender = Gender::Male, | 545 | .gender = static_cast<u32>(Gender::Male), |
| 458 | .age = Age::Old, | 546 | .age = static_cast<u32>(Age::Old), |
| 459 | .race = Race::White, | 547 | .race = static_cast<u32>(Race::White), |
| 460 | .values_count = 12, | 548 | .values_count = 12, |
| 461 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, | 549 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, |
| 462 | }, | 550 | }, |
| 463 | Service::Mii::RandomMiiData4{ | 551 | RandomMiiData4{ |
| 464 | .gender = Gender::Male, | 552 | .gender = static_cast<u32>(Gender::Male), |
| 465 | .age = Age::Young, | 553 | .age = static_cast<u32>(Age::Young), |
| 466 | .race = Race::Asian, | 554 | .race = static_cast<u32>(Race::Asian), |
| 467 | .values_count = 12, | 555 | .values_count = 12, |
| 468 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, | 556 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, |
| 469 | }, | 557 | }, |
| 470 | Service::Mii::RandomMiiData4{ | 558 | RandomMiiData4{ |
| 471 | .gender = Gender::Male, | 559 | .gender = static_cast<u32>(Gender::Male), |
| 472 | .age = Age::Normal, | 560 | .age = static_cast<u32>(Age::Normal), |
| 473 | .race = Race::Asian, | 561 | .race = static_cast<u32>(Race::Asian), |
| 474 | .values_count = 13, | 562 | .values_count = 13, |
| 475 | .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, | 563 | .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, |
| 476 | }, | 564 | }, |
| 477 | Service::Mii::RandomMiiData4{ | 565 | RandomMiiData4{ |
| 478 | .gender = Gender::Male, | 566 | .gender = static_cast<u32>(Gender::Male), |
| 479 | .age = Age::Old, | 567 | .age = static_cast<u32>(Age::Old), |
| 480 | .race = Race::Asian, | 568 | .race = static_cast<u32>(Race::Asian), |
| 481 | .values_count = 12, | 569 | .values_count = 12, |
| 482 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, | 570 | .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, |
| 483 | }, | 571 | }, |
| 484 | Service::Mii::RandomMiiData4{ | 572 | RandomMiiData4{ |
| 485 | .gender = Gender::Female, | 573 | .gender = static_cast<u32>(Gender::Female), |
| 486 | .age = Age::Young, | 574 | .age = static_cast<u32>(Age::Young), |
| 487 | .race = Race::Black, | 575 | .race = static_cast<u32>(Race::Black), |
| 488 | .values_count = 10, | 576 | .values_count = 10, |
| 489 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, | 577 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, |
| 490 | }, | 578 | }, |
| 491 | Service::Mii::RandomMiiData4{ | 579 | RandomMiiData4{ |
| 492 | .gender = Gender::Female, | 580 | .gender = static_cast<u32>(Gender::Female), |
| 493 | .age = Age::Normal, | 581 | .age = static_cast<u32>(Age::Normal), |
| 494 | .race = Race::Black, | 582 | .race = static_cast<u32>(Race::Black), |
| 495 | .values_count = 10, | 583 | .values_count = 10, |
| 496 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, | 584 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, |
| 497 | }, | 585 | }, |
| 498 | Service::Mii::RandomMiiData4{ | 586 | RandomMiiData4{ |
| 499 | .gender = Gender::Female, | 587 | .gender = static_cast<u32>(Gender::Female), |
| 500 | .age = Age::Old, | 588 | .age = static_cast<u32>(Age::Old), |
| 501 | .race = Race::Black, | 589 | .race = static_cast<u32>(Race::Black), |
| 502 | .values_count = 10, | 590 | .values_count = 10, |
| 503 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, | 591 | .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, |
| 504 | }, | 592 | }, |
| 505 | Service::Mii::RandomMiiData4{ | 593 | RandomMiiData4{ |
| 506 | .gender = Gender::Female, | 594 | .gender = static_cast<u32>(Gender::Female), |
| 507 | .age = Age::Young, | 595 | .age = static_cast<u32>(Age::Young), |
| 508 | .race = Race::White, | 596 | .race = static_cast<u32>(Race::White), |
| 509 | .values_count = 12, | 597 | .values_count = 12, |
| 510 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, | 598 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, |
| 511 | }, | 599 | }, |
| 512 | Service::Mii::RandomMiiData4{ | 600 | RandomMiiData4{ |
| 513 | .gender = Gender::Female, | 601 | .gender = static_cast<u32>(Gender::Female), |
| 514 | .age = Age::Normal, | 602 | .age = static_cast<u32>(Age::Normal), |
| 515 | .race = Race::White, | 603 | .race = static_cast<u32>(Race::White), |
| 516 | .values_count = 12, | 604 | .values_count = 12, |
| 517 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, | 605 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, |
| 518 | }, | 606 | }, |
| 519 | Service::Mii::RandomMiiData4{ | 607 | RandomMiiData4{ |
| 520 | .gender = Gender::Female, | 608 | .gender = static_cast<u32>(Gender::Female), |
| 521 | .age = Age::Old, | 609 | .age = static_cast<u32>(Age::Old), |
| 522 | .race = Race::White, | 610 | .race = static_cast<u32>(Race::White), |
| 523 | .values_count = 12, | 611 | .values_count = 12, |
| 524 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, | 612 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, |
| 525 | }, | 613 | }, |
| 526 | Service::Mii::RandomMiiData4{ | 614 | RandomMiiData4{ |
| 527 | .gender = Gender::Female, | 615 | .gender = static_cast<u32>(Gender::Female), |
| 528 | .age = Age::Young, | 616 | .age = static_cast<u32>(Age::Young), |
| 529 | .race = Race::Asian, | 617 | .race = static_cast<u32>(Race::Asian), |
| 530 | .values_count = 12, | 618 | .values_count = 12, |
| 531 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, | 619 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, |
| 532 | }, | 620 | }, |
| 533 | Service::Mii::RandomMiiData4{ | 621 | RandomMiiData4{ |
| 534 | .gender = Gender::Female, | 622 | .gender = static_cast<u32>(Gender::Female), |
| 535 | .age = Age::Normal, | 623 | .age = static_cast<u32>(Age::Normal), |
| 536 | .race = Race::Asian, | 624 | .race = static_cast<u32>(Race::Asian), |
| 537 | .values_count = 12, | 625 | .values_count = 12, |
| 538 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, | 626 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, |
| 539 | }, | 627 | }, |
| 540 | Service::Mii::RandomMiiData4{ | 628 | RandomMiiData4{ |
| 541 | .gender = Gender::Female, | 629 | .gender = static_cast<u32>(Gender::Female), |
| 542 | .age = Age::Old, | 630 | .age = static_cast<u32>(Age::Old), |
| 543 | .race = Race::Asian, | 631 | .race = static_cast<u32>(Race::Asian), |
| 544 | .values_count = 12, | 632 | .values_count = 12, |
| 545 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, | 633 | .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, |
| 546 | }, | 634 | }, |
| 547 | }; | 635 | }; |
| 548 | 636 | ||
| 549 | const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{ | 637 | const std::array<RandomMiiData3, 6> RandomMiiFacelineColor{ |
| 550 | Service::Mii::RandomMiiData3{ | 638 | RandomMiiData3{ |
| 551 | .arg_1 = 0, | 639 | .arg_1 = 0, |
| 552 | .arg_2 = 0, | 640 | .arg_2 = 0, |
| 553 | .values_count = 10, | 641 | .values_count = 10, |
| 554 | .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, | 642 | .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, |
| 555 | }, | 643 | }, |
| 556 | Service::Mii::RandomMiiData3{ | 644 | RandomMiiData3{ |
| 557 | .arg_1 = 0, | 645 | .arg_1 = 0, |
| 558 | .arg_2 = 1, | 646 | .arg_2 = 1, |
| 559 | .values_count = 10, | 647 | .values_count = 10, |
| 560 | .values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3}, | 648 | .values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3}, |
| 561 | }, | 649 | }, |
| 562 | Service::Mii::RandomMiiData3{ | 650 | RandomMiiData3{ |
| 563 | .arg_1 = 0, | 651 | .arg_1 = 0, |
| 564 | .arg_2 = 2, | 652 | .arg_2 = 2, |
| 565 | .values_count = 10, | 653 | .values_count = 10, |
| 566 | .values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2}, | 654 | .values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2}, |
| 567 | }, | 655 | }, |
| 568 | Service::Mii::RandomMiiData3{ | 656 | RandomMiiData3{ |
| 569 | .arg_1 = 1, | 657 | .arg_1 = 1, |
| 570 | .arg_2 = 0, | 658 | .arg_2 = 0, |
| 571 | .values_count = 10, | 659 | .values_count = 10, |
| 572 | .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, | 660 | .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, |
| 573 | }, | 661 | }, |
| 574 | Service::Mii::RandomMiiData3{ | 662 | RandomMiiData3{ |
| 575 | .arg_1 = 1, | 663 | .arg_1 = 1, |
| 576 | .arg_2 = 1, | 664 | .arg_2 = 1, |
| 577 | .values_count = 10, | 665 | .values_count = 10, |
| 578 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3}, | 666 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3}, |
| 579 | }, | 667 | }, |
| 580 | Service::Mii::RandomMiiData3{ | 668 | RandomMiiData3{ |
| 581 | .arg_1 = 1, | 669 | .arg_1 = 1, |
| 582 | .arg_2 = 2, | 670 | .arg_2 = 2, |
| 583 | .values_count = 10, | 671 | .values_count = 10, |
| @@ -585,407 +673,407 @@ const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{ | |||
| 585 | }, | 673 | }, |
| 586 | }; | 674 | }; |
| 587 | 675 | ||
| 588 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle{ | 676 | const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle{ |
| 589 | Service::Mii::RandomMiiData4{ | 677 | RandomMiiData4{ |
| 590 | .gender = Gender::Male, | 678 | .gender = static_cast<u32>(Gender::Male), |
| 591 | .age = Age::Young, | 679 | .age = static_cast<u32>(Age::Young), |
| 592 | .race = Race::Black, | 680 | .race = static_cast<u32>(Race::Black), |
| 593 | .values_count = 20, | 681 | .values_count = 20, |
| 594 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, | 682 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, |
| 595 | }, | 683 | }, |
| 596 | Service::Mii::RandomMiiData4{ | 684 | RandomMiiData4{ |
| 597 | .gender = Gender::Male, | 685 | .gender = static_cast<u32>(Gender::Male), |
| 598 | .age = Age::Normal, | 686 | .age = static_cast<u32>(Age::Normal), |
| 599 | .race = Race::Black, | 687 | .race = static_cast<u32>(Race::Black), |
| 600 | .values_count = 20, | 688 | .values_count = 20, |
| 601 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, | 689 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, |
| 602 | }, | 690 | }, |
| 603 | Service::Mii::RandomMiiData4{ | 691 | RandomMiiData4{ |
| 604 | .gender = Gender::Male, | 692 | .gender = static_cast<u32>(Gender::Male), |
| 605 | .age = Age::Old, | 693 | .age = static_cast<u32>(Age::Old), |
| 606 | .race = Race::Black, | 694 | .race = static_cast<u32>(Race::Black), |
| 607 | .values_count = 20, | 695 | .values_count = 20, |
| 608 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8}, | 696 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8}, |
| 609 | }, | 697 | }, |
| 610 | Service::Mii::RandomMiiData4{ | 698 | RandomMiiData4{ |
| 611 | .gender = Gender::Male, | 699 | .gender = static_cast<u32>(Gender::Male), |
| 612 | .age = Age::Young, | 700 | .age = static_cast<u32>(Age::Young), |
| 613 | .race = Race::White, | 701 | .race = static_cast<u32>(Race::White), |
| 614 | .values_count = 20, | 702 | .values_count = 20, |
| 615 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, | 703 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, |
| 616 | }, | 704 | }, |
| 617 | Service::Mii::RandomMiiData4{ | 705 | RandomMiiData4{ |
| 618 | .gender = Gender::Male, | 706 | .gender = static_cast<u32>(Gender::Male), |
| 619 | .age = Age::Normal, | 707 | .age = static_cast<u32>(Age::Normal), |
| 620 | .race = Race::White, | 708 | .race = static_cast<u32>(Race::White), |
| 621 | .values_count = 20, | 709 | .values_count = 20, |
| 622 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, | 710 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, |
| 623 | }, | 711 | }, |
| 624 | Service::Mii::RandomMiiData4{ | 712 | RandomMiiData4{ |
| 625 | .gender = Gender::Male, | 713 | .gender = static_cast<u32>(Gender::Male), |
| 626 | .age = Age::Old, | 714 | .age = static_cast<u32>(Age::Old), |
| 627 | .race = Race::White, | 715 | .race = static_cast<u32>(Race::White), |
| 628 | .values_count = 20, | 716 | .values_count = 20, |
| 629 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, | 717 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
| 630 | }, | 718 | }, |
| 631 | Service::Mii::RandomMiiData4{ | 719 | RandomMiiData4{ |
| 632 | .gender = Gender::Male, | 720 | .gender = static_cast<u32>(Gender::Male), |
| 633 | .age = Age::Young, | 721 | .age = static_cast<u32>(Age::Young), |
| 634 | .race = Race::Asian, | 722 | .race = static_cast<u32>(Race::Asian), |
| 635 | .values_count = 20, | 723 | .values_count = 20, |
| 636 | .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, | 724 | .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, |
| 637 | }, | 725 | }, |
| 638 | Service::Mii::RandomMiiData4{ | 726 | RandomMiiData4{ |
| 639 | .gender = Gender::Male, | 727 | .gender = static_cast<u32>(Gender::Male), |
| 640 | .age = Age::Normal, | 728 | .age = static_cast<u32>(Age::Normal), |
| 641 | .race = Race::Asian, | 729 | .race = static_cast<u32>(Race::Asian), |
| 642 | .values_count = 20, | 730 | .values_count = 20, |
| 643 | .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, | 731 | .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, |
| 644 | }, | 732 | }, |
| 645 | Service::Mii::RandomMiiData4{ | 733 | RandomMiiData4{ |
| 646 | .gender = Gender::Male, | 734 | .gender = static_cast<u32>(Gender::Male), |
| 647 | .age = Age::Old, | 735 | .age = static_cast<u32>(Age::Old), |
| 648 | .race = Race::Asian, | 736 | .race = static_cast<u32>(Race::Asian), |
| 649 | .values_count = 20, | 737 | .values_count = 20, |
| 650 | .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, | 738 | .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, |
| 651 | }, | 739 | }, |
| 652 | Service::Mii::RandomMiiData4{ | 740 | RandomMiiData4{ |
| 653 | .gender = Gender::Female, | 741 | .gender = static_cast<u32>(Gender::Female), |
| 654 | .age = Age::Young, | 742 | .age = static_cast<u32>(Age::Young), |
| 655 | .race = Race::Black, | 743 | .race = static_cast<u32>(Race::Black), |
| 656 | .values_count = 20, | 744 | .values_count = 20, |
| 657 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, | 745 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, |
| 658 | }, | 746 | }, |
| 659 | Service::Mii::RandomMiiData4{ | 747 | RandomMiiData4{ |
| 660 | .gender = Gender::Female, | 748 | .gender = static_cast<u32>(Gender::Female), |
| 661 | .age = Age::Normal, | 749 | .age = static_cast<u32>(Age::Normal), |
| 662 | .race = Race::Black, | 750 | .race = static_cast<u32>(Race::Black), |
| 663 | .values_count = 20, | 751 | .values_count = 20, |
| 664 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, | 752 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, |
| 665 | }, | 753 | }, |
| 666 | Service::Mii::RandomMiiData4{ | 754 | RandomMiiData4{ |
| 667 | .gender = Gender::Female, | 755 | .gender = static_cast<u32>(Gender::Female), |
| 668 | .age = Age::Old, | 756 | .age = static_cast<u32>(Age::Old), |
| 669 | .race = Race::Black, | 757 | .race = static_cast<u32>(Race::Black), |
| 670 | .values_count = 20, | 758 | .values_count = 20, |
| 671 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, | 759 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, |
| 672 | }, | 760 | }, |
| 673 | Service::Mii::RandomMiiData4{ | 761 | RandomMiiData4{ |
| 674 | .gender = Gender::Female, | 762 | .gender = static_cast<u32>(Gender::Female), |
| 675 | .age = Age::Young, | 763 | .age = static_cast<u32>(Age::Young), |
| 676 | .race = Race::White, | 764 | .race = static_cast<u32>(Race::White), |
| 677 | .values_count = 20, | 765 | .values_count = 20, |
| 678 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, | 766 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, |
| 679 | }, | 767 | }, |
| 680 | Service::Mii::RandomMiiData4{ | 768 | RandomMiiData4{ |
| 681 | .gender = Gender::Female, | 769 | .gender = static_cast<u32>(Gender::Female), |
| 682 | .age = Age::Normal, | 770 | .age = static_cast<u32>(Age::Normal), |
| 683 | .race = Race::White, | 771 | .race = static_cast<u32>(Race::White), |
| 684 | .values_count = 20, | 772 | .values_count = 20, |
| 685 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, | 773 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, |
| 686 | }, | 774 | }, |
| 687 | Service::Mii::RandomMiiData4{ | 775 | RandomMiiData4{ |
| 688 | .gender = Gender::Female, | 776 | .gender = static_cast<u32>(Gender::Female), |
| 689 | .age = Age::Old, | 777 | .age = static_cast<u32>(Age::Old), |
| 690 | .race = Race::White, | 778 | .race = static_cast<u32>(Race::White), |
| 691 | .values_count = 20, | 779 | .values_count = 20, |
| 692 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4}, | 780 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4}, |
| 693 | }, | 781 | }, |
| 694 | Service::Mii::RandomMiiData4{ | 782 | RandomMiiData4{ |
| 695 | .gender = Gender::Female, | 783 | .gender = static_cast<u32>(Gender::Female), |
| 696 | .age = Age::Young, | 784 | .age = static_cast<u32>(Age::Young), |
| 697 | .race = Race::Asian, | 785 | .race = static_cast<u32>(Race::Asian), |
| 698 | .values_count = 20, | 786 | .values_count = 20, |
| 699 | .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, | 787 | .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, |
| 700 | }, | 788 | }, |
| 701 | Service::Mii::RandomMiiData4{ | 789 | RandomMiiData4{ |
| 702 | .gender = Gender::Female, | 790 | .gender = static_cast<u32>(Gender::Female), |
| 703 | .age = Age::Normal, | 791 | .age = static_cast<u32>(Age::Normal), |
| 704 | .race = Race::Asian, | 792 | .race = static_cast<u32>(Race::Asian), |
| 705 | .values_count = 20, | 793 | .values_count = 20, |
| 706 | .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, | 794 | .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, |
| 707 | }, | 795 | }, |
| 708 | Service::Mii::RandomMiiData4{ | 796 | RandomMiiData4{ |
| 709 | .gender = Gender::Female, | 797 | .gender = static_cast<u32>(Gender::Female), |
| 710 | .age = Age::Old, | 798 | .age = static_cast<u32>(Age::Old), |
| 711 | .race = Race::Asian, | 799 | .race = static_cast<u32>(Race::Asian), |
| 712 | .values_count = 20, | 800 | .values_count = 20, |
| 713 | .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, | 801 | .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, |
| 714 | }, | 802 | }, |
| 715 | }; | 803 | }; |
| 716 | 804 | ||
| 717 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup{ | 805 | const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup{ |
| 718 | Service::Mii::RandomMiiData4{ | 806 | RandomMiiData4{ |
| 719 | .gender = Gender::Male, | 807 | .gender = static_cast<u32>(Gender::Male), |
| 720 | .age = Age::Young, | 808 | .age = static_cast<u32>(Age::Young), |
| 721 | .race = Race::Black, | 809 | .race = static_cast<u32>(Race::Black), |
| 722 | .values_count = 20, | 810 | .values_count = 20, |
| 723 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | 811 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| 724 | }, | 812 | }, |
| 725 | Service::Mii::RandomMiiData4{ | 813 | RandomMiiData4{ |
| 726 | .gender = Gender::Male, | 814 | .gender = static_cast<u32>(Gender::Male), |
| 727 | .age = Age::Normal, | 815 | .age = static_cast<u32>(Age::Normal), |
| 728 | .race = Race::Black, | 816 | .race = static_cast<u32>(Race::Black), |
| 729 | .values_count = 20, | 817 | .values_count = 20, |
| 730 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9}, | 818 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9}, |
| 731 | }, | 819 | }, |
| 732 | Service::Mii::RandomMiiData4{ | 820 | RandomMiiData4{ |
| 733 | .gender = Gender::Male, | 821 | .gender = static_cast<u32>(Gender::Male), |
| 734 | .age = Age::Old, | 822 | .age = static_cast<u32>(Age::Old), |
| 735 | .race = Race::Black, | 823 | .race = static_cast<u32>(Race::Black), |
| 736 | .values_count = 20, | 824 | .values_count = 20, |
| 737 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9}, | 825 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9}, |
| 738 | }, | 826 | }, |
| 739 | Service::Mii::RandomMiiData4{ | 827 | RandomMiiData4{ |
| 740 | .gender = Gender::Male, | 828 | .gender = static_cast<u32>(Gender::Male), |
| 741 | .age = Age::Young, | 829 | .age = static_cast<u32>(Age::Young), |
| 742 | .race = Race::White, | 830 | .race = static_cast<u32>(Race::White), |
| 743 | .values_count = 20, | 831 | .values_count = 20, |
| 744 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, | 832 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, |
| 745 | }, | 833 | }, |
| 746 | Service::Mii::RandomMiiData4{ | 834 | RandomMiiData4{ |
| 747 | .gender = Gender::Male, | 835 | .gender = static_cast<u32>(Gender::Male), |
| 748 | .age = Age::Normal, | 836 | .age = static_cast<u32>(Age::Normal), |
| 749 | .race = Race::White, | 837 | .race = static_cast<u32>(Race::White), |
| 750 | .values_count = 20, | 838 | .values_count = 20, |
| 751 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, | 839 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, |
| 752 | }, | 840 | }, |
| 753 | Service::Mii::RandomMiiData4{ | 841 | RandomMiiData4{ |
| 754 | .gender = Gender::Male, | 842 | .gender = static_cast<u32>(Gender::Male), |
| 755 | .age = Age::Old, | 843 | .age = static_cast<u32>(Age::Old), |
| 756 | .race = Race::White, | 844 | .race = static_cast<u32>(Race::White), |
| 757 | .values_count = 20, | 845 | .values_count = 20, |
| 758 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, | 846 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, |
| 759 | }, | 847 | }, |
| 760 | Service::Mii::RandomMiiData4{ | 848 | RandomMiiData4{ |
| 761 | .gender = Gender::Male, | 849 | .gender = static_cast<u32>(Gender::Male), |
| 762 | .age = Age::Young, | 850 | .age = static_cast<u32>(Age::Young), |
| 763 | .race = Race::Asian, | 851 | .race = static_cast<u32>(Race::Asian), |
| 764 | .values_count = 20, | 852 | .values_count = 20, |
| 765 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, | 853 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, |
| 766 | }, | 854 | }, |
| 767 | Service::Mii::RandomMiiData4{ | 855 | RandomMiiData4{ |
| 768 | .gender = Gender::Male, | 856 | .gender = static_cast<u32>(Gender::Male), |
| 769 | .age = Age::Normal, | 857 | .age = static_cast<u32>(Age::Normal), |
| 770 | .race = Race::Asian, | 858 | .race = static_cast<u32>(Race::Asian), |
| 771 | .values_count = 20, | 859 | .values_count = 20, |
| 772 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, | 860 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, |
| 773 | }, | 861 | }, |
| 774 | Service::Mii::RandomMiiData4{ | 862 | RandomMiiData4{ |
| 775 | .gender = Gender::Male, | 863 | .gender = static_cast<u32>(Gender::Male), |
| 776 | .age = Age::Old, | 864 | .age = static_cast<u32>(Age::Old), |
| 777 | .race = Race::Asian, | 865 | .race = static_cast<u32>(Race::Asian), |
| 778 | .values_count = 20, | 866 | .values_count = 20, |
| 779 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, | 867 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, |
| 780 | }, | 868 | }, |
| 781 | Service::Mii::RandomMiiData4{ | 869 | RandomMiiData4{ |
| 782 | .gender = Gender::Female, | 870 | .gender = static_cast<u32>(Gender::Female), |
| 783 | .age = Age::Young, | 871 | .age = static_cast<u32>(Age::Young), |
| 784 | .race = Race::Black, | 872 | .race = static_cast<u32>(Race::Black), |
| 785 | .values_count = 20, | 873 | .values_count = 20, |
| 786 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2}, | 874 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2}, |
| 787 | }, | 875 | }, |
| 788 | Service::Mii::RandomMiiData4{ | 876 | RandomMiiData4{ |
| 789 | .gender = Gender::Female, | 877 | .gender = static_cast<u32>(Gender::Female), |
| 790 | .age = Age::Normal, | 878 | .age = static_cast<u32>(Age::Normal), |
| 791 | .race = Race::Black, | 879 | .race = static_cast<u32>(Race::Black), |
| 792 | .values_count = 20, | 880 | .values_count = 20, |
| 793 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, | 881 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, |
| 794 | }, | 882 | }, |
| 795 | Service::Mii::RandomMiiData4{ | 883 | RandomMiiData4{ |
| 796 | .gender = Gender::Female, | 884 | .gender = static_cast<u32>(Gender::Female), |
| 797 | .age = Age::Old, | 885 | .age = static_cast<u32>(Age::Old), |
| 798 | .race = Race::Black, | 886 | .race = static_cast<u32>(Race::Black), |
| 799 | .values_count = 20, | 887 | .values_count = 20, |
| 800 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, | 888 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, |
| 801 | }, | 889 | }, |
| 802 | Service::Mii::RandomMiiData4{ | 890 | RandomMiiData4{ |
| 803 | .gender = Gender::Female, | 891 | .gender = static_cast<u32>(Gender::Female), |
| 804 | .age = Age::Young, | 892 | .age = static_cast<u32>(Age::Young), |
| 805 | .race = Race::White, | 893 | .race = static_cast<u32>(Race::White), |
| 806 | .values_count = 20, | 894 | .values_count = 20, |
| 807 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9}, | 895 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9}, |
| 808 | }, | 896 | }, |
| 809 | Service::Mii::RandomMiiData4{ | 897 | RandomMiiData4{ |
| 810 | .gender = Gender::Female, | 898 | .gender = static_cast<u32>(Gender::Female), |
| 811 | .age = Age::Normal, | 899 | .age = static_cast<u32>(Age::Normal), |
| 812 | .race = Race::White, | 900 | .race = static_cast<u32>(Race::White), |
| 813 | .values_count = 20, | 901 | .values_count = 20, |
| 814 | .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, | 902 | .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, |
| 815 | }, | 903 | }, |
| 816 | Service::Mii::RandomMiiData4{ | 904 | RandomMiiData4{ |
| 817 | .gender = Gender::Female, | 905 | .gender = static_cast<u32>(Gender::Female), |
| 818 | .age = Age::Old, | 906 | .age = static_cast<u32>(Age::Old), |
| 819 | .race = Race::White, | 907 | .race = static_cast<u32>(Race::White), |
| 820 | .values_count = 20, | 908 | .values_count = 20, |
| 821 | .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9}, | 909 | .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9}, |
| 822 | }, | 910 | }, |
| 823 | Service::Mii::RandomMiiData4{ | 911 | RandomMiiData4{ |
| 824 | .gender = Gender::Female, | 912 | .gender = static_cast<u32>(Gender::Female), |
| 825 | .age = Age::Young, | 913 | .age = static_cast<u32>(Age::Young), |
| 826 | .race = Race::Asian, | 914 | .race = static_cast<u32>(Race::Asian), |
| 827 | .values_count = 20, | 915 | .values_count = 20, |
| 828 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, | 916 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
| 829 | }, | 917 | }, |
| 830 | Service::Mii::RandomMiiData4{ | 918 | RandomMiiData4{ |
| 831 | .gender = Gender::Female, | 919 | .gender = static_cast<u32>(Gender::Female), |
| 832 | .age = Age::Normal, | 920 | .age = static_cast<u32>(Age::Normal), |
| 833 | .race = Race::Asian, | 921 | .race = static_cast<u32>(Race::Asian), |
| 834 | .values_count = 20, | 922 | .values_count = 20, |
| 835 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, | 923 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
| 836 | }, | 924 | }, |
| 837 | Service::Mii::RandomMiiData4{ | 925 | RandomMiiData4{ |
| 838 | .gender = Gender::Female, | 926 | .gender = static_cast<u32>(Gender::Female), |
| 839 | .age = Age::Old, | 927 | .age = static_cast<u32>(Age::Old), |
| 840 | .race = Race::Asian, | 928 | .race = static_cast<u32>(Race::Asian), |
| 841 | .values_count = 20, | 929 | .values_count = 20, |
| 842 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, | 930 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
| 843 | }, | 931 | }, |
| 844 | }; | 932 | }; |
| 845 | 933 | ||
| 846 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{ | 934 | const std::array<RandomMiiData4, 18> RandomMiiHairType{ |
| 847 | Service::Mii::RandomMiiData4{ | 935 | RandomMiiData4{ |
| 848 | .gender = Gender::Male, | 936 | .gender = static_cast<u32>(Gender::Male), |
| 849 | .age = Age::Young, | 937 | .age = static_cast<u32>(Age::Young), |
| 850 | .race = Race::Black, | 938 | .race = static_cast<u32>(Race::Black), |
| 851 | .values_count = 30, | 939 | .values_count = 30, |
| 852 | .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, | 940 | .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, |
| 853 | 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 75, 76, 86, 89}, | 941 | 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 75, 76, 86, 89}, |
| 854 | }, | 942 | }, |
| 855 | Service::Mii::RandomMiiData4{ | 943 | RandomMiiData4{ |
| 856 | .gender = Gender::Male, | 944 | .gender = static_cast<u32>(Gender::Male), |
| 857 | .age = Age::Normal, | 945 | .age = static_cast<u32>(Age::Normal), |
| 858 | .race = Race::Black, | 946 | .race = static_cast<u32>(Race::Black), |
| 859 | .values_count = 31, | 947 | .values_count = 31, |
| 860 | .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, | 948 | .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, |
| 861 | 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, | 949 | 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, |
| 862 | }, | 950 | }, |
| 863 | Service::Mii::RandomMiiData4{ | 951 | RandomMiiData4{ |
| 864 | .gender = Gender::Male, | 952 | .gender = static_cast<u32>(Gender::Male), |
| 865 | .age = Age::Old, | 953 | .age = static_cast<u32>(Age::Old), |
| 866 | .race = Race::Black, | 954 | .race = static_cast<u32>(Race::Black), |
| 867 | .values_count = 31, | 955 | .values_count = 31, |
| 868 | .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, | 956 | .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, |
| 869 | 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, | 957 | 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, |
| 870 | }, | 958 | }, |
| 871 | Service::Mii::RandomMiiData4{ | 959 | RandomMiiData4{ |
| 872 | .gender = Gender::Male, | 960 | .gender = static_cast<u32>(Gender::Male), |
| 873 | .age = Age::Young, | 961 | .age = static_cast<u32>(Age::Young), |
| 874 | .race = Race::White, | 962 | .race = static_cast<u32>(Race::White), |
| 875 | .values_count = 38, | 963 | .values_count = 38, |
| 876 | .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50, | 964 | .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50, |
| 877 | 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 75, 76, 86, 89}, | 965 | 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 75, 76, 86, 89}, |
| 878 | }, | 966 | }, |
| 879 | Service::Mii::RandomMiiData4{ | 967 | RandomMiiData4{ |
| 880 | .gender = Gender::Male, | 968 | .gender = static_cast<u32>(Gender::Male), |
| 881 | .age = Age::Normal, | 969 | .age = static_cast<u32>(Age::Normal), |
| 882 | .race = Race::White, | 970 | .race = static_cast<u32>(Race::White), |
| 883 | .values_count = 39, | 971 | .values_count = 39, |
| 884 | .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, | 972 | .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, |
| 885 | 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, | 973 | 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, |
| 886 | }, | 974 | }, |
| 887 | Service::Mii::RandomMiiData4{ | 975 | RandomMiiData4{ |
| 888 | .gender = Gender::Male, | 976 | .gender = static_cast<u32>(Gender::Male), |
| 889 | .age = Age::Old, | 977 | .age = static_cast<u32>(Age::Old), |
| 890 | .race = Race::White, | 978 | .race = static_cast<u32>(Race::White), |
| 891 | .values_count = 39, | 979 | .values_count = 39, |
| 892 | .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, | 980 | .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, |
| 893 | 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, | 981 | 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, |
| 894 | }, | 982 | }, |
| 895 | Service::Mii::RandomMiiData4{ | 983 | RandomMiiData4{ |
| 896 | .gender = Gender::Male, | 984 | .gender = static_cast<u32>(Gender::Male), |
| 897 | .age = Age::Young, | 985 | .age = static_cast<u32>(Age::Young), |
| 898 | .race = Race::Asian, | 986 | .race = static_cast<u32>(Race::Asian), |
| 899 | .values_count = 18, | 987 | .values_count = 18, |
| 900 | .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, | 988 | .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, |
| 901 | }, | 989 | }, |
| 902 | Service::Mii::RandomMiiData4{ | 990 | RandomMiiData4{ |
| 903 | .gender = Gender::Male, | 991 | .gender = static_cast<u32>(Gender::Male), |
| 904 | .age = Age::Normal, | 992 | .age = static_cast<u32>(Age::Normal), |
| 905 | .race = Race::Asian, | 993 | .race = static_cast<u32>(Race::Asian), |
| 906 | .values_count = 19, | 994 | .values_count = 19, |
| 907 | .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, | 995 | .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, |
| 908 | }, | 996 | }, |
| 909 | Service::Mii::RandomMiiData4{ | 997 | RandomMiiData4{ |
| 910 | .gender = Gender::Male, | 998 | .gender = static_cast<u32>(Gender::Male), |
| 911 | .age = Age::Old, | 999 | .age = static_cast<u32>(Age::Old), |
| 912 | .race = Race::Asian, | 1000 | .race = static_cast<u32>(Race::Asian), |
| 913 | .values_count = 19, | 1001 | .values_count = 19, |
| 914 | .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, | 1002 | .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, |
| 915 | }, | 1003 | }, |
| 916 | Service::Mii::RandomMiiData4{ | 1004 | RandomMiiData4{ |
| 917 | .gender = Gender::Female, | 1005 | .gender = static_cast<u32>(Gender::Female), |
| 918 | .age = Age::Young, | 1006 | .age = static_cast<u32>(Age::Young), |
| 919 | .race = Race::Black, | 1007 | .race = static_cast<u32>(Race::Black), |
| 920 | .values_count = 39, | 1008 | .values_count = 39, |
| 921 | .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, | 1009 | .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, |
| 922 | 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 76, 77, 79, 80, 83, 85}, | 1010 | 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 76, 77, 79, 80, 83, 85}, |
| 923 | }, | 1011 | }, |
| 924 | Service::Mii::RandomMiiData4{ | 1012 | RandomMiiData4{ |
| 925 | .gender = Gender::Female, | 1013 | .gender = static_cast<u32>(Gender::Female), |
| 926 | .age = Age::Normal, | 1014 | .age = static_cast<u32>(Age::Normal), |
| 927 | .race = Race::Black, | 1015 | .race = static_cast<u32>(Race::Black), |
| 928 | .values_count = 42, | 1016 | .values_count = 42, |
| 929 | .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, | 1017 | .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, |
| 930 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, | 1018 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, |
| 931 | 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, | 1019 | 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, |
| 932 | }, | 1020 | }, |
| 933 | Service::Mii::RandomMiiData4{ | 1021 | RandomMiiData4{ |
| 934 | .gender = Gender::Female, | 1022 | .gender = static_cast<u32>(Gender::Female), |
| 935 | .age = Age::Old, | 1023 | .age = static_cast<u32>(Age::Old), |
| 936 | .race = Race::Black, | 1024 | .race = static_cast<u32>(Race::Black), |
| 937 | .values_count = 42, | 1025 | .values_count = 42, |
| 938 | .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, | 1026 | .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, |
| 939 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, | 1027 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, |
| 940 | 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, | 1028 | 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, |
| 941 | }, | 1029 | }, |
| 942 | Service::Mii::RandomMiiData4{ | 1030 | RandomMiiData4{ |
| 943 | .gender = Gender::Female, | 1031 | .gender = static_cast<u32>(Gender::Female), |
| 944 | .age = Age::Young, | 1032 | .age = static_cast<u32>(Age::Young), |
| 945 | .race = Race::White, | 1033 | .race = static_cast<u32>(Race::White), |
| 946 | .values_count = 44, | 1034 | .values_count = 44, |
| 947 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | 1035 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 948 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50, | 1036 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50, |
| 949 | 58, 60, 62, 63, 64, 69, 71, 76, 79, 80, 81, 82, 83, 86}, | 1037 | 58, 60, 62, 63, 64, 69, 71, 76, 79, 80, 81, 82, 83, 86}, |
| 950 | }, | 1038 | }, |
| 951 | Service::Mii::RandomMiiData4{ | 1039 | RandomMiiData4{ |
| 952 | .gender = Gender::Female, | 1040 | .gender = static_cast<u32>(Gender::Female), |
| 953 | .age = Age::Normal, | 1041 | .age = static_cast<u32>(Age::Normal), |
| 954 | .race = Race::White, | 1042 | .race = static_cast<u32>(Race::White), |
| 955 | .values_count = 44, | 1043 | .values_count = 44, |
| 956 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | 1044 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 957 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, | 1045 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, |
| 958 | 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, | 1046 | 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, |
| 959 | }, | 1047 | }, |
| 960 | Service::Mii::RandomMiiData4{ | 1048 | RandomMiiData4{ |
| 961 | .gender = Gender::Female, | 1049 | .gender = static_cast<u32>(Gender::Female), |
| 962 | .age = Age::Old, | 1050 | .age = static_cast<u32>(Age::Old), |
| 963 | .race = Race::White, | 1051 | .race = static_cast<u32>(Race::White), |
| 964 | .values_count = 44, | 1052 | .values_count = 44, |
| 965 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | 1053 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 966 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, | 1054 | 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, |
| 967 | 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, | 1055 | 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, |
| 968 | }, | 1056 | }, |
| 969 | Service::Mii::RandomMiiData4{ | 1057 | RandomMiiData4{ |
| 970 | .gender = Gender::Female, | 1058 | .gender = static_cast<u32>(Gender::Female), |
| 971 | .age = Age::Young, | 1059 | .age = static_cast<u32>(Age::Young), |
| 972 | .race = Race::Asian, | 1060 | .race = static_cast<u32>(Race::Asian), |
| 973 | .values_count = 24, | 1061 | .values_count = 24, |
| 974 | .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, | 1062 | .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, |
| 975 | 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 76, 83}, | 1063 | 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 76, 83}, |
| 976 | }, | 1064 | }, |
| 977 | Service::Mii::RandomMiiData4{ | 1065 | RandomMiiData4{ |
| 978 | .gender = Gender::Female, | 1066 | .gender = static_cast<u32>(Gender::Female), |
| 979 | .age = Age::Normal, | 1067 | .age = static_cast<u32>(Age::Normal), |
| 980 | .race = Race::Asian, | 1068 | .race = static_cast<u32>(Race::Asian), |
| 981 | .values_count = 27, | 1069 | .values_count = 27, |
| 982 | .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, | 1070 | .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, |
| 983 | 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, | 1071 | 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, |
| 984 | }, | 1072 | }, |
| 985 | Service::Mii::RandomMiiData4{ | 1073 | RandomMiiData4{ |
| 986 | .gender = Gender::Female, | 1074 | .gender = static_cast<u32>(Gender::Female), |
| 987 | .age = Age::Old, | 1075 | .age = static_cast<u32>(Age::Old), |
| 988 | .race = Race::Asian, | 1076 | .race = static_cast<u32>(Race::Asian), |
| 989 | .values_count = 27, | 1077 | .values_count = 27, |
| 990 | .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, | 1078 | .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, |
| 991 | 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, | 1079 | 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, |
| @@ -993,55 +1081,55 @@ const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{ | |||
| 993 | }; | 1081 | }; |
| 994 | 1082 | ||
| 995 | const std::array<RandomMiiData3, 9> RandomMiiHairColor{ | 1083 | const std::array<RandomMiiData3, 9> RandomMiiHairColor{ |
| 996 | Service::Mii::RandomMiiData3{ | 1084 | RandomMiiData3{ |
| 997 | .arg_1 = 0, | 1085 | .arg_1 = 0, |
| 998 | .arg_2 = 0, | 1086 | .arg_2 = 0, |
| 999 | .values_count = 20, | 1087 | .values_count = 20, |
| 1000 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | 1088 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| 1001 | }, | 1089 | }, |
| 1002 | Service::Mii::RandomMiiData3{ | 1090 | RandomMiiData3{ |
| 1003 | .arg_1 = 0, | 1091 | .arg_1 = 0, |
| 1004 | .arg_2 = 1, | 1092 | .arg_2 = 1, |
| 1005 | .values_count = 20, | 1093 | .values_count = 20, |
| 1006 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | 1094 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| 1007 | }, | 1095 | }, |
| 1008 | Service::Mii::RandomMiiData3{ | 1096 | RandomMiiData3{ |
| 1009 | .arg_1 = 0, | 1097 | .arg_1 = 0, |
| 1010 | .arg_2 = 2, | 1098 | .arg_2 = 2, |
| 1011 | .values_count = 20, | 1099 | .values_count = 20, |
| 1012 | .values = {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, | 1100 | .values = {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, |
| 1013 | }, | 1101 | }, |
| 1014 | Service::Mii::RandomMiiData3{ | 1102 | RandomMiiData3{ |
| 1015 | .arg_1 = 1, | 1103 | .arg_1 = 1, |
| 1016 | .arg_2 = 0, | 1104 | .arg_2 = 0, |
| 1017 | .values_count = 20, | 1105 | .values_count = 20, |
| 1018 | .values = {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7}, | 1106 | .values = {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7}, |
| 1019 | }, | 1107 | }, |
| 1020 | Service::Mii::RandomMiiData3{ | 1108 | RandomMiiData3{ |
| 1021 | .arg_1 = 1, | 1109 | .arg_1 = 1, |
| 1022 | .arg_2 = 1, | 1110 | .arg_2 = 1, |
| 1023 | .values_count = 20, | 1111 | .values_count = 20, |
| 1024 | .values = {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, | 1112 | .values = {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, |
| 1025 | }, | 1113 | }, |
| 1026 | Service::Mii::RandomMiiData3{ | 1114 | RandomMiiData3{ |
| 1027 | .arg_1 = 1, | 1115 | .arg_1 = 1, |
| 1028 | .arg_2 = 2, | 1116 | .arg_2 = 2, |
| 1029 | .values_count = 20, | 1117 | .values_count = 20, |
| 1030 | .values = {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, | 1118 | .values = {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, |
| 1031 | }, | 1119 | }, |
| 1032 | Service::Mii::RandomMiiData3{ | 1120 | RandomMiiData3{ |
| 1033 | .arg_1 = 2, | 1121 | .arg_1 = 2, |
| 1034 | .arg_2 = 0, | 1122 | .arg_2 = 0, |
| 1035 | .values_count = 20, | 1123 | .values_count = 20, |
| 1036 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, | 1124 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, |
| 1037 | }, | 1125 | }, |
| 1038 | Service::Mii::RandomMiiData3{ | 1126 | RandomMiiData3{ |
| 1039 | .arg_1 = 2, | 1127 | .arg_1 = 2, |
| 1040 | .arg_2 = 1, | 1128 | .arg_2 = 1, |
| 1041 | .values_count = 20, | 1129 | .values_count = 20, |
| 1042 | .values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3}, | 1130 | .values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3}, |
| 1043 | }, | 1131 | }, |
| 1044 | Service::Mii::RandomMiiData3{ | 1132 | RandomMiiData3{ |
| 1045 | .arg_1 = 2, | 1133 | .arg_1 = 2, |
| 1046 | .arg_2 = 2, | 1134 | .arg_2 = 2, |
| 1047 | .values_count = 20, | 1135 | .values_count = 20, |
| @@ -1049,598 +1137,642 @@ const std::array<RandomMiiData3, 9> RandomMiiHairColor{ | |||
| 1049 | }, | 1137 | }, |
| 1050 | }; | 1138 | }; |
| 1051 | 1139 | ||
| 1052 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType{ | 1140 | const std::array<RandomMiiData4, 18> RandomMiiEyeType{ |
| 1053 | Service::Mii::RandomMiiData4{ | 1141 | RandomMiiData4{ |
| 1054 | .gender = Gender::Male, | 1142 | .gender = static_cast<u32>(Gender::Male), |
| 1055 | .age = Age::Young, | 1143 | .age = static_cast<u32>(Age::Young), |
| 1056 | .race = Race::Black, | 1144 | .race = static_cast<u32>(Race::Black), |
| 1057 | .values_count = 26, | 1145 | .values_count = 26, |
| 1058 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, | 1146 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, |
| 1059 | 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, | 1147 | 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, |
| 1060 | }, | 1148 | }, |
| 1061 | Service::Mii::RandomMiiData4{ | 1149 | RandomMiiData4{ |
| 1062 | .gender = Gender::Male, | 1150 | .gender = static_cast<u32>(Gender::Male), |
| 1063 | .age = Age::Normal, | 1151 | .age = static_cast<u32>(Age::Normal), |
| 1064 | .race = Race::Black, | 1152 | .race = static_cast<u32>(Race::Black), |
| 1065 | .values_count = 26, | 1153 | .values_count = 26, |
| 1066 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, | 1154 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, |
| 1067 | 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, | 1155 | 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, |
| 1068 | }, | 1156 | }, |
| 1069 | Service::Mii::RandomMiiData4{ | 1157 | RandomMiiData4{ |
| 1070 | .gender = Gender::Male, | 1158 | .gender = static_cast<u32>(Gender::Male), |
| 1071 | .age = Age::Old, | 1159 | .age = static_cast<u32>(Age::Old), |
| 1072 | .race = Race::Black, | 1160 | .race = static_cast<u32>(Race::Black), |
| 1073 | .values_count = 27, | 1161 | .values_count = 27, |
| 1074 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27, | 1162 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27, |
| 1075 | 29, 32, 34, 36, 38, 39, 41, 43, 47, 48, 49, 53, 57}, | 1163 | 29, 32, 34, 36, 38, 39, 41, 43, 47, 48, 49, 53, 57}, |
| 1076 | }, | 1164 | }, |
| 1077 | Service::Mii::RandomMiiData4{ | 1165 | RandomMiiData4{ |
| 1078 | .gender = Gender::Male, | 1166 | .gender = static_cast<u32>(Gender::Male), |
| 1079 | .age = Age::Young, | 1167 | .age = static_cast<u32>(Age::Young), |
| 1080 | .race = Race::White, | 1168 | .race = static_cast<u32>(Race::White), |
| 1081 | .values_count = 35, | 1169 | .values_count = 35, |
| 1082 | .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, | 1170 | .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, |
| 1083 | 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, | 1171 | 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, |
| 1084 | }, | 1172 | }, |
| 1085 | Service::Mii::RandomMiiData4{ | 1173 | RandomMiiData4{ |
| 1086 | .gender = Gender::Male, | 1174 | .gender = static_cast<u32>(Gender::Male), |
| 1087 | .age = Age::Normal, | 1175 | .age = static_cast<u32>(Age::Normal), |
| 1088 | .race = Race::White, | 1176 | .race = static_cast<u32>(Race::White), |
| 1089 | .values_count = 35, | 1177 | .values_count = 35, |
| 1090 | .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, | 1178 | .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, |
| 1091 | 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, | 1179 | 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, |
| 1092 | }, | 1180 | }, |
| 1093 | Service::Mii::RandomMiiData4{ | 1181 | RandomMiiData4{ |
| 1094 | .gender = Gender::Male, | 1182 | .gender = static_cast<u32>(Gender::Male), |
| 1095 | .age = Age::Old, | 1183 | .age = static_cast<u32>(Age::Old), |
| 1096 | .race = Race::White, | 1184 | .race = static_cast<u32>(Race::White), |
| 1097 | .values_count = 35, | 1185 | .values_count = 35, |
| 1098 | .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29, | 1186 | .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29, |
| 1099 | 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 48, 49, 50, 53, 56, 57}, | 1187 | 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 48, 49, 50, 53, 56, 57}, |
| 1100 | }, | 1188 | }, |
| 1101 | Service::Mii::RandomMiiData4{ | 1189 | RandomMiiData4{ |
| 1102 | .gender = Gender::Male, | 1190 | .gender = static_cast<u32>(Gender::Male), |
| 1103 | .age = Age::Young, | 1191 | .age = static_cast<u32>(Age::Young), |
| 1104 | .race = Race::Asian, | 1192 | .race = static_cast<u32>(Race::Asian), |
| 1105 | .values_count = 30, | 1193 | .values_count = 30, |
| 1106 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, | 1194 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, |
| 1107 | 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, | 1195 | 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, |
| 1108 | }, | 1196 | }, |
| 1109 | Service::Mii::RandomMiiData4{ | 1197 | RandomMiiData4{ |
| 1110 | .gender = Gender::Male, | 1198 | .gender = static_cast<u32>(Gender::Male), |
| 1111 | .age = Age::Normal, | 1199 | .age = static_cast<u32>(Age::Normal), |
| 1112 | .race = Race::Asian, | 1200 | .race = static_cast<u32>(Race::Asian), |
| 1113 | .values_count = 30, | 1201 | .values_count = 30, |
| 1114 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, | 1202 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, |
| 1115 | 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, | 1203 | 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, |
| 1116 | }, | 1204 | }, |
| 1117 | Service::Mii::RandomMiiData4{ | 1205 | RandomMiiData4{ |
| 1118 | .gender = Gender::Male, | 1206 | .gender = static_cast<u32>(Gender::Male), |
| 1119 | .age = Age::Old, | 1207 | .age = static_cast<u32>(Age::Old), |
| 1120 | .race = Race::Asian, | 1208 | .race = static_cast<u32>(Race::Asian), |
| 1121 | .values_count = 30, | 1209 | .values_count = 30, |
| 1122 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22, | 1210 | .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22, |
| 1123 | 26, 31, 32, 34, 36, 37, 39, 41, 44, 48, 49, 50, 51, 53, 57}, | 1211 | 26, 31, 32, 34, 36, 37, 39, 41, 44, 48, 49, 50, 51, 53, 57}, |
| 1124 | }, | 1212 | }, |
| 1125 | Service::Mii::RandomMiiData4{ | 1213 | RandomMiiData4{ |
| 1126 | .gender = Gender::Female, | 1214 | .gender = static_cast<u32>(Gender::Female), |
| 1127 | .age = Age::Young, | 1215 | .age = static_cast<u32>(Age::Young), |
| 1128 | .race = Race::Black, | 1216 | .race = static_cast<u32>(Race::Black), |
| 1129 | .values_count = 39, | 1217 | .values_count = 39, |
| 1130 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, | 1218 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, |
| 1131 | 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, | 1219 | 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, |
| 1132 | }, | 1220 | }, |
| 1133 | Service::Mii::RandomMiiData4{ | 1221 | RandomMiiData4{ |
| 1134 | .gender = Gender::Female, | 1222 | .gender = static_cast<u32>(Gender::Female), |
| 1135 | .age = Age::Normal, | 1223 | .age = static_cast<u32>(Age::Normal), |
| 1136 | .race = Race::Black, | 1224 | .race = static_cast<u32>(Race::Black), |
| 1137 | .values_count = 39, | 1225 | .values_count = 39, |
| 1138 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, | 1226 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, |
| 1139 | 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, | 1227 | 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, |
| 1140 | }, | 1228 | }, |
| 1141 | Service::Mii::RandomMiiData4{ | 1229 | RandomMiiData4{ |
| 1142 | .gender = Gender::Female, | 1230 | .gender = static_cast<u32>(Gender::Female), |
| 1143 | .age = Age::Old, | 1231 | .age = static_cast<u32>(Age::Old), |
| 1144 | .race = Race::Black, | 1232 | .race = static_cast<u32>(Race::Black), |
| 1145 | .values_count = 40, | 1233 | .values_count = 40, |
| 1146 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26, | 1234 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26, |
| 1147 | 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, | 1235 | 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, |
| 1148 | }, | 1236 | }, |
| 1149 | Service::Mii::RandomMiiData4{ | 1237 | RandomMiiData4{ |
| 1150 | .gender = Gender::Female, | 1238 | .gender = static_cast<u32>(Gender::Female), |
| 1151 | .age = Age::Young, | 1239 | .age = static_cast<u32>(Age::Young), |
| 1152 | .race = Race::White, | 1240 | .race = static_cast<u32>(Race::White), |
| 1153 | .values_count = 46, | 1241 | .values_count = 46, |
| 1154 | .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, | 1242 | .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, |
| 1155 | 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, | 1243 | 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, |
| 1156 | 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, | 1244 | 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, |
| 1157 | }, | 1245 | }, |
| 1158 | Service::Mii::RandomMiiData4{ | 1246 | RandomMiiData4{ |
| 1159 | .gender = Gender::Female, | 1247 | .gender = static_cast<u32>(Gender::Female), |
| 1160 | .age = Age::Normal, | 1248 | .age = static_cast<u32>(Age::Normal), |
| 1161 | .race = Race::White, | 1249 | .race = static_cast<u32>(Race::White), |
| 1162 | .values_count = 46, | 1250 | .values_count = 46, |
| 1163 | .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, | 1251 | .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, |
| 1164 | 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, | 1252 | 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, |
| 1165 | 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, | 1253 | 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, |
| 1166 | }, | 1254 | }, |
| 1167 | Service::Mii::RandomMiiData4{ | 1255 | RandomMiiData4{ |
| 1168 | .gender = Gender::Female, | 1256 | .gender = static_cast<u32>(Gender::Female), |
| 1169 | .age = Age::Old, | 1257 | .age = static_cast<u32>(Age::Old), |
| 1170 | .race = Race::White, | 1258 | .race = static_cast<u32>(Race::White), |
| 1171 | .values_count = 46, | 1259 | .values_count = 46, |
| 1172 | .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, | 1260 | .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, |
| 1173 | 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37, | 1261 | 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37, |
| 1174 | 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, | 1262 | 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, |
| 1175 | }, | 1263 | }, |
| 1176 | Service::Mii::RandomMiiData4{ | 1264 | RandomMiiData4{ |
| 1177 | .gender = Gender::Female, | 1265 | .gender = static_cast<u32>(Gender::Female), |
| 1178 | .age = Age::Young, | 1266 | .age = static_cast<u32>(Age::Young), |
| 1179 | .race = Race::Asian, | 1267 | .race = static_cast<u32>(Race::Asian), |
| 1180 | .values_count = 34, | 1268 | .values_count = 34, |
| 1181 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, | 1269 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, |
| 1182 | 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, | 1270 | 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, |
| 1183 | }, | 1271 | }, |
| 1184 | Service::Mii::RandomMiiData4{ | 1272 | RandomMiiData4{ |
| 1185 | .gender = Gender::Female, | 1273 | .gender = static_cast<u32>(Gender::Female), |
| 1186 | .age = Age::Normal, | 1274 | .age = static_cast<u32>(Age::Normal), |
| 1187 | .race = Race::Asian, | 1275 | .race = static_cast<u32>(Race::Asian), |
| 1188 | .values_count = 34, | 1276 | .values_count = 34, |
| 1189 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, | 1277 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, |
| 1190 | 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, | 1278 | 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, |
| 1191 | }, | 1279 | }, |
| 1192 | Service::Mii::RandomMiiData4{ | 1280 | RandomMiiData4{ |
| 1193 | .gender = Gender::Female, | 1281 | .gender = static_cast<u32>(Gender::Female), |
| 1194 | .age = Age::Old, | 1282 | .age = static_cast<u32>(Age::Old), |
| 1195 | .race = Race::Asian, | 1283 | .race = static_cast<u32>(Race::Asian), |
| 1196 | .values_count = 35, | 1284 | .values_count = 35, |
| 1197 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, | 1285 | .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, |
| 1198 | 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, | 1286 | 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, |
| 1199 | }, | 1287 | }, |
| 1200 | }; | 1288 | }; |
| 1201 | 1289 | ||
| 1202 | const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor{ | 1290 | const std::array<RandomMiiData2, 3> RandomMiiEyeColor{ |
| 1203 | Service::Mii::RandomMiiData2{ | 1291 | RandomMiiData2{ |
| 1204 | .arg_1 = 0, | 1292 | .arg_1 = 0, |
| 1205 | .values_count = 10, | 1293 | .values_count = 10, |
| 1206 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | 1294 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, |
| 1207 | }, | 1295 | }, |
| 1208 | Service::Mii::RandomMiiData2{ | 1296 | RandomMiiData2{ |
| 1209 | .arg_1 = 1, | 1297 | .arg_1 = 1, |
| 1210 | .values_count = 10, | 1298 | .values_count = 10, |
| 1211 | .values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5}, | 1299 | .values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5}, |
| 1212 | }, | 1300 | }, |
| 1213 | Service::Mii::RandomMiiData2{ | 1301 | RandomMiiData2{ |
| 1214 | .arg_1 = 2, | 1302 | .arg_1 = 2, |
| 1215 | .values_count = 10, | 1303 | .values_count = 10, |
| 1216 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | 1304 | .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, |
| 1217 | }, | 1305 | }, |
| 1218 | }; | 1306 | }; |
| 1219 | 1307 | ||
| 1220 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType{ | 1308 | const std::array<RandomMiiData4, 18> RandomMiiEyebrowType{ |
| 1221 | Service::Mii::RandomMiiData4{ | 1309 | RandomMiiData4{ |
| 1222 | .gender = Gender::Male, | 1310 | .gender = static_cast<u32>(Gender::Male), |
| 1223 | .age = Age::Young, | 1311 | .age = static_cast<u32>(Age::Young), |
| 1224 | .race = Race::Black, | 1312 | .race = static_cast<u32>(Race::Black), |
| 1225 | .values_count = 18, | 1313 | .values_count = 18, |
| 1226 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, | 1314 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, |
| 1227 | }, | 1315 | }, |
| 1228 | Service::Mii::RandomMiiData4{ | 1316 | RandomMiiData4{ |
| 1229 | .gender = Gender::Male, | 1317 | .gender = static_cast<u32>(Gender::Male), |
| 1230 | .age = Age::Normal, | 1318 | .age = static_cast<u32>(Age::Normal), |
| 1231 | .race = Race::Black, | 1319 | .race = static_cast<u32>(Race::Black), |
| 1232 | .values_count = 18, | 1320 | .values_count = 18, |
| 1233 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, | 1321 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, |
| 1234 | }, | 1322 | }, |
| 1235 | Service::Mii::RandomMiiData4{ | 1323 | RandomMiiData4{ |
| 1236 | .gender = Gender::Male, | 1324 | .gender = static_cast<u32>(Gender::Male), |
| 1237 | .age = Age::Old, | 1325 | .age = static_cast<u32>(Age::Old), |
| 1238 | .race = Race::Black, | 1326 | .race = static_cast<u32>(Race::Black), |
| 1239 | .values_count = 18, | 1327 | .values_count = 18, |
| 1240 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, | 1328 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, |
| 1241 | }, | 1329 | }, |
| 1242 | Service::Mii::RandomMiiData4{ | 1330 | RandomMiiData4{ |
| 1243 | .gender = Gender::Male, | 1331 | .gender = static_cast<u32>(Gender::Male), |
| 1244 | .age = Age::Young, | 1332 | .age = static_cast<u32>(Age::Young), |
| 1245 | .race = Race::White, | 1333 | .race = static_cast<u32>(Race::White), |
| 1246 | .values_count = 23, | 1334 | .values_count = 23, |
| 1247 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | 1335 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
| 1248 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, | 1336 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, |
| 1249 | }, | 1337 | }, |
| 1250 | Service::Mii::RandomMiiData4{ | 1338 | RandomMiiData4{ |
| 1251 | .gender = Gender::Male, | 1339 | .gender = static_cast<u32>(Gender::Male), |
| 1252 | .age = Age::Normal, | 1340 | .age = static_cast<u32>(Age::Normal), |
| 1253 | .race = Race::White, | 1341 | .race = static_cast<u32>(Race::White), |
| 1254 | .values_count = 23, | 1342 | .values_count = 23, |
| 1255 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | 1343 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
| 1256 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, | 1344 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, |
| 1257 | }, | 1345 | }, |
| 1258 | Service::Mii::RandomMiiData4{ | 1346 | RandomMiiData4{ |
| 1259 | .gender = Gender::Male, | 1347 | .gender = static_cast<u32>(Gender::Male), |
| 1260 | .age = Age::Old, | 1348 | .age = static_cast<u32>(Age::Old), |
| 1261 | .race = Race::White, | 1349 | .race = static_cast<u32>(Race::White), |
| 1262 | .values_count = 23, | 1350 | .values_count = 23, |
| 1263 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | 1351 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
| 1264 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, | 1352 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, |
| 1265 | }, | 1353 | }, |
| 1266 | Service::Mii::RandomMiiData4{ | 1354 | RandomMiiData4{ |
| 1267 | .gender = Gender::Male, | 1355 | .gender = static_cast<u32>(Gender::Male), |
| 1268 | .age = Age::Young, | 1356 | .age = static_cast<u32>(Age::Young), |
| 1269 | .race = Race::Asian, | 1357 | .race = static_cast<u32>(Race::Asian), |
| 1270 | .values_count = 21, | 1358 | .values_count = 21, |
| 1271 | .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, | 1359 | .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, |
| 1272 | }, | 1360 | }, |
| 1273 | Service::Mii::RandomMiiData4{ | 1361 | RandomMiiData4{ |
| 1274 | .gender = Gender::Male, | 1362 | .gender = static_cast<u32>(Gender::Male), |
| 1275 | .age = Age::Normal, | 1363 | .age = static_cast<u32>(Age::Normal), |
| 1276 | .race = Race::Asian, | 1364 | .race = static_cast<u32>(Race::Asian), |
| 1277 | .values_count = 21, | 1365 | .values_count = 21, |
| 1278 | .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, | 1366 | .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, |
| 1279 | }, | 1367 | }, |
| 1280 | Service::Mii::RandomMiiData4{ | 1368 | RandomMiiData4{ |
| 1281 | .gender = Gender::Male, | 1369 | .gender = static_cast<u32>(Gender::Male), |
| 1282 | .age = Age::Old, | 1370 | .age = static_cast<u32>(Age::Old), |
| 1283 | .race = Race::Asian, | 1371 | .race = static_cast<u32>(Race::Asian), |
| 1284 | .values_count = 21, | 1372 | .values_count = 21, |
| 1285 | .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, | 1373 | .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, |
| 1286 | }, | 1374 | }, |
| 1287 | Service::Mii::RandomMiiData4{ | 1375 | RandomMiiData4{ |
| 1288 | .gender = Gender::Female, | 1376 | .gender = static_cast<u32>(Gender::Female), |
| 1289 | .age = Age::Young, | 1377 | .age = static_cast<u32>(Age::Young), |
| 1290 | .race = Race::Black, | 1378 | .race = static_cast<u32>(Race::Black), |
| 1291 | .values_count = 9, | 1379 | .values_count = 9, |
| 1292 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, | 1380 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, |
| 1293 | }, | 1381 | }, |
| 1294 | Service::Mii::RandomMiiData4{ | 1382 | RandomMiiData4{ |
| 1295 | .gender = Gender::Female, | 1383 | .gender = static_cast<u32>(Gender::Female), |
| 1296 | .age = Age::Normal, | 1384 | .age = static_cast<u32>(Age::Normal), |
| 1297 | .race = Race::Black, | 1385 | .race = static_cast<u32>(Race::Black), |
| 1298 | .values_count = 9, | 1386 | .values_count = 9, |
| 1299 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, | 1387 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, |
| 1300 | }, | 1388 | }, |
| 1301 | Service::Mii::RandomMiiData4{ | 1389 | RandomMiiData4{ |
| 1302 | .gender = Gender::Female, | 1390 | .gender = static_cast<u32>(Gender::Female), |
| 1303 | .age = Age::Old, | 1391 | .age = static_cast<u32>(Age::Old), |
| 1304 | .race = Race::Black, | 1392 | .race = static_cast<u32>(Race::Black), |
| 1305 | .values_count = 9, | 1393 | .values_count = 9, |
| 1306 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, | 1394 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, |
| 1307 | }, | 1395 | }, |
| 1308 | Service::Mii::RandomMiiData4{ | 1396 | RandomMiiData4{ |
| 1309 | .gender = Gender::Female, | 1397 | .gender = static_cast<u32>(Gender::Female), |
| 1310 | .age = Age::Young, | 1398 | .age = static_cast<u32>(Age::Young), |
| 1311 | .race = Race::White, | 1399 | .race = static_cast<u32>(Race::White), |
| 1312 | .values_count = 11, | 1400 | .values_count = 11, |
| 1313 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, | 1401 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, |
| 1314 | }, | 1402 | }, |
| 1315 | Service::Mii::RandomMiiData4{ | 1403 | RandomMiiData4{ |
| 1316 | .gender = Gender::Female, | 1404 | .gender = static_cast<u32>(Gender::Female), |
| 1317 | .age = Age::Normal, | 1405 | .age = static_cast<u32>(Age::Normal), |
| 1318 | .race = Race::White, | 1406 | .race = static_cast<u32>(Race::White), |
| 1319 | .values_count = 11, | 1407 | .values_count = 11, |
| 1320 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, | 1408 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, |
| 1321 | }, | 1409 | }, |
| 1322 | Service::Mii::RandomMiiData4{ | 1410 | RandomMiiData4{ |
| 1323 | .gender = Gender::Female, | 1411 | .gender = static_cast<u32>(Gender::Female), |
| 1324 | .age = Age::Old, | 1412 | .age = static_cast<u32>(Age::Old), |
| 1325 | .race = Race::White, | 1413 | .race = static_cast<u32>(Race::White), |
| 1326 | .values_count = 11, | 1414 | .values_count = 11, |
| 1327 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, | 1415 | .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, |
| 1328 | }, | 1416 | }, |
| 1329 | Service::Mii::RandomMiiData4{ | 1417 | RandomMiiData4{ |
| 1330 | .gender = Gender::Female, | 1418 | .gender = static_cast<u32>(Gender::Female), |
| 1331 | .age = Age::Young, | 1419 | .age = static_cast<u32>(Age::Young), |
| 1332 | .race = Race::Asian, | 1420 | .race = static_cast<u32>(Race::Asian), |
| 1333 | .values_count = 9, | 1421 | .values_count = 9, |
| 1334 | .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, | 1422 | .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, |
| 1335 | }, | 1423 | }, |
| 1336 | Service::Mii::RandomMiiData4{ | 1424 | RandomMiiData4{ |
| 1337 | .gender = Gender::Female, | 1425 | .gender = static_cast<u32>(Gender::Female), |
| 1338 | .age = Age::Normal, | 1426 | .age = static_cast<u32>(Age::Normal), |
| 1339 | .race = Race::Asian, | 1427 | .race = static_cast<u32>(Race::Asian), |
| 1340 | .values_count = 9, | 1428 | .values_count = 9, |
| 1341 | .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, | 1429 | .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, |
| 1342 | }, | 1430 | }, |
| 1343 | Service::Mii::RandomMiiData4{ | 1431 | RandomMiiData4{ |
| 1344 | .gender = Gender::Female, | 1432 | .gender = static_cast<u32>(Gender::Female), |
| 1345 | .age = Age::Old, | 1433 | .age = static_cast<u32>(Age::Old), |
| 1346 | .race = Race::Asian, | 1434 | .race = static_cast<u32>(Race::Asian), |
| 1347 | .values_count = 9, | 1435 | .values_count = 9, |
| 1348 | .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, | 1436 | .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, |
| 1349 | }, | 1437 | }, |
| 1350 | }; | 1438 | }; |
| 1351 | 1439 | ||
| 1352 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType{ | 1440 | const std::array<RandomMiiData4, 18> RandomMiiNoseType{ |
| 1353 | Service::Mii::RandomMiiData4{ | 1441 | RandomMiiData4{ |
| 1354 | .gender = Gender::Male, | 1442 | .gender = static_cast<u32>(Gender::Male), |
| 1355 | .age = Age::Young, | 1443 | .age = static_cast<u32>(Age::Young), |
| 1356 | .race = Race::Black, | 1444 | .race = static_cast<u32>(Race::Black), |
| 1357 | .values_count = 11, | 1445 | .values_count = 11, |
| 1358 | .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, | 1446 | .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, |
| 1359 | }, | 1447 | }, |
| 1360 | Service::Mii::RandomMiiData4{ | 1448 | RandomMiiData4{ |
| 1361 | .gender = Gender::Male, | 1449 | .gender = static_cast<u32>(Gender::Male), |
| 1362 | .age = Age::Normal, | 1450 | .age = static_cast<u32>(Age::Normal), |
| 1363 | .race = Race::Black, | 1451 | .race = static_cast<u32>(Race::Black), |
| 1364 | .values_count = 11, | 1452 | .values_count = 11, |
| 1365 | .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, | 1453 | .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, |
| 1366 | }, | 1454 | }, |
| 1367 | Service::Mii::RandomMiiData4{ | 1455 | RandomMiiData4{ |
| 1368 | .gender = Gender::Male, | 1456 | .gender = static_cast<u32>(Gender::Male), |
| 1369 | .age = Age::Old, | 1457 | .age = static_cast<u32>(Age::Old), |
| 1370 | .race = Race::Black, | 1458 | .race = static_cast<u32>(Race::Black), |
| 1371 | .values_count = 11, | 1459 | .values_count = 11, |
| 1372 | .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, | 1460 | .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, |
| 1373 | }, | 1461 | }, |
| 1374 | Service::Mii::RandomMiiData4{ | 1462 | RandomMiiData4{ |
| 1375 | .gender = Gender::Male, | 1463 | .gender = static_cast<u32>(Gender::Male), |
| 1376 | .age = Age::Young, | 1464 | .age = static_cast<u32>(Age::Young), |
| 1377 | .race = Race::White, | 1465 | .race = static_cast<u32>(Race::White), |
| 1378 | .values_count = 18, | 1466 | .values_count = 18, |
| 1379 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, | 1467 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, |
| 1380 | }, | 1468 | }, |
| 1381 | Service::Mii::RandomMiiData4{ | 1469 | RandomMiiData4{ |
| 1382 | .gender = Gender::Male, | 1470 | .gender = static_cast<u32>(Gender::Male), |
| 1383 | .age = Age::Normal, | 1471 | .age = static_cast<u32>(Age::Normal), |
| 1384 | .race = Race::White, | 1472 | .race = static_cast<u32>(Race::White), |
| 1385 | .values_count = 18, | 1473 | .values_count = 18, |
| 1386 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, | 1474 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, |
| 1387 | }, | 1475 | }, |
| 1388 | Service::Mii::RandomMiiData4{ | 1476 | RandomMiiData4{ |
| 1389 | .gender = Gender::Male, | 1477 | .gender = static_cast<u32>(Gender::Male), |
| 1390 | .age = Age::Old, | 1478 | .age = static_cast<u32>(Age::Old), |
| 1391 | .race = Race::White, | 1479 | .race = static_cast<u32>(Race::White), |
| 1392 | .values_count = 15, | 1480 | .values_count = 15, |
| 1393 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, | 1481 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, |
| 1394 | }, | 1482 | }, |
| 1395 | Service::Mii::RandomMiiData4{ | 1483 | RandomMiiData4{ |
| 1396 | .gender = Gender::Male, | 1484 | .gender = static_cast<u32>(Gender::Male), |
| 1397 | .age = Age::Young, | 1485 | .age = static_cast<u32>(Age::Young), |
| 1398 | .race = Race::Asian, | 1486 | .race = static_cast<u32>(Race::Asian), |
| 1399 | .values_count = 18, | 1487 | .values_count = 18, |
| 1400 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, | 1488 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, |
| 1401 | }, | 1489 | }, |
| 1402 | Service::Mii::RandomMiiData4{ | 1490 | RandomMiiData4{ |
| 1403 | .gender = Gender::Male, | 1491 | .gender = static_cast<u32>(Gender::Male), |
| 1404 | .age = Age::Normal, | 1492 | .age = static_cast<u32>(Age::Normal), |
| 1405 | .race = Race::Asian, | 1493 | .race = static_cast<u32>(Race::Asian), |
| 1406 | .values_count = 18, | 1494 | .values_count = 18, |
| 1407 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, | 1495 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, |
| 1408 | }, | 1496 | }, |
| 1409 | Service::Mii::RandomMiiData4{ | 1497 | RandomMiiData4{ |
| 1410 | .gender = Gender::Male, | 1498 | .gender = static_cast<u32>(Gender::Male), |
| 1411 | .age = Age::Old, | 1499 | .age = static_cast<u32>(Age::Old), |
| 1412 | .race = Race::Asian, | 1500 | .race = static_cast<u32>(Race::Asian), |
| 1413 | .values_count = 15, | 1501 | .values_count = 15, |
| 1414 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, | 1502 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, |
| 1415 | }, | 1503 | }, |
| 1416 | Service::Mii::RandomMiiData4{ | 1504 | RandomMiiData4{ |
| 1417 | .gender = Gender::Female, | 1505 | .gender = static_cast<u32>(Gender::Female), |
| 1418 | .age = Age::Young, | 1506 | .age = static_cast<u32>(Age::Young), |
| 1419 | .race = Race::Black, | 1507 | .race = static_cast<u32>(Race::Black), |
| 1420 | .values_count = 8, | 1508 | .values_count = 8, |
| 1421 | .values = {0, 1, 3, 4, 8, 10, 13, 14}, | 1509 | .values = {0, 1, 3, 4, 8, 10, 13, 14}, |
| 1422 | }, | 1510 | }, |
| 1423 | Service::Mii::RandomMiiData4{ | 1511 | RandomMiiData4{ |
| 1424 | .gender = Gender::Female, | 1512 | .gender = static_cast<u32>(Gender::Female), |
| 1425 | .age = Age::Normal, | 1513 | .age = static_cast<u32>(Age::Normal), |
| 1426 | .race = Race::Black, | 1514 | .race = static_cast<u32>(Race::Black), |
| 1427 | .values_count = 8, | 1515 | .values_count = 8, |
| 1428 | .values = {0, 1, 3, 4, 8, 10, 13, 14}, | 1516 | .values = {0, 1, 3, 4, 8, 10, 13, 14}, |
| 1429 | }, | 1517 | }, |
| 1430 | Service::Mii::RandomMiiData4{ | 1518 | RandomMiiData4{ |
| 1431 | .gender = Gender::Female, | 1519 | .gender = static_cast<u32>(Gender::Female), |
| 1432 | .age = Age::Old, | 1520 | .age = static_cast<u32>(Age::Old), |
| 1433 | .race = Race::Black, | 1521 | .race = static_cast<u32>(Race::Black), |
| 1434 | .values_count = 8, | 1522 | .values_count = 8, |
| 1435 | .values = {0, 1, 3, 4, 8, 10, 13, 14}, | 1523 | .values = {0, 1, 3, 4, 8, 10, 13, 14}, |
| 1436 | }, | 1524 | }, |
| 1437 | Service::Mii::RandomMiiData4{ | 1525 | RandomMiiData4{ |
| 1438 | .gender = Gender::Female, | 1526 | .gender = static_cast<u32>(Gender::Female), |
| 1439 | .age = Age::Young, | 1527 | .age = static_cast<u32>(Age::Young), |
| 1440 | .race = Race::White, | 1528 | .race = static_cast<u32>(Race::White), |
| 1441 | .values_count = 12, | 1529 | .values_count = 12, |
| 1442 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, | 1530 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, |
| 1443 | }, | 1531 | }, |
| 1444 | Service::Mii::RandomMiiData4{ | 1532 | RandomMiiData4{ |
| 1445 | .gender = Gender::Female, | 1533 | .gender = static_cast<u32>(Gender::Female), |
| 1446 | .age = Age::Normal, | 1534 | .age = static_cast<u32>(Age::Normal), |
| 1447 | .race = Race::White, | 1535 | .race = static_cast<u32>(Race::White), |
| 1448 | .values_count = 11, | 1536 | .values_count = 11, |
| 1449 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, | 1537 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, |
| 1450 | }, | 1538 | }, |
| 1451 | Service::Mii::RandomMiiData4{ | 1539 | RandomMiiData4{ |
| 1452 | .gender = Gender::Female, | 1540 | .gender = static_cast<u32>(Gender::Female), |
| 1453 | .age = Age::Old, | 1541 | .age = static_cast<u32>(Age::Old), |
| 1454 | .race = Race::White, | 1542 | .race = static_cast<u32>(Race::White), |
| 1455 | .values_count = 10, | 1543 | .values_count = 10, |
| 1456 | .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, | 1544 | .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, |
| 1457 | }, | 1545 | }, |
| 1458 | Service::Mii::RandomMiiData4{ | 1546 | RandomMiiData4{ |
| 1459 | .gender = Gender::Female, | 1547 | .gender = static_cast<u32>(Gender::Female), |
| 1460 | .age = Age::Young, | 1548 | .age = static_cast<u32>(Age::Young), |
| 1461 | .race = Race::Asian, | 1549 | .race = static_cast<u32>(Race::Asian), |
| 1462 | .values_count = 12, | 1550 | .values_count = 12, |
| 1463 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, | 1551 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, |
| 1464 | }, | 1552 | }, |
| 1465 | Service::Mii::RandomMiiData4{ | 1553 | RandomMiiData4{ |
| 1466 | .gender = Gender::Female, | 1554 | .gender = static_cast<u32>(Gender::Female), |
| 1467 | .age = Age::Normal, | 1555 | .age = static_cast<u32>(Age::Normal), |
| 1468 | .race = Race::Asian, | 1556 | .race = static_cast<u32>(Race::Asian), |
| 1469 | .values_count = 11, | 1557 | .values_count = 11, |
| 1470 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, | 1558 | .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, |
| 1471 | }, | 1559 | }, |
| 1472 | Service::Mii::RandomMiiData4{ | 1560 | RandomMiiData4{ |
| 1473 | .gender = Gender::Female, | 1561 | .gender = static_cast<u32>(Gender::Female), |
| 1474 | .age = Age::Old, | 1562 | .age = static_cast<u32>(Age::Old), |
| 1475 | .race = Race::Asian, | 1563 | .race = static_cast<u32>(Race::Asian), |
| 1476 | .values_count = 10, | 1564 | .values_count = 10, |
| 1477 | .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, | 1565 | .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, |
| 1478 | }, | 1566 | }, |
| 1479 | }; | 1567 | }; |
| 1480 | 1568 | ||
| 1481 | const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType{ | 1569 | const std::array<RandomMiiData4, 18> RandomMiiMouthType{ |
| 1482 | Service::Mii::RandomMiiData4{ | 1570 | RandomMiiData4{ |
| 1483 | .gender = Gender::Male, | 1571 | .gender = static_cast<u32>(Gender::Male), |
| 1484 | .age = Age::Young, | 1572 | .age = static_cast<u32>(Age::Young), |
| 1485 | .race = Race::Black, | 1573 | .race = static_cast<u32>(Race::Black), |
| 1486 | .values_count = 25, | 1574 | .values_count = 25, |
| 1487 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18, | 1575 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18, |
| 1488 | 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, | 1576 | 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, |
| 1489 | }, | 1577 | }, |
| 1490 | Service::Mii::RandomMiiData4{ | 1578 | RandomMiiData4{ |
| 1491 | .gender = Gender::Male, | 1579 | .gender = static_cast<u32>(Gender::Male), |
| 1492 | .age = Age::Normal, | 1580 | .age = static_cast<u32>(Age::Normal), |
| 1493 | .race = Race::Black, | 1581 | .race = static_cast<u32>(Race::Black), |
| 1494 | .values_count = 27, | 1582 | .values_count = 27, |
| 1495 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, | 1583 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, |
| 1496 | 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, | 1584 | 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, |
| 1497 | }, | 1585 | }, |
| 1498 | Service::Mii::RandomMiiData4{ | 1586 | RandomMiiData4{ |
| 1499 | .gender = Gender::Male, | 1587 | .gender = static_cast<u32>(Gender::Male), |
| 1500 | .age = Age::Old, | 1588 | .age = static_cast<u32>(Age::Old), |
| 1501 | .race = Race::Black, | 1589 | .race = static_cast<u32>(Race::Black), |
| 1502 | .values_count = 28, | 1590 | .values_count = 28, |
| 1503 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, | 1591 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, |
| 1504 | 18, 19, 21, 22, 23, 25, 26, 28, 30, 31, 32, 33, 34, 35}, | 1592 | 18, 19, 21, 22, 23, 25, 26, 28, 30, 31, 32, 33, 34, 35}, |
| 1505 | }, | 1593 | }, |
| 1506 | Service::Mii::RandomMiiData4{ | 1594 | RandomMiiData4{ |
| 1507 | .gender = Gender::Male, | 1595 | .gender = static_cast<u32>(Gender::Male), |
| 1508 | .age = Age::Young, | 1596 | .age = static_cast<u32>(Age::Young), |
| 1509 | .race = Race::White, | 1597 | .race = static_cast<u32>(Race::White), |
| 1510 | .values_count = 24, | 1598 | .values_count = 24, |
| 1511 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, | 1599 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, |
| 1512 | 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, | 1600 | 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, |
| 1513 | }, | 1601 | }, |
| 1514 | Service::Mii::RandomMiiData4{ | 1602 | RandomMiiData4{ |
| 1515 | .gender = Gender::Male, | 1603 | .gender = static_cast<u32>(Gender::Male), |
| 1516 | .age = Age::Normal, | 1604 | .age = static_cast<u32>(Age::Normal), |
| 1517 | .race = Race::White, | 1605 | .race = static_cast<u32>(Race::White), |
| 1518 | .values_count = 26, | 1606 | .values_count = 26, |
| 1519 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | 1607 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
| 1520 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, | 1608 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, |
| 1521 | }, | 1609 | }, |
| 1522 | Service::Mii::RandomMiiData4{ | 1610 | RandomMiiData4{ |
| 1523 | .gender = Gender::Male, | 1611 | .gender = static_cast<u32>(Gender::Male), |
| 1524 | .age = Age::Old, | 1612 | .age = static_cast<u32>(Age::Old), |
| 1525 | .race = Race::White, | 1613 | .race = static_cast<u32>(Race::White), |
| 1526 | .values_count = 26, | 1614 | .values_count = 26, |
| 1527 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | 1615 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
| 1528 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, | 1616 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, |
| 1529 | }, | 1617 | }, |
| 1530 | Service::Mii::RandomMiiData4{ | 1618 | RandomMiiData4{ |
| 1531 | .gender = Gender::Male, | 1619 | .gender = static_cast<u32>(Gender::Male), |
| 1532 | .age = Age::Young, | 1620 | .age = static_cast<u32>(Age::Young), |
| 1533 | .race = Race::Asian, | 1621 | .race = static_cast<u32>(Race::Asian), |
| 1534 | .values_count = 24, | 1622 | .values_count = 24, |
| 1535 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, | 1623 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, |
| 1536 | 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, | 1624 | 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, |
| 1537 | }, | 1625 | }, |
| 1538 | Service::Mii::RandomMiiData4{ | 1626 | RandomMiiData4{ |
| 1539 | .gender = Gender::Male, | 1627 | .gender = static_cast<u32>(Gender::Male), |
| 1540 | .age = Age::Normal, | 1628 | .age = static_cast<u32>(Age::Normal), |
| 1541 | .race = Race::Asian, | 1629 | .race = static_cast<u32>(Race::Asian), |
| 1542 | .values_count = 26, | 1630 | .values_count = 26, |
| 1543 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | 1631 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
| 1544 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, | 1632 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, |
| 1545 | }, | 1633 | }, |
| 1546 | Service::Mii::RandomMiiData4{ | 1634 | RandomMiiData4{ |
| 1547 | .gender = Gender::Male, | 1635 | .gender = static_cast<u32>(Gender::Male), |
| 1548 | .age = Age::Old, | 1636 | .age = static_cast<u32>(Age::Old), |
| 1549 | .race = Race::Asian, | 1637 | .race = static_cast<u32>(Race::Asian), |
| 1550 | .values_count = 26, | 1638 | .values_count = 26, |
| 1551 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | 1639 | .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
| 1552 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, | 1640 | 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, |
| 1553 | }, | 1641 | }, |
| 1554 | Service::Mii::RandomMiiData4{ | 1642 | RandomMiiData4{ |
| 1555 | .gender = Gender::Female, | 1643 | .gender = static_cast<u32>(Gender::Female), |
| 1556 | .age = Age::Young, | 1644 | .age = static_cast<u32>(Age::Young), |
| 1557 | .race = Race::Black, | 1645 | .race = static_cast<u32>(Race::Black), |
| 1558 | .values_count = 25, | 1646 | .values_count = 25, |
| 1559 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, | 1647 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, |
| 1560 | 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, | 1648 | 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, |
| 1561 | }, | 1649 | }, |
| 1562 | Service::Mii::RandomMiiData4{ | 1650 | RandomMiiData4{ |
| 1563 | .gender = Gender::Female, | 1651 | .gender = static_cast<u32>(Gender::Female), |
| 1564 | .age = Age::Normal, | 1652 | .age = static_cast<u32>(Age::Normal), |
| 1565 | .race = Race::Black, | 1653 | .race = static_cast<u32>(Race::Black), |
| 1566 | .values_count = 26, | 1654 | .values_count = 26, |
| 1567 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, | 1655 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, |
| 1568 | 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, | 1656 | 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, |
| 1569 | }, | 1657 | }, |
| 1570 | Service::Mii::RandomMiiData4{ | 1658 | RandomMiiData4{ |
| 1571 | .gender = Gender::Female, | 1659 | .gender = static_cast<u32>(Gender::Female), |
| 1572 | .age = Age::Old, | 1660 | .age = static_cast<u32>(Age::Old), |
| 1573 | .race = Race::Black, | 1661 | .race = static_cast<u32>(Race::Black), |
| 1574 | .values_count = 26, | 1662 | .values_count = 26, |
| 1575 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, | 1663 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, |
| 1576 | 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, | 1664 | 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, |
| 1577 | }, | 1665 | }, |
| 1578 | Service::Mii::RandomMiiData4{ | 1666 | RandomMiiData4{ |
| 1579 | .gender = Gender::Female, | 1667 | .gender = static_cast<u32>(Gender::Female), |
| 1580 | .age = Age::Young, | 1668 | .age = static_cast<u32>(Age::Young), |
| 1581 | .race = Race::White, | 1669 | .race = static_cast<u32>(Race::White), |
| 1582 | .values_count = 25, | 1670 | .values_count = 25, |
| 1583 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, | 1671 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, |
| 1584 | 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, | 1672 | 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, |
| 1585 | }, | 1673 | }, |
| 1586 | Service::Mii::RandomMiiData4{ | 1674 | RandomMiiData4{ |
| 1587 | .gender = Gender::Female, | 1675 | .gender = static_cast<u32>(Gender::Female), |
| 1588 | .age = Age::Normal, | 1676 | .age = static_cast<u32>(Age::Normal), |
| 1589 | .race = Race::White, | 1677 | .race = static_cast<u32>(Race::White), |
| 1590 | .values_count = 26, | 1678 | .values_count = 26, |
| 1591 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, | 1679 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, |
| 1592 | 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, | 1680 | 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, |
| 1593 | }, | 1681 | }, |
| 1594 | Service::Mii::RandomMiiData4{ | 1682 | RandomMiiData4{ |
| 1595 | .gender = Gender::Female, | 1683 | .gender = static_cast<u32>(Gender::Female), |
| 1596 | .age = Age::Old, | 1684 | .age = static_cast<u32>(Age::Old), |
| 1597 | .race = Race::White, | 1685 | .race = static_cast<u32>(Race::White), |
| 1598 | .values_count = 25, | 1686 | .values_count = 25, |
| 1599 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, | 1687 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, |
| 1600 | 15, 17, 18, 19, 21, 22, 23, 24, 25, 29, 33, 35}, | 1688 | 15, 17, 18, 19, 21, 22, 23, 24, 25, 29, 33, 35}, |
| 1601 | }, | 1689 | }, |
| 1602 | Service::Mii::RandomMiiData4{ | 1690 | RandomMiiData4{ |
| 1603 | .gender = Gender::Female, | 1691 | .gender = static_cast<u32>(Gender::Female), |
| 1604 | .age = Age::Young, | 1692 | .age = static_cast<u32>(Age::Young), |
| 1605 | .race = Race::Asian, | 1693 | .race = static_cast<u32>(Race::Asian), |
| 1606 | .values_count = 24, | 1694 | .values_count = 24, |
| 1607 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, | 1695 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, |
| 1608 | 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, | 1696 | 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, |
| 1609 | }, | 1697 | }, |
| 1610 | Service::Mii::RandomMiiData4{ | 1698 | RandomMiiData4{ |
| 1611 | .gender = Gender::Female, | 1699 | .gender = static_cast<u32>(Gender::Female), |
| 1612 | .age = Age::Normal, | 1700 | .age = static_cast<u32>(Age::Normal), |
| 1613 | .race = Race::Asian, | 1701 | .race = static_cast<u32>(Race::Asian), |
| 1614 | .values_count = 25, | 1702 | .values_count = 25, |
| 1615 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, | 1703 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, |
| 1616 | 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, | 1704 | 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, |
| 1617 | }, | 1705 | }, |
| 1618 | Service::Mii::RandomMiiData4{ | 1706 | RandomMiiData4{ |
| 1619 | .gender = Gender::Female, | 1707 | .gender = static_cast<u32>(Gender::Female), |
| 1620 | .age = Age::Old, | 1708 | .age = static_cast<u32>(Age::Old), |
| 1621 | .race = Race::Asian, | 1709 | .race = static_cast<u32>(Race::Asian), |
| 1622 | .values_count = 25, | 1710 | .values_count = 25, |
| 1623 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, | 1711 | .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, |
| 1624 | 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, | 1712 | 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, |
| 1625 | }, | 1713 | }, |
| 1626 | }; | 1714 | }; |
| 1627 | 1715 | ||
| 1628 | const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType{ | 1716 | const std::array<RandomMiiData2, 3> RandomMiiGlassType{ |
| 1629 | Service::Mii::RandomMiiData2{ | 1717 | RandomMiiData2{ |
| 1630 | .arg_1 = 0, | 1718 | .arg_1 = 0, |
| 1631 | .values_count = 9, | 1719 | .values_count = 9, |
| 1632 | .values = {90, 94, 96, 100, 0, 0, 0, 0, 0}, | 1720 | .values = {90, 94, 96, 100, 0, 0, 0, 0, 0}, |
| 1633 | }, | 1721 | }, |
| 1634 | Service::Mii::RandomMiiData2{ | 1722 | RandomMiiData2{ |
| 1635 | .arg_1 = 1, | 1723 | .arg_1 = 1, |
| 1636 | .values_count = 9, | 1724 | .values_count = 9, |
| 1637 | .values = {83, 86, 90, 93, 94, 96, 98, 100, 0}, | 1725 | .values = {83, 86, 90, 93, 94, 96, 98, 100, 0}, |
| 1638 | }, | 1726 | }, |
| 1639 | Service::Mii::RandomMiiData2{ | 1727 | RandomMiiData2{ |
| 1640 | .arg_1 = 2, | 1728 | .arg_1 = 2, |
| 1641 | .values_count = 9, | 1729 | .values_count = 9, |
| 1642 | .values = {78, 83, 0, 93, 0, 0, 98, 100, 0}, | 1730 | .values = {78, 83, 0, 93, 0, 0, 98, 100, 0}, |
| 1643 | }, | 1731 | }, |
| 1644 | }; | 1732 | }; |
| 1645 | 1733 | ||
| 1734 | u8 FromVer3GetFacelineColor(u8 color) { | ||
| 1735 | return FromVer3FacelineColorTable[color]; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | u8 FromVer3GetHairColor(u8 color) { | ||
| 1739 | return FromVer3HairColorTable[color]; | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | u8 FromVer3GetEyeColor(u8 color) { | ||
| 1743 | return FromVer3EyeColorTable[color]; | ||
| 1744 | } | ||
| 1745 | |||
| 1746 | u8 FromVer3GetMouthlineColor(u8 color) { | ||
| 1747 | return FromVer3MouthlineColorTable[color]; | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | u8 FromVer3GetGlassColor(u8 color) { | ||
| 1751 | return FromVer3GlassColorTable[color]; | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | u8 FromVer3GetGlassType(u8 type) { | ||
| 1755 | return FromVer3GlassTypeTable[type]; | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | FacelineColor GetFacelineColorFromVer3(u32 color) { | ||
| 1759 | return static_cast<FacelineColor>(Ver3FacelineColorTable[color]); | ||
| 1760 | } | ||
| 1761 | |||
| 1762 | CommonColor GetHairColorFromVer3(u32 color) { | ||
| 1763 | return static_cast<CommonColor>(Ver3HairColorTable[color]); | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | CommonColor GetEyeColorFromVer3(u32 color) { | ||
| 1767 | return static_cast<CommonColor>(Ver3EyeColorTable[color]); | ||
| 1768 | } | ||
| 1769 | |||
| 1770 | CommonColor GetMouthColorFromVer3(u32 color) { | ||
| 1771 | return static_cast<CommonColor>(Ver3MouthColorTable[color]); | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | CommonColor GetGlassColorFromVer3(u32 color) { | ||
| 1775 | return static_cast<CommonColor>(Ver3GlassColorTable[color]); | ||
| 1776 | } | ||
| 1777 | |||
| 1646 | } // namespace Service::Mii::RawData | 1778 | } // namespace Service::Mii::RawData |
diff --git a/src/core/hle/service/mii/types/raw_data.h b/src/core/hle/service/mii/types/raw_data.h new file mode 100644 index 000000000..9a4cfa738 --- /dev/null +++ b/src/core/hle/service/mii/types/raw_data.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "core/hle/service/mii/mii_types.h" | ||
| 9 | |||
| 10 | namespace Service::Mii::RawData { | ||
| 11 | |||
| 12 | struct RandomMiiValues { | ||
| 13 | std::array<u8, 188> values{}; | ||
| 14 | }; | ||
| 15 | static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); | ||
| 16 | |||
| 17 | struct RandomMiiData4 { | ||
| 18 | u32 gender{}; | ||
| 19 | u32 age{}; | ||
| 20 | u32 race{}; | ||
| 21 | u32 values_count{}; | ||
| 22 | std::array<u32, 47> values{}; | ||
| 23 | }; | ||
| 24 | static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); | ||
| 25 | |||
| 26 | struct RandomMiiData3 { | ||
| 27 | u32 arg_1; | ||
| 28 | u32 arg_2; | ||
| 29 | u32 values_count; | ||
| 30 | std::array<u32, 47> values{}; | ||
| 31 | }; | ||
| 32 | static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); | ||
| 33 | |||
| 34 | struct RandomMiiData2 { | ||
| 35 | u32 arg_1; | ||
| 36 | u32 values_count; | ||
| 37 | std::array<u32, 47> values{}; | ||
| 38 | }; | ||
| 39 | static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); | ||
| 40 | |||
| 41 | extern const std::array<Service::Mii::DefaultMii, 2> BaseMii; | ||
| 42 | extern const std::array<Service::Mii::DefaultMii, 6> DefaultMii; | ||
| 43 | |||
| 44 | extern const std::array<u8, 62> EyeRotateLookup; | ||
| 45 | extern const std::array<u8, 24> EyebrowRotateLookup; | ||
| 46 | |||
| 47 | extern const std::array<RandomMiiData4, 18> RandomMiiFaceline; | ||
| 48 | extern const std::array<RandomMiiData3, 6> RandomMiiFacelineColor; | ||
| 49 | extern const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle; | ||
| 50 | extern const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup; | ||
| 51 | extern const std::array<RandomMiiData4, 18> RandomMiiHairType; | ||
| 52 | extern const std::array<RandomMiiData3, 9> RandomMiiHairColor; | ||
| 53 | extern const std::array<RandomMiiData4, 18> RandomMiiEyeType; | ||
| 54 | extern const std::array<RandomMiiData2, 3> RandomMiiEyeColor; | ||
| 55 | extern const std::array<RandomMiiData4, 18> RandomMiiEyebrowType; | ||
| 56 | extern const std::array<RandomMiiData4, 18> RandomMiiNoseType; | ||
| 57 | extern const std::array<RandomMiiData4, 18> RandomMiiMouthType; | ||
| 58 | extern const std::array<RandomMiiData2, 3> RandomMiiGlassType; | ||
| 59 | |||
| 60 | u8 FromVer3GetFacelineColor(u8 color); | ||
| 61 | u8 FromVer3GetHairColor(u8 color); | ||
| 62 | u8 FromVer3GetEyeColor(u8 color); | ||
| 63 | u8 FromVer3GetMouthlineColor(u8 color); | ||
| 64 | u8 FromVer3GetGlassColor(u8 color); | ||
| 65 | u8 FromVer3GetGlassType(u8 type); | ||
| 66 | |||
| 67 | FacelineColor GetFacelineColorFromVer3(u32 color); | ||
| 68 | CommonColor GetHairColorFromVer3(u32 color); | ||
| 69 | CommonColor GetEyeColorFromVer3(u32 color); | ||
| 70 | CommonColor GetMouthColorFromVer3(u32 color); | ||
| 71 | CommonColor GetGlassColorFromVer3(u32 color); | ||
| 72 | |||
| 73 | } // namespace Service::Mii::RawData | ||
diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp new file mode 100644 index 000000000..8fce636c7 --- /dev/null +++ b/src/core/hle/service/mii/types/store_data.cpp | |||
| @@ -0,0 +1,643 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/mii/mii_util.h" | ||
| 5 | #include "core/hle/service/mii/types/raw_data.h" | ||
| 6 | #include "core/hle/service/mii/types/store_data.h" | ||
| 7 | |||
| 8 | namespace Service::Mii { | ||
| 9 | |||
| 10 | void StoreData::BuildDefault(u32 mii_index) { | ||
| 11 | const auto& default_mii = RawData::DefaultMii[mii_index]; | ||
| 12 | core_data.SetDefault(); | ||
| 13 | |||
| 14 | core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type)); | ||
| 15 | core_data.SetFacelineColor( | ||
| 16 | RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color))); | ||
| 17 | core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle)); | ||
| 18 | core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup)); | ||
| 19 | |||
| 20 | core_data.SetHairType(static_cast<HairType>(default_mii.hair_type)); | ||
| 21 | core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color))); | ||
| 22 | core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip)); | ||
| 23 | core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type)); | ||
| 24 | core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color))); | ||
| 25 | core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale)); | ||
| 26 | core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect)); | ||
| 27 | core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate)); | ||
| 28 | core_data.SetEyeX(static_cast<u8>(default_mii.eye_x)); | ||
| 29 | core_data.SetEyeY(static_cast<u8>(default_mii.eye_y)); | ||
| 30 | |||
| 31 | core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type)); | ||
| 32 | core_data.SetEyebrowColor( | ||
| 33 | RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color))); | ||
| 34 | core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale)); | ||
| 35 | core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect)); | ||
| 36 | core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate)); | ||
| 37 | core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x)); | ||
| 38 | core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y)); | ||
| 39 | |||
| 40 | core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type)); | ||
| 41 | core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale)); | ||
| 42 | core_data.SetNoseY(static_cast<u8>(default_mii.nose_y)); | ||
| 43 | |||
| 44 | core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type)); | ||
| 45 | core_data.SetMouthColor( | ||
| 46 | RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color))); | ||
| 47 | core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale)); | ||
| 48 | core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect)); | ||
| 49 | core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y)); | ||
| 50 | |||
| 51 | core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type)); | ||
| 52 | core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type)); | ||
| 53 | core_data.SetBeardColor( | ||
| 54 | RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color))); | ||
| 55 | core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale)); | ||
| 56 | core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y)); | ||
| 57 | |||
| 58 | core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type)); | ||
| 59 | core_data.SetGlassColor( | ||
| 60 | RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color))); | ||
| 61 | core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale)); | ||
| 62 | core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y)); | ||
| 63 | |||
| 64 | core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type)); | ||
| 65 | core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale)); | ||
| 66 | core_data.SetMoleX(static_cast<u8>(default_mii.mole_x)); | ||
| 67 | core_data.SetMoleY(static_cast<u8>(default_mii.mole_y)); | ||
| 68 | |||
| 69 | core_data.SetHeight(static_cast<u8>(default_mii.height)); | ||
| 70 | core_data.SetBuild(static_cast<u8>(default_mii.weight)); | ||
| 71 | core_data.SetGender(static_cast<Gender>(default_mii.gender)); | ||
| 72 | core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color)); | ||
| 73 | core_data.SetRegionMove(static_cast<u8>(default_mii.region_move)); | ||
| 74 | core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region)); | ||
| 75 | core_data.SetType(static_cast<u8>(default_mii.type)); | ||
| 76 | core_data.SetNickname(default_mii.nickname); | ||
| 77 | |||
| 78 | const auto device_id = MiiUtil::GetDeviceId(); | ||
| 79 | create_id = MiiUtil::MakeCreateId(); | ||
| 80 | device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); | ||
| 81 | data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); | ||
| 82 | } | ||
| 83 | |||
| 84 | void StoreData::BuildBase(Gender gender) { | ||
| 85 | const auto& default_mii = RawData::BaseMii[gender == Gender::Female ? 1 : 0]; | ||
| 86 | core_data.SetDefault(); | ||
| 87 | |||
| 88 | core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type)); | ||
| 89 | core_data.SetFacelineColor( | ||
| 90 | RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color))); | ||
| 91 | core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle)); | ||
| 92 | core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup)); | ||
| 93 | |||
| 94 | core_data.SetHairType(static_cast<HairType>(default_mii.hair_type)); | ||
| 95 | core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color))); | ||
| 96 | core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip)); | ||
| 97 | core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type)); | ||
| 98 | core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color))); | ||
| 99 | core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale)); | ||
| 100 | core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect)); | ||
| 101 | core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate)); | ||
| 102 | core_data.SetEyeX(static_cast<u8>(default_mii.eye_x)); | ||
| 103 | core_data.SetEyeY(static_cast<u8>(default_mii.eye_y)); | ||
| 104 | |||
| 105 | core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type)); | ||
| 106 | core_data.SetEyebrowColor( | ||
| 107 | RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color))); | ||
| 108 | core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale)); | ||
| 109 | core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect)); | ||
| 110 | core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate)); | ||
| 111 | core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x)); | ||
| 112 | core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y)); | ||
| 113 | |||
| 114 | core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type)); | ||
| 115 | core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale)); | ||
| 116 | core_data.SetNoseY(static_cast<u8>(default_mii.nose_y)); | ||
| 117 | |||
| 118 | core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type)); | ||
| 119 | core_data.SetMouthColor( | ||
| 120 | RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color))); | ||
| 121 | core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale)); | ||
| 122 | core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect)); | ||
| 123 | core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y)); | ||
| 124 | |||
| 125 | core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type)); | ||
| 126 | core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type)); | ||
| 127 | core_data.SetBeardColor( | ||
| 128 | RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color))); | ||
| 129 | core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale)); | ||
| 130 | core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y)); | ||
| 131 | |||
| 132 | core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type)); | ||
| 133 | core_data.SetGlassColor( | ||
| 134 | RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color))); | ||
| 135 | core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale)); | ||
| 136 | core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y)); | ||
| 137 | |||
| 138 | core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type)); | ||
| 139 | core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale)); | ||
| 140 | core_data.SetMoleX(static_cast<u8>(default_mii.mole_x)); | ||
| 141 | core_data.SetMoleY(static_cast<u8>(default_mii.mole_y)); | ||
| 142 | |||
| 143 | core_data.SetHeight(static_cast<u8>(default_mii.height)); | ||
| 144 | core_data.SetBuild(static_cast<u8>(default_mii.weight)); | ||
| 145 | core_data.SetGender(static_cast<Gender>(default_mii.gender)); | ||
| 146 | core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color)); | ||
| 147 | core_data.SetRegionMove(static_cast<u8>(default_mii.region_move)); | ||
| 148 | core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region)); | ||
| 149 | core_data.SetType(static_cast<u8>(default_mii.type)); | ||
| 150 | core_data.SetNickname(default_mii.nickname); | ||
| 151 | |||
| 152 | const auto device_id = MiiUtil::GetDeviceId(); | ||
| 153 | create_id = MiiUtil::MakeCreateId(); | ||
| 154 | device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); | ||
| 155 | data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); | ||
| 156 | } | ||
| 157 | |||
| 158 | void StoreData::BuildRandom(Age age, Gender gender, Race race) { | ||
| 159 | core_data.BuildRandom(age, gender, race); | ||
| 160 | const auto device_id = MiiUtil::GetDeviceId(); | ||
| 161 | create_id = MiiUtil::MakeCreateId(); | ||
| 162 | device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); | ||
| 163 | data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); | ||
| 164 | } | ||
| 165 | |||
| 166 | void StoreData::SetInvalidName() { | ||
| 167 | const auto& invalid_name = core_data.GetInvalidNickname(); | ||
| 168 | const auto device_id = MiiUtil::GetDeviceId(); | ||
| 169 | core_data.SetNickname(invalid_name); | ||
| 170 | device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); | ||
| 171 | data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); | ||
| 172 | } | ||
| 173 | |||
| 174 | bool StoreData::IsSpecial() const { | ||
| 175 | return GetType() == 1; | ||
| 176 | } | ||
| 177 | |||
| 178 | u32 StoreData::IsValid() const { | ||
| 179 | // TODO: complete this | ||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | |||
| 183 | void StoreData::SetFontRegion(FontRegion value) { | ||
| 184 | core_data.SetFontRegion(value); | ||
| 185 | } | ||
| 186 | |||
| 187 | void StoreData::SetFavoriteColor(FavoriteColor value) { | ||
| 188 | core_data.SetFavoriteColor(value); | ||
| 189 | } | ||
| 190 | |||
| 191 | void StoreData::SetGender(Gender value) { | ||
| 192 | core_data.SetGender(value); | ||
| 193 | } | ||
| 194 | |||
| 195 | void StoreData::SetHeight(u8 value) { | ||
| 196 | core_data.SetHeight(value); | ||
| 197 | } | ||
| 198 | |||
| 199 | void StoreData::SetBuild(u8 value) { | ||
| 200 | core_data.SetBuild(value); | ||
| 201 | } | ||
| 202 | |||
| 203 | void StoreData::SetType(u8 value) { | ||
| 204 | core_data.SetType(value); | ||
| 205 | } | ||
| 206 | |||
| 207 | void StoreData::SetRegionMove(u8 value) { | ||
| 208 | core_data.SetRegionMove(value); | ||
| 209 | } | ||
| 210 | |||
| 211 | void StoreData::SetFacelineType(FacelineType value) { | ||
| 212 | core_data.SetFacelineType(value); | ||
| 213 | } | ||
| 214 | |||
| 215 | void StoreData::SetFacelineColor(FacelineColor value) { | ||
| 216 | core_data.SetFacelineColor(value); | ||
| 217 | } | ||
| 218 | |||
| 219 | void StoreData::SetFacelineWrinkle(FacelineWrinkle value) { | ||
| 220 | core_data.SetFacelineWrinkle(value); | ||
| 221 | } | ||
| 222 | |||
| 223 | void StoreData::SetFacelineMake(FacelineMake value) { | ||
| 224 | core_data.SetFacelineMake(value); | ||
| 225 | } | ||
| 226 | |||
| 227 | void StoreData::SetHairType(HairType value) { | ||
| 228 | core_data.SetHairType(value); | ||
| 229 | } | ||
| 230 | |||
| 231 | void StoreData::SetHairColor(CommonColor value) { | ||
| 232 | core_data.SetHairColor(value); | ||
| 233 | } | ||
| 234 | |||
| 235 | void StoreData::SetHairFlip(HairFlip value) { | ||
| 236 | core_data.SetHairFlip(value); | ||
| 237 | } | ||
| 238 | |||
| 239 | void StoreData::SetEyeType(EyeType value) { | ||
| 240 | core_data.SetEyeType(value); | ||
| 241 | } | ||
| 242 | |||
| 243 | void StoreData::SetEyeColor(CommonColor value) { | ||
| 244 | core_data.SetEyeColor(value); | ||
| 245 | } | ||
| 246 | |||
| 247 | void StoreData::SetEyeScale(u8 value) { | ||
| 248 | core_data.SetEyeScale(value); | ||
| 249 | } | ||
| 250 | |||
| 251 | void StoreData::SetEyeAspect(u8 value) { | ||
| 252 | core_data.SetEyeAspect(value); | ||
| 253 | } | ||
| 254 | |||
| 255 | void StoreData::SetEyeRotate(u8 value) { | ||
| 256 | core_data.SetEyeRotate(value); | ||
| 257 | } | ||
| 258 | |||
| 259 | void StoreData::SetEyeX(u8 value) { | ||
| 260 | core_data.SetEyeX(value); | ||
| 261 | } | ||
| 262 | |||
| 263 | void StoreData::SetEyeY(u8 value) { | ||
| 264 | core_data.SetEyeY(value); | ||
| 265 | } | ||
| 266 | |||
| 267 | void StoreData::SetEyebrowType(EyebrowType value) { | ||
| 268 | core_data.SetEyebrowType(value); | ||
| 269 | } | ||
| 270 | |||
| 271 | void StoreData::SetEyebrowColor(CommonColor value) { | ||
| 272 | core_data.SetEyebrowColor(value); | ||
| 273 | } | ||
| 274 | |||
| 275 | void StoreData::SetEyebrowScale(u8 value) { | ||
| 276 | core_data.SetEyebrowScale(value); | ||
| 277 | } | ||
| 278 | |||
| 279 | void StoreData::SetEyebrowAspect(u8 value) { | ||
| 280 | core_data.SetEyebrowAspect(value); | ||
| 281 | } | ||
| 282 | |||
| 283 | void StoreData::SetEyebrowRotate(u8 value) { | ||
| 284 | core_data.SetEyebrowRotate(value); | ||
| 285 | } | ||
| 286 | |||
| 287 | void StoreData::SetEyebrowX(u8 value) { | ||
| 288 | core_data.SetEyebrowX(value); | ||
| 289 | } | ||
| 290 | |||
| 291 | void StoreData::SetEyebrowY(u8 value) { | ||
| 292 | core_data.SetEyebrowY(value); | ||
| 293 | } | ||
| 294 | |||
| 295 | void StoreData::SetNoseType(NoseType value) { | ||
| 296 | core_data.SetNoseType(value); | ||
| 297 | } | ||
| 298 | |||
| 299 | void StoreData::SetNoseScale(u8 value) { | ||
| 300 | core_data.SetNoseScale(value); | ||
| 301 | } | ||
| 302 | |||
| 303 | void StoreData::SetNoseY(u8 value) { | ||
| 304 | core_data.SetNoseY(value); | ||
| 305 | } | ||
| 306 | |||
| 307 | void StoreData::SetMouthType(u8 value) { | ||
| 308 | core_data.SetMouthType(value); | ||
| 309 | } | ||
| 310 | |||
| 311 | void StoreData::SetMouthColor(CommonColor value) { | ||
| 312 | core_data.SetMouthColor(value); | ||
| 313 | } | ||
| 314 | |||
| 315 | void StoreData::SetMouthScale(u8 value) { | ||
| 316 | core_data.SetMouthScale(value); | ||
| 317 | } | ||
| 318 | |||
| 319 | void StoreData::SetMouthAspect(u8 value) { | ||
| 320 | core_data.SetMouthAspect(value); | ||
| 321 | } | ||
| 322 | |||
| 323 | void StoreData::SetMouthY(u8 value) { | ||
| 324 | core_data.SetMouthY(value); | ||
| 325 | } | ||
| 326 | |||
| 327 | void StoreData::SetBeardColor(CommonColor value) { | ||
| 328 | core_data.SetBeardColor(value); | ||
| 329 | } | ||
| 330 | |||
| 331 | void StoreData::SetBeardType(BeardType value) { | ||
| 332 | core_data.SetBeardType(value); | ||
| 333 | } | ||
| 334 | |||
| 335 | void StoreData::SetMustacheType(MustacheType value) { | ||
| 336 | core_data.SetMustacheType(value); | ||
| 337 | } | ||
| 338 | |||
| 339 | void StoreData::SetMustacheScale(u8 value) { | ||
| 340 | core_data.SetMustacheScale(value); | ||
| 341 | } | ||
| 342 | |||
| 343 | void StoreData::SetMustacheY(u8 value) { | ||
| 344 | core_data.SetMustacheY(value); | ||
| 345 | } | ||
| 346 | |||
| 347 | void StoreData::SetGlassType(GlassType value) { | ||
| 348 | core_data.SetGlassType(value); | ||
| 349 | } | ||
| 350 | |||
| 351 | void StoreData::SetGlassColor(CommonColor value) { | ||
| 352 | core_data.SetGlassColor(value); | ||
| 353 | } | ||
| 354 | |||
| 355 | void StoreData::SetGlassScale(u8 value) { | ||
| 356 | core_data.SetGlassScale(value); | ||
| 357 | } | ||
| 358 | |||
| 359 | void StoreData::SetGlassY(u8 value) { | ||
| 360 | core_data.SetGlassY(value); | ||
| 361 | } | ||
| 362 | |||
| 363 | void StoreData::SetMoleType(MoleType value) { | ||
| 364 | core_data.SetMoleType(value); | ||
| 365 | } | ||
| 366 | |||
| 367 | void StoreData::SetMoleScale(u8 value) { | ||
| 368 | core_data.SetMoleScale(value); | ||
| 369 | } | ||
| 370 | |||
| 371 | void StoreData::SetMoleX(u8 value) { | ||
| 372 | core_data.SetMoleX(value); | ||
| 373 | } | ||
| 374 | |||
| 375 | void StoreData::SetMoleY(u8 value) { | ||
| 376 | core_data.SetMoleY(value); | ||
| 377 | } | ||
| 378 | |||
| 379 | void StoreData::SetNickname(Nickname value) { | ||
| 380 | core_data.SetNickname(value); | ||
| 381 | } | ||
| 382 | |||
| 383 | Common::UUID StoreData::GetCreateId() const { | ||
| 384 | return create_id; | ||
| 385 | } | ||
| 386 | |||
| 387 | FontRegion StoreData::GetFontRegion() const { | ||
| 388 | return static_cast<FontRegion>(core_data.GetFontRegion()); | ||
| 389 | } | ||
| 390 | |||
| 391 | FavoriteColor StoreData::GetFavoriteColor() const { | ||
| 392 | return core_data.GetFavoriteColor(); | ||
| 393 | } | ||
| 394 | |||
| 395 | Gender StoreData::GetGender() const { | ||
| 396 | return core_data.GetGender(); | ||
| 397 | } | ||
| 398 | |||
| 399 | u8 StoreData::GetHeight() const { | ||
| 400 | return core_data.GetHeight(); | ||
| 401 | } | ||
| 402 | |||
| 403 | u8 StoreData::GetBuild() const { | ||
| 404 | return core_data.GetBuild(); | ||
| 405 | } | ||
| 406 | |||
| 407 | u8 StoreData::GetType() const { | ||
| 408 | return core_data.GetType(); | ||
| 409 | } | ||
| 410 | |||
| 411 | u8 StoreData::GetRegionMove() const { | ||
| 412 | return core_data.GetRegionMove(); | ||
| 413 | } | ||
| 414 | |||
| 415 | FacelineType StoreData::GetFacelineType() const { | ||
| 416 | return core_data.GetFacelineType(); | ||
| 417 | } | ||
| 418 | |||
| 419 | FacelineColor StoreData::GetFacelineColor() const { | ||
| 420 | return core_data.GetFacelineColor(); | ||
| 421 | } | ||
| 422 | |||
| 423 | FacelineWrinkle StoreData::GetFacelineWrinkle() const { | ||
| 424 | return core_data.GetFacelineWrinkle(); | ||
| 425 | } | ||
| 426 | |||
| 427 | FacelineMake StoreData::GetFacelineMake() const { | ||
| 428 | return core_data.GetFacelineMake(); | ||
| 429 | } | ||
| 430 | |||
| 431 | HairType StoreData::GetHairType() const { | ||
| 432 | return core_data.GetHairType(); | ||
| 433 | } | ||
| 434 | |||
| 435 | CommonColor StoreData::GetHairColor() const { | ||
| 436 | return core_data.GetHairColor(); | ||
| 437 | } | ||
| 438 | |||
| 439 | HairFlip StoreData::GetHairFlip() const { | ||
| 440 | return core_data.GetHairFlip(); | ||
| 441 | } | ||
| 442 | |||
| 443 | EyeType StoreData::GetEyeType() const { | ||
| 444 | return core_data.GetEyeType(); | ||
| 445 | } | ||
| 446 | |||
| 447 | CommonColor StoreData::GetEyeColor() const { | ||
| 448 | return core_data.GetEyeColor(); | ||
| 449 | } | ||
| 450 | |||
| 451 | u8 StoreData::GetEyeScale() const { | ||
| 452 | return core_data.GetEyeScale(); | ||
| 453 | } | ||
| 454 | |||
| 455 | u8 StoreData::GetEyeAspect() const { | ||
| 456 | return core_data.GetEyeAspect(); | ||
| 457 | } | ||
| 458 | |||
| 459 | u8 StoreData::GetEyeRotate() const { | ||
| 460 | return core_data.GetEyeRotate(); | ||
| 461 | } | ||
| 462 | |||
| 463 | u8 StoreData::GetEyeX() const { | ||
| 464 | return core_data.GetEyeX(); | ||
| 465 | } | ||
| 466 | |||
| 467 | u8 StoreData::GetEyeY() const { | ||
| 468 | return core_data.GetEyeY(); | ||
| 469 | } | ||
| 470 | |||
| 471 | EyebrowType StoreData::GetEyebrowType() const { | ||
| 472 | return core_data.GetEyebrowType(); | ||
| 473 | } | ||
| 474 | |||
| 475 | CommonColor StoreData::GetEyebrowColor() const { | ||
| 476 | return core_data.GetEyebrowColor(); | ||
| 477 | } | ||
| 478 | |||
| 479 | u8 StoreData::GetEyebrowScale() const { | ||
| 480 | return core_data.GetEyebrowScale(); | ||
| 481 | } | ||
| 482 | |||
| 483 | u8 StoreData::GetEyebrowAspect() const { | ||
| 484 | return core_data.GetEyebrowAspect(); | ||
| 485 | } | ||
| 486 | |||
| 487 | u8 StoreData::GetEyebrowRotate() const { | ||
| 488 | return core_data.GetEyebrowRotate(); | ||
| 489 | } | ||
| 490 | |||
| 491 | u8 StoreData::GetEyebrowX() const { | ||
| 492 | return core_data.GetEyebrowX(); | ||
| 493 | } | ||
| 494 | |||
| 495 | u8 StoreData::GetEyebrowY() const { | ||
| 496 | return core_data.GetEyebrowY(); | ||
| 497 | } | ||
| 498 | |||
| 499 | NoseType StoreData::GetNoseType() const { | ||
| 500 | return core_data.GetNoseType(); | ||
| 501 | } | ||
| 502 | |||
| 503 | u8 StoreData::GetNoseScale() const { | ||
| 504 | return core_data.GetNoseScale(); | ||
| 505 | } | ||
| 506 | |||
| 507 | u8 StoreData::GetNoseY() const { | ||
| 508 | return core_data.GetNoseY(); | ||
| 509 | } | ||
| 510 | |||
| 511 | MouthType StoreData::GetMouthType() const { | ||
| 512 | return core_data.GetMouthType(); | ||
| 513 | } | ||
| 514 | |||
| 515 | CommonColor StoreData::GetMouthColor() const { | ||
| 516 | return core_data.GetMouthColor(); | ||
| 517 | } | ||
| 518 | |||
| 519 | u8 StoreData::GetMouthScale() const { | ||
| 520 | return core_data.GetMouthScale(); | ||
| 521 | } | ||
| 522 | |||
| 523 | u8 StoreData::GetMouthAspect() const { | ||
| 524 | return core_data.GetMouthAspect(); | ||
| 525 | } | ||
| 526 | |||
| 527 | u8 StoreData::GetMouthY() const { | ||
| 528 | return core_data.GetMouthY(); | ||
| 529 | } | ||
| 530 | |||
| 531 | CommonColor StoreData::GetBeardColor() const { | ||
| 532 | return core_data.GetBeardColor(); | ||
| 533 | } | ||
| 534 | |||
| 535 | BeardType StoreData::GetBeardType() const { | ||
| 536 | return core_data.GetBeardType(); | ||
| 537 | } | ||
| 538 | |||
| 539 | MustacheType StoreData::GetMustacheType() const { | ||
| 540 | return core_data.GetMustacheType(); | ||
| 541 | } | ||
| 542 | |||
| 543 | u8 StoreData::GetMustacheScale() const { | ||
| 544 | return core_data.GetMustacheScale(); | ||
| 545 | } | ||
| 546 | |||
| 547 | u8 StoreData::GetMustacheY() const { | ||
| 548 | return core_data.GetMustacheY(); | ||
| 549 | } | ||
| 550 | |||
| 551 | GlassType StoreData::GetGlassType() const { | ||
| 552 | return core_data.GetGlassType(); | ||
| 553 | } | ||
| 554 | |||
| 555 | CommonColor StoreData::GetGlassColor() const { | ||
| 556 | return core_data.GetGlassColor(); | ||
| 557 | } | ||
| 558 | |||
| 559 | u8 StoreData::GetGlassScale() const { | ||
| 560 | return core_data.GetGlassScale(); | ||
| 561 | } | ||
| 562 | |||
| 563 | u8 StoreData::GetGlassY() const { | ||
| 564 | return core_data.GetGlassY(); | ||
| 565 | } | ||
| 566 | |||
| 567 | MoleType StoreData::GetMoleType() const { | ||
| 568 | return core_data.GetMoleType(); | ||
| 569 | } | ||
| 570 | |||
| 571 | u8 StoreData::GetMoleScale() const { | ||
| 572 | return core_data.GetMoleScale(); | ||
| 573 | } | ||
| 574 | |||
| 575 | u8 StoreData::GetMoleX() const { | ||
| 576 | return core_data.GetMoleX(); | ||
| 577 | } | ||
| 578 | |||
| 579 | u8 StoreData::GetMoleY() const { | ||
| 580 | return core_data.GetMoleY(); | ||
| 581 | } | ||
| 582 | |||
| 583 | Nickname StoreData::GetNickname() const { | ||
| 584 | return core_data.GetNickname(); | ||
| 585 | } | ||
| 586 | |||
| 587 | bool StoreData::operator==(const StoreData& data) { | ||
| 588 | bool is_identical = data.core_data.IsValid() == 0; | ||
| 589 | is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data; | ||
| 590 | is_identical &= GetCreateId() == data.GetCreateId(); | ||
| 591 | is_identical &= GetFontRegion() == data.GetFontRegion(); | ||
| 592 | is_identical &= GetFavoriteColor() == data.GetFavoriteColor(); | ||
| 593 | is_identical &= GetGender() == data.GetGender(); | ||
| 594 | is_identical &= GetHeight() == data.GetHeight(); | ||
| 595 | is_identical &= GetBuild() == data.GetBuild(); | ||
| 596 | is_identical &= GetType() == data.GetType(); | ||
| 597 | is_identical &= GetRegionMove() == data.GetRegionMove(); | ||
| 598 | is_identical &= GetFacelineType() == data.GetFacelineType(); | ||
| 599 | is_identical &= GetFacelineColor() == data.GetFacelineColor(); | ||
| 600 | is_identical &= GetFacelineWrinkle() == data.GetFacelineWrinkle(); | ||
| 601 | is_identical &= GetFacelineMake() == data.GetFacelineMake(); | ||
| 602 | is_identical &= GetHairType() == data.GetHairType(); | ||
| 603 | is_identical &= GetHairColor() == data.GetHairColor(); | ||
| 604 | is_identical &= GetHairFlip() == data.GetHairFlip(); | ||
| 605 | is_identical &= GetEyeType() == data.GetEyeType(); | ||
| 606 | is_identical &= GetEyeColor() == data.GetEyeColor(); | ||
| 607 | is_identical &= GetEyeScale() == data.GetEyeScale(); | ||
| 608 | is_identical &= GetEyeAspect() == data.GetEyeAspect(); | ||
| 609 | is_identical &= GetEyeRotate() == data.GetEyeRotate(); | ||
| 610 | is_identical &= GetEyeX() == data.GetEyeX(); | ||
| 611 | is_identical &= GetEyeY() == data.GetEyeY(); | ||
| 612 | is_identical &= GetEyebrowType() == data.GetEyebrowType(); | ||
| 613 | is_identical &= GetEyebrowColor() == data.GetEyebrowColor(); | ||
| 614 | is_identical &= GetEyebrowScale() == data.GetEyebrowScale(); | ||
| 615 | is_identical &= GetEyebrowAspect() == data.GetEyebrowAspect(); | ||
| 616 | is_identical &= GetEyebrowRotate() == data.GetEyebrowRotate(); | ||
| 617 | is_identical &= GetEyebrowX() == data.GetEyebrowX(); | ||
| 618 | is_identical &= GetEyebrowY() == data.GetEyebrowY(); | ||
| 619 | is_identical &= GetNoseType() == data.GetNoseType(); | ||
| 620 | is_identical &= GetNoseScale() == data.GetNoseScale(); | ||
| 621 | is_identical &= GetNoseY() == data.GetNoseY(); | ||
| 622 | is_identical &= GetMouthType() == data.GetMouthType(); | ||
| 623 | is_identical &= GetMouthColor() == data.GetMouthColor(); | ||
| 624 | is_identical &= GetMouthScale() == data.GetMouthScale(); | ||
| 625 | is_identical &= GetMouthAspect() == data.GetMouthAspect(); | ||
| 626 | is_identical &= GetMouthY() == data.GetMouthY(); | ||
| 627 | is_identical &= GetBeardColor() == data.GetBeardColor(); | ||
| 628 | is_identical &= GetBeardType() == data.GetBeardType(); | ||
| 629 | is_identical &= GetMustacheType() == data.GetMustacheType(); | ||
| 630 | is_identical &= GetMustacheScale() == data.GetMustacheScale(); | ||
| 631 | is_identical &= GetMustacheY() == data.GetMustacheY(); | ||
| 632 | is_identical &= GetGlassType() == data.GetGlassType(); | ||
| 633 | is_identical &= GetGlassColor() == data.GetGlassColor(); | ||
| 634 | is_identical &= GetGlassScale() == data.GetGlassScale(); | ||
| 635 | is_identical &= GetGlassY() == data.GetGlassY(); | ||
| 636 | is_identical &= GetMoleType() == data.GetMoleType(); | ||
| 637 | is_identical &= GetMoleScale() == data.GetMoleScale(); | ||
| 638 | is_identical &= GetMoleX() == data.GetMoleX(); | ||
| 639 | is_identical &= data.GetMoleY() == data.GetMoleY(); | ||
| 640 | return is_identical; | ||
| 641 | } | ||
| 642 | |||
| 643 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h new file mode 100644 index 000000000..224c32cf8 --- /dev/null +++ b/src/core/hle/service/mii/types/store_data.h | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/mii/mii_types.h" | ||
| 7 | #include "core/hle/service/mii/types/core_data.h" | ||
| 8 | |||
| 9 | namespace Service::Mii { | ||
| 10 | |||
| 11 | class StoreData { | ||
| 12 | public: | ||
| 13 | // nn::mii::detail::StoreDataRaw::BuildDefault | ||
| 14 | void BuildDefault(u32 mii_index); | ||
| 15 | // nn::mii::detail::StoreDataRaw::BuildDefault | ||
| 16 | |||
| 17 | void BuildBase(Gender gender); | ||
| 18 | // nn::mii::detail::StoreDataRaw::BuildRandom | ||
| 19 | void BuildRandom(Age age, Gender gender, Race race); | ||
| 20 | |||
| 21 | bool IsSpecial() const; | ||
| 22 | |||
| 23 | u32 IsValid() const; | ||
| 24 | |||
| 25 | void SetFontRegion(FontRegion value); | ||
| 26 | void SetFavoriteColor(FavoriteColor value); | ||
| 27 | void SetGender(Gender value); | ||
| 28 | void SetHeight(u8 value); | ||
| 29 | void SetBuild(u8 value); | ||
| 30 | void SetType(u8 value); | ||
| 31 | void SetRegionMove(u8 value); | ||
| 32 | void SetFacelineType(FacelineType value); | ||
| 33 | void SetFacelineColor(FacelineColor value); | ||
| 34 | void SetFacelineWrinkle(FacelineWrinkle value); | ||
| 35 | void SetFacelineMake(FacelineMake value); | ||
| 36 | void SetHairType(HairType value); | ||
| 37 | void SetHairColor(CommonColor value); | ||
| 38 | void SetHairFlip(HairFlip value); | ||
| 39 | void SetEyeType(EyeType value); | ||
| 40 | void SetEyeColor(CommonColor value); | ||
| 41 | void SetEyeScale(u8 value); | ||
| 42 | void SetEyeAspect(u8 value); | ||
| 43 | void SetEyeRotate(u8 value); | ||
| 44 | void SetEyeX(u8 value); | ||
| 45 | void SetEyeY(u8 value); | ||
| 46 | void SetEyebrowType(EyebrowType value); | ||
| 47 | void SetEyebrowColor(CommonColor value); | ||
| 48 | void SetEyebrowScale(u8 value); | ||
| 49 | void SetEyebrowAspect(u8 value); | ||
| 50 | void SetEyebrowRotate(u8 value); | ||
| 51 | void SetEyebrowX(u8 value); | ||
| 52 | void SetEyebrowY(u8 value); | ||
| 53 | void SetNoseType(NoseType value); | ||
| 54 | void SetNoseScale(u8 value); | ||
| 55 | void SetNoseY(u8 value); | ||
| 56 | void SetMouthType(u8 value); | ||
| 57 | void SetMouthColor(CommonColor value); | ||
| 58 | void SetMouthScale(u8 value); | ||
| 59 | void SetMouthAspect(u8 value); | ||
| 60 | void SetMouthY(u8 value); | ||
| 61 | void SetBeardColor(CommonColor value); | ||
| 62 | void SetBeardType(BeardType value); | ||
| 63 | void SetMustacheType(MustacheType value); | ||
| 64 | void SetMustacheScale(u8 value); | ||
| 65 | void SetMustacheY(u8 value); | ||
| 66 | void SetGlassType(GlassType value); | ||
| 67 | void SetGlassColor(CommonColor value); | ||
| 68 | void SetGlassScale(u8 value); | ||
| 69 | void SetGlassY(u8 value); | ||
| 70 | void SetMoleType(MoleType value); | ||
| 71 | void SetMoleScale(u8 value); | ||
| 72 | void SetMoleX(u8 value); | ||
| 73 | void SetMoleY(u8 value); | ||
| 74 | void SetNickname(Nickname nickname); | ||
| 75 | void SetInvalidName(); | ||
| 76 | |||
| 77 | Common::UUID GetCreateId() const; | ||
| 78 | FontRegion GetFontRegion() const; | ||
| 79 | FavoriteColor GetFavoriteColor() const; | ||
| 80 | Gender GetGender() const; | ||
| 81 | u8 GetHeight() const; | ||
| 82 | u8 GetBuild() const; | ||
| 83 | u8 GetType() const; | ||
| 84 | u8 GetRegionMove() const; | ||
| 85 | FacelineType GetFacelineType() const; | ||
| 86 | FacelineColor GetFacelineColor() const; | ||
| 87 | FacelineWrinkle GetFacelineWrinkle() const; | ||
| 88 | FacelineMake GetFacelineMake() const; | ||
| 89 | HairType GetHairType() const; | ||
| 90 | CommonColor GetHairColor() const; | ||
| 91 | HairFlip GetHairFlip() const; | ||
| 92 | EyeType GetEyeType() const; | ||
| 93 | CommonColor GetEyeColor() const; | ||
| 94 | u8 GetEyeScale() const; | ||
| 95 | u8 GetEyeAspect() const; | ||
| 96 | u8 GetEyeRotate() const; | ||
| 97 | u8 GetEyeX() const; | ||
| 98 | u8 GetEyeY() const; | ||
| 99 | EyebrowType GetEyebrowType() const; | ||
| 100 | CommonColor GetEyebrowColor() const; | ||
| 101 | u8 GetEyebrowScale() const; | ||
| 102 | u8 GetEyebrowAspect() const; | ||
| 103 | u8 GetEyebrowRotate() const; | ||
| 104 | u8 GetEyebrowX() const; | ||
| 105 | u8 GetEyebrowY() const; | ||
| 106 | NoseType GetNoseType() const; | ||
| 107 | u8 GetNoseScale() const; | ||
| 108 | u8 GetNoseY() const; | ||
| 109 | MouthType GetMouthType() const; | ||
| 110 | CommonColor GetMouthColor() const; | ||
| 111 | u8 GetMouthScale() const; | ||
| 112 | u8 GetMouthAspect() const; | ||
| 113 | u8 GetMouthY() const; | ||
| 114 | CommonColor GetBeardColor() const; | ||
| 115 | BeardType GetBeardType() const; | ||
| 116 | MustacheType GetMustacheType() const; | ||
| 117 | u8 GetMustacheScale() const; | ||
| 118 | u8 GetMustacheY() const; | ||
| 119 | GlassType GetGlassType() const; | ||
| 120 | CommonColor GetGlassColor() const; | ||
| 121 | u8 GetGlassScale() const; | ||
| 122 | u8 GetGlassY() const; | ||
| 123 | MoleType GetMoleType() const; | ||
| 124 | u8 GetMoleScale() const; | ||
| 125 | u8 GetMoleX() const; | ||
| 126 | u8 GetMoleY() const; | ||
| 127 | Nickname GetNickname() const; | ||
| 128 | |||
| 129 | bool operator==(const StoreData& data); | ||
| 130 | |||
| 131 | private: | ||
| 132 | CoreData core_data{}; | ||
| 133 | Common::UUID create_id{}; | ||
| 134 | u16 data_crc{}; | ||
| 135 | u16 device_crc{}; | ||
| 136 | }; | ||
| 137 | static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size."); | ||
| 138 | |||
| 139 | struct StoreDataElement { | ||
| 140 | StoreData store_data{}; | ||
| 141 | Source source{}; | ||
| 142 | }; | ||
| 143 | static_assert(sizeof(StoreDataElement) == 0x48, "StoreDataElement has incorrect size."); | ||
| 144 | |||
| 145 | }; // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp new file mode 100644 index 000000000..1c28e0b1b --- /dev/null +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/mii/mii_util.h" | ||
| 5 | #include "core/hle/service/mii/types/raw_data.h" | ||
| 6 | #include "core/hle/service/mii/types/store_data.h" | ||
| 7 | #include "core/hle/service/mii/types/ver3_store_data.h" | ||
| 8 | |||
| 9 | namespace Service::Mii { | ||
| 10 | |||
| 11 | void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) { | ||
| 12 | faceline_color = static_cast<u8>(store_data.GetFacelineColor()) & 0xf; | ||
| 13 | hair_color = static_cast<u8>(store_data.GetHairColor()) & 0x7f; | ||
| 14 | eye_color = static_cast<u8>(store_data.GetEyeColor()) & 0x7f; | ||
| 15 | eyebrow_color = static_cast<u8>(store_data.GetEyebrowColor()) & 0x7f; | ||
| 16 | mouth_color = static_cast<u8>(store_data.GetMouthColor()) & 0x7f; | ||
| 17 | beard_color = static_cast<u8>(store_data.GetBeardColor()) & 0x7f; | ||
| 18 | glass_color = static_cast<u8>(store_data.GetGlassColor()) & 0x7f; | ||
| 19 | glass_type = static_cast<u8>(store_data.GetGlassType()) & 0x1f; | ||
| 20 | } | ||
| 21 | |||
| 22 | void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { | ||
| 23 | out_store_data.BuildBase(Gender::Male); | ||
| 24 | |||
| 25 | if (!IsValid()) { | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | |||
| 29 | // TODO: We are ignoring a bunch of data from the mii_v3 | ||
| 30 | |||
| 31 | out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value())); | ||
| 32 | out_store_data.SetFavoriteColor( | ||
| 33 | static_cast<FavoriteColor>(mii_information.favorite_color.Value())); | ||
| 34 | out_store_data.SetHeight(height); | ||
| 35 | out_store_data.SetBuild(build); | ||
| 36 | |||
| 37 | out_store_data.SetNickname(mii_name); | ||
| 38 | out_store_data.SetFontRegion( | ||
| 39 | static_cast<FontRegion>(static_cast<u8>(region_information.font_region))); | ||
| 40 | |||
| 41 | out_store_data.SetFacelineType( | ||
| 42 | static_cast<FacelineType>(appearance_bits1.faceline_type.Value())); | ||
| 43 | out_store_data.SetFacelineColor( | ||
| 44 | static_cast<FacelineColor>(appearance_bits1.faceline_color.Value())); | ||
| 45 | out_store_data.SetFacelineWrinkle( | ||
| 46 | static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value())); | ||
| 47 | out_store_data.SetFacelineMake( | ||
| 48 | static_cast<FacelineMake>(appearance_bits2.faceline_make.Value())); | ||
| 49 | |||
| 50 | out_store_data.SetHairType(static_cast<HairType>(hair_type)); | ||
| 51 | out_store_data.SetHairColor(static_cast<CommonColor>(appearance_bits3.hair_color.Value())); | ||
| 52 | out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value())); | ||
| 53 | |||
| 54 | out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value())); | ||
| 55 | out_store_data.SetEyeColor(static_cast<CommonColor>(appearance_bits4.eye_color.Value())); | ||
| 56 | out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale)); | ||
| 57 | out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect)); | ||
| 58 | out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate)); | ||
| 59 | out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x)); | ||
| 60 | out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y)); | ||
| 61 | |||
| 62 | out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value())); | ||
| 63 | out_store_data.SetEyebrowColor( | ||
| 64 | static_cast<CommonColor>(appearance_bits5.eyebrow_color.Value())); | ||
| 65 | out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale)); | ||
| 66 | out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect)); | ||
| 67 | out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate)); | ||
| 68 | out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x)); | ||
| 69 | out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y)); | ||
| 70 | |||
| 71 | out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value())); | ||
| 72 | out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale)); | ||
| 73 | out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y)); | ||
| 74 | |||
| 75 | out_store_data.SetMouthType(static_cast<u8>(appearance_bits7.mouth_type)); | ||
| 76 | out_store_data.SetMouthColor(static_cast<CommonColor>(appearance_bits7.mouth_color.Value())); | ||
| 77 | out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale)); | ||
| 78 | out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect)); | ||
| 79 | out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y)); | ||
| 80 | |||
| 81 | out_store_data.SetMustacheType( | ||
| 82 | static_cast<MustacheType>(appearance_bits8.mustache_type.Value())); | ||
| 83 | out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale)); | ||
| 84 | out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y)); | ||
| 85 | |||
| 86 | out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value())); | ||
| 87 | out_store_data.SetBeardColor(static_cast<CommonColor>(appearance_bits9.beard_color.Value())); | ||
| 88 | |||
| 89 | out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value())); | ||
| 90 | out_store_data.SetGlassColor(static_cast<CommonColor>(appearance_bits10.glass_color.Value())); | ||
| 91 | out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale)); | ||
| 92 | out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y)); | ||
| 93 | |||
| 94 | out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value())); | ||
| 95 | out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale)); | ||
| 96 | out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x)); | ||
| 97 | out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y)); | ||
| 98 | } | ||
| 99 | |||
| 100 | void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { | ||
| 101 | version = 1; | ||
| 102 | mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); | ||
| 103 | mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); | ||
| 104 | height = store_data.GetHeight(); | ||
| 105 | build = store_data.GetBuild(); | ||
| 106 | |||
| 107 | mii_name = store_data.GetNickname(); | ||
| 108 | region_information.font_region.Assign(static_cast<u8>(store_data.GetFontRegion())); | ||
| 109 | |||
| 110 | appearance_bits1.faceline_type.Assign(static_cast<u8>(store_data.GetFacelineType())); | ||
| 111 | appearance_bits2.faceline_wrinkle.Assign(static_cast<u8>(store_data.GetFacelineWrinkle())); | ||
| 112 | appearance_bits2.faceline_make.Assign(static_cast<u8>(store_data.GetFacelineMake())); | ||
| 113 | |||
| 114 | hair_type = static_cast<u8>(store_data.GetHairType()); | ||
| 115 | appearance_bits3.hair_flip.Assign(static_cast<u8>(store_data.GetHairFlip())); | ||
| 116 | |||
| 117 | appearance_bits4.eye_type.Assign(static_cast<u8>(store_data.GetEyeType())); | ||
| 118 | appearance_bits4.eye_scale.Assign(store_data.GetEyeScale()); | ||
| 119 | appearance_bits4.eye_aspect.Assign(store_data.GetEyebrowAspect()); | ||
| 120 | appearance_bits4.eye_rotate.Assign(store_data.GetEyeRotate()); | ||
| 121 | appearance_bits4.eye_x.Assign(store_data.GetEyeX()); | ||
| 122 | appearance_bits4.eye_y.Assign(store_data.GetEyeY()); | ||
| 123 | |||
| 124 | appearance_bits5.eyebrow_type.Assign(static_cast<u8>(store_data.GetEyebrowType())); | ||
| 125 | appearance_bits5.eyebrow_scale.Assign(store_data.GetEyebrowScale()); | ||
| 126 | appearance_bits5.eyebrow_aspect.Assign(store_data.GetEyebrowAspect()); | ||
| 127 | appearance_bits5.eyebrow_rotate.Assign(store_data.GetEyebrowRotate()); | ||
| 128 | appearance_bits5.eyebrow_x.Assign(store_data.GetEyebrowX()); | ||
| 129 | appearance_bits5.eyebrow_y.Assign(store_data.GetEyebrowY()); | ||
| 130 | |||
| 131 | appearance_bits6.nose_type.Assign(static_cast<u8>(store_data.GetNoseType())); | ||
| 132 | appearance_bits6.nose_scale.Assign(store_data.GetNoseScale()); | ||
| 133 | appearance_bits6.nose_y.Assign(store_data.GetNoseY()); | ||
| 134 | |||
| 135 | appearance_bits7.mouth_type.Assign(static_cast<u8>(store_data.GetMouthType())); | ||
| 136 | appearance_bits7.mouth_scale.Assign(store_data.GetMouthScale()); | ||
| 137 | appearance_bits7.mouth_aspect.Assign(store_data.GetMouthAspect()); | ||
| 138 | appearance_bits8.mouth_y.Assign(store_data.GetMouthY()); | ||
| 139 | |||
| 140 | appearance_bits8.mustache_type.Assign(static_cast<u8>(store_data.GetMustacheType())); | ||
| 141 | appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale()); | ||
| 142 | appearance_bits9.mustache_y.Assign(store_data.GetMustacheY()); | ||
| 143 | |||
| 144 | appearance_bits9.beard_type.Assign(static_cast<u8>(store_data.GetBeardType())); | ||
| 145 | |||
| 146 | appearance_bits10.glass_scale.Assign(store_data.GetGlassScale()); | ||
| 147 | appearance_bits10.glass_y.Assign(store_data.GetGlassY()); | ||
| 148 | |||
| 149 | appearance_bits11.mole_type.Assign(static_cast<u8>(store_data.GetMoleType())); | ||
| 150 | appearance_bits11.mole_scale.Assign(store_data.GetMoleScale()); | ||
| 151 | appearance_bits11.mole_x.Assign(store_data.GetMoleX()); | ||
| 152 | appearance_bits11.mole_y.Assign(store_data.GetMoleY()); | ||
| 153 | |||
| 154 | // These types are converted to V3 from a table | ||
| 155 | appearance_bits1.faceline_color.Assign( | ||
| 156 | RawData::FromVer3GetFacelineColor(static_cast<u8>(store_data.GetFacelineColor()))); | ||
| 157 | appearance_bits3.hair_color.Assign( | ||
| 158 | RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetHairColor()))); | ||
| 159 | appearance_bits4.eye_color.Assign( | ||
| 160 | RawData::FromVer3GetEyeColor(static_cast<u8>(store_data.GetEyeColor()))); | ||
| 161 | appearance_bits5.eyebrow_color.Assign( | ||
| 162 | RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetEyebrowColor()))); | ||
| 163 | appearance_bits7.mouth_color.Assign( | ||
| 164 | RawData::FromVer3GetMouthlineColor(static_cast<u8>(store_data.GetMouthColor()))); | ||
| 165 | appearance_bits9.beard_color.Assign( | ||
| 166 | RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetBeardColor()))); | ||
| 167 | appearance_bits10.glass_color.Assign( | ||
| 168 | RawData::FromVer3GetGlassColor(static_cast<u8>(store_data.GetGlassColor()))); | ||
| 169 | appearance_bits10.glass_type.Assign( | ||
| 170 | RawData::FromVer3GetGlassType(static_cast<u8>(store_data.GetGlassType()))); | ||
| 171 | |||
| 172 | crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16)); | ||
| 173 | } | ||
| 174 | |||
| 175 | u32 Ver3StoreData::IsValid() const { | ||
| 176 | bool is_valid = version == 0 || version == 3; | ||
| 177 | |||
| 178 | is_valid = is_valid && (mii_name.data[0] != '\0'); | ||
| 179 | |||
| 180 | is_valid = is_valid && (mii_information.birth_month < 13); | ||
| 181 | is_valid = is_valid && (mii_information.birth_day < 32); | ||
| 182 | is_valid = is_valid && (mii_information.favorite_color <= static_cast<u8>(FavoriteColor::Max)); | ||
| 183 | is_valid = is_valid && (height <= MaxHeight); | ||
| 184 | is_valid = is_valid && (build <= MaxBuild); | ||
| 185 | |||
| 186 | is_valid = is_valid && (appearance_bits1.faceline_type <= static_cast<u8>(FacelineType::Max)); | ||
| 187 | is_valid = is_valid && (appearance_bits1.faceline_color <= MaxVer3CommonColor - 2); | ||
| 188 | is_valid = | ||
| 189 | is_valid && (appearance_bits2.faceline_wrinkle <= static_cast<u8>(FacelineWrinkle::Max)); | ||
| 190 | is_valid = is_valid && (appearance_bits2.faceline_make <= static_cast<u8>(FacelineMake::Max)); | ||
| 191 | |||
| 192 | is_valid = is_valid && (hair_type <= static_cast<u8>(HairType::Max)); | ||
| 193 | is_valid = is_valid && (appearance_bits3.hair_color <= MaxVer3CommonColor); | ||
| 194 | |||
| 195 | is_valid = is_valid && (appearance_bits4.eye_type <= static_cast<u8>(EyeType::Max)); | ||
| 196 | is_valid = is_valid && (appearance_bits4.eye_color <= MaxVer3CommonColor - 2); | ||
| 197 | is_valid = is_valid && (appearance_bits4.eye_scale <= MaxEyeScale); | ||
| 198 | is_valid = is_valid && (appearance_bits4.eye_aspect <= MaxEyeAspect); | ||
| 199 | is_valid = is_valid && (appearance_bits4.eye_rotate <= MaxEyeRotate); | ||
| 200 | is_valid = is_valid && (appearance_bits4.eye_x <= MaxEyeX); | ||
| 201 | is_valid = is_valid && (appearance_bits4.eye_y <= MaxEyeY); | ||
| 202 | |||
| 203 | is_valid = is_valid && (appearance_bits5.eyebrow_type <= static_cast<u8>(EyebrowType::Max)); | ||
| 204 | is_valid = is_valid && (appearance_bits5.eyebrow_color <= MaxVer3CommonColor); | ||
| 205 | is_valid = is_valid && (appearance_bits5.eyebrow_scale <= MaxEyebrowScale); | ||
| 206 | is_valid = is_valid && (appearance_bits5.eyebrow_aspect <= MaxEyebrowAspect); | ||
| 207 | is_valid = is_valid && (appearance_bits5.eyebrow_rotate <= MaxEyebrowRotate); | ||
| 208 | is_valid = is_valid && (appearance_bits5.eyebrow_x <= MaxEyebrowX); | ||
| 209 | is_valid = is_valid && (appearance_bits5.eyebrow_y <= MaxEyebrowY); | ||
| 210 | |||
| 211 | is_valid = is_valid && (appearance_bits6.nose_type <= static_cast<u8>(NoseType::Max)); | ||
| 212 | is_valid = is_valid && (appearance_bits6.nose_scale <= MaxNoseScale); | ||
| 213 | is_valid = is_valid && (appearance_bits6.nose_y <= MaxNoseY); | ||
| 214 | |||
| 215 | is_valid = is_valid && (appearance_bits7.mouth_type <= static_cast<u8>(MouthType::Max)); | ||
| 216 | is_valid = is_valid && (appearance_bits7.mouth_color <= MaxVer3CommonColor - 3); | ||
| 217 | is_valid = is_valid && (appearance_bits7.mouth_scale <= MaxMouthScale); | ||
| 218 | is_valid = is_valid && (appearance_bits7.mouth_aspect <= MaxMoutAspect); | ||
| 219 | is_valid = is_valid && (appearance_bits8.mouth_y <= MaxMouthY); | ||
| 220 | |||
| 221 | is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max)); | ||
| 222 | is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale); | ||
| 223 | is_valid = is_valid && (appearance_bits9.mustache_y <= MasMustacheY); | ||
| 224 | |||
| 225 | is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max)); | ||
| 226 | is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor); | ||
| 227 | |||
| 228 | is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType); | ||
| 229 | is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2); | ||
| 230 | is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale); | ||
| 231 | is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassScale); | ||
| 232 | |||
| 233 | is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max)); | ||
| 234 | is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale); | ||
| 235 | is_valid = is_valid && (appearance_bits11.mole_x <= MaxMoleX); | ||
| 236 | is_valid = is_valid && (appearance_bits11.mole_y <= MaxMoleY); | ||
| 237 | |||
| 238 | return is_valid; | ||
| 239 | } | ||
| 240 | |||
| 241 | } // namespace Service::Mii | ||
diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h new file mode 100644 index 000000000..47907bf7d --- /dev/null +++ b/src/core/hle/service/mii/types/ver3_store_data.h | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/mii/mii_types.h" | ||
| 7 | |||
| 8 | namespace Service::Mii { | ||
| 9 | class StoreData; | ||
| 10 | |||
| 11 | // This is nn::mii::Ver3StoreData | ||
| 12 | // Based on citra HLE::Applets::MiiData and PretendoNetwork. | ||
| 13 | // https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48 | ||
| 14 | // https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 | ||
| 15 | |||
| 16 | struct NfpStoreDataExtension { | ||
| 17 | void SetFromStoreData(const StoreData& store_data); | ||
| 18 | |||
| 19 | u8 faceline_color; | ||
| 20 | u8 hair_color; | ||
| 21 | u8 eye_color; | ||
| 22 | u8 eyebrow_color; | ||
| 23 | u8 mouth_color; | ||
| 24 | u8 beard_color; | ||
| 25 | u8 glass_color; | ||
| 26 | u8 glass_type; | ||
| 27 | }; | ||
| 28 | static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size"); | ||
| 29 | |||
| 30 | #pragma pack(push, 4) | ||
| 31 | class Ver3StoreData { | ||
| 32 | public: | ||
| 33 | void BuildToStoreData(StoreData& out_store_data) const; | ||
| 34 | void BuildFromStoreData(const StoreData& store_data); | ||
| 35 | |||
| 36 | u32 IsValid() const; | ||
| 37 | |||
| 38 | u8 version; | ||
| 39 | union { | ||
| 40 | u8 raw; | ||
| 41 | |||
| 42 | BitField<0, 1, u8> allow_copying; | ||
| 43 | BitField<1, 1, u8> profanity_flag; | ||
| 44 | BitField<2, 2, u8> region_lock; | ||
| 45 | BitField<4, 2, u8> font_region; | ||
| 46 | } region_information; | ||
| 47 | u16_be mii_id; | ||
| 48 | u64_be system_id; | ||
| 49 | u32_be specialness_and_creation_date; | ||
| 50 | std::array<u8, 6> creator_mac; | ||
| 51 | u16_be padding; | ||
| 52 | union { | ||
| 53 | u16 raw; | ||
| 54 | |||
| 55 | BitField<0, 1, u16> gender; | ||
| 56 | BitField<1, 4, u16> birth_month; | ||
| 57 | BitField<5, 5, u16> birth_day; | ||
| 58 | BitField<10, 4, u16> favorite_color; | ||
| 59 | BitField<14, 1, u16> favorite; | ||
| 60 | } mii_information; | ||
| 61 | Nickname mii_name; | ||
| 62 | u8 height; | ||
| 63 | u8 build; | ||
| 64 | union { | ||
| 65 | u8 raw; | ||
| 66 | |||
| 67 | BitField<0, 1, u8> disable_sharing; | ||
| 68 | BitField<1, 4, u8> faceline_type; | ||
| 69 | BitField<5, 3, u8> faceline_color; | ||
| 70 | } appearance_bits1; | ||
| 71 | union { | ||
| 72 | u8 raw; | ||
| 73 | |||
| 74 | BitField<0, 4, u8> faceline_wrinkle; | ||
| 75 | BitField<4, 4, u8> faceline_make; | ||
| 76 | } appearance_bits2; | ||
| 77 | u8 hair_type; | ||
| 78 | union { | ||
| 79 | u8 raw; | ||
| 80 | |||
| 81 | BitField<0, 3, u8> hair_color; | ||
| 82 | BitField<3, 1, u8> hair_flip; | ||
| 83 | } appearance_bits3; | ||
| 84 | union { | ||
| 85 | u32 raw; | ||
| 86 | |||
| 87 | BitField<0, 6, u32> eye_type; | ||
| 88 | BitField<6, 3, u32> eye_color; | ||
| 89 | BitField<9, 4, u32> eye_scale; | ||
| 90 | BitField<13, 3, u32> eye_aspect; | ||
| 91 | BitField<16, 5, u32> eye_rotate; | ||
| 92 | BitField<21, 4, u32> eye_x; | ||
| 93 | BitField<25, 5, u32> eye_y; | ||
| 94 | } appearance_bits4; | ||
| 95 | union { | ||
| 96 | u32 raw; | ||
| 97 | |||
| 98 | BitField<0, 5, u32> eyebrow_type; | ||
| 99 | BitField<5, 3, u32> eyebrow_color; | ||
| 100 | BitField<8, 4, u32> eyebrow_scale; | ||
| 101 | BitField<12, 3, u32> eyebrow_aspect; | ||
| 102 | BitField<16, 4, u32> eyebrow_rotate; | ||
| 103 | BitField<21, 4, u32> eyebrow_x; | ||
| 104 | BitField<25, 5, u32> eyebrow_y; | ||
| 105 | } appearance_bits5; | ||
| 106 | union { | ||
| 107 | u16 raw; | ||
| 108 | |||
| 109 | BitField<0, 5, u16> nose_type; | ||
| 110 | BitField<5, 4, u16> nose_scale; | ||
| 111 | BitField<9, 5, u16> nose_y; | ||
| 112 | } appearance_bits6; | ||
| 113 | union { | ||
| 114 | u16 raw; | ||
| 115 | |||
| 116 | BitField<0, 6, u16> mouth_type; | ||
| 117 | BitField<6, 3, u16> mouth_color; | ||
| 118 | BitField<9, 4, u16> mouth_scale; | ||
| 119 | BitField<13, 3, u16> mouth_aspect; | ||
| 120 | } appearance_bits7; | ||
| 121 | union { | ||
| 122 | u8 raw; | ||
| 123 | |||
| 124 | BitField<0, 5, u8> mouth_y; | ||
| 125 | BitField<5, 3, u8> mustache_type; | ||
| 126 | } appearance_bits8; | ||
| 127 | u8 allow_copying; | ||
| 128 | union { | ||
| 129 | u16 raw; | ||
| 130 | |||
| 131 | BitField<0, 3, u16> beard_type; | ||
| 132 | BitField<3, 3, u16> beard_color; | ||
| 133 | BitField<6, 4, u16> mustache_scale; | ||
| 134 | BitField<10, 5, u16> mustache_y; | ||
| 135 | } appearance_bits9; | ||
| 136 | union { | ||
| 137 | u16 raw; | ||
| 138 | |||
| 139 | BitField<0, 4, u16> glass_type; | ||
| 140 | BitField<4, 3, u16> glass_color; | ||
| 141 | BitField<7, 4, u16> glass_scale; | ||
| 142 | BitField<11, 5, u16> glass_y; | ||
| 143 | } appearance_bits10; | ||
| 144 | union { | ||
| 145 | u16 raw; | ||
| 146 | |||
| 147 | BitField<0, 1, u16> mole_type; | ||
| 148 | BitField<1, 4, u16> mole_scale; | ||
| 149 | BitField<5, 5, u16> mole_x; | ||
| 150 | BitField<10, 5, u16> mole_y; | ||
| 151 | } appearance_bits11; | ||
| 152 | |||
| 153 | Nickname author_name; | ||
| 154 | INSERT_PADDING_BYTES(0x2); | ||
| 155 | u16_be crc; | ||
| 156 | }; | ||
| 157 | static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); | ||
| 158 | #pragma pack(pop) | ||
| 159 | |||
| 160 | }; // namespace Service::Mii | ||
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 49446bc42..5dda12343 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -28,7 +28,6 @@ | |||
| 28 | #include "core/hle/kernel/k_event.h" | 28 | #include "core/hle/kernel/k_event.h" |
| 29 | #include "core/hle/service/ipc_helpers.h" | 29 | #include "core/hle/service/ipc_helpers.h" |
| 30 | #include "core/hle/service/mii/mii_manager.h" | 30 | #include "core/hle/service/mii/mii_manager.h" |
| 31 | #include "core/hle/service/mii/types.h" | ||
| 32 | #include "core/hle/service/nfc/common/amiibo_crypto.h" | 31 | #include "core/hle/service/nfc/common/amiibo_crypto.h" |
| 33 | #include "core/hle/service/nfc/common/device.h" | 32 | #include "core/hle/service/nfc/common/device.h" |
| 34 | #include "core/hle/service/nfc/mifare_result.h" | 33 | #include "core/hle/service/nfc/mifare_result.h" |
| @@ -681,12 +680,16 @@ Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const { | |||
| 681 | return ResultRegistrationIsNotInitialized; | 680 | return ResultRegistrationIsNotInitialized; |
| 682 | } | 681 | } |
| 683 | 682 | ||
| 684 | Service::Mii::MiiManager manager; | 683 | Mii::CharInfo char_info{}; |
| 684 | Mii::StoreData store_data{}; | ||
| 685 | tag_data.owner_mii.BuildToStoreData(store_data); | ||
| 686 | char_info.SetFromStoreData(store_data); | ||
| 687 | |||
| 685 | const auto& settings = tag_data.settings; | 688 | const auto& settings = tag_data.settings; |
| 686 | 689 | ||
| 687 | // TODO: Validate this data | 690 | // TODO: Validate this data |
| 688 | register_info = { | 691 | register_info = { |
| 689 | .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), | 692 | .mii_char_info = char_info, |
| 690 | .creation_date = settings.init_date.GetWriteDate(), | 693 | .creation_date = settings.init_date.GetWriteDate(), |
| 691 | .amiibo_name = GetAmiiboName(settings), | 694 | .amiibo_name = GetAmiiboName(settings), |
| 692 | .font_region = settings.settings.font_region, | 695 | .font_region = settings.settings.font_region, |
| @@ -825,8 +828,11 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe | |||
| 825 | return ResultWrongDeviceState; | 828 | return ResultWrongDeviceState; |
| 826 | } | 829 | } |
| 827 | 830 | ||
| 828 | Service::Mii::MiiManager manager; | 831 | Service::Mii::StoreData store_data{}; |
| 829 | const auto mii = manager.BuildDefault(0); | 832 | Service::Mii::NfpStoreDataExtension extension{}; |
| 833 | store_data.BuildBase(Mii::Gender::Male); | ||
| 834 | extension.SetFromStoreData(store_data); | ||
| 835 | |||
| 830 | auto& settings = tag_data.settings; | 836 | auto& settings = tag_data.settings; |
| 831 | 837 | ||
| 832 | if (tag_data.settings.settings.amiibo_initialized == 0) { | 838 | if (tag_data.settings.settings.amiibo_initialized == 0) { |
| @@ -835,8 +841,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe | |||
| 835 | } | 841 | } |
| 836 | 842 | ||
| 837 | SetAmiiboName(settings, register_info.amiibo_name); | 843 | SetAmiiboName(settings, register_info.amiibo_name); |
| 838 | tag_data.owner_mii = manager.BuildFromStoreData(mii); | 844 | tag_data.owner_mii.BuildFromStoreData(store_data); |
| 839 | tag_data.mii_extension = manager.SetFromStoreData(mii); | 845 | tag_data.mii_extension = extension; |
| 840 | tag_data.unknown = 0; | 846 | tag_data.unknown = 0; |
| 841 | tag_data.unknown2 = {}; | 847 | tag_data.unknown2 = {}; |
| 842 | settings.country_code_id = 0; | 848 | settings.country_code_id = 0; |
| @@ -1453,7 +1459,7 @@ void NfcDevice::UpdateRegisterInfoCrc() { | |||
| 1453 | 1459 | ||
| 1454 | void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, | 1460 | void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, |
| 1455 | const NFP::EncryptedNTAG215File& encrypted_file) const { | 1461 | const NFP::EncryptedNTAG215File& encrypted_file) const { |
| 1456 | Service::Mii::MiiManager manager; | 1462 | Service::Mii::StoreData store_data{}; |
| 1457 | auto& settings = stubbed_tag_data.settings; | 1463 | auto& settings = stubbed_tag_data.settings; |
| 1458 | 1464 | ||
| 1459 | stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); | 1465 | stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); |
| @@ -1467,7 +1473,8 @@ void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, | |||
| 1467 | SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); | 1473 | SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); |
| 1468 | settings.settings.font_region.Assign(0); | 1474 | settings.settings.font_region.Assign(0); |
| 1469 | settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); | 1475 | settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); |
| 1470 | stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); | 1476 | store_data.BuildBase(Mii::Gender::Male); |
| 1477 | stubbed_tag_data.owner_mii.BuildFromStoreData(store_data); | ||
| 1471 | 1478 | ||
| 1472 | // Admin info | 1479 | // Admin info |
| 1473 | settings.settings.amiibo_initialized.Assign(1); | 1480 | settings.settings.amiibo_initialized.Assign(1); |
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index aed12a7f8..f96d21220 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h | |||
| @@ -6,7 +6,9 @@ | |||
| 6 | #include <array> | 6 | #include <array> |
| 7 | 7 | ||
| 8 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 9 | #include "core/hle/service/mii/types.h" | 9 | #include "core/hle/service/mii/types/char_info.h" |
| 10 | #include "core/hle/service/mii/types/store_data.h" | ||
| 11 | #include "core/hle/service/mii/types/ver3_store_data.h" | ||
| 10 | #include "core/hle/service/nfc/nfc_types.h" | 12 | #include "core/hle/service/nfc/nfc_types.h" |
| 11 | 13 | ||
| 12 | namespace Service::NFP { | 14 | namespace Service::NFP { |
| @@ -322,7 +324,7 @@ static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | |||
| 322 | 324 | ||
| 323 | // This is nn::nfp::RegisterInfoPrivate | 325 | // This is nn::nfp::RegisterInfoPrivate |
| 324 | struct RegisterInfoPrivate { | 326 | struct RegisterInfoPrivate { |
| 325 | Service::Mii::MiiStoreData mii_store_data; | 327 | Service::Mii::StoreData mii_store_data; |
| 326 | WriteDate creation_date; | 328 | WriteDate creation_date; |
| 327 | AmiiboName amiibo_name; | 329 | AmiiboName amiibo_name; |
| 328 | u8 font_region; | 330 | u8 font_region; |
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index a51ca5444..0ca05257e 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp | |||
| @@ -160,8 +160,8 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { | |||
| 160 | u32 address{}; | 160 | u32 address{}; |
| 161 | auto& smmu_allocator = host1x.Allocator(); | 161 | auto& smmu_allocator = host1x.Allocator(); |
| 162 | auto& smmu_memory_manager = host1x.MemoryManager(); | 162 | auto& smmu_memory_manager = host1x.MemoryManager(); |
| 163 | while (!(address = | 163 | while ((address = smmu_allocator.Allocate( |
| 164 | smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) { | 164 | static_cast<u32>(handle_description->aligned_size))) == 0) { |
| 165 | // Free handles until the allocation succeeds | 165 | // Free handles until the allocation succeeds |
| 166 | std::scoped_lock queueLock(unmap_queue_lock); | 166 | std::scoped_lock queueLock(unmap_queue_lock); |
| 167 | if (auto freeHandleDesc{unmap_queue.front()}) { | 167 | if (auto freeHandleDesc{unmap_queue.front()}) { |
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index bac21752a..491b76d48 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp | |||
| @@ -19,6 +19,12 @@ enum class ServerEnvironmentType : u8 { | |||
| 19 | Dp, | 19 | Dp, |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | // This is nn::nsd::EnvironmentIdentifier | ||
| 23 | struct EnvironmentIdentifier { | ||
| 24 | std::array<u8, 8> identifier; | ||
| 25 | }; | ||
| 26 | static_assert(sizeof(EnvironmentIdentifier) == 0x8); | ||
| 27 | |||
| 22 | NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { | 28 | NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { |
| 23 | // clang-format off | 29 | // clang-format off |
| 24 | static const FunctionInfo functions[] = { | 30 | static const FunctionInfo functions[] = { |
| @@ -101,8 +107,9 @@ void NSD::ResolveEx(HLERequestContext& ctx) { | |||
| 101 | } | 107 | } |
| 102 | 108 | ||
| 103 | void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) { | 109 | void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) { |
| 104 | const std::string environment_identifier = "lp1"; | 110 | constexpr EnvironmentIdentifier lp1 = { |
| 105 | ctx.WriteBuffer(environment_identifier); | 111 | .identifier = {'l', 'p', '1', '\0', '\0', '\0', '\0', '\0'}}; |
| 112 | ctx.WriteBuffer(lp1); | ||
| 106 | 113 | ||
| 107 | IPC::ResponseBuilder rb{ctx, 2}; | 114 | IPC::ResponseBuilder rb{ctx, 2}; |
| 108 | rb.Push(ResultSuccess); | 115 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index 22e4a6f49..c657c4efd 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp | |||
| @@ -150,6 +150,12 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte | |||
| 150 | const std::string host = Common::StringFromBuffer(host_buffer); | 150 | const std::string host = Common::StringFromBuffer(host_buffer); |
| 151 | // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. | 151 | // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. |
| 152 | 152 | ||
| 153 | // Prevent resolution of Nintendo servers | ||
| 154 | if (host.find("srv.nintendo.net") != std::string::npos) { | ||
| 155 | LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); | ||
| 156 | return {0, GetAddrInfoError::AGAIN}; | ||
| 157 | } | ||
| 158 | |||
| 153 | auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt); | 159 | auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt); |
| 154 | if (!res.has_value()) { | 160 | if (!res.has_value()) { |
| 155 | return {0, Translate(res.error())}; | 161 | return {0, Translate(res.error())}; |
| @@ -261,6 +267,12 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext | |||
| 261 | const auto host_buffer = ctx.ReadBuffer(0); | 267 | const auto host_buffer = ctx.ReadBuffer(0); |
| 262 | const std::string host = Common::StringFromBuffer(host_buffer); | 268 | const std::string host = Common::StringFromBuffer(host_buffer); |
| 263 | 269 | ||
| 270 | // Prevent resolution of Nintendo servers | ||
| 271 | if (host.find("srv.nintendo.net") != std::string::npos) { | ||
| 272 | LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); | ||
| 273 | return {0, GetAddrInfoError::AGAIN}; | ||
| 274 | } | ||
| 275 | |||
| 264 | std::optional<std::string> service = std::nullopt; | 276 | std::optional<std::string> service = std::nullopt; |
| 265 | if (ctx.CanReadBuffer(1)) { | 277 | if (ctx.CanReadBuffer(1)) { |
| 266 | const std::span<const u8> service_buffer = ctx.ReadBuffer(1); | 278 | const std::span<const u8> service_buffer = ctx.ReadBuffer(1); |
diff --git a/src/core/hle/service/ssl/ssl_backend_schannel.cpp b/src/core/hle/service/ssl/ssl_backend_schannel.cpp index d834a0c1f..212057cfc 100644 --- a/src/core/hle/service/ssl/ssl_backend_schannel.cpp +++ b/src/core/hle/service/ssl/ssl_backend_schannel.cpp | |||
| @@ -477,7 +477,8 @@ public: | |||
| 477 | return ResultInternalError; | 477 | return ResultInternalError; |
| 478 | } | 478 | } |
| 479 | PCCERT_CONTEXT some_cert = nullptr; | 479 | PCCERT_CONTEXT some_cert = nullptr; |
| 480 | while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) { | 480 | while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert)) != |
| 481 | nullptr) { | ||
| 481 | out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded), | 482 | out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded), |
| 482 | static_cast<u8*>(some_cert->pbCertEncoded) + | 483 | static_cast<u8*>(some_cert->pbCertEncoded) + |
| 483 | some_cert->cbCertEncoded); | 484 | some_cert->cbCertEncoded); |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 6bb02393c..2eb978379 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -217,7 +217,7 @@ private: | |||
| 217 | IPC::ResponseBuilder rb{ctx, 6}; | 217 | IPC::ResponseBuilder rb{ctx, 6}; |
| 218 | rb.Push(ResultSuccess); | 218 | rb.Push(ResultSuccess); |
| 219 | 219 | ||
| 220 | if (Settings::values.use_docked_mode.GetValue()) { | 220 | if (Settings::IsDockedMode()) { |
| 221 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); | 221 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); |
| 222 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); | 222 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); |
| 223 | } else { | 223 | } else { |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 07c65dc1a..b6e355622 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -108,7 +108,7 @@ std::string GetFileTypeString(FileType type) { | |||
| 108 | return "unknown"; | 108 | return "unknown"; |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | constexpr std::array<const char*, 66> RESULT_MESSAGES{ | 111 | constexpr std::array<const char*, 68> RESULT_MESSAGES{ |
| 112 | "The operation completed successfully.", | 112 | "The operation completed successfully.", |
| 113 | "The loader requested to load is already loaded.", | 113 | "The loader requested to load is already loaded.", |
| 114 | "The operation is not implemented.", | 114 | "The operation is not implemented.", |
| @@ -175,6 +175,8 @@ constexpr std::array<const char*, 66> RESULT_MESSAGES{ | |||
| 175 | "The KIP BLZ decompression of the section failed unexpectedly.", | 175 | "The KIP BLZ decompression of the section failed unexpectedly.", |
| 176 | "The INI file has a bad header.", | 176 | "The INI file has a bad header.", |
| 177 | "The INI file contains more than the maximum allowable number of KIP files.", | 177 | "The INI file contains more than the maximum allowable number of KIP files.", |
| 178 | "Integrity verification could not be performed for this file.", | ||
| 179 | "Integrity verification failed.", | ||
| 178 | }; | 180 | }; |
| 179 | 181 | ||
| 180 | std::string GetResultStatusString(ResultStatus status) { | 182 | std::string GetResultStatusString(ResultStatus status) { |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 721eb8e8c..b4828f7cd 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <functional> | ||
| 6 | #include <iosfwd> | 7 | #include <iosfwd> |
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <optional> | 9 | #include <optional> |
| @@ -132,6 +133,8 @@ enum class ResultStatus : u16 { | |||
| 132 | ErrorBLZDecompressionFailed, | 133 | ErrorBLZDecompressionFailed, |
| 133 | ErrorBadINIHeader, | 134 | ErrorBadINIHeader, |
| 134 | ErrorINITooManyKIPs, | 135 | ErrorINITooManyKIPs, |
| 136 | ErrorIntegrityVerificationNotImplemented, | ||
| 137 | ErrorIntegrityVerificationFailed, | ||
| 135 | }; | 138 | }; |
| 136 | 139 | ||
| 137 | std::string GetResultStatusString(ResultStatus status); | 140 | std::string GetResultStatusString(ResultStatus status); |
| @@ -170,6 +173,13 @@ public: | |||
| 170 | virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0; | 173 | virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0; |
| 171 | 174 | ||
| 172 | /** | 175 | /** |
| 176 | * Try to verify the integrity of the file. | ||
| 177 | */ | ||
| 178 | virtual ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) { | ||
| 179 | return ResultStatus::ErrorIntegrityVerificationNotImplemented; | ||
| 180 | } | ||
| 181 | |||
| 182 | /** | ||
| 173 | * Get the code (typically .code section) of the application | 183 | * Get the code (typically .code section) of the application |
| 174 | * | 184 | * |
| 175 | * @param[out] buffer Reference to buffer to store data | 185 | * @param[out] buffer Reference to buffer to store data |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 09d40e695..4feb6968a 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #include <utility> | 4 | #include <utility> |
| 5 | 5 | ||
| 6 | #include "common/hex_util.h" | ||
| 7 | #include "common/scope_exit.h" | ||
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| 7 | #include "core/file_sys/content_archive.h" | 9 | #include "core/file_sys/content_archive.h" |
| 8 | #include "core/file_sys/nca_metadata.h" | 10 | #include "core/file_sys/nca_metadata.h" |
| @@ -12,6 +14,7 @@ | |||
| 12 | #include "core/hle/service/filesystem/filesystem.h" | 14 | #include "core/hle/service/filesystem/filesystem.h" |
| 13 | #include "core/loader/deconstructed_rom_directory.h" | 15 | #include "core/loader/deconstructed_rom_directory.h" |
| 14 | #include "core/loader/nca.h" | 16 | #include "core/loader/nca.h" |
| 17 | #include "mbedtls/sha256.h" | ||
| 15 | 18 | ||
| 16 | namespace Loader { | 19 | namespace Loader { |
| 17 | 20 | ||
| @@ -80,6 +83,79 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S | |||
| 80 | return load_result; | 83 | return load_result; |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 86 | ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) { | ||
| 87 | using namespace Common::Literals; | ||
| 88 | |||
| 89 | constexpr size_t NcaFileNameWithHashLength = 36; | ||
| 90 | constexpr size_t NcaFileNameHashLength = 32; | ||
| 91 | constexpr size_t NcaSha256HashLength = 32; | ||
| 92 | constexpr size_t NcaSha256HalfHashLength = NcaSha256HashLength / 2; | ||
| 93 | |||
| 94 | // Get the file name. | ||
| 95 | const auto name = file->GetName(); | ||
| 96 | |||
| 97 | // We won't try to verify meta NCAs. | ||
| 98 | if (name.ends_with(".cnmt.nca")) { | ||
| 99 | return ResultStatus::Success; | ||
| 100 | } | ||
| 101 | |||
| 102 | // Check if we can verify this file. NCAs should be named after their hashes. | ||
| 103 | if (!name.ends_with(".nca") || name.size() != NcaFileNameWithHashLength) { | ||
| 104 | LOG_WARNING(Loader, "Unable to validate NCA with name {}", name); | ||
| 105 | return ResultStatus::ErrorIntegrityVerificationNotImplemented; | ||
| 106 | } | ||
| 107 | |||
| 108 | // Get the expected truncated hash of the NCA. | ||
| 109 | const auto input_hash = | ||
| 110 | Common::HexStringToVector(file->GetName().substr(0, NcaFileNameHashLength), false); | ||
| 111 | |||
| 112 | // Declare buffer to read into. | ||
| 113 | std::vector<u8> buffer(4_MiB); | ||
| 114 | |||
| 115 | // Initialize sha256 verification context. | ||
| 116 | mbedtls_sha256_context ctx; | ||
| 117 | mbedtls_sha256_init(&ctx); | ||
| 118 | mbedtls_sha256_starts_ret(&ctx, 0); | ||
| 119 | |||
| 120 | // Ensure we maintain a clean state on exit. | ||
| 121 | SCOPE_EXIT({ mbedtls_sha256_free(&ctx); }); | ||
| 122 | |||
| 123 | // Declare counters. | ||
| 124 | const size_t total_size = file->GetSize(); | ||
| 125 | size_t processed_size = 0; | ||
| 126 | |||
| 127 | // Begin iterating the file. | ||
| 128 | while (processed_size < total_size) { | ||
| 129 | // Refill the buffer. | ||
| 130 | const size_t intended_read_size = std::min(buffer.size(), total_size - processed_size); | ||
| 131 | const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size); | ||
| 132 | |||
| 133 | // Update the hash function with the buffer contents. | ||
| 134 | mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size); | ||
| 135 | |||
| 136 | // Update counters. | ||
| 137 | processed_size += read_size; | ||
| 138 | |||
| 139 | // Call the progress function. | ||
| 140 | if (!progress_callback(processed_size, total_size)) { | ||
| 141 | return ResultStatus::ErrorIntegrityVerificationFailed; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | // Finalize context and compute the output hash. | ||
| 146 | std::array<u8, NcaSha256HashLength> output_hash; | ||
| 147 | mbedtls_sha256_finish_ret(&ctx, output_hash.data()); | ||
| 148 | |||
| 149 | // Compare to expected. | ||
| 150 | if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) { | ||
| 151 | LOG_ERROR(Loader, "NCA hash mismatch detected for file {}", name); | ||
| 152 | return ResultStatus::ErrorIntegrityVerificationFailed; | ||
| 153 | } | ||
| 154 | |||
| 155 | // File verified. | ||
| 156 | return ResultStatus::Success; | ||
| 157 | } | ||
| 158 | |||
| 83 | ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { | 159 | ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { |
| 84 | if (nca == nullptr) { | 160 | if (nca == nullptr) { |
| 85 | return ResultStatus::ErrorNotInitialized; | 161 | return ResultStatus::ErrorNotInitialized; |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index cf356ce63..96779e27f 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -39,6 +39,8 @@ public: | |||
| 39 | 39 | ||
| 40 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | 40 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; |
| 41 | 41 | ||
| 42 | ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) override; | ||
| 43 | |||
| 42 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 44 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 45 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 44 | 46 | ||
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index f9b2549a3..fe2af1ae6 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -117,6 +117,42 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S | |||
| 117 | return result; | 117 | return result; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | ResultStatus AppLoader_NSP::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) { | ||
| 121 | // Extracted-type NSPs can't be verified. | ||
| 122 | if (nsp->IsExtractedType()) { | ||
| 123 | return ResultStatus::ErrorIntegrityVerificationNotImplemented; | ||
| 124 | } | ||
| 125 | |||
| 126 | // Get list of all NCAs. | ||
| 127 | const auto ncas = nsp->GetNCAsCollapsed(); | ||
| 128 | |||
| 129 | size_t total_size = 0; | ||
| 130 | size_t processed_size = 0; | ||
| 131 | |||
| 132 | // Loop over NCAs, collecting the total size to verify. | ||
| 133 | for (const auto& nca : ncas) { | ||
| 134 | total_size += nca->GetBaseFile()->GetSize(); | ||
| 135 | } | ||
| 136 | |||
| 137 | // Loop over NCAs again, verifying each. | ||
| 138 | for (const auto& nca : ncas) { | ||
| 139 | AppLoader_NCA loader_nca(nca->GetBaseFile()); | ||
| 140 | |||
| 141 | const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) { | ||
| 142 | return progress_callback(processed_size + nca_processed_size, total_size); | ||
| 143 | }; | ||
| 144 | |||
| 145 | const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback); | ||
| 146 | if (verification_result != ResultStatus::Success) { | ||
| 147 | return verification_result; | ||
| 148 | } | ||
| 149 | |||
| 150 | processed_size += nca->GetBaseFile()->GetSize(); | ||
| 151 | } | ||
| 152 | |||
| 153 | return ResultStatus::Success; | ||
| 154 | } | ||
| 155 | |||
| 120 | ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) { | 156 | ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) { |
| 121 | return secondary_loader->ReadRomFS(out_file); | 157 | return secondary_loader->ReadRomFS(out_file); |
| 122 | } | 158 | } |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 79df4586a..7ce436c67 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -45,6 +45,8 @@ public: | |||
| 45 | 45 | ||
| 46 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | 46 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; |
| 47 | 47 | ||
| 48 | ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) override; | ||
| 49 | |||
| 48 | ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; | 50 | ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; |
| 49 | ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; | 51 | ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; |
| 50 | ResultStatus ReadProgramId(u64& out_program_id) override; | 52 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 3a76bc788..12d72c380 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -85,6 +85,40 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S | |||
| 85 | return result; | 85 | return result; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | ResultStatus AppLoader_XCI::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) { | ||
| 89 | // Verify secure partition, as it is the only thing we can process. | ||
| 90 | auto secure_partition = xci->GetSecurePartitionNSP(); | ||
| 91 | |||
| 92 | // Get list of all NCAs. | ||
| 93 | const auto ncas = secure_partition->GetNCAsCollapsed(); | ||
| 94 | |||
| 95 | size_t total_size = 0; | ||
| 96 | size_t processed_size = 0; | ||
| 97 | |||
| 98 | // Loop over NCAs, collecting the total size to verify. | ||
| 99 | for (const auto& nca : ncas) { | ||
| 100 | total_size += nca->GetBaseFile()->GetSize(); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Loop over NCAs again, verifying each. | ||
| 104 | for (const auto& nca : ncas) { | ||
| 105 | AppLoader_NCA loader_nca(nca->GetBaseFile()); | ||
| 106 | |||
| 107 | const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) { | ||
| 108 | return progress_callback(processed_size + nca_processed_size, total_size); | ||
| 109 | }; | ||
| 110 | |||
| 111 | const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback); | ||
| 112 | if (verification_result != ResultStatus::Success) { | ||
| 113 | return verification_result; | ||
| 114 | } | ||
| 115 | |||
| 116 | processed_size += nca->GetBaseFile()->GetSize(); | ||
| 117 | } | ||
| 118 | |||
| 119 | return ResultStatus::Success; | ||
| 120 | } | ||
| 121 | |||
| 88 | ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) { | 122 | ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) { |
| 89 | return nca_loader->ReadRomFS(out_file); | 123 | return nca_loader->ReadRomFS(out_file); |
| 90 | } | 124 | } |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index ff05e6f62..b02e136d3 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -45,6 +45,8 @@ public: | |||
| 45 | 45 | ||
| 46 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | 46 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; |
| 47 | 47 | ||
| 48 | ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) override; | ||
| 49 | |||
| 48 | ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; | 50 | ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; |
| 49 | ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; | 51 | ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; |
| 50 | ResultStatus ReadProgramId(u64& out_program_id) override; | 52 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 62b3f6636..c26179e03 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 15 | 15 | ||
| 16 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 17 | #include "common/settings_enums.h" | ||
| 17 | #include "core/file_sys/control_metadata.h" | 18 | #include "core/file_sys/control_metadata.h" |
| 18 | #include "core/file_sys/patch_manager.h" | 19 | #include "core/file_sys/patch_manager.h" |
| 19 | #include "core/loader/loader.h" | 20 | #include "core/loader/loader.h" |
| @@ -275,7 +276,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, | |||
| 275 | static_cast<u32>(Settings::values.shader_backend.GetValue())); | 276 | static_cast<u32>(Settings::values.shader_backend.GetValue())); |
| 276 | AddField(field_type, "Renderer_UseAsynchronousShaders", | 277 | AddField(field_type, "Renderer_UseAsynchronousShaders", |
| 277 | Settings::values.use_asynchronous_shaders.GetValue()); | 278 | Settings::values.use_asynchronous_shaders.GetValue()); |
| 278 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue()); | 279 | AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode()); |
| 279 | } | 280 | } |
| 280 | 281 | ||
| 281 | bool TelemetrySession::SubmitTestcase() { | 282 | bool TelemetrySession::SubmitTestcase() { |
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index d707dabe2..93038f161 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp | |||
| @@ -368,9 +368,9 @@ int main(int argc, char** argv) { | |||
| 368 | if (auto room = network.GetRoom().lock()) { | 368 | if (auto room = network.GetRoom().lock()) { |
| 369 | AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game, | 369 | AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game, |
| 370 | .id = preferred_game_id}; | 370 | .id = preferred_game_id}; |
| 371 | if (!room->Create(room_name, room_description, bind_address, port, password, max_members, | 371 | if (!room->Create(room_name, room_description, bind_address, static_cast<u16>(port), |
| 372 | username, preferred_game_info, std::move(verify_backend), ban_list, | 372 | password, max_members, username, preferred_game_info, |
| 373 | enable_yuzu_mods)) { | 373 | std::move(verify_backend), ban_list, enable_yuzu_mods)) { |
| 374 | LOG_INFO(Network, "Failed to create room: "); | 374 | LOG_INFO(Network, "Failed to create room: "); |
| 375 | return -1; | 375 | return -1; |
| 376 | } | 376 | } |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 322c29065..5c127c8ef 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -37,8 +37,6 @@ add_library(input_common STATIC | |||
| 37 | 37 | ||
| 38 | if (MSVC) | 38 | if (MSVC) |
| 39 | target_compile_options(input_common PRIVATE | 39 | target_compile_options(input_common PRIVATE |
| 40 | /W4 | ||
| 41 | |||
| 42 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data | 40 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data |
| 43 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 41 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 44 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | 42 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss |
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 870e76ab0..188e862d7 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -835,15 +835,15 @@ public: | |||
| 835 | return input_engine->SupportsNfc(identifier); | 835 | return input_engine->SupportsNfc(identifier); |
| 836 | } | 836 | } |
| 837 | 837 | ||
| 838 | Common::Input::NfcState StartNfcPolling() { | 838 | Common::Input::NfcState StartNfcPolling() override { |
| 839 | return input_engine->StartNfcPolling(identifier); | 839 | return input_engine->StartNfcPolling(identifier); |
| 840 | } | 840 | } |
| 841 | 841 | ||
| 842 | Common::Input::NfcState StopNfcPolling() { | 842 | Common::Input::NfcState StopNfcPolling() override { |
| 843 | return input_engine->StopNfcPolling(identifier); | 843 | return input_engine->StopNfcPolling(identifier); |
| 844 | } | 844 | } |
| 845 | 845 | ||
| 846 | Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { | 846 | Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override { |
| 847 | return input_engine->ReadAmiiboData(identifier, out_data); | 847 | return input_engine->ReadAmiiboData(identifier, out_data); |
| 848 | } | 848 | } |
| 849 | 849 | ||
| @@ -852,11 +852,11 @@ public: | |||
| 852 | } | 852 | } |
| 853 | 853 | ||
| 854 | Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, | 854 | Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, |
| 855 | Common::Input::MifareRequest& out_data) { | 855 | Common::Input::MifareRequest& out_data) override { |
| 856 | return input_engine->ReadMifareData(identifier, request, out_data); | 856 | return input_engine->ReadMifareData(identifier, request, out_data); |
| 857 | } | 857 | } |
| 858 | 858 | ||
| 859 | Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { | 859 | Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override { |
| 860 | return input_engine->WriteMifareData(identifier, request); | 860 | return input_engine->WriteMifareData(identifier, request); |
| 861 | } | 861 | } |
| 862 | 862 | ||
diff --git a/src/network/room.cpp b/src/network/room.cpp index e456ea09c..d87db37de 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp | |||
| @@ -805,7 +805,7 @@ IPv4Address Room::RoomImpl::GenerateFakeIPAddress() { | |||
| 805 | std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE | 805 | std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE |
| 806 | do { | 806 | do { |
| 807 | for (std::size_t i = 2; i < result_ip.size(); ++i) { | 807 | for (std::size_t i = 2; i < result_ip.size(); ++i) { |
| 808 | result_ip[i] = dis(random_gen); | 808 | result_ip[i] = static_cast<u8>(dis(random_gen)); |
| 809 | } | 809 | } |
| 810 | } while (!IsValidFakeIPAddress(result_ip)); | 810 | } while (!IsValidFakeIPAddress(result_ip)); |
| 811 | 811 | ||
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 07e75f9d8..83b763447 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -245,8 +245,6 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit) | |||
| 245 | 245 | ||
| 246 | if (MSVC) | 246 | if (MSVC) |
| 247 | target_compile_options(shader_recompiler PRIVATE | 247 | target_compile_options(shader_recompiler PRIVATE |
| 248 | /W4 | ||
| 249 | |||
| 250 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data | 248 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data |
| 251 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 249 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 252 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | 250 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index 3ad668a47..d9872ecc2 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp | |||
| @@ -558,7 +558,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 558 | if (multi_component) { | 558 | if (multi_component) { |
| 559 | if (info.num_derivates >= 3) { | 559 | if (info.num_derivates >= 3) { |
| 560 | const auto offset_vec{ctx.var_alloc.Consume(offset)}; | 560 | const auto offset_vec{ctx.var_alloc.Consume(offset)}; |
| 561 | ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yz, {}.y));", texel, texture, | 561 | ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, |
| 562 | coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); | 562 | coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); |
| 563 | return; | 563 | return; |
| 564 | } | 564 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 7d901c04b..34240b36f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -91,6 +91,34 @@ public: | |||
| 91 | } | 91 | } |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, | ||
| 95 | Id offset, Id lod_clamp) { | ||
| 96 | if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { | ||
| 97 | throw LogicError("Derivates must be present"); | ||
| 98 | } | ||
| 99 | boost::container::static_vector<Id, 3> deriv_1_accum{ | ||
| 100 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), | ||
| 101 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), | ||
| 102 | ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), | ||
| 103 | }; | ||
| 104 | boost::container::static_vector<Id, 3> deriv_2_accum{ | ||
| 105 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), | ||
| 106 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), | ||
| 107 | ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), | ||
| 108 | }; | ||
| 109 | const Id derivates_id1{ctx.OpCompositeConstruct( | ||
| 110 | ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; | ||
| 111 | const Id derivates_id2{ctx.OpCompositeConstruct( | ||
| 112 | ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; | ||
| 113 | Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); | ||
| 114 | if (Sirit::ValidId(offset)) { | ||
| 115 | Add(spv::ImageOperandsMask::Offset, offset); | ||
| 116 | } | ||
| 117 | if (has_lod_clamp) { | ||
| 118 | Add(spv::ImageOperandsMask::MinLod, lod_clamp); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 94 | std::span<const Id> Span() const noexcept { | 122 | std::span<const Id> Span() const noexcept { |
| 95 | return std::span{operands.data(), operands.size()}; | 123 | return std::span{operands.data(), operands.size()}; |
| 96 | } | 124 | } |
| @@ -524,8 +552,11 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I | |||
| 524 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | 552 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 525 | Id derivates, Id offset, Id lod_clamp) { | 553 | Id derivates, Id offset, Id lod_clamp) { |
| 526 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 554 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 527 | const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, | 555 | const auto operands = |
| 528 | offset, lod_clamp); | 556 | info.num_derivates == 3 |
| 557 | ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) | ||
| 558 | : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, | ||
| 559 | lod_clamp); | ||
| 529 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, | 560 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, |
| 530 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], | 561 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], |
| 531 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); | 562 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index bec5db173..238fb40e3 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -74,6 +74,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) { | |||
| 74 | throw InvalidArgument("Invalid image format {}", format); | 74 | throw InvalidArgument("Invalid image format {}", format); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | spv::ImageFormat GetImageFormatForBuffer(ImageFormat format) { | ||
| 78 | const auto spv_format = GetImageFormat(format); | ||
| 79 | return spv_format == spv::ImageFormat::Unknown ? spv::ImageFormat::R32ui : spv_format; | ||
| 80 | } | ||
| 81 | |||
| 77 | Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { | 82 | Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { |
| 78 | const spv::ImageFormat format{GetImageFormat(desc.format)}; | 83 | const spv::ImageFormat format{GetImageFormat(desc.format)}; |
| 79 | const Id type{ctx.U32[1]}; | 84 | const Id type{ctx.U32[1]}; |
| @@ -1271,7 +1276,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) { | |||
| 1271 | if (desc.count != 1) { | 1276 | if (desc.count != 1) { |
| 1272 | throw NotImplementedException("Array of image buffers"); | 1277 | throw NotImplementedException("Array of image buffers"); |
| 1273 | } | 1278 | } |
| 1274 | const spv::ImageFormat format{GetImageFormat(desc.format)}; | 1279 | const spv::ImageFormat format{GetImageFormatForBuffer(desc.format)}; |
| 1275 | const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; | 1280 | const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; |
| 1276 | const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; | 1281 | const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; |
| 1277 | const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; | 1282 | const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp index 753c62098..e593132e6 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp | |||
| @@ -161,7 +161,8 @@ enum class SpecialRegister : u64 { | |||
| 161 | LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY"); | 161 | LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY"); |
| 162 | return ir.Imm32(0); // This is the default value hardware returns. | 162 | return ir.Imm32(0); // This is the default value hardware returns. |
| 163 | default: | 163 | default: |
| 164 | throw NotImplementedException("S2R special register {}", special_register); | 164 | LOG_CRITICAL(Shader, "(STUBBED) Special register {}", special_register); |
| 165 | return ir.Imm32(0); // This is the default value hardware returns. | ||
| 165 | } | 166 | } |
| 166 | } | 167 | } |
| 167 | } // Anonymous namespace | 168 | } // Anonymous namespace |
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index e85f9977b..b6e3bc875 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp | |||
| @@ -55,7 +55,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { | |||
| 55 | // Pushing more values than space available should partially succeed. | 55 | // Pushing more values than space available should partially succeed. |
| 56 | { | 56 | { |
| 57 | std::vector<char> to_push(6); | 57 | std::vector<char> to_push(6); |
| 58 | std::iota(to_push.begin(), to_push.end(), 88); | 58 | std::iota(to_push.begin(), to_push.end(), static_cast<char>(88)); |
| 59 | const std::size_t count = buf.Push(to_push); | 59 | const std::size_t count = buf.Push(to_push); |
| 60 | REQUIRE(count == 3U); | 60 | REQUIRE(count == 3U); |
| 61 | } | 61 | } |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index f0f450edb..8be7bd594 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -289,8 +289,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad | |||
| 289 | MarkWrittenBuffer(buffer_id, *cpu_addr, size); | 289 | MarkWrittenBuffer(buffer_id, *cpu_addr, size); |
| 290 | break; | 290 | break; |
| 291 | case ObtainBufferOperation::DiscardWrite: { | 291 | case ObtainBufferOperation::DiscardWrite: { |
| 292 | IntervalType interval{*cpu_addr, size}; | 292 | VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64); |
| 293 | VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64); | ||
| 294 | IntervalType interval{cpu_addr_start, cpu_addr_end}; | ||
| 293 | ClearDownload(interval); | 295 | ClearDownload(interval); |
| 296 | common_ranges.subtract(interval); | ||
| 294 | break; | 297 | break; |
| 295 | } | 298 | } |
| 296 | default: | 299 | default: |
| @@ -1159,6 +1162,11 @@ void BufferCache<P>::UpdateDrawIndirect() { | |||
| 1159 | .size = static_cast<u32>(size), | 1162 | .size = static_cast<u32>(size), |
| 1160 | .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), | 1163 | .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), |
| 1161 | }; | 1164 | }; |
| 1165 | VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64); | ||
| 1166 | VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64); | ||
| 1167 | IntervalType interval{cpu_addr_start, cpu_addr_end}; | ||
| 1168 | ClearDownload(interval); | ||
| 1169 | common_ranges.subtract(interval); | ||
| 1162 | }; | 1170 | }; |
| 1163 | if (current_draw_indirect->include_count) { | 1171 | if (current_draw_indirect->include_count) { |
| 1164 | update(current_draw_indirect->count_start_address, sizeof(u32), | 1172 | update(current_draw_indirect->count_start_address, sizeof(u32), |
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 9f1b340a9..58ce0d8c2 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | namespace Tegra { | 14 | namespace Tegra { |
| 15 | 15 | ||
| 16 | constexpr u32 MacroRegistersStart = 0xE00; | 16 | constexpr u32 MacroRegistersStart = 0xE00; |
| 17 | constexpr u32 ComputeInline = 0x6D; | ||
| 17 | 18 | ||
| 18 | DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, | 19 | DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, |
| 19 | Control::ChannelState& channel_state_) | 20 | Control::ChannelState& channel_state_) |
| @@ -83,12 +84,35 @@ bool DmaPusher::Step() { | |||
| 83 | dma_state.dma_get, command_list_header.size * sizeof(u32)); | 84 | dma_state.dma_get, command_list_header.size * sizeof(u32)); |
| 84 | } | 85 | } |
| 85 | } | 86 | } |
| 86 | Core::Memory::GpuGuestMemory<Tegra::CommandHeader, | 87 | const auto safe_process = [&] { |
| 87 | Core::Memory::GuestMemoryFlags::UnsafeRead> | 88 | Core::Memory::GpuGuestMemory<Tegra::CommandHeader, |
| 88 | headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers); | 89 | Core::Memory::GuestMemoryFlags::SafeRead> |
| 89 | ProcessCommands(headers); | 90 | headers(memory_manager, dma_state.dma_get, command_list_header.size, |
| 91 | &command_headers); | ||
| 92 | ProcessCommands(headers); | ||
| 93 | }; | ||
| 94 | const auto unsafe_process = [&] { | ||
| 95 | Core::Memory::GpuGuestMemory<Tegra::CommandHeader, | ||
| 96 | Core::Memory::GuestMemoryFlags::UnsafeRead> | ||
| 97 | headers(memory_manager, dma_state.dma_get, command_list_header.size, | ||
| 98 | &command_headers); | ||
| 99 | ProcessCommands(headers); | ||
| 100 | }; | ||
| 101 | if (Settings::IsGPULevelHigh()) { | ||
| 102 | if (dma_state.method >= MacroRegistersStart) { | ||
| 103 | unsafe_process(); | ||
| 104 | return true; | ||
| 105 | } | ||
| 106 | if (subchannel_type[dma_state.subchannel] == Engines::EngineTypes::KeplerCompute && | ||
| 107 | dma_state.method == ComputeInline) { | ||
| 108 | unsafe_process(); | ||
| 109 | return true; | ||
| 110 | } | ||
| 111 | safe_process(); | ||
| 112 | return true; | ||
| 113 | } | ||
| 114 | unsafe_process(); | ||
| 90 | } | 115 | } |
| 91 | |||
| 92 | return true; | 116 | return true; |
| 93 | } | 117 | } |
| 94 | 118 | ||
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 8a2784cdc..c9fab2d90 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h | |||
| @@ -130,8 +130,10 @@ public: | |||
| 130 | 130 | ||
| 131 | void DispatchCalls(); | 131 | void DispatchCalls(); |
| 132 | 132 | ||
| 133 | void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id) { | 133 | void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, |
| 134 | Engines::EngineTypes engine_type) { | ||
| 134 | subchannels[subchannel_id] = engine; | 135 | subchannels[subchannel_id] = engine; |
| 136 | subchannel_type[subchannel_id] = engine_type; | ||
| 135 | } | 137 | } |
| 136 | 138 | ||
| 137 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); | 139 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); |
| @@ -170,6 +172,7 @@ private: | |||
| 170 | const bool ib_enable{true}; ///< IB mode enabled | 172 | const bool ib_enable{true}; ///< IB mode enabled |
| 171 | 173 | ||
| 172 | std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; | 174 | std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; |
| 175 | std::array<Engines::EngineTypes, max_subchannels> subchannel_type; | ||
| 173 | 176 | ||
| 174 | GPU& gpu; | 177 | GPU& gpu; |
| 175 | Core::System& system; | 178 | Core::System& system; |
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h index 392322358..54631ee6c 100644 --- a/src/video_core/engines/engine_interface.h +++ b/src/video_core/engines/engine_interface.h | |||
| @@ -11,6 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | namespace Tegra::Engines { | 12 | namespace Tegra::Engines { |
| 13 | 13 | ||
| 14 | enum class EngineTypes : u32 { | ||
| 15 | KeplerCompute, | ||
| 16 | Maxwell3D, | ||
| 17 | Fermi2D, | ||
| 18 | MaxwellDMA, | ||
| 19 | KeplerMemory, | ||
| 20 | }; | ||
| 21 | |||
| 14 | class EngineInterface { | 22 | class EngineInterface { |
| 15 | public: | 23 | public: |
| 16 | virtual ~EngineInterface() = default; | 24 | virtual ~EngineInterface() = default; |
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 7242d2529..21bf8aeb4 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h | |||
| @@ -69,6 +69,14 @@ public: | |||
| 69 | /// Binds a rasterizer to this engine. | 69 | /// Binds a rasterizer to this engine. |
| 70 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); | 70 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); |
| 71 | 71 | ||
| 72 | GPUVAddr ExecTargetAddress() const { | ||
| 73 | return regs.dest.Address(); | ||
| 74 | } | ||
| 75 | |||
| 76 | u32 GetUploadSize() const { | ||
| 77 | return copy_size; | ||
| 78 | } | ||
| 79 | |||
| 72 | private: | 80 | private: |
| 73 | void ProcessData(std::span<const u8> read_buffer); | 81 | void ProcessData(std::span<const u8> read_buffer); |
| 74 | 82 | ||
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index a38d9528a..cd61ab222 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -43,16 +43,33 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal | |||
| 43 | 43 | ||
| 44 | switch (method) { | 44 | switch (method) { |
| 45 | case KEPLER_COMPUTE_REG_INDEX(exec_upload): { | 45 | case KEPLER_COMPUTE_REG_INDEX(exec_upload): { |
| 46 | UploadInfo info{.upload_address = upload_address, | ||
| 47 | .exec_address = upload_state.ExecTargetAddress(), | ||
| 48 | .copy_size = upload_state.GetUploadSize()}; | ||
| 49 | uploads.push_back(info); | ||
| 46 | upload_state.ProcessExec(regs.exec_upload.linear != 0); | 50 | upload_state.ProcessExec(regs.exec_upload.linear != 0); |
| 47 | break; | 51 | break; |
| 48 | } | 52 | } |
| 49 | case KEPLER_COMPUTE_REG_INDEX(data_upload): { | 53 | case KEPLER_COMPUTE_REG_INDEX(data_upload): { |
| 54 | upload_address = current_dma_segment; | ||
| 50 | upload_state.ProcessData(method_argument, is_last_call); | 55 | upload_state.ProcessData(method_argument, is_last_call); |
| 51 | break; | 56 | break; |
| 52 | } | 57 | } |
| 53 | case KEPLER_COMPUTE_REG_INDEX(launch): | 58 | case KEPLER_COMPUTE_REG_INDEX(launch): { |
| 59 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); | ||
| 60 | |||
| 61 | for (auto& data : uploads) { | ||
| 62 | const GPUVAddr offset = data.exec_address - launch_desc_loc; | ||
| 63 | if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) && | ||
| 64 | memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) { | ||
| 65 | indirect_compute = {data.upload_address}; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | uploads.clear(); | ||
| 54 | ProcessLaunch(); | 69 | ProcessLaunch(); |
| 70 | indirect_compute = std::nullopt; | ||
| 55 | break; | 71 | break; |
| 72 | } | ||
| 56 | default: | 73 | default: |
| 57 | break; | 74 | break; |
| 58 | } | 75 | } |
| @@ -62,6 +79,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun | |||
| 62 | u32 methods_pending) { | 79 | u32 methods_pending) { |
| 63 | switch (method) { | 80 | switch (method) { |
| 64 | case KEPLER_COMPUTE_REG_INDEX(data_upload): | 81 | case KEPLER_COMPUTE_REG_INDEX(data_upload): |
| 82 | upload_address = current_dma_segment; | ||
| 65 | upload_state.ProcessData(base_start, amount); | 83 | upload_state.ProcessData(base_start, amount); |
| 66 | return; | 84 | return; |
| 67 | default: | 85 | default: |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 2092e685f..735e05fb4 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <optional> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| @@ -36,6 +37,9 @@ namespace Tegra::Engines { | |||
| 36 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ | 37 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ |
| 37 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) | 38 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) |
| 38 | 39 | ||
| 40 | #define LAUNCH_REG_INDEX(field_name) \ | ||
| 41 | (offsetof(Tegra::Engines::KeplerCompute::LaunchParams, field_name) / sizeof(u32)) | ||
| 42 | |||
| 39 | class KeplerCompute final : public EngineInterface { | 43 | class KeplerCompute final : public EngineInterface { |
| 40 | public: | 44 | public: |
| 41 | explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); | 45 | explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); |
| @@ -201,6 +205,10 @@ public: | |||
| 201 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, | 205 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, |
| 202 | u32 methods_pending) override; | 206 | u32 methods_pending) override; |
| 203 | 207 | ||
| 208 | std::optional<GPUVAddr> GetIndirectComputeAddress() const { | ||
| 209 | return indirect_compute; | ||
| 210 | } | ||
| 211 | |||
| 204 | private: | 212 | private: |
| 205 | void ProcessLaunch(); | 213 | void ProcessLaunch(); |
| 206 | 214 | ||
| @@ -216,6 +224,15 @@ private: | |||
| 216 | MemoryManager& memory_manager; | 224 | MemoryManager& memory_manager; |
| 217 | VideoCore::RasterizerInterface* rasterizer = nullptr; | 225 | VideoCore::RasterizerInterface* rasterizer = nullptr; |
| 218 | Upload::State upload_state; | 226 | Upload::State upload_state; |
| 227 | GPUVAddr upload_address; | ||
| 228 | |||
| 229 | struct UploadInfo { | ||
| 230 | GPUVAddr upload_address; | ||
| 231 | GPUVAddr exec_address; | ||
| 232 | u32 copy_size; | ||
| 233 | }; | ||
| 234 | std::vector<UploadInfo> uploads; | ||
| 235 | std::optional<GPUVAddr> indirect_compute{}; | ||
| 219 | }; | 236 | }; |
| 220 | 237 | ||
| 221 | #define ASSERT_REG_POSITION(field_name, position) \ | 238 | #define ASSERT_REG_POSITION(field_name, position) \ |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index c3696096d..06e349e43 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -257,6 +257,7 @@ u32 Maxwell3D::GetMaxCurrentVertices() { | |||
| 257 | const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); | 257 | const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); |
| 258 | num_vertices = std::max( | 258 | num_vertices = std::max( |
| 259 | num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); | 259 | num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); |
| 260 | break; | ||
| 260 | } | 261 | } |
| 261 | return num_vertices; | 262 | return num_vertices; |
| 262 | } | 263 | } |
| @@ -269,10 +270,13 @@ size_t Maxwell3D::EstimateIndexBufferSize() { | |||
| 269 | std::numeric_limits<u32>::max()}; | 270 | std::numeric_limits<u32>::max()}; |
| 270 | const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); | 271 | const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); |
| 271 | const size_t log2_byte_size = Common::Log2Ceil64(byte_size); | 272 | const size_t log2_byte_size = Common::Log2Ceil64(byte_size); |
| 273 | const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; | ||
| 274 | const size_t lower_cap = | ||
| 275 | std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); | ||
| 272 | return std::min<size_t>( | 276 | return std::min<size_t>( |
| 273 | memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / | 277 | memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / |
| 274 | byte_size, | 278 | byte_size, |
| 275 | static_cast<size_t>(end_address - start_address)); | 279 | lower_cap); |
| 276 | } | 280 | } |
| 277 | 281 | ||
| 278 | u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { | 282 | u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { |
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 7718a09b3..6de2543b7 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp | |||
| @@ -34,19 +34,24 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) { | |||
| 34 | bound_engines[method_call.subchannel] = engine_id; | 34 | bound_engines[method_call.subchannel] = engine_id; |
| 35 | switch (engine_id) { | 35 | switch (engine_id) { |
| 36 | case EngineID::FERMI_TWOD_A: | 36 | case EngineID::FERMI_TWOD_A: |
| 37 | dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); | 37 | dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel, |
| 38 | EngineTypes::Fermi2D); | ||
| 38 | break; | 39 | break; |
| 39 | case EngineID::MAXWELL_B: | 40 | case EngineID::MAXWELL_B: |
| 40 | dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel); | 41 | dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel, |
| 42 | EngineTypes::Maxwell3D); | ||
| 41 | break; | 43 | break; |
| 42 | case EngineID::KEPLER_COMPUTE_B: | 44 | case EngineID::KEPLER_COMPUTE_B: |
| 43 | dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel); | 45 | dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel, |
| 46 | EngineTypes::KeplerCompute); | ||
| 44 | break; | 47 | break; |
| 45 | case EngineID::MAXWELL_DMA_COPY_A: | 48 | case EngineID::MAXWELL_DMA_COPY_A: |
| 46 | dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel); | 49 | dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel, |
| 50 | EngineTypes::MaxwellDMA); | ||
| 47 | break; | 51 | break; |
| 48 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: | 52 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: |
| 49 | dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel); | 53 | dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel, |
| 54 | EngineTypes::KeplerMemory); | ||
| 50 | break; | 55 | break; |
| 51 | default: | 56 | default: |
| 52 | UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); | 57 | UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); |
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index 220cce28a..8d7da50fc 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp | |||
| @@ -319,6 +319,7 @@ void Codec::Decode() { | |||
| 319 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); | 319 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); |
| 320 | return; | 320 | return; |
| 321 | } | 321 | } |
| 322 | bool is_interlaced = initial_frame->interlaced_frame != 0; | ||
| 322 | if (av_codec_ctx->hw_device_ctx) { | 323 | if (av_codec_ctx->hw_device_ctx) { |
| 323 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | 324 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; |
| 324 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); | 325 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); |
| @@ -334,7 +335,7 @@ void Codec::Decode() { | |||
| 334 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); | 335 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); |
| 335 | return; | 336 | return; |
| 336 | } | 337 | } |
| 337 | if (!final_frame->interlaced_frame) { | 338 | if (!is_interlaced) { |
| 338 | av_frames.push(std::move(final_frame)); | 339 | av_frames.push(std::move(final_frame)); |
| 339 | } else { | 340 | } else { |
| 340 | if (!filters_initialized) { | 341 | if (!filters_initialized) { |
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index 905505ca1..5d0bb9cc4 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp | |||
| @@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192)) | |||
| 27 | 27 | ||
| 28 | namespace Tegra { | 28 | namespace Tegra { |
| 29 | 29 | ||
| 30 | static void Dump(u64 hash, std::span<const u32> code) { | 30 | static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) { |
| 31 | const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)}; | 31 | const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)}; |
| 32 | const auto macro_dir{base_dir / "macros"}; | 32 | const auto macro_dir{base_dir / "macros"}; |
| 33 | if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { | 33 | if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { |
| 34 | LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); | 34 | LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); |
| 35 | return; | 35 | return; |
| 36 | } | 36 | } |
| 37 | const auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; | 37 | auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; |
| 38 | |||
| 39 | if (decompiled) { | ||
| 40 | auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)}; | ||
| 41 | if (Common::FS::Exists(name)) { | ||
| 42 | (void)Common::FS::RenameFile(name, new_name); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | name = new_name; | ||
| 46 | } | ||
| 47 | |||
| 38 | std::fstream macro_file(name, std::ios::out | std::ios::binary); | 48 | std::fstream macro_file(name, std::ios::out | std::ios::binary); |
| 39 | if (!macro_file) { | 49 | if (!macro_file) { |
| 40 | LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}", | 50 | LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}", |
| @@ -90,9 +100,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { | |||
| 90 | if (!mid_method.has_value()) { | 100 | if (!mid_method.has_value()) { |
| 91 | cache_info.lle_program = Compile(macro_code->second); | 101 | cache_info.lle_program = Compile(macro_code->second); |
| 92 | cache_info.hash = Common::HashValue(macro_code->second); | 102 | cache_info.hash = Common::HashValue(macro_code->second); |
| 93 | if (Settings::values.dump_macros) { | ||
| 94 | Dump(cache_info.hash, macro_code->second); | ||
| 95 | } | ||
| 96 | } else { | 103 | } else { |
| 97 | const auto& macro_cached = uploaded_macro_code[mid_method.value()]; | 104 | const auto& macro_cached = uploaded_macro_code[mid_method.value()]; |
| 98 | const auto rebased_method = method - mid_method.value(); | 105 | const auto rebased_method = method - mid_method.value(); |
| @@ -102,9 +109,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { | |||
| 102 | code.size() * sizeof(u32)); | 109 | code.size() * sizeof(u32)); |
| 103 | cache_info.hash = Common::HashValue(code); | 110 | cache_info.hash = Common::HashValue(code); |
| 104 | cache_info.lle_program = Compile(code); | 111 | cache_info.lle_program = Compile(code); |
| 105 | if (Settings::values.dump_macros) { | ||
| 106 | Dump(cache_info.hash, code); | ||
| 107 | } | ||
| 108 | } | 112 | } |
| 109 | 113 | ||
| 110 | auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); | 114 | auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); |
| @@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { | |||
| 117 | MICROPROFILE_SCOPE(MacroHLE); | 121 | MICROPROFILE_SCOPE(MacroHLE); |
| 118 | cache_info.hle_program->Execute(parameters, method); | 122 | cache_info.hle_program->Execute(parameters, method); |
| 119 | } | 123 | } |
| 124 | |||
| 125 | if (Settings::values.dump_macros) { | ||
| 126 | Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program); | ||
| 127 | } | ||
| 120 | } | 128 | } |
| 121 | } | 129 | } |
| 122 | 130 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1ba31be88..dd03efecd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -380,6 +380,17 @@ void RasterizerOpenGL::DispatchCompute() { | |||
| 380 | pipeline->SetEngine(kepler_compute, gpu_memory); | 380 | pipeline->SetEngine(kepler_compute, gpu_memory); |
| 381 | pipeline->Configure(); | 381 | pipeline->Configure(); |
| 382 | const auto& qmd{kepler_compute->launch_description}; | 382 | const auto& qmd{kepler_compute->launch_description}; |
| 383 | auto indirect_address = kepler_compute->GetIndirectComputeAddress(); | ||
| 384 | if (indirect_address) { | ||
| 385 | // DispatchIndirect | ||
| 386 | static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; | ||
| 387 | const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; | ||
| 388 | const auto [buffer, offset] = | ||
| 389 | buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); | ||
| 390 | glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer->Handle()); | ||
| 391 | glDispatchComputeIndirect(static_cast<GLintptr>(offset)); | ||
| 392 | return; | ||
| 393 | } | ||
| 383 | glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); | 394 | glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); |
| 384 | ++num_queued_commands; | 395 | ++num_queued_commands; |
| 385 | has_written_global_memory |= pipeline->WritesGlobalMemory(); | 396 | has_written_global_memory |= pipeline->WritesGlobalMemory(); |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 454bb66a4..c4c30d807 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -66,21 +66,6 @@ std::string BuildCommaSeparatedExtensions( | |||
| 66 | return fmt::format("{}", fmt::join(available_extensions, ",")); | 66 | return fmt::format("{}", fmt::join(available_extensions, ",")); |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | DebugCallback MakeDebugCallback(const vk::Instance& instance, const vk::InstanceDispatch& dld) { | ||
| 70 | if (!Settings::values.renderer_debug) { | ||
| 71 | return DebugCallback{}; | ||
| 72 | } | ||
| 73 | const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld); | ||
| 74 | const auto it = std::ranges::find_if(*properties, [](const auto& prop) { | ||
| 75 | return std::strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, prop.extensionName) == 0; | ||
| 76 | }); | ||
| 77 | if (it != properties->end()) { | ||
| 78 | return CreateDebugUtilsCallback(instance); | ||
| 79 | } else { | ||
| 80 | return CreateDebugReportCallback(instance); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | } // Anonymous namespace | 69 | } // Anonymous namespace |
| 85 | 70 | ||
| 86 | Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, | 71 | Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, |
| @@ -103,7 +88,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, | |||
| 103 | cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())), | 88 | cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())), |
| 104 | instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, | 89 | instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, |
| 105 | Settings::values.renderer_debug.GetValue())), | 90 | Settings::values.renderer_debug.GetValue())), |
| 106 | debug_callback(MakeDebugCallback(instance, dld)), | 91 | debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance) |
| 92 | : vk::DebugUtilsMessenger{}), | ||
| 107 | surface(CreateSurface(instance, render_window.GetWindowInfo())), | 93 | surface(CreateSurface(instance, render_window.GetWindowInfo())), |
| 108 | device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(), | 94 | device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(), |
| 109 | scheduler(device, state_tracker), | 95 | scheduler(device, state_tracker), |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 89e98425e..590bc1c64 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -35,8 +35,6 @@ class GPU; | |||
| 35 | 35 | ||
| 36 | namespace Vulkan { | 36 | namespace Vulkan { |
| 37 | 37 | ||
| 38 | using DebugCallback = std::variant<vk::DebugUtilsMessenger, vk::DebugReportCallback>; | ||
| 39 | |||
| 40 | Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, | 38 | Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, |
| 41 | VkSurfaceKHR surface); | 39 | VkSurfaceKHR surface); |
| 42 | 40 | ||
| @@ -75,7 +73,7 @@ private: | |||
| 75 | vk::InstanceDispatch dld; | 73 | vk::InstanceDispatch dld; |
| 76 | 74 | ||
| 77 | vk::Instance instance; | 75 | vk::Instance instance; |
| 78 | DebugCallback debug_callback; | 76 | vk::DebugUtilsMessenger debug_messenger; |
| 79 | vk::SurfaceKHR surface; | 77 | vk::SurfaceKHR surface; |
| 80 | 78 | ||
| 81 | ScreenInfo screen_info; | 79 | ScreenInfo screen_info; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 60a6ac651..e15865d16 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -529,17 +529,20 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi | |||
| 529 | buffer_handles.push_back(handle); | 529 | buffer_handles.push_back(handle); |
| 530 | } | 530 | } |
| 531 | if (device.IsExtExtendedDynamicStateSupported()) { | 531 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 532 | scheduler.Record([bindings_ = std::move(bindings), | 532 | scheduler.Record([this, bindings_ = std::move(bindings), |
| 533 | buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { | 533 | buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { |
| 534 | cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, | 534 | cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, |
| 535 | bindings_.max_index - bindings_.min_index, | 535 | std::min(bindings_.max_index - bindings_.min_index, |
| 536 | device.GetMaxVertexInputBindings()), | ||
| 536 | buffer_handles_.data(), bindings_.offsets.data(), | 537 | buffer_handles_.data(), bindings_.offsets.data(), |
| 537 | bindings_.sizes.data(), bindings_.strides.data()); | 538 | bindings_.sizes.data(), bindings_.strides.data()); |
| 538 | }); | 539 | }); |
| 539 | } else { | 540 | } else { |
| 540 | scheduler.Record([bindings_ = std::move(bindings), | 541 | scheduler.Record([this, bindings_ = std::move(bindings), |
| 541 | buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { | 542 | buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { |
| 542 | cmdbuf.BindVertexBuffers(bindings_.min_index, bindings_.max_index - bindings_.min_index, | 543 | cmdbuf.BindVertexBuffers(bindings_.min_index, |
| 544 | std::min(bindings_.max_index - bindings_.min_index, | ||
| 545 | device.GetMaxVertexInputBindings()), | ||
| 543 | buffer_handles_.data(), bindings_.offsets.data()); | 546 | buffer_handles_.data(), bindings_.offsets.data()); |
| 544 | }); | 547 | }); |
| 545 | } | 548 | } |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c1314ca99..4f83a88e1 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 611 | 611 | ||
| 612 | const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; | 612 | const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; |
| 613 | Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); | 613 | Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); |
| 614 | if (Settings::values.dump_shaders) { | ||
| 615 | env.Dump(hash, key.unique_hashes[index]); | ||
| 616 | } | ||
| 617 | if (!uses_vertex_a || index != 1) { | 614 | if (!uses_vertex_a || index != 1) { |
| 618 | // Normal path | 615 | // Normal path |
| 619 | programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); | 616 | programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); |
| @@ -624,6 +621,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 624 | programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); | 621 | programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); |
| 625 | } | 622 | } |
| 626 | 623 | ||
| 624 | if (Settings::values.dump_shaders) { | ||
| 625 | env.Dump(hash, key.unique_hashes[index]); | ||
| 626 | } | ||
| 627 | |||
| 627 | if (programs[index].info.requires_layer_emulation) { | 628 | if (programs[index].info.requires_layer_emulation) { |
| 628 | layer_source_program = &programs[index]; | 629 | layer_source_program = &programs[index]; |
| 629 | } | 630 | } |
| @@ -664,6 +665,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 664 | std::move(modules), infos); | 665 | std::move(modules), infos); |
| 665 | 666 | ||
| 666 | } catch (const Shader::Exception& exception) { | 667 | } catch (const Shader::Exception& exception) { |
| 668 | auto hash = key.Hash(); | ||
| 669 | size_t env_index{0}; | ||
| 670 | for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | ||
| 671 | if (key.unique_hashes[index] == 0) { | ||
| 672 | continue; | ||
| 673 | } | ||
| 674 | Shader::Environment& env{*envs[env_index]}; | ||
| 675 | ++env_index; | ||
| 676 | |||
| 677 | const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; | ||
| 678 | Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); | ||
| 679 | env.Dump(hash, key.unique_hashes[index]); | ||
| 680 | } | ||
| 667 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); | 681 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); |
| 668 | return nullptr; | 682 | return nullptr; |
| 669 | } | 683 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 032f694bc..01e76a82c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -463,6 +463,20 @@ void RasterizerVulkan::DispatchCompute() { | |||
| 463 | pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); | 463 | pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); |
| 464 | 464 | ||
| 465 | const auto& qmd{kepler_compute->launch_description}; | 465 | const auto& qmd{kepler_compute->launch_description}; |
| 466 | auto indirect_address = kepler_compute->GetIndirectComputeAddress(); | ||
| 467 | if (indirect_address) { | ||
| 468 | // DispatchIndirect | ||
| 469 | static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; | ||
| 470 | const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; | ||
| 471 | const auto [buffer, offset] = | ||
| 472 | buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); | ||
| 473 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 474 | scheduler.Record([indirect_buffer = buffer->Handle(), | ||
| 475 | indirect_offset = offset](vk::CommandBuffer cmdbuf) { | ||
| 476 | cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset); | ||
| 477 | }); | ||
| 478 | return; | ||
| 479 | } | ||
| 466 | const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; | 480 | const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; |
| 467 | scheduler.RequestOutsideRenderPassOperationContext(); | 481 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 468 | scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); | 482 | scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); |
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index 67e8065a4..448df2d3a 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp | |||
| @@ -63,22 +63,6 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, | |||
| 63 | return VK_FALSE; | 63 | return VK_FALSE; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, | ||
| 67 | uint64_t object, size_t location, int32_t messageCode, | ||
| 68 | const char* pLayerPrefix, const char* pMessage, void* pUserData) { | ||
| 69 | const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags); | ||
| 70 | const std::string_view message{pMessage}; | ||
| 71 | if (severity & VK_DEBUG_REPORT_ERROR_BIT_EXT) { | ||
| 72 | LOG_CRITICAL(Render_Vulkan, "{}", message); | ||
| 73 | } else if (severity & VK_DEBUG_REPORT_WARNING_BIT_EXT) { | ||
| 74 | LOG_WARNING(Render_Vulkan, "{}", message); | ||
| 75 | } else if (severity & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { | ||
| 76 | LOG_INFO(Render_Vulkan, "{}", message); | ||
| 77 | } else if (severity & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { | ||
| 78 | LOG_DEBUG(Render_Vulkan, "{}", message); | ||
| 79 | } | ||
| 80 | return VK_FALSE; | ||
| 81 | } | ||
| 82 | } // Anonymous namespace | 66 | } // Anonymous namespace |
| 83 | 67 | ||
| 84 | vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) { | 68 | vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) { |
| @@ -98,15 +82,4 @@ vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) { | |||
| 98 | }); | 82 | }); |
| 99 | } | 83 | } |
| 100 | 84 | ||
| 101 | vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance) { | ||
| 102 | return instance.CreateDebugReportCallback({ | ||
| 103 | .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, | ||
| 104 | .pNext = nullptr, | ||
| 105 | .flags = VK_DEBUG_REPORT_DEBUG_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT | | ||
| 106 | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, | ||
| 107 | .pfnCallback = DebugReportCallback, | ||
| 108 | .pUserData = nullptr, | ||
| 109 | }); | ||
| 110 | } | ||
| 111 | |||
| 112 | } // namespace Vulkan | 85 | } // namespace Vulkan |
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h index a8af7b406..5e940782f 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.h +++ b/src/video_core/vulkan_common/vulkan_debug_callback.h | |||
| @@ -9,6 +9,4 @@ namespace Vulkan { | |||
| 9 | 9 | ||
| 10 | vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance); | 10 | vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance); |
| 11 | 11 | ||
| 12 | vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance); | ||
| 13 | |||
| 14 | } // namespace Vulkan | 12 | } // namespace Vulkan |
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index bc16145be..180657a75 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp | |||
| @@ -76,11 +76,9 @@ namespace { | |||
| 76 | extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); | 76 | extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); |
| 77 | } | 77 | } |
| 78 | #endif | 78 | #endif |
| 79 | if (enable_validation) { | 79 | if (enable_validation && |
| 80 | const bool debug_utils = | 80 | AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) { |
| 81 | AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}); | 81 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| 82 | extensions.push_back(debug_utils ? VK_EXT_DEBUG_UTILS_EXTENSION_NAME | ||
| 83 | : VK_EXT_DEBUG_REPORT_EXTENSION_NAME); | ||
| 84 | } | 82 | } |
| 85 | return extensions; | 83 | return extensions; |
| 86 | } | 84 | } |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 78e5a248f..c3f388d89 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp | |||
| @@ -92,6 +92,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 92 | X(vkCmdCopyImage); | 92 | X(vkCmdCopyImage); |
| 93 | X(vkCmdCopyImageToBuffer); | 93 | X(vkCmdCopyImageToBuffer); |
| 94 | X(vkCmdDispatch); | 94 | X(vkCmdDispatch); |
| 95 | X(vkCmdDispatchIndirect); | ||
| 95 | X(vkCmdDraw); | 96 | X(vkCmdDraw); |
| 96 | X(vkCmdDrawIndexed); | 97 | X(vkCmdDrawIndexed); |
| 97 | X(vkCmdDrawIndirect); | 98 | X(vkCmdDrawIndirect); |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index c226a2a29..049fa8038 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -203,6 +203,7 @@ struct DeviceDispatch : InstanceDispatch { | |||
| 203 | PFN_vkCmdCopyImage vkCmdCopyImage{}; | 203 | PFN_vkCmdCopyImage vkCmdCopyImage{}; |
| 204 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; | 204 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; |
| 205 | PFN_vkCmdDispatch vkCmdDispatch{}; | 205 | PFN_vkCmdDispatch vkCmdDispatch{}; |
| 206 | PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect{}; | ||
| 206 | PFN_vkCmdDraw vkCmdDraw{}; | 207 | PFN_vkCmdDraw vkCmdDraw{}; |
| 207 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; | 208 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; |
| 208 | PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; | 209 | PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; |
| @@ -1209,6 +1210,10 @@ public: | |||
| 1209 | dld->vkCmdDispatch(handle, x, y, z); | 1210 | dld->vkCmdDispatch(handle, x, y, z); |
| 1210 | } | 1211 | } |
| 1211 | 1212 | ||
| 1213 | void DispatchIndirect(VkBuffer indirect_buffer, VkDeviceSize offset) const noexcept { | ||
| 1214 | dld->vkCmdDispatchIndirect(handle, indirect_buffer, offset); | ||
| 1215 | } | ||
| 1216 | |||
| 1212 | void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, | 1217 | void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, |
| 1213 | VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, | 1218 | VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, |
| 1214 | Span<VkBufferMemoryBarrier> buffer_barriers, | 1219 | Span<VkBufferMemoryBarrier> buffer_barriers, |
diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp index 129eb1968..f88f67620 100644 --- a/src/web_service/verify_user_jwt.cpp +++ b/src/web_service/verify_user_jwt.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #if defined(__GNUC__) || defined(__clang__) | 4 | #if defined(__GNUC__) || defined(__clang__) |
| 5 | #pragma GCC diagnostic push | 5 | #pragma GCC diagnostic push |
| 6 | #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" | 6 | #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" |
| 7 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions | ||
| 7 | #endif | 8 | #endif |
| 8 | #include <jwt/jwt.hpp> | 9 | #include <jwt/jwt.hpp> |
| 9 | #if defined(__GNUC__) || defined(__clang__) | 10 | #if defined(__GNUC__) || defined(__clang__) |
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp index 4988fcc83..b457a736a 100644 --- a/src/yuzu/applets/qt_amiibo_settings.cpp +++ b/src/yuzu/applets/qt_amiibo_settings.cpp | |||
| @@ -160,7 +160,8 @@ void QtAmiiboSettingsDialog::LoadAmiiboData() { | |||
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | const auto amiibo_name = std::string(register_info.amiibo_name.data()); | 162 | const auto amiibo_name = std::string(register_info.amiibo_name.data()); |
| 163 | const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data()); | 163 | const auto owner_name = |
| 164 | Common::UTF16ToUTF8(register_info.mii_char_info.GetNickname().data.data()); | ||
| 164 | const auto creation_date = | 165 | const auto creation_date = |
| 165 | QDate(register_info.creation_date.year, register_info.creation_date.month, | 166 | QDate(register_info.creation_date.year, register_info.creation_date.month, |
| 166 | register_info.creation_date.day); | 167 | register_info.creation_date.day); |
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 00aafb8f8..d15559518 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <thread> | 5 | #include <thread> |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/settings.h" | ||
| 9 | #include "common/settings_enums.h" | ||
| 8 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 9 | #include "core/core.h" | 11 | #include "core/core.h" |
| 10 | #include "core/hid/emulated_controller.h" | 12 | #include "core/hid/emulated_controller.h" |
| @@ -226,9 +228,11 @@ int QtControllerSelectorDialog::exec() { | |||
| 226 | } | 228 | } |
| 227 | 229 | ||
| 228 | void QtControllerSelectorDialog::ApplyConfiguration() { | 230 | void QtControllerSelectorDialog::ApplyConfiguration() { |
| 229 | const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); | 231 | const bool pre_docked_mode = Settings::IsDockedMode(); |
| 230 | Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); | 232 | const bool docked_mode_selected = ui->radioDocked->isChecked(); |
| 231 | OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system); | 233 | Settings::values.use_docked_mode.SetValue( |
| 234 | docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld); | ||
| 235 | OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system); | ||
| 232 | 236 | ||
| 233 | Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); | 237 | Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); |
| 234 | Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); | 238 | Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); |
| @@ -616,8 +620,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) { | |||
| 616 | ui->radioDocked->setEnabled(!is_handheld); | 620 | ui->radioDocked->setEnabled(!is_handheld); |
| 617 | ui->radioUndocked->setEnabled(!is_handheld); | 621 | ui->radioUndocked->setEnabled(!is_handheld); |
| 618 | 622 | ||
| 619 | ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); | 623 | ui->radioDocked->setChecked(Settings::IsDockedMode()); |
| 620 | ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); | 624 | ui->radioUndocked->setChecked(!Settings::IsDockedMode()); |
| 621 | 625 | ||
| 622 | // Also force into undocked mode if the controller type is handheld. | 626 | // Also force into undocked mode if the controller type is handheld. |
| 623 | if (is_handheld) { | 627 | if (is_handheld) { |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 407988b8f..2afa72140 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -928,8 +928,8 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { | |||
| 928 | const Layout::FramebufferLayout layout{[]() { | 928 | const Layout::FramebufferLayout layout{[]() { |
| 929 | u32 height = UISettings::values.screenshot_height.GetValue(); | 929 | u32 height = UISettings::values.screenshot_height.GetValue(); |
| 930 | if (height == 0) { | 930 | if (height == 0) { |
| 931 | height = Settings::values.use_docked_mode.GetValue() ? Layout::ScreenDocked::Height | 931 | height = Settings::IsDockedMode() ? Layout::ScreenDocked::Height |
| 932 | : Layout::ScreenUndocked::Height; | 932 | : Layout::ScreenUndocked::Height; |
| 933 | height *= Settings::values.resolution_info.up_factor; | 933 | height *= Settings::values.resolution_info.up_factor; |
| 934 | } | 934 | } |
| 935 | const u32 width = | 935 | const u32 width = |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index b22c83303..1de093447 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/fs/path_util.h" | 9 | #include "common/fs/path_util.h" |
| 10 | #include "common/settings.h" | 10 | #include "common/settings.h" |
| 11 | #include "common/settings_common.h" | 11 | #include "common/settings_common.h" |
| 12 | #include "common/settings_enums.h" | ||
| 12 | #include "core/core.h" | 13 | #include "core/core.h" |
| 13 | #include "core/hle/service/acc/profile_manager.h" | 14 | #include "core/hle/service/acc/profile_manager.h" |
| 14 | #include "core/hle/service/hid/controllers/npad.h" | 15 | #include "core/hle/service/hid/controllers/npad.h" |
| @@ -85,9 +86,9 @@ const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_ma | |||
| 85 | {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, | 86 | {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, |
| 86 | }; | 87 | }; |
| 87 | 88 | ||
| 88 | const std::map<bool, QString> Config::use_docked_mode_texts_map = { | 89 | const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = { |
| 89 | {true, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, | 90 | {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, |
| 90 | {false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, | 91 | {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, |
| 91 | }; | 92 | }; |
| 92 | 93 | ||
| 93 | const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { | 94 | const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { |
| @@ -376,7 +377,7 @@ void Config::ReadControlValues() { | |||
| 376 | const auto controller_type = Settings::values.players.GetValue()[0].controller_type; | 377 | const auto controller_type = Settings::values.players.GetValue()[0].controller_type; |
| 377 | if (controller_type == Settings::ControllerType::Handheld) { | 378 | if (controller_type == Settings::ControllerType::Handheld) { |
| 378 | Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); | 379 | Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); |
| 379 | Settings::values.use_docked_mode.SetValue(false); | 380 | Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); |
| 380 | } | 381 | } |
| 381 | 382 | ||
| 382 | if (IsCustomConfig()) { | 383 | if (IsCustomConfig()) { |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 0ac74c8e7..727feebfb 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <QMetaType> | 9 | #include <QMetaType> |
| 10 | #include <QVariant> | 10 | #include <QVariant> |
| 11 | #include "common/settings.h" | 11 | #include "common/settings.h" |
| 12 | #include "common/settings_enums.h" | ||
| 12 | #include "yuzu/uisettings.h" | 13 | #include "yuzu/uisettings.h" |
| 13 | 14 | ||
| 14 | class QSettings; | 15 | class QSettings; |
| @@ -51,7 +52,7 @@ public: | |||
| 51 | 52 | ||
| 52 | static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; | 53 | static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; |
| 53 | static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; | 54 | static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; |
| 54 | static const std::map<bool, QString> use_docked_mode_texts_map; | 55 | static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map; |
| 55 | static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; | 56 | static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; |
| 56 | static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; | 57 | static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; |
| 57 | static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; | 58 | static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 8622dc184..fd6bebf0f 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -193,14 +193,10 @@ void ConfigureGraphics::PopulateVSyncModeSelection() { | |||
| 193 | : vsync_mode_combobox_enum_map[current_index]; | 193 | : vsync_mode_combobox_enum_map[current_index]; |
| 194 | int index{}; | 194 | int index{}; |
| 195 | const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device | 195 | const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device |
| 196 | if (device == -1) { | ||
| 197 | // Invalid device | ||
| 198 | return; | ||
| 199 | } | ||
| 200 | 196 | ||
| 201 | const auto& present_modes = //< relevant vector of present modes for the selected device or API | 197 | const auto& present_modes = //< relevant vector of present modes for the selected device or API |
| 202 | backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] | 198 | backend == Settings::RendererBackend::Vulkan && device > -1 ? device_present_modes[device] |
| 203 | : default_present_modes; | 199 | : default_present_modes; |
| 204 | 200 | ||
| 205 | vsync_mode_combobox->clear(); | 201 | vsync_mode_combobox->clear(); |
| 206 | vsync_mode_combobox_enum_map.clear(); | 202 | vsync_mode_combobox_enum_map.clear(); |
| @@ -497,11 +493,19 @@ void ConfigureGraphics::RetrieveVulkanDevices() { | |||
| 497 | } | 493 | } |
| 498 | 494 | ||
| 499 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | 495 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { |
| 500 | if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { | 496 | const auto selected_backend = [&]() { |
| 501 | return Settings::values.renderer_backend.GetValue(true); | 497 | if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { |
| 498 | return Settings::values.renderer_backend.GetValue(true); | ||
| 499 | } | ||
| 500 | return static_cast<Settings::RendererBackend>( | ||
| 501 | combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index()) | ||
| 502 | .at(api_combobox->currentIndex()) | ||
| 503 | .first); | ||
| 504 | }(); | ||
| 505 | |||
| 506 | if (selected_backend == Settings::RendererBackend::Vulkan && | ||
| 507 | UISettings::values.has_broken_vulkan) { | ||
| 508 | return Settings::RendererBackend::OpenGL; | ||
| 502 | } | 509 | } |
| 503 | return static_cast<Settings::RendererBackend>( | 510 | return selected_backend; |
| 504 | combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index()) | ||
| 505 | .at(api_combobox->currentIndex()) | ||
| 506 | .first); | ||
| 507 | } | 511 | } |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 7fce85bca..e8f9ebfd8 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | #include <thread> | 5 | #include <thread> |
| 6 | 6 | ||
| 7 | #include "common/settings.h" | ||
| 8 | #include "common/settings_enums.h" | ||
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| 8 | #include "core/hid/emulated_controller.h" | 10 | #include "core/hid/emulated_controller.h" |
| 9 | #include "core/hid/hid_core.h" | 11 | #include "core/hid/hid_core.h" |
| @@ -197,9 +199,11 @@ void ConfigureInput::ApplyConfiguration() { | |||
| 197 | 199 | ||
| 198 | advanced->ApplyConfiguration(); | 200 | advanced->ApplyConfiguration(); |
| 199 | 201 | ||
| 200 | const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); | 202 | const bool pre_docked_mode = Settings::IsDockedMode(); |
| 201 | Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); | 203 | const bool docked_mode_selected = ui->radioDocked->isChecked(); |
| 202 | OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system); | 204 | Settings::values.use_docked_mode.SetValue( |
| 205 | docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld); | ||
| 206 | OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system); | ||
| 203 | 207 | ||
| 204 | Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); | 208 | Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); |
| 205 | Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); | 209 | Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); |
| @@ -267,8 +271,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) { | |||
| 267 | ui->radioDocked->setEnabled(!is_handheld); | 271 | ui->radioDocked->setEnabled(!is_handheld); |
| 268 | ui->radioUndocked->setEnabled(!is_handheld); | 272 | ui->radioUndocked->setEnabled(!is_handheld); |
| 269 | 273 | ||
| 270 | ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); | 274 | ui->radioDocked->setChecked(Settings::IsDockedMode()); |
| 271 | ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); | 275 | ui->radioUndocked->setChecked(!Settings::IsDockedMode()); |
| 272 | 276 | ||
| 273 | // Also force into undocked mode if the controller type is handheld. | 277 | // Also force into undocked mode if the controller type is handheld. |
| 274 | if (is_handheld) { | 278 | if (is_handheld) { |
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 4f9e8db08..b91d6ad4a 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | #include "common/fs/fs_util.h" | 19 | #include "common/fs/fs_util.h" |
| 20 | #include "common/settings_enums.h" | 20 | #include "common/settings_enums.h" |
| 21 | #include "common/settings_input.h" | ||
| 21 | #include "configuration/shared_widget.h" | 22 | #include "configuration/shared_widget.h" |
| 22 | #include "core/core.h" | 23 | #include "core/core.h" |
| 23 | #include "core/file_sys/control_metadata.h" | 24 | #include "core/file_sys/control_metadata.h" |
| @@ -98,6 +99,12 @@ void ConfigurePerGame::ApplyConfiguration() { | |||
| 98 | addons_tab->ApplyConfiguration(); | 99 | addons_tab->ApplyConfiguration(); |
| 99 | input_tab->ApplyConfiguration(); | 100 | input_tab->ApplyConfiguration(); |
| 100 | 101 | ||
| 102 | if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type == | ||
| 103 | Settings::ControllerType::Handheld) { | ||
| 104 | Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); | ||
| 105 | Settings::values.use_docked_mode.SetGlobal(true); | ||
| 106 | } | ||
| 107 | |||
| 101 | system.ApplySettings(); | 108 | system.ApplySettings(); |
| 102 | Settings::LogSettings(); | 109 | Settings::LogSettings(); |
| 103 | 110 | ||
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index c4833f4e7..0c8e5c8b4 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -106,6 +106,11 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) { | |||
| 106 | push(Settings::values.linkage.by_category[Settings::Category::System]); | 106 | push(Settings::values.linkage.by_category[Settings::Category::System]); |
| 107 | 107 | ||
| 108 | for (auto setting : settings) { | 108 | for (auto setting : settings) { |
| 109 | if (setting->Id() == Settings::values.use_docked_mode.Id() && | ||
| 110 | Settings::IsConfiguringGlobal()) { | ||
| 111 | continue; | ||
| 112 | } | ||
| 113 | |||
| 109 | ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); | 114 | ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); |
| 110 | 115 | ||
| 111 | if (widget == nullptr) { | 116 | if (widget == nullptr) { |
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 335810788..276bdbaba 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp | |||
| @@ -135,7 +135,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 135 | INSERT(Settings, region_index, "Region:", ""); | 135 | INSERT(Settings, region_index, "Region:", ""); |
| 136 | INSERT(Settings, time_zone_index, "Time Zone:", ""); | 136 | INSERT(Settings, time_zone_index, "Time Zone:", ""); |
| 137 | INSERT(Settings, sound_index, "Sound Output Mode:", ""); | 137 | INSERT(Settings, sound_index, "Sound Output Mode:", ""); |
| 138 | INSERT(Settings, use_docked_mode, "", ""); | 138 | INSERT(Settings, use_docked_mode, "Console Mode:", ""); |
| 139 | INSERT(Settings, current_user, "", ""); | 139 | INSERT(Settings, current_user, "", ""); |
| 140 | 140 | ||
| 141 | // Controls | 141 | // Controls |
| @@ -379,6 +379,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 379 | PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), | 379 | PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), |
| 380 | PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), | 380 | PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), |
| 381 | }}); | 381 | }}); |
| 382 | translations->insert( | ||
| 383 | {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), | ||
| 384 | {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); | ||
| 382 | 385 | ||
| 383 | #undef PAIR | 386 | #undef PAIR |
| 384 | #undef CTX_PAIR | 387 | #undef CTX_PAIR |
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index 4e45bc844..d63093985 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <QLineEdit> | 23 | #include <QLineEdit> |
| 24 | #include <QObject> | 24 | #include <QObject> |
| 25 | #include <QPushButton> | 25 | #include <QPushButton> |
| 26 | #include <QRadioButton> | ||
| 26 | #include <QRegularExpression> | 27 | #include <QRegularExpression> |
| 27 | #include <QSizePolicy> | 28 | #include <QSizePolicy> |
| 28 | #include <QSlider> | 29 | #include <QSlider> |
| @@ -171,6 +172,65 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer, | |||
| 171 | return combobox; | 172 | return combobox; |
| 172 | } | 173 | } |
| 173 | 174 | ||
| 175 | QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer, | ||
| 176 | std::function<void()>& restore_func, | ||
| 177 | const std::function<void()>& touch) { | ||
| 178 | const auto type = setting.EnumIndex(); | ||
| 179 | |||
| 180 | QWidget* group = new QWidget(this); | ||
| 181 | QHBoxLayout* layout = new QHBoxLayout(group); | ||
| 182 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 183 | group->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); | ||
| 184 | |||
| 185 | const ComboboxTranslations* enumeration{nullptr}; | ||
| 186 | if (combobox_enumerations.contains(type)) { | ||
| 187 | enumeration = &combobox_enumerations.at(type); | ||
| 188 | for (const auto& [id, name] : *enumeration) { | ||
| 189 | QRadioButton* radio_button = new QRadioButton(name, group); | ||
| 190 | layout->addWidget(radio_button); | ||
| 191 | radio_buttons.push_back({id, radio_button}); | ||
| 192 | } | ||
| 193 | } else { | ||
| 194 | return group; | ||
| 195 | } | ||
| 196 | |||
| 197 | const auto get_selected = [=]() -> int { | ||
| 198 | for (const auto& [id, button] : radio_buttons) { | ||
| 199 | if (button->isChecked()) { | ||
| 200 | return id; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | return -1; | ||
| 204 | }; | ||
| 205 | |||
| 206 | const auto set_index = [=](u32 value) { | ||
| 207 | for (const auto& [id, button] : radio_buttons) { | ||
| 208 | button->setChecked(id == value); | ||
| 209 | } | ||
| 210 | }; | ||
| 211 | |||
| 212 | const u32 setting_value = std::stoi(setting.ToString()); | ||
| 213 | set_index(setting_value); | ||
| 214 | |||
| 215 | serializer = [get_selected]() { | ||
| 216 | int current = get_selected(); | ||
| 217 | return std::to_string(current); | ||
| 218 | }; | ||
| 219 | |||
| 220 | restore_func = [this, set_index]() { | ||
| 221 | const u32 global_value = std::stoi(RelevantDefault(setting)); | ||
| 222 | set_index(global_value); | ||
| 223 | }; | ||
| 224 | |||
| 225 | if (!Settings::IsConfiguringGlobal()) { | ||
| 226 | for (const auto& [id, button] : radio_buttons) { | ||
| 227 | QObject::connect(button, &QAbstractButton::clicked, [touch]() { touch(); }); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | return group; | ||
| 232 | } | ||
| 233 | |||
| 174 | QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer, | 234 | QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer, |
| 175 | std::function<void()>& restore_func, | 235 | std::function<void()>& restore_func, |
| 176 | const std::function<void()>& touch, bool managed) { | 236 | const std::function<void()>& touch, bool managed) { |
| @@ -411,6 +471,8 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu | |||
| 411 | return RequestType::Slider; | 471 | return RequestType::Slider; |
| 412 | case Settings::Specialization::Countable: | 472 | case Settings::Specialization::Countable: |
| 413 | return RequestType::SpinBox; | 473 | return RequestType::SpinBox; |
| 474 | case Settings::Specialization::Radio: | ||
| 475 | return RequestType::RadioGroup; | ||
| 414 | default: | 476 | default: |
| 415 | break; | 477 | break; |
| 416 | } | 478 | } |
| @@ -439,7 +501,11 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu | |||
| 439 | if (setting.TypeId() == typeid(bool)) { | 501 | if (setting.TypeId() == typeid(bool)) { |
| 440 | data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch); | 502 | data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch); |
| 441 | } else if (setting.IsEnum()) { | 503 | } else if (setting.IsEnum()) { |
| 442 | data_component = CreateCombobox(serializer, restore_func, touch); | 504 | if (request == RequestType::RadioGroup) { |
| 505 | data_component = CreateRadioGroup(serializer, restore_func, touch); | ||
| 506 | } else { | ||
| 507 | data_component = CreateCombobox(serializer, restore_func, touch); | ||
| 508 | } | ||
| 443 | } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || | 509 | } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || |
| 444 | type == typeid(s64) || type == typeid(u8)) { | 510 | type == typeid(s64) || type == typeid(u8)) { |
| 445 | switch (request) { | 511 | switch (request) { |
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h index e64693bab..5303dd898 100644 --- a/src/yuzu/configuration/shared_widget.h +++ b/src/yuzu/configuration/shared_widget.h | |||
| @@ -22,6 +22,7 @@ class QObject; | |||
| 22 | class QPushButton; | 22 | class QPushButton; |
| 23 | class QSlider; | 23 | class QSlider; |
| 24 | class QSpinBox; | 24 | class QSpinBox; |
| 25 | class QRadioButton; | ||
| 25 | 26 | ||
| 26 | namespace Settings { | 27 | namespace Settings { |
| 27 | class BasicSetting; | 28 | class BasicSetting; |
| @@ -38,6 +39,7 @@ enum class RequestType { | |||
| 38 | LineEdit, | 39 | LineEdit, |
| 39 | HexEdit, | 40 | HexEdit, |
| 40 | DateTimeEdit, | 41 | DateTimeEdit, |
| 42 | RadioGroup, | ||
| 41 | MaxEnum, | 43 | MaxEnum, |
| 42 | }; | 44 | }; |
| 43 | 45 | ||
| @@ -91,6 +93,7 @@ public: | |||
| 91 | QSlider* slider{}; | 93 | QSlider* slider{}; |
| 92 | QComboBox* combobox{}; | 94 | QComboBox* combobox{}; |
| 93 | QDateTimeEdit* date_time_edit{}; | 95 | QDateTimeEdit* date_time_edit{}; |
| 96 | std::vector<std::pair<u32, QRadioButton*>> radio_buttons{}; | ||
| 94 | 97 | ||
| 95 | private: | 98 | private: |
| 96 | void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed, | 99 | void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed, |
| @@ -106,6 +109,9 @@ private: | |||
| 106 | QWidget* CreateCombobox(std::function<std::string()>& serializer, | 109 | QWidget* CreateCombobox(std::function<std::string()>& serializer, |
| 107 | std::function<void()>& restore_func, | 110 | std::function<void()>& restore_func, |
| 108 | const std::function<void()>& touch); | 111 | const std::function<void()>& touch); |
| 112 | QWidget* CreateRadioGroup(std::function<std::string()>& serializer, | ||
| 113 | std::function<void()>& restore_func, | ||
| 114 | const std::function<void()>& touch); | ||
| 109 | QWidget* CreateLineEdit(std::function<std::string()>& serializer, | 115 | QWidget* CreateLineEdit(std::function<std::string()>& serializer, |
| 110 | std::function<void()>& restore_func, const std::function<void()>& touch, | 116 | std::function<void()>& restore_func, const std::function<void()>& touch, |
| 111 | bool managed = true); | 117 | bool managed = true); |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b5a02700d..f254c1e1c 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -557,6 +557,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 557 | QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); | 557 | QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); |
| 558 | QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); | 558 | QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); |
| 559 | QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); | 559 | QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); |
| 560 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); | ||
| 560 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 561 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 561 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 562 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 562 | #ifndef WIN32 | 563 | #ifndef WIN32 |
| @@ -588,10 +589,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 588 | emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); | 589 | emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); |
| 589 | }); | 590 | }); |
| 590 | connect(start_game, &QAction::triggered, [this, path]() { | 591 | connect(start_game, &QAction::triggered, [this, path]() { |
| 591 | emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal); | 592 | emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal, |
| 593 | AmLaunchType::UserInitiated); | ||
| 592 | }); | 594 | }); |
| 593 | connect(start_game_global, &QAction::triggered, [this, path]() { | 595 | connect(start_game_global, &QAction::triggered, [this, path]() { |
| 594 | emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global); | 596 | emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global, |
| 597 | AmLaunchType::UserInitiated); | ||
| 595 | }); | 598 | }); |
| 596 | connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { | 599 | connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { |
| 597 | emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); | 600 | emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); |
| @@ -628,6 +631,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 628 | connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() { | 631 | connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() { |
| 629 | emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC); | 632 | emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC); |
| 630 | }); | 633 | }); |
| 634 | connect(verify_integrity, &QAction::triggered, | ||
| 635 | [this, path]() { emit VerifyIntegrityRequested(path); }); | ||
| 631 | connect(copy_tid, &QAction::triggered, | 636 | connect(copy_tid, &QAction::triggered, |
| 632 | [this, program_id]() { emit CopyTIDRequested(program_id); }); | 637 | [this, program_id]() { emit CopyTIDRequested(program_id); }); |
| 633 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { | 638 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 6c2f75e53..1fcbbf0ba 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -28,6 +28,7 @@ class GameListWorker; | |||
| 28 | class GameListSearchField; | 28 | class GameListSearchField; |
| 29 | class GameListDir; | 29 | class GameListDir; |
| 30 | class GMainWindow; | 30 | class GMainWindow; |
| 31 | enum class AmLaunchType; | ||
| 31 | enum class StartGameType; | 32 | enum class StartGameType; |
| 32 | 33 | ||
| 33 | namespace FileSys { | 34 | namespace FileSys { |
| @@ -103,7 +104,7 @@ public: | |||
| 103 | 104 | ||
| 104 | signals: | 105 | signals: |
| 105 | void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, | 106 | void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, |
| 106 | StartGameType type); | 107 | StartGameType type, AmLaunchType launch_type); |
| 107 | void GameChosen(const QString& game_path, const u64 title_id = 0); | 108 | void GameChosen(const QString& game_path, const u64 title_id = 0); |
| 108 | void ShouldCancelWorker(); | 109 | void ShouldCancelWorker(); |
| 109 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target, | 110 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target, |
| @@ -113,6 +114,7 @@ signals: | |||
| 113 | void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, | 114 | void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, |
| 114 | const std::string& game_path); | 115 | const std::string& game_path); |
| 115 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); | 116 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); |
| 117 | void VerifyIntegrityRequested(const std::string& game_path); | ||
| 116 | void CopyTIDRequested(u64 program_id); | 118 | void CopyTIDRequested(u64 program_id); |
| 117 | void CreateShortcut(u64 program_id, const std::string& game_path, | 119 | void CreateShortcut(u64 program_id, const std::string& game_path, |
| 118 | GameListShortcutTarget target); | 120 | GameListShortcutTarget target); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 8e933af64..97d216638 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <iostream> | 8 | #include <iostream> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include "core/loader/nca.h" | ||
| 11 | #ifdef __APPLE__ | 12 | #ifdef __APPLE__ |
| 12 | #include <unistd.h> // for chdir | 13 | #include <unistd.h> // for chdir |
| 13 | #endif | 14 | #endif |
| @@ -442,8 +443,13 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 442 | "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>" | 443 | "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>" |
| 443 | "here for instructions to fix the issue</a>.")); | 444 | "here for instructions to fix the issue</a>.")); |
| 444 | 445 | ||
| 446 | #ifdef HAS_OPENGL | ||
| 445 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; | 447 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; |
| 448 | #else | ||
| 449 | Settings::values.renderer_backend = Settings::RendererBackend::Null; | ||
| 450 | #endif | ||
| 446 | 451 | ||
| 452 | UpdateAPIText(); | ||
| 447 | renderer_status_button->setDisabled(true); | 453 | renderer_status_button->setDisabled(true); |
| 448 | renderer_status_button->setChecked(false); | 454 | renderer_status_button->setChecked(false); |
| 449 | } else { | 455 | } else { |
| @@ -1158,9 +1164,9 @@ void GMainWindow::InitializeWidgets() { | |||
| 1158 | [this](const QPoint& menu_location) { | 1164 | [this](const QPoint& menu_location) { |
| 1159 | QMenu context_menu; | 1165 | QMenu context_menu; |
| 1160 | 1166 | ||
| 1161 | for (auto const& docked_mode_pair : Config::use_docked_mode_texts_map) { | 1167 | for (auto const& pair : Config::use_docked_mode_texts_map) { |
| 1162 | context_menu.addAction(docked_mode_pair.second, [this, docked_mode_pair] { | 1168 | context_menu.addAction(pair.second, [this, &pair] { |
| 1163 | if (docked_mode_pair.first != Settings::values.use_docked_mode.GetValue()) { | 1169 | if (pair.first != Settings::values.use_docked_mode.GetValue()) { |
| 1164 | OnToggleDockedMode(); | 1170 | OnToggleDockedMode(); |
| 1165 | } | 1171 | } |
| 1166 | }); | 1172 | }); |
| @@ -1447,6 +1453,8 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 1447 | &GMainWindow::OnGameListRemoveInstalledEntry); | 1453 | &GMainWindow::OnGameListRemoveInstalledEntry); |
| 1448 | connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); | 1454 | connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); |
| 1449 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); | 1455 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); |
| 1456 | connect(game_list, &GameList::VerifyIntegrityRequested, this, | ||
| 1457 | &GMainWindow::OnGameListVerifyIntegrity); | ||
| 1450 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 1458 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 1451 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 1459 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| 1452 | &GMainWindow::OnGameListNavigateToGamedbEntry); | 1460 | &GMainWindow::OnGameListNavigateToGamedbEntry); |
| @@ -1547,6 +1555,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1547 | 1555 | ||
| 1548 | // Help | 1556 | // Help |
| 1549 | connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); | 1557 | connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); |
| 1558 | connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); | ||
| 1550 | connect_menu(ui->action_About, &GMainWindow::OnAbout); | 1559 | connect_menu(ui->action_About, &GMainWindow::OnAbout); |
| 1551 | } | 1560 | } |
| 1552 | 1561 | ||
| @@ -1698,7 +1707,8 @@ void GMainWindow::AllowOSSleep() { | |||
| 1698 | #endif | 1707 | #endif |
| 1699 | } | 1708 | } |
| 1700 | 1709 | ||
| 1701 | bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { | 1710 | bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index, |
| 1711 | AmLaunchType launch_type) { | ||
| 1702 | // Shutdown previous session if the emu thread is still active... | 1712 | // Shutdown previous session if the emu thread is still active... |
| 1703 | if (emu_thread != nullptr) { | 1713 | if (emu_thread != nullptr) { |
| 1704 | ShutdownGame(); | 1714 | ShutdownGame(); |
| @@ -1710,6 +1720,10 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p | |||
| 1710 | 1720 | ||
| 1711 | system->SetFilesystem(vfs); | 1721 | system->SetFilesystem(vfs); |
| 1712 | 1722 | ||
| 1723 | if (launch_type == AmLaunchType::UserInitiated) { | ||
| 1724 | system->GetUserChannel().clear(); | ||
| 1725 | } | ||
| 1726 | |||
| 1713 | system->SetAppletFrontendSet({ | 1727 | system->SetAppletFrontendSet({ |
| 1714 | std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings | 1728 | std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings |
| 1715 | (UISettings::values.controller_applet_disabled.GetValue() == true) | 1729 | (UISettings::values.controller_applet_disabled.GetValue() == true) |
| @@ -1849,7 +1863,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { | |||
| 1849 | } | 1863 | } |
| 1850 | 1864 | ||
| 1851 | void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, | 1865 | void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, |
| 1852 | StartGameType type) { | 1866 | StartGameType type, AmLaunchType launch_type) { |
| 1853 | LOG_INFO(Frontend, "yuzu starting..."); | 1867 | LOG_INFO(Frontend, "yuzu starting..."); |
| 1854 | StoreRecentFile(filename); // Put the filename on top of the list | 1868 | StoreRecentFile(filename); // Put the filename on top of the list |
| 1855 | 1869 | ||
| @@ -1893,7 +1907,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1893 | } | 1907 | } |
| 1894 | } | 1908 | } |
| 1895 | 1909 | ||
| 1896 | if (!LoadROM(filename, program_id, program_index)) { | 1910 | if (!LoadROM(filename, program_id, program_index, launch_type)) { |
| 1897 | return; | 1911 | return; |
| 1898 | } | 1912 | } |
| 1899 | 1913 | ||
| @@ -2010,8 +2024,16 @@ bool GMainWindow::OnShutdownBegin() { | |||
| 2010 | 2024 | ||
| 2011 | emit EmulationStopping(); | 2025 | emit EmulationStopping(); |
| 2012 | 2026 | ||
| 2027 | int shutdown_time = 1000; | ||
| 2028 | |||
| 2029 | if (system->DebuggerEnabled()) { | ||
| 2030 | shutdown_time = 0; | ||
| 2031 | } else if (system->GetExitLocked()) { | ||
| 2032 | shutdown_time = 5000; | ||
| 2033 | } | ||
| 2034 | |||
| 2013 | shutdown_timer.setSingleShot(true); | 2035 | shutdown_timer.setSingleShot(true); |
| 2014 | shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); | 2036 | shutdown_timer.start(shutdown_time); |
| 2015 | connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); | 2037 | connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); |
| 2016 | connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); | 2038 | connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); |
| 2017 | 2039 | ||
| @@ -2267,40 +2289,62 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { | |||
| 2267 | QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); | 2289 | QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); |
| 2268 | } | 2290 | } |
| 2269 | 2291 | ||
| 2270 | static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { | 2292 | static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog, |
| 2271 | std::size_t out = 0; | 2293 | const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest, |
| 2272 | 2294 | bool full) { | |
| 2273 | for (const auto& subdir : dir->GetSubdirectories()) { | ||
| 2274 | out += 1 + CalculateRomFSEntrySize(subdir, full); | ||
| 2275 | } | ||
| 2276 | |||
| 2277 | return out + (full ? dir->GetFiles().size() : 0); | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src, | ||
| 2281 | const FileSys::VirtualDir& dest, std::size_t block_size, bool full) { | ||
| 2282 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | 2295 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) |
| 2283 | return false; | 2296 | return false; |
| 2284 | if (dialog.wasCanceled()) | 2297 | if (dialog.wasCanceled()) |
| 2285 | return false; | 2298 | return false; |
| 2286 | 2299 | ||
| 2300 | std::vector<u8> buffer(CopyBufferSize); | ||
| 2301 | auto last_timestamp = std::chrono::steady_clock::now(); | ||
| 2302 | |||
| 2303 | const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file, | ||
| 2304 | const FileSys::VirtualFile& dest_file) { | ||
| 2305 | if (src_file == nullptr || dest_file == nullptr) { | ||
| 2306 | return false; | ||
| 2307 | } | ||
| 2308 | if (!dest_file->Resize(src_file->GetSize())) { | ||
| 2309 | return false; | ||
| 2310 | } | ||
| 2311 | |||
| 2312 | for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) { | ||
| 2313 | if (dialog.wasCanceled()) { | ||
| 2314 | dest_file->Resize(0); | ||
| 2315 | return false; | ||
| 2316 | } | ||
| 2317 | |||
| 2318 | using namespace std::literals::chrono_literals; | ||
| 2319 | const auto new_timestamp = std::chrono::steady_clock::now(); | ||
| 2320 | |||
| 2321 | if ((new_timestamp - last_timestamp) > 33ms) { | ||
| 2322 | last_timestamp = new_timestamp; | ||
| 2323 | dialog.setValue( | ||
| 2324 | static_cast<int>(std::min(read_size, total_size) * 100 / total_size)); | ||
| 2325 | QCoreApplication::processEvents(); | ||
| 2326 | } | ||
| 2327 | |||
| 2328 | const auto read = src_file->Read(buffer.data(), buffer.size(), i); | ||
| 2329 | dest_file->Write(buffer.data(), read, i); | ||
| 2330 | |||
| 2331 | read_size += read; | ||
| 2332 | } | ||
| 2333 | |||
| 2334 | return true; | ||
| 2335 | }; | ||
| 2336 | |||
| 2287 | if (full) { | 2337 | if (full) { |
| 2288 | for (const auto& file : src->GetFiles()) { | 2338 | for (const auto& file : src->GetFiles()) { |
| 2289 | const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); | 2339 | const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); |
| 2290 | if (!FileSys::VfsRawCopy(file, out, block_size)) | 2340 | if (!QtRawCopy(file, out)) |
| 2291 | return false; | ||
| 2292 | dialog.setValue(dialog.value() + 1); | ||
| 2293 | if (dialog.wasCanceled()) | ||
| 2294 | return false; | 2341 | return false; |
| 2295 | } | 2342 | } |
| 2296 | } | 2343 | } |
| 2297 | 2344 | ||
| 2298 | for (const auto& dir : src->GetSubdirectories()) { | 2345 | for (const auto& dir : src->GetSubdirectories()) { |
| 2299 | const auto out = dest->CreateSubdirectory(dir->GetName()); | 2346 | const auto out = dest->CreateSubdirectory(dir->GetName()); |
| 2300 | if (!RomFSRawCopy(dialog, dir, out, block_size, full)) | 2347 | if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full)) |
| 2301 | return false; | ||
| 2302 | dialog.setValue(dialog.value() + 1); | ||
| 2303 | if (dialog.wasCanceled()) | ||
| 2304 | return false; | 2348 | return false; |
| 2305 | } | 2349 | } |
| 2306 | 2350 | ||
| @@ -2573,50 +2617,48 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2573 | return; | 2617 | return; |
| 2574 | } | 2618 | } |
| 2575 | 2619 | ||
| 2576 | FileSys::VirtualFile base_romfs; | 2620 | FileSys::VirtualFile packed_update_raw{}; |
| 2577 | if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { | 2621 | loader->ReadUpdateRaw(packed_update_raw); |
| 2578 | failed(); | ||
| 2579 | return; | ||
| 2580 | } | ||
| 2581 | 2622 | ||
| 2582 | const auto& installed = system->GetContentProvider(); | 2623 | const auto& installed = system->GetContentProvider(); |
| 2583 | const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); | ||
| 2584 | 2624 | ||
| 2585 | if (!romfs_title_id) { | 2625 | u64 title_id{}; |
| 2626 | u8 raw_type{}; | ||
| 2627 | if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) { | ||
| 2586 | failed(); | 2628 | failed(); |
| 2587 | return; | 2629 | return; |
| 2588 | } | 2630 | } |
| 2589 | 2631 | ||
| 2590 | const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program | 2632 | const auto type = static_cast<FileSys::ContentRecordType>(raw_type); |
| 2591 | : FileSys::ContentRecordType::Data; | 2633 | const auto base_nca = installed.GetEntry(title_id, type); |
| 2592 | const auto base_nca = installed.GetEntry(*romfs_title_id, type); | ||
| 2593 | if (!base_nca) { | 2634 | if (!base_nca) { |
| 2594 | failed(); | 2635 | failed(); |
| 2595 | return; | 2636 | return; |
| 2596 | } | 2637 | } |
| 2597 | 2638 | ||
| 2639 | const FileSys::NCA update_nca{packed_update_raw, nullptr}; | ||
| 2640 | if (type != FileSys::ContentRecordType::Program || | ||
| 2641 | update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS || | ||
| 2642 | update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) { | ||
| 2643 | packed_update_raw = {}; | ||
| 2644 | } | ||
| 2645 | |||
| 2646 | const auto base_romfs = base_nca->GetRomFS(); | ||
| 2647 | if (!base_romfs) { | ||
| 2648 | failed(); | ||
| 2649 | return; | ||
| 2650 | } | ||
| 2651 | |||
| 2598 | const auto dump_dir = | 2652 | const auto dump_dir = |
| 2599 | target == DumpRomFSTarget::Normal | 2653 | target == DumpRomFSTarget::Normal |
| 2600 | ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) | 2654 | ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) |
| 2601 | : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; | 2655 | : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; |
| 2602 | const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); | 2656 | const auto romfs_dir = fmt::format("{:016X}/romfs", title_id); |
| 2603 | 2657 | ||
| 2604 | const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); | 2658 | const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); |
| 2605 | 2659 | ||
| 2606 | FileSys::VirtualFile romfs; | 2660 | const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; |
| 2607 | 2661 | auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); | |
| 2608 | if (*romfs_title_id == program_id) { | ||
| 2609 | const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed}; | ||
| 2610 | romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false); | ||
| 2611 | } else { | ||
| 2612 | romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS(); | ||
| 2613 | } | ||
| 2614 | |||
| 2615 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | ||
| 2616 | if (extracted == nullptr) { | ||
| 2617 | failed(); | ||
| 2618 | return; | ||
| 2619 | } | ||
| 2620 | 2662 | ||
| 2621 | const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); | 2663 | const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); |
| 2622 | 2664 | ||
| @@ -2640,11 +2682,16 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2640 | return; | 2682 | return; |
| 2641 | } | 2683 | } |
| 2642 | 2684 | ||
| 2685 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | ||
| 2686 | if (extracted == nullptr) { | ||
| 2687 | failed(); | ||
| 2688 | return; | ||
| 2689 | } | ||
| 2690 | |||
| 2643 | const auto full = res == selections.constFirst(); | 2691 | const auto full = res == selections.constFirst(); |
| 2644 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); | ||
| 2645 | 2692 | ||
| 2646 | // The minimum required space is the size of the extracted RomFS + 1 GiB | 2693 | // The expected required space is the size of the RomFS + 1 GiB |
| 2647 | const auto minimum_free_space = extracted->GetSize() + 0x40000000; | 2694 | const auto minimum_free_space = romfs->GetSize() + 0x40000000; |
| 2648 | 2695 | ||
| 2649 | if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) { | 2696 | if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) { |
| 2650 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), | 2697 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), |
| @@ -2655,12 +2702,15 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2655 | return; | 2702 | return; |
| 2656 | } | 2703 | } |
| 2657 | 2704 | ||
| 2658 | QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, | 2705 | QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this); |
| 2659 | static_cast<s32>(entry_size), this); | ||
| 2660 | progress.setWindowModality(Qt::WindowModal); | 2706 | progress.setWindowModality(Qt::WindowModal); |
| 2661 | progress.setMinimumDuration(100); | 2707 | progress.setMinimumDuration(100); |
| 2708 | progress.setAutoClose(false); | ||
| 2709 | progress.setAutoReset(false); | ||
| 2710 | |||
| 2711 | size_t read_size = 0; | ||
| 2662 | 2712 | ||
| 2663 | if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) { | 2713 | if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) { |
| 2664 | progress.close(); | 2714 | progress.close(); |
| 2665 | QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), | 2715 | QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), |
| 2666 | tr("The operation completed successfully.")); | 2716 | tr("The operation completed successfully.")); |
| @@ -2672,6 +2722,54 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2672 | } | 2722 | } |
| 2673 | } | 2723 | } |
| 2674 | 2724 | ||
| 2725 | void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { | ||
| 2726 | const auto NotImplemented = [this] { | ||
| 2727 | QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"), | ||
| 2728 | tr("File contents were not checked for validity.")); | ||
| 2729 | }; | ||
| 2730 | const auto Failed = [this] { | ||
| 2731 | QMessageBox::critical(this, tr("Integrity verification failed!"), | ||
| 2732 | tr("File contents may be corrupt.")); | ||
| 2733 | }; | ||
| 2734 | |||
| 2735 | const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); | ||
| 2736 | if (loader == nullptr) { | ||
| 2737 | NotImplemented(); | ||
| 2738 | return; | ||
| 2739 | } | ||
| 2740 | |||
| 2741 | QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); | ||
| 2742 | progress.setWindowModality(Qt::WindowModal); | ||
| 2743 | progress.setMinimumDuration(100); | ||
| 2744 | progress.setAutoClose(false); | ||
| 2745 | progress.setAutoReset(false); | ||
| 2746 | |||
| 2747 | const auto QtProgressCallback = [&](size_t processed_size, size_t total_size) { | ||
| 2748 | if (progress.wasCanceled()) { | ||
| 2749 | return false; | ||
| 2750 | } | ||
| 2751 | |||
| 2752 | progress.setValue(static_cast<int>((processed_size * 100) / total_size)); | ||
| 2753 | return true; | ||
| 2754 | }; | ||
| 2755 | |||
| 2756 | const auto status = loader->VerifyIntegrity(QtProgressCallback); | ||
| 2757 | if (progress.wasCanceled() || | ||
| 2758 | status == Loader::ResultStatus::ErrorIntegrityVerificationNotImplemented) { | ||
| 2759 | NotImplemented(); | ||
| 2760 | return; | ||
| 2761 | } | ||
| 2762 | |||
| 2763 | if (status == Loader::ResultStatus::ErrorIntegrityVerificationFailed) { | ||
| 2764 | Failed(); | ||
| 2765 | return; | ||
| 2766 | } | ||
| 2767 | |||
| 2768 | progress.close(); | ||
| 2769 | QMessageBox::information(this, tr("Integrity verification succeeded!"), | ||
| 2770 | tr("The operation completed successfully.")); | ||
| 2771 | } | ||
| 2772 | |||
| 2675 | void GMainWindow::OnGameListCopyTID(u64 program_id) { | 2773 | void GMainWindow::OnGameListCopyTID(u64 program_id) { |
| 2676 | QClipboard* clipboard = QGuiApplication::clipboard(); | 2774 | QClipboard* clipboard = QGuiApplication::clipboard(); |
| 2677 | clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id))); | 2775 | clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id))); |
| @@ -3261,7 +3359,7 @@ void GMainWindow::OnPauseContinueGame() { | |||
| 3261 | } | 3359 | } |
| 3262 | 3360 | ||
| 3263 | void GMainWindow::OnStopGame() { | 3361 | void GMainWindow::OnStopGame() { |
| 3264 | if (system->GetExitLock() && !ConfirmForceLockedExit()) { | 3362 | if (system->GetExitLocked() && !ConfirmForceLockedExit()) { |
| 3265 | return; | 3363 | return; |
| 3266 | } | 3364 | } |
| 3267 | 3365 | ||
| @@ -3278,7 +3376,8 @@ void GMainWindow::OnLoadComplete() { | |||
| 3278 | 3376 | ||
| 3279 | void GMainWindow::OnExecuteProgram(std::size_t program_index) { | 3377 | void GMainWindow::OnExecuteProgram(std::size_t program_index) { |
| 3280 | ShutdownGame(); | 3378 | ShutdownGame(); |
| 3281 | BootGame(last_filename_booted, 0, program_index); | 3379 | BootGame(last_filename_booted, 0, program_index, StartGameType::Normal, |
| 3380 | AmLaunchType::ApplicationInitiated); | ||
| 3282 | } | 3381 | } |
| 3283 | 3382 | ||
| 3284 | void GMainWindow::OnExit() { | 3383 | void GMainWindow::OnExit() { |
| @@ -3674,7 +3773,7 @@ void GMainWindow::OnTasReset() { | |||
| 3674 | } | 3773 | } |
| 3675 | 3774 | ||
| 3676 | void GMainWindow::OnToggleDockedMode() { | 3775 | void GMainWindow::OnToggleDockedMode() { |
| 3677 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | 3776 | const bool is_docked = Settings::IsDockedMode(); |
| 3678 | auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 3777 | auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 3679 | auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | 3778 | auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 3680 | 3779 | ||
| @@ -3688,7 +3787,8 @@ void GMainWindow::OnToggleDockedMode() { | |||
| 3688 | controller_dialog->refreshConfiguration(); | 3787 | controller_dialog->refreshConfiguration(); |
| 3689 | } | 3788 | } |
| 3690 | 3789 | ||
| 3691 | Settings::values.use_docked_mode.SetValue(!is_docked); | 3790 | Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld |
| 3791 | : Settings::ConsoleMode::Docked); | ||
| 3692 | UpdateDockedButton(); | 3792 | UpdateDockedButton(); |
| 3693 | OnDockedModeChanged(is_docked, !is_docked, *system); | 3793 | OnDockedModeChanged(is_docked, !is_docked, *system); |
| 3694 | } | 3794 | } |
| @@ -3757,10 +3857,14 @@ void GMainWindow::OnToggleAdaptingFilter() { | |||
| 3757 | 3857 | ||
| 3758 | void GMainWindow::OnToggleGraphicsAPI() { | 3858 | void GMainWindow::OnToggleGraphicsAPI() { |
| 3759 | auto api = Settings::values.renderer_backend.GetValue(); | 3859 | auto api = Settings::values.renderer_backend.GetValue(); |
| 3760 | if (api == Settings::RendererBackend::OpenGL) { | 3860 | if (api != Settings::RendererBackend::Vulkan) { |
| 3761 | api = Settings::RendererBackend::Vulkan; | 3861 | api = Settings::RendererBackend::Vulkan; |
| 3762 | } else { | 3862 | } else { |
| 3863 | #ifdef HAS_OPENGL | ||
| 3763 | api = Settings::RendererBackend::OpenGL; | 3864 | api = Settings::RendererBackend::OpenGL; |
| 3865 | #else | ||
| 3866 | api = Settings::RendererBackend::Null; | ||
| 3867 | #endif | ||
| 3764 | } | 3868 | } |
| 3765 | Settings::values.renderer_backend.SetValue(api); | 3869 | Settings::values.renderer_backend.SetValue(api); |
| 3766 | renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan); | 3870 | renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan); |
| @@ -3904,6 +4008,108 @@ void GMainWindow::OnOpenYuzuFolder() { | |||
| 3904 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); | 4008 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); |
| 3905 | } | 4009 | } |
| 3906 | 4010 | ||
| 4011 | void GMainWindow::OnVerifyInstalledContents() { | ||
| 4012 | // Declare sizes. | ||
| 4013 | size_t total_size = 0; | ||
| 4014 | size_t processed_size = 0; | ||
| 4015 | |||
| 4016 | // Initialize a progress dialog. | ||
| 4017 | QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); | ||
| 4018 | progress.setWindowModality(Qt::WindowModal); | ||
| 4019 | progress.setMinimumDuration(100); | ||
| 4020 | progress.setAutoClose(false); | ||
| 4021 | progress.setAutoReset(false); | ||
| 4022 | |||
| 4023 | // Declare a list of file names which failed to verify. | ||
| 4024 | std::vector<std::string> failed; | ||
| 4025 | |||
| 4026 | // Declare progress callback. | ||
| 4027 | auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) { | ||
| 4028 | if (progress.wasCanceled()) { | ||
| 4029 | return false; | ||
| 4030 | } | ||
| 4031 | progress.setValue(static_cast<int>(((processed_size + nca_processed) * 100) / total_size)); | ||
| 4032 | return true; | ||
| 4033 | }; | ||
| 4034 | |||
| 4035 | // Get content registries. | ||
| 4036 | auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); | ||
| 4037 | auto user_contents = system->GetFileSystemController().GetUserNANDContents(); | ||
| 4038 | |||
| 4039 | std::vector<FileSys::RegisteredCache*> content_providers; | ||
| 4040 | if (bis_contents) { | ||
| 4041 | content_providers.push_back(bis_contents); | ||
| 4042 | } | ||
| 4043 | if (user_contents) { | ||
| 4044 | content_providers.push_back(user_contents); | ||
| 4045 | } | ||
| 4046 | |||
| 4047 | // Get associated NCA files. | ||
| 4048 | std::vector<FileSys::VirtualFile> nca_files; | ||
| 4049 | |||
| 4050 | // Get all installed IDs. | ||
| 4051 | for (auto nca_provider : content_providers) { | ||
| 4052 | const auto entries = nca_provider->ListEntriesFilter(); | ||
| 4053 | |||
| 4054 | for (const auto& entry : entries) { | ||
| 4055 | auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type); | ||
| 4056 | if (!nca_file) { | ||
| 4057 | continue; | ||
| 4058 | } | ||
| 4059 | |||
| 4060 | total_size += nca_file->GetSize(); | ||
| 4061 | nca_files.push_back(std::move(nca_file)); | ||
| 4062 | } | ||
| 4063 | } | ||
| 4064 | |||
| 4065 | // Using the NCA loader, determine if all NCAs are valid. | ||
| 4066 | for (auto& nca_file : nca_files) { | ||
| 4067 | Loader::AppLoader_NCA nca_loader(nca_file); | ||
| 4068 | |||
| 4069 | auto status = nca_loader.VerifyIntegrity(QtProgressCallback); | ||
| 4070 | if (progress.wasCanceled()) { | ||
| 4071 | break; | ||
| 4072 | } | ||
| 4073 | if (status != Loader::ResultStatus::Success) { | ||
| 4074 | FileSys::NCA nca(nca_file); | ||
| 4075 | const auto title_id = nca.GetTitleId(); | ||
| 4076 | std::string title_name = "unknown"; | ||
| 4077 | |||
| 4078 | const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id), | ||
| 4079 | FileSys::ContentRecordType::Control); | ||
| 4080 | if (control && control->GetStatus() == Loader::ResultStatus::Success) { | ||
| 4081 | const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), | ||
| 4082 | *provider}; | ||
| 4083 | const auto [nacp, logo] = pm.ParseControlNCA(*control); | ||
| 4084 | if (nacp) { | ||
| 4085 | title_name = nacp->GetApplicationName(); | ||
| 4086 | } | ||
| 4087 | } | ||
| 4088 | |||
| 4089 | if (title_id > 0) { | ||
| 4090 | failed.push_back( | ||
| 4091 | fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name)); | ||
| 4092 | } else { | ||
| 4093 | failed.push_back(fmt::format("{} (unknown)", nca_file->GetName())); | ||
| 4094 | } | ||
| 4095 | } | ||
| 4096 | |||
| 4097 | processed_size += nca_file->GetSize(); | ||
| 4098 | } | ||
| 4099 | |||
| 4100 | progress.close(); | ||
| 4101 | |||
| 4102 | if (failed.size() > 0) { | ||
| 4103 | auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(failed, "\n"))); | ||
| 4104 | QMessageBox::critical( | ||
| 4105 | this, tr("Integrity verification failed!"), | ||
| 4106 | tr("Verification failed for the following files:\n\n%1").arg(failed_names)); | ||
| 4107 | } else { | ||
| 4108 | QMessageBox::information(this, tr("Integrity verification succeeded!"), | ||
| 4109 | tr("The operation completed successfully.")); | ||
| 4110 | } | ||
| 4111 | } | ||
| 4112 | |||
| 3907 | void GMainWindow::OnAbout() { | 4113 | void GMainWindow::OnAbout() { |
| 3908 | AboutDialog aboutDialog(this); | 4114 | AboutDialog aboutDialog(this); |
| 3909 | aboutDialog.exec(); | 4115 | aboutDialog.exec(); |
| @@ -4118,10 +4324,10 @@ void GMainWindow::UpdateGPUAccuracyButton() { | |||
| 4118 | } | 4324 | } |
| 4119 | 4325 | ||
| 4120 | void GMainWindow::UpdateDockedButton() { | 4326 | void GMainWindow::UpdateDockedButton() { |
| 4121 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | 4327 | const auto console_mode = Settings::values.use_docked_mode.GetValue(); |
| 4122 | dock_status_button->setChecked(is_docked); | 4328 | dock_status_button->setChecked(Settings::IsDockedMode()); |
| 4123 | dock_status_button->setText( | 4329 | dock_status_button->setText( |
| 4124 | Config::use_docked_mode_texts_map.find(is_docked)->second.toUpper()); | 4330 | Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); |
| 4125 | } | 4331 | } |
| 4126 | 4332 | ||
| 4127 | void GMainWindow::UpdateAPIText() { | 4333 | void GMainWindow::UpdateAPIText() { |
| @@ -4349,28 +4555,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() { | |||
| 4349 | return mii_nca->GetRomFS().get() != nullptr; | 4555 | return mii_nca->GetRomFS().get() != nullptr; |
| 4350 | } | 4556 | } |
| 4351 | 4557 | ||
| 4352 | std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, | 4558 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, |
| 4353 | u64 program_id) { | 4559 | u64* selected_title_id, u8* selected_content_record_type) { |
| 4354 | const auto dlc_entries = | 4560 | using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>; |
| 4355 | installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | 4561 | boost::container::flat_map<u64, ContentInfo> available_title_ids; |
| 4356 | std::vector<FileSys::ContentProviderEntry> dlc_match; | 4562 | |
| 4357 | dlc_match.reserve(dlc_entries.size()); | 4563 | const auto RetrieveEntries = [&](FileSys::TitleType title_type, |
| 4358 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | 4564 | FileSys::ContentRecordType record_type) { |
| 4359 | [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { | 4565 | const auto entries = installed.ListEntriesFilter(title_type, record_type); |
| 4360 | return FileSys::GetBaseTitleID(entry.title_id) == program_id && | 4566 | for (const auto& entry : entries) { |
| 4361 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; | 4567 | if (FileSys::GetBaseTitleID(entry.title_id) == program_id && |
| 4362 | }); | 4568 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { |
| 4363 | 4569 | available_title_ids[entry.title_id] = {title_type, record_type}; | |
| 4364 | std::vector<u64> romfs_tids; | 4570 | } |
| 4365 | romfs_tids.push_back(program_id); | 4571 | } |
| 4366 | for (const auto& entry : dlc_match) { | 4572 | }; |
| 4367 | romfs_tids.push_back(entry.title_id); | 4573 | |
| 4368 | } | 4574 | RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); |
| 4369 | 4575 | RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | |
| 4370 | if (romfs_tids.size() > 1) { | 4576 | |
| 4371 | QStringList list{QStringLiteral("Base")}; | 4577 | if (available_title_ids.empty()) { |
| 4372 | for (std::size_t i = 1; i < romfs_tids.size(); ++i) { | 4578 | return false; |
| 4373 | list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); | 4579 | } |
| 4580 | |||
| 4581 | size_t title_index = 0; | ||
| 4582 | |||
| 4583 | if (available_title_ids.size() > 1) { | ||
| 4584 | QStringList list; | ||
| 4585 | for (auto& [title_id, content_info] : available_title_ids) { | ||
| 4586 | const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); | ||
| 4587 | if (content_info.first == FileSys::TitleType::Application) { | ||
| 4588 | list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id)); | ||
| 4589 | } else { | ||
| 4590 | list.push_back( | ||
| 4591 | QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); | ||
| 4592 | } | ||
| 4374 | } | 4593 | } |
| 4375 | 4594 | ||
| 4376 | bool ok; | 4595 | bool ok; |
| @@ -4378,13 +4597,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv | |||
| 4378 | this, tr("Select RomFS Dump Target"), | 4597 | this, tr("Select RomFS Dump Target"), |
| 4379 | tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); | 4598 | tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); |
| 4380 | if (!ok) { | 4599 | if (!ok) { |
| 4381 | return {}; | 4600 | return false; |
| 4382 | } | 4601 | } |
| 4383 | 4602 | ||
| 4384 | return romfs_tids[list.indexOf(res)]; | 4603 | title_index = list.indexOf(res); |
| 4385 | } | 4604 | } |
| 4386 | 4605 | ||
| 4387 | return program_id; | 4606 | const auto selected_info = available_title_ids.nth(title_index); |
| 4607 | *selected_title_id = selected_info->first; | ||
| 4608 | *selected_content_record_type = static_cast<u8>(selected_info->second.second); | ||
| 4609 | return true; | ||
| 4388 | } | 4610 | } |
| 4389 | 4611 | ||
| 4390 | bool GMainWindow::ConfirmClose() { | 4612 | bool GMainWindow::ConfirmClose() { |
| @@ -4514,6 +4736,8 @@ void GMainWindow::RequestGameExit() { | |||
| 4514 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); | 4736 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); |
| 4515 | bool has_signalled = false; | 4737 | bool has_signalled = false; |
| 4516 | 4738 | ||
| 4739 | system->SetExitRequested(true); | ||
| 4740 | |||
| 4517 | if (applet_oe != nullptr) { | 4741 | if (applet_oe != nullptr) { |
| 4518 | applet_oe->GetMessageQueue()->RequestExit(); | 4742 | applet_oe->GetMessageQueue()->RequestExit(); |
| 4519 | has_signalled = true; | 4743 | has_signalled = true; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1b7055122..cf191f698 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -58,6 +58,11 @@ enum class StartGameType { | |||
| 58 | Global, // Only uses global configuration | 58 | Global, // Only uses global configuration |
| 59 | }; | 59 | }; |
| 60 | 60 | ||
| 61 | enum class AmLaunchType { | ||
| 62 | UserInitiated, | ||
| 63 | ApplicationInitiated, | ||
| 64 | }; | ||
| 65 | |||
| 61 | namespace Core { | 66 | namespace Core { |
| 62 | enum class SystemResultStatus : u32; | 67 | enum class SystemResultStatus : u32; |
| 63 | class System; | 68 | class System; |
| @@ -239,9 +244,11 @@ private: | |||
| 239 | void PreventOSSleep(); | 244 | void PreventOSSleep(); |
| 240 | void AllowOSSleep(); | 245 | void AllowOSSleep(); |
| 241 | 246 | ||
| 242 | bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index); | 247 | bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index, |
| 248 | AmLaunchType launch_type); | ||
| 243 | void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0, | 249 | void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0, |
| 244 | StartGameType with_config = StartGameType::Normal); | 250 | StartGameType with_config = StartGameType::Normal, |
| 251 | AmLaunchType launch_type = AmLaunchType::UserInitiated); | ||
| 245 | void ShutdownGame(); | 252 | void ShutdownGame(); |
| 246 | 253 | ||
| 247 | void ShowTelemetryCallout(); | 254 | void ShowTelemetryCallout(); |
| @@ -313,6 +320,7 @@ private slots: | |||
| 313 | void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, | 320 | void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, |
| 314 | const std::string& game_path); | 321 | const std::string& game_path); |
| 315 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); | 322 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); |
| 323 | void OnGameListVerifyIntegrity(const std::string& game_path); | ||
| 316 | void OnGameListCopyTID(u64 program_id); | 324 | void OnGameListCopyTID(u64 program_id); |
| 317 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 325 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 318 | const CompatibilityList& compatibility_list); | 326 | const CompatibilityList& compatibility_list); |
| @@ -342,6 +350,7 @@ private slots: | |||
| 342 | void OnConfigurePerGame(); | 350 | void OnConfigurePerGame(); |
| 343 | void OnLoadAmiibo(); | 351 | void OnLoadAmiibo(); |
| 344 | void OnOpenYuzuFolder(); | 352 | void OnOpenYuzuFolder(); |
| 353 | void OnVerifyInstalledContents(); | ||
| 345 | void OnAbout(); | 354 | void OnAbout(); |
| 346 | void OnToggleFilterBar(); | 355 | void OnToggleFilterBar(); |
| 347 | void OnToggleStatusBar(); | 356 | void OnToggleStatusBar(); |
| @@ -375,7 +384,8 @@ private: | |||
| 375 | void RemoveAllTransferableShaderCaches(u64 program_id); | 384 | void RemoveAllTransferableShaderCaches(u64 program_id); |
| 376 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); | 385 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); |
| 377 | void RemoveCacheStorage(u64 program_id); | 386 | void RemoveCacheStorage(u64 program_id); |
| 378 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 387 | bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, |
| 388 | u64* selected_title_id, u8* selected_content_record_type); | ||
| 379 | InstallResult InstallNSPXCI(const QString& filename); | 389 | InstallResult InstallNSPXCI(const QString& filename); |
| 380 | InstallResult InstallNCA(const QString& filename); | 390 | InstallResult InstallNCA(const QString& filename); |
| 381 | void MigrateConfigFiles(); | 391 | void MigrateConfigFiles(); |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 013ba0ceb..e54d7d75d 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -148,6 +148,7 @@ | |||
| 148 | <addaction name="action_Configure_Tas"/> | 148 | <addaction name="action_Configure_Tas"/> |
| 149 | </widget> | 149 | </widget> |
| 150 | <addaction name="action_Rederive"/> | 150 | <addaction name="action_Rederive"/> |
| 151 | <addaction name="action_Verify_installed_contents"/> | ||
| 151 | <addaction name="separator"/> | 152 | <addaction name="separator"/> |
| 152 | <addaction name="action_Capture_Screenshot"/> | 153 | <addaction name="action_Capture_Screenshot"/> |
| 153 | <addaction name="menuTAS"/> | 154 | <addaction name="menuTAS"/> |
| @@ -214,6 +215,11 @@ | |||
| 214 | <string>&Reinitialize keys...</string> | 215 | <string>&Reinitialize keys...</string> |
| 215 | </property> | 216 | </property> |
| 216 | </action> | 217 | </action> |
| 218 | <action name="action_Verify_installed_contents"> | ||
| 219 | <property name="text"> | ||
| 220 | <string>Verify installed contents</string> | ||
| 221 | </property> | ||
| 222 | </action> | ||
| 217 | <action name="action_About"> | 223 | <action name="action_About"> |
| 218 | <property name="text"> | 224 | <property name="text"> |
| 219 | <string>&About yuzu</string> | 225 | <string>&About yuzu</string> |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index d0433ffc6..55d0938f7 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -265,7 +265,7 @@ int main(int argc, char** argv) { | |||
| 265 | password = match[2]; | 265 | password = match[2]; |
| 266 | address = match[3]; | 266 | address = match[3]; |
| 267 | if (!match[4].str().empty()) | 267 | if (!match[4].str().empty()) |
| 268 | port = std::stoi(match[4]); | 268 | port = static_cast<u16>(std::stoi(match[4])); |
| 269 | std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); | 269 | std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); |
| 270 | if (!std::regex_match(nickname, nickname_re)) { | 270 | if (!std::regex_match(nickname, nickname_re)) { |
| 271 | std::cout | 271 | std::cout |
| @@ -358,6 +358,7 @@ int main(int argc, char** argv) { | |||
| 358 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 358 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 359 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 359 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 360 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | 360 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |
| 361 | system.GetUserChannel().clear(); | ||
| 361 | 362 | ||
| 362 | const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; | 363 | const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; |
| 363 | 364 | ||