diff options
250 files changed, 10766 insertions, 8973 deletions
diff --git a/.gitmodules b/.gitmodules index b72a2ec8c..45dd0d259 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "enet"] path = externals/enet url = https://github.com/lsalzman/enet.git -[submodule "inih"] - path = externals/inih/inih - url = https://github.com/benhoyt/inih.git [submodule "cubeb"] path = externals/cubeb url = https://github.com/mozilla/cubeb.git @@ -61,3 +58,6 @@ [submodule "breakpad"] path = externals/breakpad url = https://github.com/yuzu-emu/breakpad.git +[submodule "simpleini"] + path = externals/simpleini + url = https://github.com/brofield/simpleini.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c35e0946..ec7975b87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,12 +285,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) find_package(Boost 1.79.0 REQUIRED context) find_package(enet 1.3 MODULE) find_package(fmt 9 REQUIRED) -find_package(inih 52 MODULE COMPONENTS INIReader) find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) find_package(lz4 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED) find_package(Opus 1.3 MODULE) find_package(RenderDoc MODULE) +find_package(SimpleIni MODULE) find_package(stb MODULE) find_package(VulkanMemoryAllocator CONFIG) find_package(ZLIB 1.2 REQUIRED) diff --git a/CMakeModules/FindSimpleIni.cmake b/CMakeModules/FindSimpleIni.cmake new file mode 100644 index 000000000..ce75d7690 --- /dev/null +++ b/CMakeModules/FindSimpleIni.cmake @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_path(SimpleIni_INCLUDE_DIR SimpleIni.h) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SimpleIni + REQUIRED_VARS SimpleIni_INCLUDE_DIR +) + +if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni) + add_library(SimpleIni::SimpleIni INTERFACE IMPORTED) + set_target_properties(SimpleIni::SimpleIni PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SimpleIni_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(SimpleIni_INCLUDE_DIR) diff --git a/CMakeModules/Findinih.cmake b/CMakeModules/Findinih.cmake deleted file mode 100644 index 791befebd..000000000 --- a/CMakeModules/Findinih.cmake +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf> -# -# SPDX-License-Identifier: GPL-3.0-or-later - -find_package(PkgConfig QUIET) -pkg_search_module(INIH QUIET IMPORTED_TARGET inih) -if (INIReader IN_LIST inih_FIND_COMPONENTS) - pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) - if (INIREADER_FOUND) - set(inih_INIReader_FOUND TRUE) - endif() -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(inih - REQUIRED_VARS INIH_LINK_LIBRARIES - VERSION_VAR INIH_VERSION - HANDLE_COMPONENTS -) - -if (inih_FOUND AND NOT TARGET inih::inih) - add_library(inih::inih ALIAS PkgConfig::INIH) -endif() - -if (inih_FOUND AND inih_INIReader_FOUND AND NOT TARGET inih::INIReader) - add_library(inih::INIReader ALIAS PkgConfig::INIREADER) -endif() diff --git a/dist/languages/.tx/config b/dist/languages/.tx/config index 30e76b925..cca7b3d67 100644 --- a/dist/languages/.tx/config +++ b/dist/languages/.tx/config @@ -6,3 +6,8 @@ file_filter = <lang>.ts source_file = en.ts source_lang = en type = QT + +[o:yuzu-emulator:p:yuzu:r:yuzu-android] +file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml +source_file = ../../src/android/app/src/main/res/values/strings.xml +type = ANDROID diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index be8b0b5e8..fc922c31b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -34,11 +34,6 @@ endif() # Glad add_subdirectory(glad) -# inih -if (NOT TARGET inih::INIReader) - add_subdirectory(inih) -endif() - # mbedtls add_subdirectory(mbedtls) target_include_directories(mbedtls PUBLIC ./mbedtls/include) @@ -295,3 +290,8 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB) endif() endif() + +# SimpleIni +if (NOT TARGET SimpleIni::SimpleIni) + add_subdirectory(simpleini) +endif() diff --git a/externals/inih/CMakeLists.txt b/externals/inih/CMakeLists.txt deleted file mode 100644 index ebb60a976..000000000 --- a/externals/inih/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2014 Gui Andrade <admin@archshift.com> -# SPDX-License-Identifier: GPL-2.0-or-later - -add_library(inih - inih/ini.c - inih/ini.h - inih/cpp/INIReader.cpp - inih/cpp/INIReader.h -) - -create_target_directory_groups(inih) -target_include_directories(inih INTERFACE inih/cpp) -add_library(inih::INIReader ALIAS inih) diff --git a/externals/inih/inih b/externals/inih/inih deleted file mode 160000 -Subproject 9cecf0643da0846e77f64d10a126d9f48b9e05e diff --git a/externals/simpleini b/externals/simpleini new file mode 160000 +Subproject 382ddbb4b92c0b26aa1b32cefba2002119a5b1f diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2ca4904a..e04d2418b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,6 +187,7 @@ add_subdirectory(audio_core) add_subdirectory(video_core) add_subdirectory(network) add_subdirectory(input_common) +add_subdirectory(frontend_common) add_subdirectory(shader_recompiler) if (YUZU_ROOM) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 021b070e0..5721327e7 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -219,7 +219,6 @@ dependencies { implementation("io.coil-kt:coil:2.2.2") implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.window:window:1.2.0-beta03") - implementation("org.ini4j:ini4j:0.5.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") 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 9ebd6c732..f2ba2504c 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 @@ -230,8 +230,6 @@ object NativeLibrary { */ external fun onTouchReleased(finger_id: Int) - external fun reloadSettings() - external fun initGameIni(gameID: String?) external fun setAppDirectory(directory: String) 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 054e4b755..f41d7bdbf 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 @@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val isEmulationActive = emulationViewModel.emulationStarted.value && + !emulationViewModel.isEmulationStopping.value pictureInPictureParamsBuilder.setAutoEnterEnabled( - BooleanSetting.PICTURE_IN_PICTURE.boolean + BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive ) } setPictureInPictureParams(pictureInPictureParamsBuilder.build()) 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 0c82cdba8..2ef638559 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 @@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toDrawable import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.preference.PreferenceManager import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) : data = Uri.parse(holder.game.path) } - val layerDrawable = ResourcesCompat.getDrawable( - YuzuApplication.appContext.resources, - R.drawable.shortcut, - null - ) as LayerDrawable - layerDrawable.setDrawableByLayerId( - R.id.shortcut_foreground, - GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) - ) - val inset = YuzuApplication.appContext.resources - .getDimensionPixelSize(R.dimen.icon_inset) - layerDrawable.setLayerInset(1, inset, inset, inset, inset) - val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) - .setShortLabel(holder.game.title) - .setIcon( - IconCompat.createWithAdaptiveBitmap( - layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + val layerDrawable = ResourcesCompat.getDrawable( + YuzuApplication.appContext.resources, + R.drawable.shortcut, + null + ) as LayerDrawable + layerDrawable.setDrawableByLayerId( + R.id.shortcut_foreground, + GameIconUtils.getGameIcon(activity, holder.game) + .toDrawable(YuzuApplication.appContext.resources) ) - ) - .setIntent(openIntent) - .build() - ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + val inset = YuzuApplication.appContext.resources + .getDimensionPixelSize(R.dimen.icon_inset) + layerDrawable.setLayerInset(1, inset, inset, inset, inset) + val shortcut = + ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) + .setShortLabel(holder.game.title) + .setIcon( + IconCompat.createWithAdaptiveBitmap( + layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) + ) + ) + .setIntent(openIntent) + .build() + ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + } + } val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) view.findNavController().navigate(action) 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 08e2a973d..d005c656e 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 @@ -7,7 +7,7 @@ import android.text.TextUtils import android.widget.Toast import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile +import org.yuzu.yuzu_emu.utils.NativeConfig object Settings { private val context get() = YuzuApplication.appContext @@ -19,7 +19,7 @@ object Settings { context.getString(R.string.ini_saved), Toast.LENGTH_SHORT ).show() - SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) + NativeConfig.saveSettings() } else { // TODO: Save custom game settings Toast.makeText( @@ -82,7 +82,6 @@ object Settings { enum class MenuTag(val titleId: Int) { SECTION_ROOT(R.string.advanced_settings), - SECTION_GENERAL(R.string.preferences_general), SECTION_SYSTEM(R.string.preferences_system), SECTION_RENDERER(R.string.preferences_graphics), SECTION_AUDIO(R.string.preferences_audio), 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 522cc49df..425160024 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 @@ -3,10 +3,13 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.DrawableRes + class RunnableSetting( titleId: Int, descriptionId: Int, val isRuntimeRunnable: Boolean, + @DrawableRes val iconId: Int = 0, val runnable: () -> Unit ) : SettingsItem(emptySetting, titleId, descriptionId) { override val type = TYPE_RUNNABLE 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 b3b3fc209..6aba69dbe 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 @@ -73,7 +73,7 @@ abstract class SettingsItem( R.string.frame_limit_slider, R.string.frame_limit_slider_description, 1, - 200, + 400, "%" ) ) 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 b343e527e..94953b18a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt @@ -3,11 +3,14 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.Settings class SubmenuSetting( - titleId: Int, - descriptionId: Int, + @StringRes titleId: Int, + @StringRes descriptionId: Int, + @DrawableRes val iconId: Int, val menuKey: Settings.MenuTag ) : SettingsItem(emptySetting, titleId, descriptionId) { override val type = TYPE_SUBMENU 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 c73edd50e..48bdbdd75 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 @@ -21,7 +21,6 @@ import androidx.navigation.navArgs import com.google.android.material.color.MaterialColors import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import org.yuzu.yuzu_emu.NativeLibrary import java.io.IOException import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding @@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() { settingsViewModel.shouldSave = false // Delete settings file because the user may have changed values that do not exist in the UI + NativeConfig.unloadConfig() val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) if (!settingsFile.delete()) { throw IOException("Failed to delete $settingsFile") } - NativeLibrary.reloadSettings() + NativeConfig.initializeConfig() Toast.makeText( applicationContext, 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 70d8ec14b..769baf744 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 @@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.divider.MaterialDividerItemDecoration import com.google.android.material.transition.MaterialSharedAxis import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -68,15 +67,9 @@ class SettingsFragment : Fragment() { ) binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) - val dividerDecoration = MaterialDividerItemDecoration( - requireContext(), - LinearLayoutManager.VERTICAL - ) - dividerDecoration.isLastItemDecorated = false binding.listSettings.apply { adapter = settingsAdapter layoutManager = LinearLayoutManager(requireContext()) - addItemDecoration(dividerDecoration) } binding.toolbarSettings.setNavigationOnClickListener { @@ -94,17 +87,6 @@ class SettingsFragment : Fragment() { } } } - launch { - settingsViewModel.isUsingSearch.collectLatest { - if (it) { - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) - } else { - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - } - } - } } if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { @@ -112,8 +94,6 @@ class SettingsFragment : Fragment() { binding.toolbarSettings.setOnMenuItemClickListener { when (it.itemId) { R.id.action_search -> { - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) view.findNavController() .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) true @@ -129,11 +109,6 @@ class SettingsFragment : Fragment() { setInsets() } - override fun onResume() { - super.onResume() - settingsViewModel.setIsUsingSearch(false) - } - private fun setInsets() { ViewCompat.setOnApplyWindowInsetsListener( binding.root @@ -144,10 +119,9 @@ class SettingsFragment : Fragment() { val leftInsets = barInsets.left + cutoutInsets.left val rightInsets = barInsets.right + cutoutInsets.right - val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams - mlpSettingsList.leftMargin = sideMargin + leftInsets - mlpSettingsList.rightMargin = sideMargin + rightInsets + mlpSettingsList.leftMargin = leftInsets + mlpSettingsList.rightMargin = rightInsets binding.listSettings.layoutParams = mlpSettingsList binding.listSettings.updatePadding( bottom = barInsets.bottom 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 766414a6c..8b71e32f3 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,7 +3,6 @@ package org.yuzu.yuzu_emu.features.settings.ui -import android.content.Context import android.content.SharedPreferences import android.os.Build import android.widget.Toast @@ -32,8 +31,6 @@ class SettingsFragmentPresenter( private val preferences: SharedPreferences get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - private val context: Context get() = YuzuApplication.appContext - // Extension for populating settings list based on paired settings fun ArrayList<SettingsItem>.add(key: String) { val item = SettingsItem.settingsItems[key]!! @@ -53,7 +50,6 @@ class SettingsFragmentPresenter( val sl = ArrayList<SettingsItem>() when (menuTag) { Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) - Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl) Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) @@ -75,30 +71,53 @@ class SettingsFragmentPresenter( private fun addConfigSettings(sl: ArrayList<SettingsItem>) { sl.apply { - add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL)) - add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM)) - add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER)) - add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO)) - add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG)) add( - RunnableSetting(R.string.reset_to_default, 0, false) { - settingsViewModel.setShouldShowResetSettingsDialog(true) - } + SubmenuSetting( + R.string.preferences_system, + R.string.preferences_system_description, + R.drawable.ic_system_settings, + Settings.MenuTag.SECTION_SYSTEM + ) + ) + add( + SubmenuSetting( + R.string.preferences_graphics, + R.string.preferences_graphics_description, + R.drawable.ic_graphics, + Settings.MenuTag.SECTION_RENDERER + ) + ) + add( + SubmenuSetting( + R.string.preferences_audio, + R.string.preferences_audio_description, + R.drawable.ic_audio, + Settings.MenuTag.SECTION_AUDIO + ) + ) + add( + SubmenuSetting( + R.string.preferences_debug, + R.string.preferences_debug_description, + R.drawable.ic_code, + Settings.MenuTag.SECTION_DEBUG + ) + ) + add( + RunnableSetting( + R.string.reset_to_default, + R.string.reset_to_default_description, + false, + R.drawable.ic_restore + ) { settingsViewModel.setShouldShowResetSettingsDialog(true) } ) } } - private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { + private fun addSystemSettings(sl: ArrayList<SettingsItem>) { sl.apply { add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) add(ShortSetting.RENDERER_SPEED_LIMIT.key) - add(IntSetting.CPU_ACCURACY.key) - add(BooleanSetting.PICTURE_IN_PICTURE.key) - } - } - - private fun addSystemSettings(sl: ArrayList<SettingsItem>) { - sl.apply { add(BooleanSetting.USE_DOCKED_MODE.key) add(IntSetting.REGION_INDEX.key) add(IntSetting.LANGUAGE_INDEX.key) @@ -116,6 +135,7 @@ class SettingsFragmentPresenter( add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_ASPECT_RATIO.key) + add(BooleanSetting.PICTURE_IN_PICTURE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) @@ -249,6 +269,7 @@ class SettingsFragmentPresenter( add(BooleanSetting.RENDERER_DEBUG.key) add(HeaderSetting(R.string.cpu)) + add(IntSetting.CPU_ACCURACY.key) add(BooleanSetting.CPU_DEBUG_MODE.key) add(SettingsItem.FASTMEM_COMBINED) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt index 83a2e94f1..036195624 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt @@ -4,6 +4,7 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View +import androidx.core.content.res.ResourcesCompat import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting @@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA override fun bind(item: SettingsItem) { setting = item as RunnableSetting + if (item.iconId != 0) { + binding.icon.visibility = View.VISIBLE + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.resources, + item.iconId, + binding.icon.context.theme + ) + ) + } else { + binding.icon.visibility = View.GONE + } + binding.textSettingName.setText(item.nameId) if (item.descriptionId != 0) { binding.textSettingDescription.setText(item.descriptionId) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt index 1cf581a9d..8100c65dd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt @@ -4,6 +4,7 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View +import androidx.core.content.res.ResourcesCompat import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting @@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd override fun bind(item: SettingsItem) { this.item = item as SubmenuSetting + if (item.iconId != 0) { + binding.icon.visibility = View.VISIBLE + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.resources, + item.iconId, + binding.icon.context.theme + ) + ) + } else { + binding.icon.visibility = View.GONE + } + binding.textSettingName.setText(item.nameId) if (item.descriptionId != 0) { binding.textSettingDescription.setText(item.descriptionId) 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 2b04d666a..3ae5b4653 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,15 +3,8 @@ package org.yuzu.yuzu_emu.features.settings.utils -import android.widget.Toast import java.io.* -import org.ini4j.Wini -import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.features.settings.model.* import org.yuzu.yuzu_emu.utils.DirectoryInitialization -import org.yuzu.yuzu_emu.utils.Log -import org.yuzu.yuzu_emu.utils.NativeConfig /** * Contains static methods for interacting with .ini files in which settings are stored. @@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig object SettingsFile { const val FILE_NAME_CONFIG = "config" - /** - * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error - * telling why it failed. - * - * @param fileName The target filename without a path or extension. - */ - fun saveFile(fileName: String) { - val ini = getSettingsFile(fileName) - try { - val wini = Wini(ini) - for (specificCategory in Settings.Category.values()) { - val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal) - for (setting in Settings.settingsList) { - if (setting.key!!.isEmpty()) continue - - val settingCategoryHeader = - NativeConfig.getConfigHeader(setting.category.ordinal) - val iniSetting: String? = wini.get(categoryHeader, setting.key) - if (iniSetting != null || settingCategoryHeader == categoryHeader) { - wini.put(settingCategoryHeader, setting.key, setting.valueAsString) - } - } - } - wini.store() - } catch (e: IOException) { - Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) - val context = YuzuApplication.appContext - Toast.makeText( - context, - context.getString(R.string.error_saving, fileName, e.message), - Toast.LENGTH_SHORT - ).show() - } - } - fun getSettingsFile(fileName: String): File = File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index 2ff827c6b..a1620fbb7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt @@ -114,10 +114,10 @@ class AboutFragment : Fragment() { val leftInsets = barInsets.left + cutoutInsets.left val rightInsets = barInsets.right + cutoutInsets.right - val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams - mlpAppBar.leftMargin = leftInsets - mlpAppBar.rightMargin = rightInsets - binding.appbarAbout.layoutParams = mlpAppBar + val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams + mlpToolbar.leftMargin = leftInsets + mlpToolbar.rightMargin = rightInsets + binding.toolbarAbout.layoutParams = mlpToolbar val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams mlpScrollAbout.leftMargin = leftInsets diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index ec116ab62..6940fc757 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt @@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.ui.main.MainActivity +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter class InstallableFragment : Fragment() { private var _binding: FragmentInstallablesBinding? = null @@ -78,7 +80,15 @@ class InstallableFragment : Fragment() { R.string.manage_save_data, R.string.import_export_saves_description, install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, - export = { mainActivity.exportSave() } + export = { + mainActivity.exportSaves.launch( + "yuzu saves - ${ + LocalDateTime.now().format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + ) + }.zip" + ) + } ) } else { Installable( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt index 9d0594c6e..f95d545bf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt @@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) - returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) } override fun onCreateView( @@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - settingsViewModel.setIsUsingSearch(true) if (savedInstanceState != null) { binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index de84b2adb..2fa3ab31b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -18,8 +18,8 @@ class Game( val version: String = "", val isHomebrew: Boolean = false ) : Parcelable { - val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" - val keyLastPlayedTime get() = "${programId}_LastPlayed" + val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime" + val keyLastPlayedTime get() = "${path}_LastPlayed" override fun equals(other: Any?): Boolean { if (other !is Game) { 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 index 53fa7a8de..6f947674e 100644 --- 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 @@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() { val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList private val _shouldReloadSettingsList = MutableStateFlow(false) - val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch - private val _isUsingSearch = MutableStateFlow(false) - val sliderProgress: StateFlow<Int> get() = _sliderProgress private val _sliderProgress = MutableStateFlow(-1) @@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() { _shouldReloadSettingsList.value = value } - fun setIsUsingSearch(value: Boolean) { - _isUsingSearch.value = value - } - fun setSliderTextValue(value: Float, units: String) { _sliderProgress.value = value.toInt() _sliderTextValue.value = String.format( 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 211b7cf69..bd2f4cd25 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 @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main import android.content.Intent import android.net.Uri import android.os.Bundle -import android.provider.DocumentsContract import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.WindowManager @@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.databinding.ActivityMainBinding -import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment @@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.utils.* import java.io.BufferedInputStream import java.io.BufferedOutputStream -import java.io.FileOutputStream -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { // Get first subfolder in saves folder (should be the user folder) val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" - private var lastZipCreated: File? = null override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() @@ -632,6 +625,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } // Clear existing user data + NativeConfig.unloadConfig() File(DirectoryInitialization.userDirectory!!).deleteRecursively() // Copy archive to internal storage @@ -650,6 +644,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { // Reinitialize relevant data NativeLibrary.initializeSystem(true) + NativeConfig.initializeConfig() gamesViewModel.reloadGames(false) return@newInstance getString(R.string.user_data_import_success) @@ -657,74 +652,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } /** - * Zips the save files located in the given folder path and creates a new zip file with the current date and time. - * @return true if the zip file is successfully created, false otherwise. - */ - private fun zipSave(): Boolean { - try { - val tempFolder = File(getPublicFilesDir().canonicalPath, "temp") - tempFolder.mkdirs() - val saveFolder = File(savesFolderRoot) - val outputZipFile = File( - tempFolder, - "yuzu saves - ${ - LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - }.zip" - ) - outputZipFile.createNewFile() - val result = FileUtil.zipFromInternalStorage( - saveFolder, - savesFolderRoot, - BufferedOutputStream(FileOutputStream(outputZipFile)) - ) - if (result == TaskState.Failed) { - return false - } - lastZipCreated = outputZipFile - } catch (e: Exception) { - return false - } - return true - } - - /** * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. */ - fun exportSave() { - CoroutineScope(Dispatchers.IO).launch { - val wasZipCreated = zipSave() - val lastZipFile = lastZipCreated - if (!wasZipCreated || lastZipFile == null) { - withContext(Dispatchers.Main) { - Toast.makeText( - this@MainActivity, - getString(R.string.export_save_failed), - Toast.LENGTH_LONG - ).show() - } - return@launch - } + val exportSaves = registerForActivityResult( + ActivityResultContracts.CreateDocument("application/zip") + ) { result -> + if (result == null) { + return@registerForActivityResult + } - withContext(Dispatchers.Main) { - val file = DocumentFile.fromSingleUri( - this@MainActivity, - DocumentsContract.buildDocumentUri( - DocumentProvider.AUTHORITY, - "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" - ) - )!! - val intent = Intent(Intent.ACTION_SEND) - .setDataAndType(file.uri, "application/zip") - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .putExtra(Intent.EXTRA_STREAM, file.uri) - startForResultExportSave.launch( - Intent.createChooser( - intent, - getString(R.string.share_save_file) - ) - ) + IndeterminateProgressDialogFragment.newInstance( + this, + R.string.save_files_exporting, + false + ) { + val zipResult = FileUtil.zipFromInternalStorage( + File(savesFolderRoot), + savesFolderRoot, + BufferedOutputStream(contentResolver.openOutputStream(result)) + ) + return@newInstance when (zipResult) { + TaskState.Completed -> getString(R.string.export_success) + TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) } - } + }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) } private val startForResultExportSave = 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 5e9a1176a..21270fc84 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 @@ -16,6 +16,7 @@ object DirectoryInitialization { if (!areDirectoriesReady) { initializeInternalStorage() NativeLibrary.initializeSystem(false) + NativeConfig.initializeConfig() areDirectoriesReady = true } } 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 index 654d62f52..2e9b0beb8 100644 --- 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 @@ -8,9 +8,9 @@ import android.graphics.BitmapFactory import android.widget.ImageView import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toDrawable +import androidx.lifecycle.LifecycleOwner import coil.ImageLoader import coil.decode.DataSource -import coil.executeBlocking import coil.fetch.DrawableResult import coil.fetch.FetchResult import coil.fetch.Fetcher @@ -76,12 +76,13 @@ object GameIconUtils { imageLoader.enqueue(request) } - fun getGameIcon(game: Game): Bitmap { + suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap { val request = ImageRequest.Builder(YuzuApplication.appContext) .data(game) + .lifecycle(lifecycleOwner) .error(R.drawable.default_icon) .build() - return imageLoader.executeBlocking(request) + return imageLoader.execute(request) .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index 47bde5081..e63382e1d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -27,6 +27,8 @@ object InputHandler { 0x054C -> getInputDS5ButtonKey(event.keyCode) 0x057E -> getInputJoyconButtonKey(event.keyCode) 0x1532 -> getInputRazerButtonKey(event.keyCode) + 0x3537 -> getInputRedmagicButtonKey(event.keyCode) + 0x358A -> getInputBackboneLabsButtonKey(event.keyCode) else -> getInputGenericButtonKey(event.keyCode) } @@ -227,6 +229,42 @@ object InputHandler { } } + private fun getInputRedmagicButtonKey(key: Int): Int { + return when (key) { + KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B + KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A + KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y + KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X + KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L + KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R + KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL + KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR + KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L + KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R + KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS + KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS + else -> -1 + } + } + + private fun getInputBackboneLabsButtonKey(key: Int): Int { + return when (key) { + KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B + KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A + KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y + KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X + KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L + KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R + KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL + KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR + KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L + KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R + KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS + KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS + else -> -1 + } + } + private fun getInputGenericButtonKey(key: Int): Int { return when (key) { KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A 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 index 9425f8b99..87e579fa7 100644 --- 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 @@ -4,6 +4,30 @@ package org.yuzu.yuzu_emu.utils object NativeConfig { + /** + * Creates a Config object and opens the emulation config. + */ + @Synchronized + external fun initializeConfig() + + /** + * Destroys the stored config object. This automatically saves the existing config. + */ + @Synchronized + external fun unloadConfig() + + /** + * Reads values saved to the config file and saves them. + */ + @Synchronized + external fun reloadSettings() + + /** + * Saves settings values in memory to disk. + */ + @Synchronized + external fun saveSettings() + external fun getBoolean(key: String, getDefault: Boolean): Boolean external fun setBoolean(key: String, value: Boolean) diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 88a570f68..2acc93da8 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -6,9 +6,6 @@ add_library(yuzu-android SHARED android_common/android_common.h applets/software_keyboard.cpp applets/software_keyboard.h - config.cpp - config.h - default_ini.h emu_window/emu_window.cpp emu_window/emu_window.h id_cache.cpp @@ -16,15 +13,17 @@ add_library(yuzu-android SHARED native.cpp native.h native_config.cpp - uisettings.cpp + android_settings.cpp game_metadata.cpp native_log.cpp + android_config.cpp + android_config.h ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) -target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) -target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) +target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common) +target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log) if (ARCHITECTURE_arm64) target_link_libraries(yuzu-android PRIVATE adrenotools) endif() diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp new file mode 100644 index 000000000..3041c25c9 --- /dev/null +++ b/src/android/app/src/main/jni/android_config.cpp @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "android_config.h" +#include "android_settings.h" +#include "common/settings_setting.h" + +AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type) + : Config(config_type) { + Initialize(config_name); + if (config_type != ConfigType::InputProfile) { + ReadAndroidValues(); + SaveAndroidValues(); + } +} + +AndroidConfig::~AndroidConfig() { + if (global) { + AndroidConfig::SaveAllValues(); + } +} + +void AndroidConfig::ReloadAllValues() { + Reload(); + ReadAndroidValues(); + SaveAndroidValues(); +} + +void AndroidConfig::SaveAllValues() { + Save(); + SaveAndroidValues(); +} + +void AndroidConfig::ReadAndroidValues() { + if (global) { + ReadAndroidUIValues(); + } +} + +void AndroidConfig::ReadAndroidUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); + + ReadCategory(Settings::Category::Android); + + EndGroup(); +} + +void AndroidConfig::SaveAndroidValues() { + if (global) { + SaveAndroidUIValues(); + } + + WriteToIni(); +} + +void AndroidConfig::SaveAndroidUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); + + WriteCategory(Settings::Category::Android); + + EndGroup(); +} + +std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { + auto& map = Settings::values.linkage.by_category; + if (map.contains(category)) { + return Settings::values.linkage.by_category[category]; + } + return AndroidSettings::values.linkage.by_category[category]; +} diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h new file mode 100644 index 000000000..e679392fd --- /dev/null +++ b/src/android/app/src/main/jni/android_config.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "frontend_common/config.h" + +class AndroidConfig final : public Config { +public: + explicit AndroidConfig(const std::string& config_name = "config", + ConfigType config_type = ConfigType::GlobalConfig); + ~AndroidConfig() override; + + void ReloadAllValues() override; + void SaveAllValues() override; + +protected: + void ReadAndroidValues(); + void ReadAndroidUIValues(); + void ReadHidbusValues() override {} + void ReadDebugControlValues() override {} + void ReadPathValues() override {} + void ReadShortcutValues() override {} + void ReadUIValues() override {} + void ReadUIGamelistValues() override {} + void ReadUILayoutValues() override {} + void ReadMultiplayerValues() override {} + + void SaveAndroidValues(); + void SaveAndroidUIValues(); + void SaveHidbusValues() override {} + void SaveDebugControlValues() override {} + void SavePathValues() override {} + void SaveShortcutValues() override {} + void SaveUIValues() override {} + void SaveUIGamelistValues() override {} + void SaveUILayoutValues() override {} + void SaveMultiplayerValues() override {} + + std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override; +}; diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/android_settings.cpp index f2f0bad50..16023a6b0 100644 --- a/src/android/app/src/main/jni/uisettings.cpp +++ b/src/android/app/src/main/jni/android_settings.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "uisettings.h" +#include "android_settings.h" namespace AndroidSettings { diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/android_settings.h index 494654af7..37bc33918 100644 --- a/src/android/app/src/main/jni/uisettings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -13,7 +13,7 @@ struct Values { Settings::Linkage linkage; // Android - Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture", + Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", Settings::Category::Android}; Settings::Setting<s32> screen_layout{linkage, 5, diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp deleted file mode 100644 index 81120ab0f..000000000 --- a/src/android/app/src/main/jni/config.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <memory> -#include <optional> -#include <sstream> - -#include <INIReader.h> -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/settings_enums.h" -#include "core/hle/service/acc/profile_manager.h" -#include "input_common/main.h" -#include "jni/config.h" -#include "jni/default_ini.h" -#include "uisettings.h" - -namespace FS = Common::FS; - -Config::Config(const std::string& config_name, ConfigType config_type) - : type(config_type), global{config_type == ConfigType::GlobalConfig} { - Initialize(config_name); -} - -Config::~Config() = default; - -bool Config::LoadINI(const std::string& default_contents, bool retry) { - void(FS::CreateParentDir(config_loc)); - config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc)); - const auto config_loc_str = FS::PathToUTF8String(config_loc); - if (config->ParseError() < 0) { - if (retry) { - LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", - config_loc_str); - - void(FS::CreateParentDir(config_loc)); - void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents)); - - config = std::make_unique<INIReader>(config_loc_str); - - return LoadINI(default_contents, false); - } - LOG_ERROR(Config, "Failed."); - return false; - } - LOG_INFO(Config, "Successfully loaded {}", config_loc_str); - return true; -} - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { - std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault()); - if (setting_value.empty()) { - setting_value = setting.GetDefault(); - } - setting = std::move(setting_value); -} - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { - setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); -} - -template <typename Type, bool ranged> -void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { - setting = static_cast<Type>( - config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault()))); -} - -void Config::ReadValues() { - ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); - ReadSetting("ControlsGeneral", Settings::values.touch_device); - ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled); - ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled); - ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); - ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); - ReadSetting("ControlsGeneral", Settings::values.motion_enabled); - Settings::values.touchscreen.enabled = - config->GetBoolean("ControlsGeneral", "touch_enabled", true); - Settings::values.touchscreen.rotation_angle = - config->GetInteger("ControlsGeneral", "touch_angle", 0); - Settings::values.touchscreen.diameter_x = - config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); - Settings::values.touchscreen.diameter_y = - config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); - - int num_touch_from_button_maps = - config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); - if (num_touch_from_button_maps > 0) { - for (int i = 0; i < num_touch_from_button_maps; ++i) { - Settings::TouchFromButtonMap map; - map.name = config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_name"), - "default"); - const int num_touch_maps = config->GetInteger( - "ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), - 0); - map.buttons.reserve(num_touch_maps); - - for (int j = 0; j < num_touch_maps; ++j) { - std::string touch_mapping = - config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_bind_") + std::to_string(j), - ""); - map.buttons.emplace_back(std::move(touch_mapping)); - } - - Settings::values.touch_from_button_maps.emplace_back(std::move(map)); - } - } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); - num_touch_from_button_maps = 1; - } - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); - - ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); - - // Data Storage - ReadSetting("Data Storage", Settings::values.use_virtual_sd); - FS::SetYuzuPath(FS::YuzuPath::NANDDir, - config->Get("Data Storage", "nand_directory", - FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); - FS::SetYuzuPath(FS::YuzuPath::SDMCDir, - config->Get("Data Storage", "sdmc_directory", - FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); - FS::SetYuzuPath(FS::YuzuPath::LoadDir, - config->Get("Data Storage", "load_directory", - FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); - FS::SetYuzuPath(FS::YuzuPath::DumpDir, - config->Get("Data Storage", "dump_directory", - FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - ReadSetting("Data Storage", Settings::values.gamecard_inserted); - ReadSetting("Data Storage", Settings::values.gamecard_current_game); - ReadSetting("Data Storage", Settings::values.gamecard_path); - - // System - ReadSetting("System", Settings::values.current_user); - Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, - Service::Account::MAX_USERS - 1); - - // Disable docked mode by default on Android - Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false) - ? Settings::ConsoleMode::Docked - : Settings::ConsoleMode::Handheld); - - const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); - if (rng_seed_enabled) { - Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0)); - } else { - Settings::values.rng_seed.SetValue(0); - } - Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled); - - const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false); - if (custom_rtc_enabled) { - Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0); - } else { - Settings::values.custom_rtc = 0; - } - Settings::values.custom_rtc_enabled = custom_rtc_enabled; - - ReadSetting("System", Settings::values.language_index); - ReadSetting("System", Settings::values.region_index); - ReadSetting("System", Settings::values.time_zone_index); - ReadSetting("System", Settings::values.sound_index); - - // Core - ReadSetting("Core", Settings::values.use_multi_core); - ReadSetting("Core", Settings::values.memory_layout_mode); - - // Cpu - ReadSetting("Cpu", Settings::values.cpu_accuracy); - ReadSetting("Cpu", Settings::values.cpu_debug_mode); - ReadSetting("Cpu", Settings::values.cpuopt_page_tables); - ReadSetting("Cpu", Settings::values.cpuopt_block_linking); - ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer); - ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher); - ReadSetting("Cpu", Settings::values.cpuopt_context_elimination); - ReadSetting("Cpu", Settings::values.cpuopt_const_prop); - ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); - ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); - ReadSetting("Cpu", Settings::values.cpuopt_fastmem); - ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); - ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); - ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor); - - // Renderer - ReadSetting("Renderer", Settings::values.renderer_backend); - ReadSetting("Renderer", Settings::values.renderer_debug); - ReadSetting("Renderer", Settings::values.renderer_shader_feedback); - ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); - ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); - ReadSetting("Renderer", Settings::values.vulkan_device); - - ReadSetting("Renderer", Settings::values.resolution_setup); - ReadSetting("Renderer", Settings::values.scaling_filter); - ReadSetting("Renderer", Settings::values.fsr_sharpening_slider); - ReadSetting("Renderer", Settings::values.anti_aliasing); - ReadSetting("Renderer", Settings::values.fullscreen_mode); - ReadSetting("Renderer", Settings::values.aspect_ratio); - ReadSetting("Renderer", Settings::values.max_anisotropy); - ReadSetting("Renderer", Settings::values.use_speed_limit); - ReadSetting("Renderer", Settings::values.speed_limit); - ReadSetting("Renderer", Settings::values.use_disk_shader_cache); - ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation); - ReadSetting("Renderer", Settings::values.vsync_mode); - ReadSetting("Renderer", Settings::values.shader_backend); - ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); - ReadSetting("Renderer", Settings::values.nvdec_emulation); - ReadSetting("Renderer", Settings::values.use_fast_gpu_time); - ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); - - ReadSetting("Renderer", Settings::values.bg_red); - ReadSetting("Renderer", Settings::values.bg_green); - ReadSetting("Renderer", Settings::values.bg_blue); - - // Use GPU accuracy normal by default on Android - Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger( - "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal))); - - // Use GPU default anisotropic filtering on Android - Settings::values.max_anisotropy = - static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1)); - - // Disable ASTC compute by default on Android - Settings::values.accelerate_astc.SetValue( - config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu - : Settings::AstcDecodeMode::Cpu); - - // Enable asynchronous presentation by default on Android - Settings::values.async_presentation = - config->GetBoolean("Renderer", "async_presentation", true); - - // Disable force_max_clock by default on Android - Settings::values.renderer_force_max_clock = - config->GetBoolean("Renderer", "force_max_clock", false); - - // Disable use_reactive_flushing by default on Android - Settings::values.use_reactive_flushing = - config->GetBoolean("Renderer", "use_reactive_flushing", false); - - // Audio - ReadSetting("Audio", Settings::values.sink_id); - ReadSetting("Audio", Settings::values.audio_output_device_id); - ReadSetting("Audio", Settings::values.volume); - - // Miscellaneous - // log_filter has a different default here than from common - Settings::values.log_filter = "*:Info"; - ReadSetting("Miscellaneous", Settings::values.use_dev_keys); - - // Debugging - Settings::values.record_frame_times = - config->GetBoolean("Debugging", "record_frame_times", false); - ReadSetting("Debugging", Settings::values.dump_exefs); - ReadSetting("Debugging", Settings::values.dump_nso); - ReadSetting("Debugging", Settings::values.enable_fs_access_log); - ReadSetting("Debugging", Settings::values.reporting_services); - ReadSetting("Debugging", Settings::values.quest_flag); - ReadSetting("Debugging", Settings::values.use_debug_asserts); - ReadSetting("Debugging", Settings::values.use_auto_stub); - ReadSetting("Debugging", Settings::values.disable_macro_jit); - ReadSetting("Debugging", Settings::values.disable_macro_hle); - ReadSetting("Debugging", Settings::values.use_gdbstub); - ReadSetting("Debugging", Settings::values.gdbstub_port); - - const auto title_list = config->Get("AddOns", "title_ids", ""); - std::stringstream ss(title_list); - std::string line; - while (std::getline(ss, line, '|')) { - const auto title_id = std::strtoul(line.c_str(), nullptr, 16); - const auto disabled_list = config->Get("AddOns", "disabled_" + line, ""); - - std::stringstream inner_ss(disabled_list); - std::string inner_line; - std::vector<std::string> out; - while (std::getline(inner_ss, inner_line, '|')) { - out.push_back(inner_line); - } - - Settings::values.disabled_addons.insert_or_assign(title_id, out); - } - - // Web Service - ReadSetting("WebService", Settings::values.enable_telemetry); - ReadSetting("WebService", Settings::values.web_api_url); - ReadSetting("WebService", Settings::values.yuzu_username); - ReadSetting("WebService", Settings::values.yuzu_token); - - // Network - ReadSetting("Network", Settings::values.network_interface); - - // Android - ReadSetting("Android", AndroidSettings::values.picture_in_picture); - ReadSetting("Android", AndroidSettings::values.screen_layout); -} - -void Config::Initialize(const std::string& config_name) { - const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); - const auto config_file = fmt::format("{}.ini", config_name); - - switch (type) { - case ConfigType::GlobalConfig: - config_loc = FS::PathToUTF8String(fs_config_loc / config_file); - break; - case ConfigType::PerGameConfig: - config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); - break; - case ConfigType::InputProfile: - config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); - LoadINI(DefaultINI::android_config_file); - return; - } - LoadINI(DefaultINI::android_config_file); - ReadValues(); -} diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h deleted file mode 100644 index e1e8f47ed..000000000 --- a/src/android/app/src/main/jni/config.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <filesystem> -#include <memory> -#include <optional> -#include <string> - -#include "common/settings.h" - -class INIReader; - -class Config { - bool LoadINI(const std::string& default_contents = "", bool retry = true); - -public: - enum class ConfigType { - GlobalConfig, - PerGameConfig, - InputProfile, - }; - - explicit Config(const std::string& config_name = "config", - ConfigType config_type = ConfigType::GlobalConfig); - ~Config(); - - void Initialize(const std::string& config_name); - -private: - /** - * Applies a value read from the config to a Setting. - * - * @param group The name of the INI group - * @param setting The yuzu setting to modify - */ - template <typename Type, bool ranged> - void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); - - void ReadValues(); - - const ConfigType type; - std::unique_ptr<INIReader> config; - std::string config_loc; - const bool global; -}; diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h deleted file mode 100644 index d81422a74..000000000 --- a/src/android/app/src/main/jni/default_ini.h +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace DefaultINI { - -const char* android_config_file = R"( - -[ControlsP0] -# The input devices and parameters for each Switch native input -# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... -# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." -# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values - -# Indicates if this player should be connected at boot -connected= - -# for button input, the following devices are available: -# - "keyboard" (default) for keyboard input. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "button"(optional): the index of the button to bind -# - "hat"(optional): the index of the hat to bind as direction buttons -# - "axis"(optional): the index of the axis to bind -# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is -# triggered if the axis value crosses -# - "direction"(only used for axis): "+" means the button is triggered when the axis value -# is greater than the threshold; "-" means the button is triggered when the axis value -# is smaller than the threshold -button_a= -button_b= -button_x= -button_y= -button_lstick= -button_rstick= -button_l= -button_r= -button_zl= -button_zr= -button_plus= -button_minus= -button_dleft= -button_dup= -button_dright= -button_ddown= -button_lstick_left= -button_lstick_up= -button_lstick_right= -button_lstick_down= -button_sl= -button_sr= -button_home= -button_screenshot= - -# for analog input, the following devices are available: -# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: -# - "up", "down", "left", "right": sub-devices for each direction. -# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" -# - "modifier": sub-devices as a modifier. -# - "modifier_scale": a float number representing the applied modifier scale to the analog input. -# Must be in range of 0.0-1.0. Defaults to 0.5 -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "axis_x": the index of the axis to bind as x-axis (default to 0) -# - "axis_y": the index of the axis to bind as y-axis (default to 1) -lstick= -rstick= - -# for motion input, the following devices are available: -# - "keyboard" (default) for emulating random motion input from buttons. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for motion input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "motion": the index of the motion sensor to bind -# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: -# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" -# - "port": the port of the cemu hook server -# - "pad": the index of the joystick -# - "motion": the index of the motion sensor of the joystick to bind -motionleft= -motionright= - -[ControlsGeneral] -# To use the debug_pad, prepend `debug_pad_` before each button setting above. -# i.e. debug_pad_button_a= - -# Enable debug pad inputs to the guest -# 0 (default): Disabled, 1: Enabled -debug_pad_enabled = - -# Whether to enable or disable vibration -# 0: Disabled, 1 (default): Enabled -vibration_enabled= - -# Whether to enable or disable accurate vibrations -# 0 (default): Disabled, 1: Enabled -enable_accurate_vibrations= - -# Enables controller motion inputs -# 0: Disabled, 1 (default): Enabled -motion_enabled = - -# Defines the udp device's touch screen coordinate system for cemuhookudp devices -# - "min_x", "min_y", "max_x", "max_y" -touch_device= - -# for mapping buttons to touch inputs. -#touch_from_button_map=1 -#touch_from_button_maps_0_name=default -#touch_from_button_maps_0_count=2 -#touch_from_button_maps_0_bind_0=foo -#touch_from_button_maps_0_bind_1=bar -# etc. - -# List of Cemuhook UDP servers, delimited by ','. -# Default: 127.0.0.1:26760 -# Example: 127.0.0.1:26760,123.4.5.67:26761 -udp_input_servers = - -# Enable controlling an axis via a mouse input. -# 0 (default): Off, 1: On -mouse_panning = - -# Set mouse sensitivity. -# Default: 1.0 -mouse_panning_sensitivity = - -# Emulate an analog control stick from keyboard inputs. -# 0 (default): Disabled, 1: Enabled -emulate_analog_keyboard = - -# Enable mouse inputs to the guest -# 0 (default): Disabled, 1: Enabled -mouse_enabled = - -# Enable keyboard inputs to the guest -# 0 (default): Disabled, 1: Enabled -keyboard_enabled = - -[Core] -# Whether to use multi-core for CPU emulation -# 0: Disabled, 1 (default): Enabled -use_multi_core = - -# Enable unsafe extended guest system memory layout (8GB DRAM) -# 0 (default): Disabled, 1: Enabled -use_unsafe_extended_memory_layout = - -[Cpu] -# Adjusts various optimizations. -# Auto-select mode enables choice unsafe optimizations. -# Accurate enables only safe optimizations. -# Unsafe allows any unsafe optimizations. -# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations -cpu_accuracy = - -# Allow disabling safe optimizations. -# 0 (default): Disabled, 1: Enabled -cpu_debug_mode = - -# Enable inline page tables optimization (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_page_tables = - -# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) -# 0: Disabled, 1 (default): Enabled -cpuopt_block_linking = - -# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) -# 0: Disabled, 1 (default): Enabled -cpuopt_return_stack_buffer = - -# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) -# 0: Disabled, 1 (default): Enabled -cpuopt_fast_dispatcher = - -# Enable context elimination CPU Optimization (reduce host memory use for guest context) -# 0: Disabled, 1 (default): Enabled -cpuopt_context_elimination = - -# Enable constant propagation CPU optimization (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_const_prop = - -# Enable miscellaneous CPU optimizations (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_misc_ir = - -# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) -# 0: Disabled, 1 (default): Enabled -cpuopt_reduce_misalign_checks = - -# Enable Host MMU Emulation (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem = - -# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem_exclusives = - -# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_recompile_exclusives = - -# Enable optimization to ignore invalid memory accesses (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_ignore_memory_aborts = - -# Enable unfuse FMA (improve performance on CPUs without FMA) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_unfuse_fma = - -# Enable faster FRSQRTE and FRECPE -# Only enabled if cpu_accuracy is set to Unsafe. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_reduce_fp_error = - -# Enable faster ASIMD instructions (32 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_standard_fpcr = - -# Enable inaccurate NaN handling -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_inaccurate_nan = - -# Disable address space checks (64 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_fastmem_check = - -# Enable faster exclusive instructions -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_global_monitor = - -[Renderer] -# Which backend API to use. -# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null -backend = - -# Whether to enable asynchronous presentation (Vulkan only) -# 0: Off, 1 (default): On -async_presentation = - -# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied). -# 0 (default): Disabled, 1: Enabled -force_max_clock = - -# Enable graphics API debugging mode. -# 0 (default): Disabled, 1: Enabled -debug = - -# Enable shader feedback. -# 0 (default): Disabled, 1: Enabled -renderer_shader_feedback = - -# Enable Nsight Aftermath crash dumps -# 0 (default): Disabled, 1: Enabled -nsight_aftermath = - -# Disable shader loop safety checks, executing the shader without loop logic changes -# 0 (default): Disabled, 1: Enabled -disable_shader_loop_safety_checks = - -# Which Vulkan physical device to use (defaults to 0) -vulkan_device = - -# 0: 0.5x (360p/540p) [EXPERIMENTAL] -# 1: 0.75x (540p/810p) [EXPERIMENTAL] -# 2 (default): 1x (720p/1080p) -# 3: 2x (1440p/2160p) -# 4: 3x (2160p/3240p) -# 5: 4x (2880p/4320p) -# 6: 5x (3600p/5400p) -# 7: 6x (4320p/6480p) -resolution_setup = - -# Pixel filter to use when up- or down-sampling rendered frames. -# 0: Nearest Neighbor -# 1 (default): Bilinear -# 2: Bicubic -# 3: Gaussian -# 4: ScaleForce -# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only] -scaling_filter = - -# Anti-Aliasing (AA) -# 0 (default): None, 1: FXAA -anti_aliasing = - -# Whether to use fullscreen or borderless window mode -# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen -fullscreen_mode = - -# Aspect ratio -# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window -aspect_ratio = - -# Anisotropic filtering -# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x -max_anisotropy = - -# Whether to enable VSync or not. -# OpenGL: Values other than 0 enable VSync -# Vulkan: FIFO is selected if the requested mode is not supported by the driver. -# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. -# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. -# Mailbox can have lower latency than FIFO and does not tear but may drop frames. -# Immediate (no synchronization) just presents whatever is available and can exhibit tearing. -# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed -use_vsync = - -# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is -# not available and GLASM is selected, GLSL will be used. -# 0: GLSL, 1 (default): GLASM, 2: SPIR-V -shader_backend = - -# Whether to allow asynchronous shader building. -# 0 (default): Off, 1: On -use_asynchronous_shaders = - -# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. -# 0 (default): Off, 1: On -use_reactive_flushing = - -# NVDEC emulation. -# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding -nvdec_emulation = - -# Accelerate ASTC texture decoding. -# 0 (default): Off, 1: On -accelerate_astc = - -# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value -# 0: Off, 1: On (default) -use_speed_limit = - -# Limits the speed of the game to run no faster than this value as a percentage of target speed -# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) -speed_limit = - -# Whether to use disk based shader cache -# 0: Off, 1 (default): On -use_disk_shader_cache = - -# Which gpu accuracy level to use -# 0 (default): Normal, 1: High, 2: Extreme (Very slow) -gpu_accuracy = - -# Whether to use asynchronous GPU emulation -# 0 : Off (slow), 1 (default): On (fast) -use_asynchronous_gpu_emulation = - -# Inform the guest that GPU operations completed more quickly than they did. -# 0: Off, 1 (default): On -use_fast_gpu_time = - -# Force unmodified buffers to be flushed, which can cost performance. -# 0: Off (default), 1: On -use_pessimistic_flushes = - -# Whether to use garbage collection or not for GPU caches. -# 0 (default): Off, 1: On -use_caches_gc = - -# The clear color for the renderer. What shows up on the sides of the bottom screen. -# Must be in range of 0-255. Defaults to 0 for all. -bg_red = -bg_blue = -bg_green = - -[Audio] -# Which audio output engine to use. -# auto (default): Auto-select -# cubeb: Cubeb audio engine (if available) -# sdl2: SDL2 audio engine (if available) -# null: No audio output -output_engine = - -# Which audio device to use. -# auto (default): Auto-select -output_device = - -# Output volume. -# 100 (default): 100%, 0; mute -volume = - -[Data Storage] -# Whether to create a virtual SD card. -# 1: Yes, 0 (default): No -use_virtual_sd = - -# Whether or not to enable gamecard emulation -# 1: Yes, 0 (default): No -gamecard_inserted = - -# Whether or not the gamecard should be emulated as the current game -# If 'gamecard_inserted' is 0 this setting is irrelevant -# 1: Yes, 0 (default): No -gamecard_current_game = - -# Path to an XCI file to use as the gamecard -# If 'gamecard_inserted' is 0 this setting is irrelevant -# If 'gamecard_current_game' is 1 this setting is irrelevant -gamecard_path = - -[System] -# Whether the system is docked -# 1 (default): Yes, 0: No -use_docked_mode = - -# Sets the seed for the RNG generator built into the switch -# rng_seed will be ignored and randomly generated if rng_seed_enabled is false -rng_seed_enabled = -rng_seed = - -# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service -# This will auto-increment, with the time set being the time the game is started -# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used -custom_rtc_enabled = -custom_rtc = - -# Sets the systems language index -# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, -# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, -# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese -language_index = - -# The system region that yuzu will use during emulation -# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan -region_index = - -# The system time zone that yuzu will use during emulation -# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone -time_zone_index = - -# Sets the sound output mode. -# 0: Mono, 1 (default): Stereo, 2: Surround -sound_index = - -[Miscellaneous] -# A filter which removes logs below a certain logging level. -# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical -log_filter = *:Trace - -# Use developer keys -# 0 (default): Disabled, 1: Enabled -use_dev_keys = - -[Debugging] -# Record frame time data, can be found in the log directory. Boolean value -record_frame_times = -# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them -dump_exefs=false -# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them -dump_nso=false -# Determines whether or not yuzu will save the filesystem access log. -enable_fs_access_log=false -# Enables verbose reporting services -reporting_services = -# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode -# false: Retail/Normal Mode (default), true: Kiosk Mode -quest_flag = -# Determines whether debug asserts should be enabled, which will throw an exception on asserts. -# false: Disabled (default), true: Enabled -use_debug_asserts = -# Determines whether unimplemented HLE service calls should be automatically stubbed. -# false: Disabled (default), true: Enabled -use_auto_stub = -# Enables/Disables the macro JIT compiler -disable_macro_jit=false -# Determines whether to enable the GDB stub and wait for the debugger to attach before running. -# false: Disabled (default), true: Enabled -use_gdbstub=false -# The port to use for the GDB server, if it is enabled. -gdbstub_port=6543 - -[WebService] -# Whether or not to enable telemetry -# 0: No, 1 (default): Yes -enable_telemetry = -# URL for Web API -web_api_url = https://api.yuzu-emu.org -# Username and token for yuzu Web Service -# See https://profile.yuzu-emu.org/ for more info -yuzu_username = -yuzu_token = - -[Network] -# Name of the network interface device to use with yuzu LAN play. -# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' -# e.g. On Windows: 'Ethernet', 'Wi-Fi' -network_interface = - -[AddOns] -# Used to disable add-ons -# List of title IDs of games that will have add-ons disabled (separated by '|'): -title_ids = -# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') -# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey -)"; -} // namespace DefaultINI diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 64663b084..617288ae4 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -52,8 +52,8 @@ #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "frontend_common/config.h" #include "jni/android_common/android_common.h" -#include "jni/config.h" #include "jni/id_cache.h" #include "jni/native.h" #include "video_core/renderer_base.h" @@ -664,8 +664,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, jboolean reload) { - // Create the default config.ini. - Config{}; // Initialize the emulated system. if (!reload) { EmulationSession::GetInstance().System().Initialize(); @@ -680,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} -void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { - Config{}; -} - -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, - jstring j_game_id) { - std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); - - env->ReleaseStringUTFChars(j_game_id, game_id.data()); -} - jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { jdoubleArray j_stats = env->NewDoubleArray(4); diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index 8a704960c..8e81816e5 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp @@ -5,11 +5,14 @@ #include <jni.h> +#include "android_config.h" +#include "android_settings.h" #include "common/logging/log.h" #include "common/settings.h" +#include "frontend_common/config.h" #include "jni/android_common/android_common.h" -#include "jni/config.h" -#include "uisettings.h" + +std::unique_ptr<AndroidConfig> config; template <typename T> Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { @@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { extern "C" { +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) { + config = std::make_unique<AndroidConfig>(); +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) { + config.reset(); +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) { + config->AndroidConfig::ReloadAllValues(); +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) { + config->AndroidConfig::SaveAllValues(); +} + jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, jstring jkey, jboolean getDefault) { auto setting = getSetting<bool>(env, jkey); diff --git a/src/android/app/src/main/res/drawable/ic_audio.xml b/src/android/app/src/main/res/drawable/ic_audio.xml new file mode 100644 index 000000000..e306c3b0c --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_audio.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_code.xml b/src/android/app/src/main/res/drawable/ic_code.xml new file mode 100644 index 000000000..26f83b39b --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_code.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_graphics.xml b/src/android/app/src/main/res/drawable/ic_graphics.xml new file mode 100644 index 000000000..2fdb5a4d6 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_graphics.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_system_settings.xml b/src/android/app/src/main/res/drawable/ic_system_settings.xml new file mode 100644 index 000000000..7701a2bab --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_system_settings.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/> +</vector> diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml new file mode 100644 index 000000000..a26ffbc73 --- /dev/null +++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/coordinator_about" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/colorSurface"> + + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/appbar_about" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> + + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/toolbar_about" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + app:navigationIcon="@drawable/ic_back" + app:title="@string/about" /> + + </com.google.android.material.appbar.AppBarLayout> + + <androidx.core.widget.NestedScrollView + android:id="@+id/scroll_about" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fadeScrollbars="false" + android:scrollbars="vertical" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + <LinearLayout + android:id="@+id/content_about" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/image_logo" + android:layout_width="200dp" + android:layout_height="200dp" + android:layout_gravity="center_horizontal" + android:padding="20dp" + android:src="@drawable/ic_yuzu_title" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp"> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.TitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:text="@string/about" + android:textAlignment="viewStart" /> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.BodyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="6dp" + android:text="@string/about_app_description" + android:textAlignment="viewStart" /> + + </LinearLayout> + + <com.google.android.material.divider.MaterialDivider + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="20dp" /> + + <LinearLayout + android:id="@+id/button_contributors" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp"> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.TitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:text="@string/contributors" + android:textAlignment="viewStart" /> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.BodyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="6dp" + android:text="@string/contributors_description" + android:textAlignment="viewStart" /> + + </LinearLayout> + + <com.google.android.material.divider.MaterialDivider + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="20dp" /> + + <LinearLayout + android:id="@+id/button_licenses" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp"> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.TitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:text="@string/licenses" + android:textAlignment="viewStart" /> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.BodyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="6dp" + android:text="@string/licenses_description" + android:textAlignment="viewStart" /> + + </LinearLayout> + + <com.google.android.material.divider.MaterialDivider + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="20dp" /> + + <LinearLayout + android:id="@+id/button_build_hash" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp"> + + <com.google.android.material.textview.MaterialTextView + style="@style/TextAppearance.Material3.TitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:text="@string/build" + android:textAlignment="viewStart" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_build_hash" + style="@style/TextAppearance.Material3.BodyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="6dp" + android:textAlignment="viewStart" + tools:text="abc123" /> + + </LinearLayout> + + <com.google.android.material.divider.MaterialDivider + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="20dp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="40dp" + android:layout_marginTop="12dp" + android:layout_marginBottom="16dp" + android:gravity="center_horizontal" + android:orientation="horizontal"> + + <Button + android:id="@+id/button_discord" + style="?attr/materialIconButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + app:icon="@drawable/ic_discord" + app:iconGravity="textEnd" + app:iconSize="24dp" + app:iconTint="?attr/colorOnSurface" /> + + <Button + android:id="@+id/button_website" + style="?attr/materialIconButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + app:icon="@drawable/ic_website" + app:iconGravity="textEnd" + app:iconSize="24dp" + app:iconTint="?attr/colorOnSurface" /> + + <Button + android:id="@+id/button_github" + style="?attr/materialIconButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + app:icon="@drawable/ic_github" + app:iconGravity="textEnd" + app:iconSize="24dp" + app:iconTint="?attr/colorOnSurface" /> + + </LinearLayout> + + </LinearLayout> + + </LinearLayout> + + </androidx.core.widget.NestedScrollView> + +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml index 6e8a232f9..cb667c928 100644 --- a/src/android/app/src/main/res/layout/card_home_option.xml +++ b/src/android/app/src/main/res/layout/card_home_option.xml @@ -6,8 +6,8 @@ android:id="@+id/option_card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginVertical="12dp" - android:layout_marginHorizontal="16dp" + android:layout_marginBottom="24dp" + android:layout_marginHorizontal="12dp" android:background="?attr/selectableItemBackground" android:backgroundTint="?attr/colorSurfaceVariant" android:clickable="true" diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml index 3e1d98451..a24f5230e 100644 --- a/src/android/app/src/main/res/layout/fragment_about.xml +++ b/src/android/app/src/main/res/layout/fragment_about.xml @@ -38,17 +38,17 @@ <ImageView android:id="@+id/image_logo" - android:layout_width="250dp" - android:layout_height="250dp" - android:layout_marginTop="20dp" + android:layout_width="150dp" + android:layout_height="150dp" + android:layout_marginTop="24dp" + android:layout_marginBottom="28dp" android:layout_gravity="center_horizontal" android:src="@drawable/ic_yuzu_title" /> <com.google.android.material.divider.MaterialDivider android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="20dp" - android:layout_marginTop="28dp" /> + android:layout_marginHorizontal="20dp" /> <LinearLayout android:layout_width="match_parent" 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 cd6360b45..5252adf54 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -139,7 +139,7 @@ <com.google.android.material.textview.MaterialTextView android:id="@+id/show_fps_text" - style="@style/TextAppearance.Material3.BodyMedium" + style="@style/TextAppearance.Material3.BodySmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left" @@ -147,7 +147,8 @@ android:focusable="false" android:paddingHorizontal="20dp" android:textColor="@android:color/white" - android:textSize="12sp" + android:shadowColor="@android:color/black" + android:shadowRadius="3" tools:ignore="RtlHardcoded" /> </FrameLayout> diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml index 1cb421dcb..d84093ba3 100644 --- a/src/android/app/src/main/res/layout/fragment_home_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml @@ -14,13 +14,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:background="?attr/colorSurface"> + android:background="?attr/colorSurface" + android:paddingHorizontal="8dp"> <ImageView android:id="@+id/logo_image" - android:layout_width="128dp" - android:layout_height="128dp" - android:layout_margin="64dp" + android:layout_width="96dp" + android:layout_height="96dp" + android:layout_marginVertical="32dp" android:layout_gravity="center_horizontal" android:src="@drawable/ic_yuzu_full" /> diff --git a/src/android/app/src/main/res/layout/fragment_search.xml b/src/android/app/src/main/res/layout/fragment_search.xml index b8d54d947..efdfd7047 100644 --- a/src/android/app/src/main/res/layout/fragment_search.xml +++ b/src/android/app/src/main/res/layout/fragment_search.xml @@ -127,6 +127,7 @@ android:layout_height="wrap_content" android:clipToPadding="false" android:paddingVertical="4dp" + app:checkedChip="@id/chip_recently_played" app:chipSpacingHorizontal="12dp" app:singleLine="true" app:singleSelection="true"> diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml index f1037a740..544280e75 100644 --- a/src/android/app/src/main/res/layout/list_item_setting.xml +++ b/src/android/app/src/main/res/layout/list_item_setting.xml @@ -10,41 +10,59 @@ android:focusable="true" android:gravity="center_vertical" android:minHeight="72dp" - android:padding="@dimen/spacing_large"> + android:padding="16dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> + android:orientation="horizontal"> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/text_setting_name" - style="@style/TextAppearance.Material3.HeadlineMedium" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAlignment="viewStart" - android:textSize="16sp" - app:lineHeight="22dp" - tools:text="Setting Name" /> - - <com.google.android.material.textview.MaterialTextView - android:id="@+id/text_setting_description" - style="@style/TextAppearance.Material3.BodySmall" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_small" - android:textAlignment="viewStart" - tools:text="@string/app_disclaimer" /> + <ImageView + android:id="@+id/icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="8dp" + android:layout_marginEnd="24dp" + android:layout_gravity="center_vertical" + android:visibility="gone" + app:tint="?attr/colorOnSurface" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/text_setting_value" - style="@style/TextAppearance.Material3.LabelMedium" + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_small" - android:textAlignment="viewStart" - android:textStyle="bold" - tools:text="1x" /> + android:orientation="vertical"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_name" + style="@style/TextAppearance.Material3.HeadlineMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" + android:textSize="17sp" + app:lineHeight="22dp" + tools:text="Setting Name" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_description" + style="@style/TextAppearance.Material3.BodySmall" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:textAlignment="viewStart" + tools:text="@string/app_disclaimer" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_value" + style="@style/TextAppearance.Material3.LabelMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:textAlignment="viewStart" + android:textStyle="bold" + android:textSize="13sp" + tools:text="1x" /> + + </LinearLayout> </LinearLayout> diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml index a5767adee..a8f5aff78 100644 --- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml +++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml @@ -8,9 +8,7 @@ android:clickable="true" android:focusable="true" android:minHeight="72dp" - android:paddingVertical="@dimen/spacing_large" - android:paddingStart="@dimen/spacing_large" - android:paddingEnd="24dp"> + android:padding="16dp"> <com.google.android.material.materialswitch.MaterialSwitch android:id="@+id/switch_widget" @@ -24,7 +22,7 @@ android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerVertical="true" - android:layout_marginEnd="@dimen/spacing_large" + android:layout_marginEnd="24dp" android:layout_toStartOf="@+id/switch_widget" android:gravity="center_vertical" android:orientation="vertical"> @@ -35,7 +33,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAlignment="viewStart" - android:textSize="16sp" + android:textSize="17sp" app:lineHeight="28dp" tools:text="@string/frame_limit_enable" /> diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml index cf85bc0da..21276b19e 100644 --- a/src/android/app/src/main/res/layout/list_item_settings_header.xml +++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml @@ -7,7 +7,8 @@ android:layout_height="wrap_content" android:layout_gravity="start|center_vertical" android:paddingHorizontal="@dimen/spacing_large" - android:paddingVertical="16dp" + android:paddingTop="16dp" + android:paddingBottom="8dp" android:textAlignment="viewStart" android:textColor="?attr/colorPrimary" android:textStyle="bold" diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index dc10159c9..51bcc49a3 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -2,7 +2,6 @@ <resources> <string-array name="regionNames"> - <item>@string/auto</item> <item>@string/region_australia</item> <item>@string/region_china</item> <item>@string/region_europe</item> @@ -13,7 +12,6 @@ </string-array> <integer-array name="regionValues"> - <item>-1</item> <item>3</item> <item>4</item> <item>2</item> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index c551a6106..471af8795 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -91,6 +91,7 @@ <string name="manage_save_data">Manage save data</string> <string name="manage_save_data_description">Save data found. Please select an option below.</string> <string name="import_export_saves_description">Import or export save files</string> + <string name="save_files_exporting">Exporting save files…</string> <string name="save_file_imported_success">Imported successfully</string> <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> @@ -240,6 +241,7 @@ <string name="shutting_down">Shutting down…</string> <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> <string name="reset_to_default">Reset to default</string> + <string name="reset_to_default_description">Resets all advanced settings</string> <string name="reset_all_settings">Reset all settings?</string> <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> <string name="settings_reset">Settings reset</string> @@ -255,6 +257,7 @@ <string name="cancelling">Cancelling</string> <string name="install">Install</string> <string name="delete">Delete</string> + <string name="export_success">Exported successfully</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Select GPU driver</string> @@ -271,10 +274,14 @@ <string name="preferences_settings">Settings</string> <string name="preferences_general">General</string> <string name="preferences_system">System</string> + <string name="preferences_system_description">Docked mode, region, language</string> <string name="preferences_graphics">Graphics</string> + <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string> <string name="preferences_audio">Audio</string> + <string name="preferences_audio_description">Output engine, volume</string> <string name="preferences_theme">Theme and color</string> <string name="preferences_debug">Debug</string> + <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Your ROM is encrypted</string> diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp index 7f1ed0450..05cf3975d 100644 --- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp @@ -12,7 +12,7 @@ bool IsValidChannelCount(u32 channel_count) { } bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { - return total_stream_count > 0 && stereo_stream_count > 0 && + return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); } } // namespace diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp index c6fd45f47..b7fed5304 100644 --- a/src/audio_core/opus/decoder.cpp +++ b/src/audio_core/opus/decoder.cpp @@ -148,7 +148,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; OpusPacketHeader header{ReverseHeader(*header_p)}; - LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", + LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", header.size, input_data.size_bytes(), in_data.size_bytes()); R_UNLESS(in_data.size_bytes() >= header.size && diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index bbb598bc5..51a23fe15 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -146,7 +146,7 @@ public: return; } - paused = true; + SignalPause(); if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); } diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 7b89151de..96e0efce2 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -111,7 +111,7 @@ public: if (device == 0 || paused) { return; } - paused = true; + SignalPause(); SDL_PauseAudioDevice(device, 1); } diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index d66d04fae..2a09db599 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() { void SinkStream::WaitFreeSpace(std::stop_token stop_token) { std::unique_lock lk{release_mutex}; release_cv.wait_for(lk, std::chrono::milliseconds(5), - [this]() { return queued_buffers < max_queue_size; }); + [this]() { return paused || queued_buffers < max_queue_size; }); if (queued_buffers > max_queue_size + 3) { Common::CondvarWait(release_cv, lk, stop_token, - [this] { return queued_buffers < max_queue_size; }); + [this] { return paused || queued_buffers < max_queue_size; }); } } +void SinkStream::SignalPause() { + { + std::scoped_lock lk{release_mutex}; + paused = true; + } + release_cv.notify_one(); +} + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 6a4996ca3..f2ccd19b8 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h @@ -214,6 +214,12 @@ public: void WaitFreeSpace(std::stop_token stop_token); protected: + /** + * Unblocks the ADSP if the stream is paused. + */ + void SignalPause(); + +protected: /// Core system Core::System& system; /// Type of this stream diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 98b43e49c..a10131eb2 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) { case Category::Ui: case Category::UiGeneral: return "UI"; + case Category::UiAudio: + return "UiAudio"; case Category::UiLayout: - return "UiLayout"; + return "UILayout"; case Category::UiGameList: - return "UiGameList"; + return "UIGameList"; case Category::Screenshots: return "Screenshots"; case Category::Shortcuts: diff --git a/src/common/settings.h b/src/common/settings.h index 0455241b3..b929fd957 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -153,7 +153,7 @@ struct Values { true, true}; Setting<bool, false> audio_muted{ - linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; + linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true}; Setting<bool, false> dump_audio_commands{ linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; @@ -232,7 +232,11 @@ struct Values { SwitchableSetting<bool> use_asynchronous_gpu_emulation{ linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, +#ifdef ANDROID + AstcDecodeMode::Cpu, +#else AstcDecodeMode::Gpu, +#endif AstcDecodeMode::Cpu, AstcDecodeMode::CpuAsynchronous, "accelerate_astc", @@ -304,7 +308,11 @@ struct Values { linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, +#ifdef ANDROID + GpuAccuracy::Normal, +#else GpuAccuracy::High, +#endif GpuAccuracy::Normal, GpuAccuracy::Extreme, "gpu_accuracy", @@ -313,20 +321,38 @@ struct Values { true, true}; GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; - SwitchableSetting<AnisotropyMode, true> max_anisotropy{ - linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, - "max_anisotropy", Category::RendererAdvanced}; + SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage, +#ifdef ANDROID + AnisotropyMode::Default, +#else + AnisotropyMode::Automatic, +#endif + AnisotropyMode::Automatic, + AnisotropyMode::X16, + "max_anisotropy", + Category::RendererAdvanced}; SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, "astc_recompression", Category::RendererAdvanced}; - SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", - Category::RendererAdvanced}; + SwitchableSetting<bool> async_presentation{linkage, +#ifdef ANDROID + true, +#else + false, +#endif + "async_presentation", Category::RendererAdvanced}; SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", Category::RendererAdvanced}; - SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", + SwitchableSetting<bool> use_reactive_flushing{linkage, +#ifdef ANDROID + false, +#else + true, +#endif + "use_reactive_flushing", Category::RendererAdvanced}; SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", Category::RendererAdvanced}; @@ -392,7 +418,11 @@ struct Values { Setting<s32> current_user{linkage, 0, "current_user", Category::System}; SwitchableSetting<ConsoleMode> use_docked_mode{linkage, +#ifdef ANDROID + ConsoleMode::Handheld, +#else ConsoleMode::Docked, +#endif "use_docked_mode", Category::System, Specialization::Radio, diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 1800ab10d..7943223eb 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -32,6 +32,7 @@ enum class Category : u32 { AddOns, Controls, Ui, + UiAudio, UiGeneral, UiLayout, UiGameList, diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp index 0a6eea3cf..a6007e7b2 100644 --- a/src/common/settings_input.cpp +++ b/src/common/settings_input.cpp @@ -6,10 +6,11 @@ namespace Settings { namespace NativeButton { const std::array<const char*, NumButtons> mapping = {{ - "button_a", "button_b", "button_x", "button_y", "button_lstick", - "button_rstick", "button_l", "button_r", "button_zl", "button_zr", - "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", - "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", + "button_a", "button_b", "button_x", "button_y", "button_lstick", + "button_rstick", "button_l", "button_r", "button_zl", "button_zr", + "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", + "button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot", + "button_slright", "button_srright", }}; } diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 46f38c703..53a95ef8f 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h @@ -29,12 +29,15 @@ enum Values : int { DRight, DDown, - SL, - SR, + SLLeft, + SRLeft, Home, Screenshot, + SLRight, + SRRight, + NumButtons, }; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8be3bdd08..66c10fc3f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -521,17 +521,28 @@ add_library(core STATIC hle/service/grc/grc.h hle/service/hid/hid.cpp hle/service/hid/hid.h + hle/service/hid/hid_debug_server.cpp + hle/service/hid/hid_debug_server.h + hle/service/hid/hid_firmware_settings.cpp + hle/service/hid/hid_firmware_settings.h + hle/service/hid/hid_server.cpp + hle/service/hid/hid_server.h + hle/service/hid/hid_system_server.cpp + hle/service/hid/hid_system_server.h + hle/service/hid/hid_util.h hle/service/hid/hidbus.cpp hle/service/hid/hidbus.h hle/service/hid/irs.cpp hle/service/hid/irs.h hle/service/hid/irs_ring_lifo.h + hle/service/hid/resource_manager.cpp + hle/service/hid/resource_manager.h hle/service/hid/ring_lifo.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h hle/service/hid/errors.h - hle/service/hid/controllers/console_sixaxis.cpp - hle/service/hid/controllers/console_sixaxis.h + hle/service/hid/controllers/console_six_axis.cpp + hle/service/hid/controllers/console_six_axis.h hle/service/hid/controllers/controller_base.cpp hle/service/hid/controllers/controller_base.h hle/service/hid/controllers/debug_pad.cpp @@ -546,6 +557,10 @@ add_library(core STATIC hle/service/hid/controllers/npad.h hle/service/hid/controllers/palma.cpp hle/service/hid/controllers/palma.h + hle/service/hid/controllers/seven_six_axis.cpp + hle/service/hid/controllers/seven_six_axis.h + hle/service/hid/controllers/six_axis.cpp + hle/service/hid/controllers/six_axis.h hle/service/hid/controllers/stubbed.cpp hle/service/hid/controllers/stubbed.h hle/service/hid/controllers/touchscreen.cpp @@ -715,6 +730,7 @@ add_library(core STATIC hle/service/nvnflinger/producer_listener.h hle/service/nvnflinger/status.h hle/service/nvnflinger/ui/fence.h + hle/service/nvnflinger/ui/graphic_buffer.cpp hle/service/nvnflinger/ui/graphic_buffer.h hle/service/nvnflinger/window.h hle/service/olsc/olsc.cpp diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 5e27dde58..558fba5bd 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -153,6 +153,14 @@ void ARM_Interface::Run() { Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; HaltReason hr{}; + // If the thread is scheduled for termination, exit the thread. + if (current_thread->HasDpc()) { + if (current_thread->IsTerminationRequested()) { + current_thread->Exit(); + UNREACHABLE(); + } + } + // Notify the debugger and go to sleep if a step was performed // and this thread has been scheduled again. if (current_thread->GetStepState() == StepState::StepPerformed) { @@ -174,14 +182,6 @@ void ARM_Interface::Run() { } system.ExitCPUProfile(); - // If the thread is scheduled for termination, exit the thread. - if (current_thread->HasDpc()) { - if (current_thread->IsTerminationRequested()) { - current_thread->Exit(); - UNREACHABLE(); - } - } - // Notify the debugger and go to sleep if a breakpoint was hit, // or if the thread is unable to continue for any reason. if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e671b270f..d6b5abc68 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { } void CoreTiming::ClearPendingEvents() { + std::scoped_lock lock{basic_lock}; event_queue.clear(); } @@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const { } bool CoreTiming::HasPendingEvents() const { + std::scoped_lock lock{basic_lock}; return !(wait_set && event_queue.empty()); } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 26a8b93a7..21548f0a9 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -161,7 +161,7 @@ private: std::shared_ptr<EventType> ev_lost; Common::Event event{}; Common::Event pause_event{}; - std::mutex basic_lock; + mutable std::mutex basic_lock; std::mutex advance_lock; std::unique_ptr<std::jthread> timer_thread; std::atomic<bool> paused{}; diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 79114bb6d..fae15a556 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; using ConsoleMotionValues = ConsoleMotionInfo; using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; -struct TouchFinger { - u64 last_touch{}; - Common::Point<float> position{}; - u32 id{}; - TouchAttribute attribute{}; - bool pressed{}; -}; - // Contains all motion related data that is used on the services struct ConsoleMotion { Common::Vec3f accel{}; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index b08a71446..a6e681e15 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -8,6 +8,7 @@ #include "common/thread.h" #include "core/hid/emulated_controller.h" #include "core/hid/input_converter.h" +#include "core/hle/service/hid/hid_util.h" namespace Core::HID { constexpr s32 HID_JOYSTICK_MAX = 0x7fff; @@ -82,7 +83,7 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde } void EmulatedController::ReloadFromSettings() { - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; for (std::size_t index = 0; index < player.buttons.size(); ++index) { @@ -118,7 +119,7 @@ void EmulatedController::ReloadFromSettings() { } void EmulatedController::ReloadColorsFromSettings() { - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; // Avoid updating colors if overridden by physical controller @@ -215,7 +216,7 @@ void EmulatedController::LoadDevices() { } void EmulatedController::LoadTASParams() { - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); Common::ParamPackage common_params{}; common_params.Set("engine", "tas"); common_params.Set("port", static_cast<int>(player_index)); @@ -243,10 +244,12 @@ void EmulatedController::LoadTASParams() { tas_button_params[Settings::NativeButton::DUp].Set("button", 13); tas_button_params[Settings::NativeButton::DRight].Set("button", 14); tas_button_params[Settings::NativeButton::DDown].Set("button", 15); - tas_button_params[Settings::NativeButton::SL].Set("button", 16); - tas_button_params[Settings::NativeButton::SR].Set("button", 17); + tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16); + tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17); tas_button_params[Settings::NativeButton::Home].Set("button", 18); tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + tas_button_params[Settings::NativeButton::SLRight].Set("button", 20); + tas_button_params[Settings::NativeButton::SRRight].Set("button", 21); tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); @@ -262,7 +265,7 @@ void EmulatedController::LoadTASParams() { } void EmulatedController::LoadVirtualGamepadParams() { - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); Common::ParamPackage common_params{}; common_params.Set("engine", "virtual_gamepad"); common_params.Set("port", static_cast<int>(player_index)); @@ -296,10 +299,12 @@ void EmulatedController::LoadVirtualGamepadParams() { virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); - virtual_button_params[Settings::NativeButton::SL].Set("button", 16); - virtual_button_params[Settings::NativeButton::SR].Set("button", 17); + virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16); + virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17); virtual_button_params[Settings::NativeButton::Home].Set("button", 18); virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20); + virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21); virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); @@ -504,9 +509,11 @@ void EmulatedController::ReloadInput() { }); } turbo_button_state = 0; + is_initalized = true; } void EmulatedController::UnloadInput() { + is_initalized = false; for (auto& button : button_devices) { button.reset(); } @@ -611,7 +618,7 @@ bool EmulatedController::IsConfiguring() const { } void EmulatedController::SaveCurrentConfig() { - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); auto& player = Settings::values.players.GetValue()[player_index]; player.connected = is_connected; player.controller_type = MapNPadToSettingsType(npad_type); @@ -867,12 +874,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback controller.npad_button_state.down.Assign(current_status.value); controller.debug_pad_button_state.d_down.Assign(current_status.value); break; - case Settings::NativeButton::SL: + case Settings::NativeButton::SLLeft: controller.npad_button_state.left_sl.Assign(current_status.value); + break; + case Settings::NativeButton::SLRight: controller.npad_button_state.right_sl.Assign(current_status.value); break; - case Settings::NativeButton::SR: + case Settings::NativeButton::SRLeft: controller.npad_button_state.left_sr.Assign(current_status.value); + break; + case Settings::NativeButton::SRRight: controller.npad_button_state.right_sr.Assign(current_status.value); break; case Settings::NativeButton::Home: @@ -1198,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { } bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { + if (!is_initalized) { + return false; + } if (device_index >= output_devices.size()) { return false; } if (!output_devices[device_index]) { return false; } - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; @@ -1230,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v } bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { - const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; + if (!is_initalized) { + return false; + } + if (!player.vibration_enabled) { return false; } @@ -1252,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); + if (!is_initalized) { + return Common::Input::DriverResult::InvalidHandle; + } + auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[3]; @@ -1297,6 +1319,10 @@ bool EmulatedController::SetCameraFormat( Core::IrSensor::ImageTransferProcessorFormat camera_format) { LOG_INFO(Service_HID, "Set camera format {}", camera_format); + if (!is_initalized) { + return false; + } + auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& camera_output_device = output_devices[2]; @@ -1320,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) { } bool EmulatedController::HasNfc() const { + + if (!is_initalized) { + return false; + } + const auto& nfc_output_device = output_devices[3]; switch (npad_type) { @@ -1357,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() { } bool EmulatedController::StartNfcPolling() { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1368,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() { } bool EmulatedController::StopNfcPolling() { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1379,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() { } bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1391,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, Common::Input::MifareRequest& out_data) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1403,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ } bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1414,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req } bool EmulatedController::WriteNfc(const std::vector<u8>& data) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1425,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) { } void EmulatedController::SetLedPattern() { + if (!is_initalized) { + return; + } + for (auto& device : output_devices) { if (!device) { continue; @@ -1640,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { } if (is_connected) { LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", - NpadIdTypeToIndex(npad_id_type)); + Service::HID::NpadIdTypeToIndex(npad_id_type)); } npad_type = npad_type_; } @@ -1890,12 +1949,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const { case Settings::NativeButton::DDown: button_mask.down.Assign(1); break; - case Settings::NativeButton::SL: + case Settings::NativeButton::SLLeft: button_mask.left_sl.Assign(1); + break; + case Settings::NativeButton::SLRight: button_mask.right_sl.Assign(1); break; - case Settings::NativeButton::SR: + case Settings::NativeButton::SRLeft: button_mask.left_sr.Assign(1); + break; + case Settings::NativeButton::SRRight: button_mask.right_sr.Assign(1); break; default: diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index ea18c2343..d6e20ab66 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -559,6 +559,7 @@ private: NpadStyleTag supported_style_tag{NpadStyleSet::All}; bool is_connected{false}; bool is_configuring{false}; + bool is_initalized{false}; bool system_buttons_enabled{true}; f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; u32 turbo_button_state{0}; diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp index cf53c04d9..2cf25a870 100644 --- a/src/core/hid/hid_core.cpp +++ b/src/core/hid/hid_core.cpp @@ -6,6 +6,7 @@ #include "core/hid/emulated_controller.h" #include "core/hid/emulated_devices.h" #include "core/hid/hid_core.h" +#include "core/hle/service/hid/hid_util.h" namespace Core::HID { @@ -98,11 +99,11 @@ const EmulatedDevices* HIDCore::GetEmulatedDevices() const { } EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { - return GetEmulatedController(IndexToNpadIdType(index)); + return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); } const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { - return GetEmulatedController(IndexToNpadIdType(index)); + return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); } void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 7ba75a50c..4bf285f36 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "common/point.h" #include "common/uuid.h" +#include "common/vector_math.h" namespace Core::HID { @@ -218,6 +219,13 @@ enum class NpadIdType : u32 { Invalid = 0xFFFFFFFF, }; +enum class NpadInterfaceType : u8 { + Bluetooth = 1, + Rail = 2, + Usb = 3, + Embedded = 4, +}; + // This is nn::hid::NpadStyleIndex enum class NpadStyleIndex : u8 { None = 0, @@ -356,6 +364,14 @@ struct TouchState { }; static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); +struct TouchFinger { + u64 last_touch{}; + Common::Point<float> position{}; + u32 id{}; + TouchAttribute attribute{}; + bool pressed{}; +}; + // This is nn::hid::TouchScreenConfigurationForNx struct TouchScreenConfigurationForNx { TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; @@ -583,6 +599,29 @@ struct SixAxisSensorIcInformation { static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, "SixAxisSensorIcInformation is an invalid size"); +// This is nn::hid::SixAxisSensorAttribute +struct SixAxisSensorAttribute { + union { + u32 raw{}; + BitField<0, 1, u32> is_connected; + BitField<1, 1, u32> is_interpolated; + }; +}; +static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); + +// This is nn::hid::SixAxisSensorState +struct SixAxisSensorState { + s64 delta_time{}; + s64 sampling_number{}; + Common::Vec3f accel{}; + Common::Vec3f gyro{}; + Common::Vec3f rotation{}; + std::array<Common::Vec3f, 3> orientation{}; + SixAxisSensorAttribute attribute{}; + INSERT_PADDING_BYTES(4); // Reserved +}; +static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); + // This is nn::hid::VibrationDeviceHandle struct VibrationDeviceHandle { NpadStyleIndex npad_type{NpadStyleIndex::None}; @@ -693,60 +732,4 @@ struct UniquePadId { }; static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); -/// Converts a NpadIdType to an array index. -constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { - switch (npad_id_type) { - case NpadIdType::Player1: - return 0; - case NpadIdType::Player2: - return 1; - case NpadIdType::Player3: - return 2; - case NpadIdType::Player4: - return 3; - case NpadIdType::Player5: - return 4; - case NpadIdType::Player6: - return 5; - case NpadIdType::Player7: - return 6; - case NpadIdType::Player8: - return 7; - case NpadIdType::Handheld: - return 8; - case NpadIdType::Other: - return 9; - default: - return 0; - } -} - -/// Converts an array index to a NpadIdType -constexpr NpadIdType IndexToNpadIdType(size_t index) { - switch (index) { - case 0: - return NpadIdType::Player1; - case 1: - return NpadIdType::Player2; - case 2: - return NpadIdType::Player3; - case 3: - return NpadIdType::Player4; - case 4: - return NpadIdType::Player5; - case 5: - return NpadIdType::Player6; - case 6: - return NpadIdType::Player7; - case 7: - return NpadIdType::Player8; - case 8: - return NpadIdType::Handheld; - case 9: - return NpadIdType::Other; - default: - return NpadIdType::Invalid; - } -} - } // namespace Core::HID diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp index 76d6b8ab0..a6bdd28f2 100644 --- a/src/core/hid/input_interpreter.cpp +++ b/src/core/hid/input_interpreter.cpp @@ -5,21 +5,22 @@ #include "core/hid/hid_types.h" #include "core/hid/input_interpreter.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/sm/sm.h" InputInterpreter::InputInterpreter(Core::System& system) : npad{system.ServiceManager() - .GetService<Service::HID::Hid>("hid") - ->GetAppletResource() - ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { + .GetService<Service::HID::IHidServer>("hid") + ->GetResourceManager() + ->GetNpad()} { ResetButtonStates(); } InputInterpreter::~InputInterpreter() = default; void InputInterpreter::PollInput() { - const auto button_state = npad.GetAndResetPressState(); + const auto button_state = npad->GetAndResetPressState(); previous_index = current_index; current_index = (current_index + 1) % button_states.size(); diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h index 8c521b381..3569aac93 100644 --- a/src/core/hid/input_interpreter.h +++ b/src/core/hid/input_interpreter.h @@ -16,7 +16,7 @@ enum class NpadButton : u64; } namespace Service::HID { -class Controller_NPad; +class NPad; } /** @@ -101,7 +101,7 @@ public: } private: - Service::HID::Controller_NPad& npad; + std::shared_ptr<Service::HID::NPad> npad; /// Stores 9 consecutive button states polled from HID. std::array<Core::HID::NpadButton, 9> button_states{}; diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 6a57ad55c..47dc8fd35 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -1617,9 +1617,9 @@ void KPageTableBase::RemapPageGroup(PageLinkedList* page_list, KProcessAddress a const KMemoryInfo info = it->GetMemoryInfo(); // Determine the range to map. - KProcessAddress map_address = std::max(info.GetAddress(), GetInteger(start_address)); + KProcessAddress map_address = std::max<u64>(info.GetAddress(), GetInteger(start_address)); const KProcessAddress map_end_address = - std::min(info.GetEndAddress(), GetInteger(end_address)); + std::min<u64>(info.GetEndAddress(), GetInteger(end_address)); ASSERT(map_end_address != map_address); // Determine if we should disable head merge. diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cc643ea09..a266d7c21 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -13,6 +13,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/savedata_factory.h" +#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" @@ -21,6 +22,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applet_cabinet.h" +#include "core/hle/service/am/applets/applet_controller.h" #include "core/hle/service/am/applets/applet_mii_edit_types.h" #include "core/hle/service/am/applets/applet_profile_select.h" #include "core/hle/service/am/applets/applet_software_keyboard_types.h" @@ -35,6 +37,7 @@ #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" @@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_) static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, - {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, + {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"}, {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, {11, nullptr, "ReleaseForegroundRights"}, {12, nullptr, "RejectToChangeIntoBackground"}, @@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) { rb.Push<u64>(process_id); } +void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) { + const u64 process_id = 0; + + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(process_id); +} + void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; @@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {6, nullptr, "GetPopInteractiveInDataEvent"}, {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, - {12, nullptr, "GetMainAppletIdentityInfo"}, + {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"}, {13, nullptr, "CanUseApplicationCore"}, {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, {15, nullptr, "GetMainAppletApplicationControlProperty"}, @@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) case Applets::AppletId::SoftwareKeyboard: PushInShowSoftwareKeyboard(); break; + case Applets::AppletId::Controller: + PushInShowController(); + break; default: break; } @@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { rb.PushRaw(applet_info); } -void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { +void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) { struct AppletIdentityInfo { Applets::AppletId applet_id; INSERT_PADDING_BYTES(0x4); u64 application_id; }; + static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); + + LOG_WARNING(Service_AM, "(STUBBED) called"); + + const AppletIdentityInfo applet_info{ + .applet_id = Applets::AppletId::QLaunch, + .application_id = 0x0100000000001000ull, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(applet_info); +} +void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { + struct AppletIdentityInfo { + Applets::AppletId applet_id; + INSERT_PADDING_BYTES(0x4); + u64 application_id; + }; + static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); LOG_WARNING(Service_AM, "(STUBBED) called"); const AppletIdentityInfo applet_info{ @@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() { queue_data.emplace_back(std::move(settings_data)); } +void ILibraryAppletSelfAccessor::PushInShowController() { + const Applets::CommonArguments common_args = { + .arguments_version = Applets::CommonArgumentVersion::Version3, + .size = Applets::CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8), + .theme_color = Applets::ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + Applets::ControllerSupportArgNew user_args = { + .header = {.player_count_min = 1, + .player_count_max = 4, + .enable_take_over_connection = true, + .enable_left_justify = false, + .enable_permit_joy_dual = true, + .enable_single_mode = false, + .enable_identification_color = false}, + .identification_colors = {}, + .enable_explain_text = false, + .explain_text = {}, + }; + + Applets::ControllerSupportArgPrivate private_args = { + .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate), + .arg_size = sizeof(Applets::ControllerSupportArgNew), + .is_home_menu = true, + .flag_1 = true, + .mode = Applets::ControllerSupportMode::ShowControllerSupport, + .caller = Applets::ControllerSupportCaller:: + Application, // switchbrew: Always zero except with + // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem, + // which sets this to the input param + .style_set = Core::HID::NpadStyleSet::None, + .joy_hold_type = 0, + }; + std::vector<u8> common_args_data(sizeof(common_args)); + std::vector<u8> private_args_data(sizeof(private_args)); + std::vector<u8> user_args_data(sizeof(user_args)); + + std::memcpy(common_args_data.data(), &common_args, sizeof(common_args)); + std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); + std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); + + queue_data.emplace_back(std::move(common_args_data)); + queue_data.emplace_back(std::move(private_args_data)); + queue_data.emplace_back(std::move(user_args_data)); +} + void ILibraryAppletSelfAccessor::PushInShowCabinetData() { const Applets::CommonArguments arguments{ .arguments_version = Applets::CommonArgumentVersion::Version3, diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 8f8cb8a9e..905a71b9f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -87,6 +87,7 @@ public: private: void GetAppletResourceUserId(HLERequestContext& ctx); + void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx); void AcquireForegroundRights(HLERequestContext& ctx); }; @@ -345,6 +346,7 @@ private: void PopInData(HLERequestContext& ctx); void PushOutData(HLERequestContext& ctx); void GetLibraryAppletInfo(HLERequestContext& ctx); + void GetMainAppletIdentityInfo(HLERequestContext& ctx); void ExitProcessAndReturn(HLERequestContext& ctx); void GetCallerAppletIdentityInfo(HLERequestContext& ctx); void GetDesirableKeyboardLayout(HLERequestContext& ctx); @@ -355,6 +357,7 @@ private: void PushInShowCabinetData(); void PushInShowMiiEditData(); void PushInShowSoftwareKeyboard(); + void PushInShowController(); std::deque<std::vector<u8>> queue_data; }; diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index b379dadeb..9d1960cb7 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) Service::NFP::RegisterInfoPrivate register_info{}; std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); - + register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); + register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'}); nfp_device->SetRegisterInfoPrivate(register_info); break; } diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index f6c64f633..9f839f3d7 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 { struct ControllerSupportArgPrivate { u32 arg_private_size{}; u32 arg_size{}; - bool flag_0{}; + bool is_home_menu{}; bool flag_1{}; ControllerSupportMode mode{}; ControllerSupportCaller caller{}; diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 8069f75b7..c65e32489 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -127,7 +127,7 @@ public: private: void GetCore(HLERequestContext& ctx) { - LOG_DEBUG(Service_BTM, "called"); + LOG_WARNING(Service_BTM, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); @@ -263,13 +263,13 @@ public: explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "StartGamepadPairing"}, - {1, nullptr, "CancelGamepadPairing"}, + {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"}, + {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"}, {2, nullptr, "ClearGamepadPairingDatabase"}, {3, nullptr, "GetPairedGamepadCount"}, {4, nullptr, "EnableRadio"}, {5, nullptr, "DisableRadio"}, - {6, nullptr, "GetRadioOnOff"}, + {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"}, {7, nullptr, "AcquireRadioEvent"}, {8, nullptr, "AcquireGamepadPairingEvent"}, {9, nullptr, "IsGamepadPairingStarted"}, @@ -280,18 +280,58 @@ public: {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, {15, nullptr, "ConnectAudioDevice"}, {16, nullptr, "IsConnectingAudioDevice"}, - {17, nullptr, "GetConnectedAudioDevices"}, + {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, {18, nullptr, "DisconnectAudioDevice"}, {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, {20, nullptr, "GetPairedAudioDevices"}, {21, nullptr, "RemoveAudioDevicePairing"}, - {22, nullptr, "RequestAudioDeviceConnectionRejection"}, - {23, nullptr, "CancelAudioDeviceConnectionRejection"} + {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, + {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} }; // clang-format on RegisterHandlers(functions); } + +private: + void IsRadioEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(true); + } + + void StartGamepadPairing(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CancelGamepadPairing(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetConnectedAudioDevices(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(0); + } + + void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; class BTM_SYS final : public ServiceFramework<BTM_SYS> { @@ -308,7 +348,7 @@ public: private: void GetCore(HLERequestContext& ctx) { - LOG_DEBUG(Service_BTM, "called"); + LOG_WARNING(Service_BTM, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 9d05f9801..0507b14e7 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -32,7 +32,7 @@ public: {10200, nullptr, "SendFriendRequestForApplication"}, {10211, nullptr, "AddFacedFriendRequestForApplication"}, {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, - {10420, nullptr, "IsBlockedUserListCacheAvailable"}, + {10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"}, {10421, nullptr, "EnsureBlockedUserListAvailable"}, {10500, nullptr, "GetProfileList"}, {10600, nullptr, "DeclareOpenOnlinePlaySession"}, @@ -206,6 +206,17 @@ private: rb.Push(true); } + void CheckBlockedUserListAvailability(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw<Common::UUID>()}; + + LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(true); + } + KernelHelpers::ServiceContext service_context; Kernel::KEvent* completion_event; diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp new file mode 100644 index 000000000..b2bf1d78d --- /dev/null +++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hid/emulated_console.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/controllers/console_six_axis.h" +#include "core/memory.h" + +namespace Service::HID { +constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; + +ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + console = hid_core.GetEmulatedConsole(); + static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, + "ConsoleSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); +} + +ConsoleSixAxis::~ConsoleSixAxis() = default; + +void ConsoleSixAxis::OnInit() {} + +void ConsoleSixAxis::OnRelease() {} + +void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + if (!IsControllerActivated()) { + return; + } + + const auto motion_status = console->GetMotion(); + + shared_memory->sampling_number++; + shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; + shared_memory->verticalization_error = motion_status.verticalization_error; + shared_memory->gyro_bias = motion_status.gyro_bias; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h new file mode 100644 index 000000000..5b7c6a29a --- /dev/null +++ b/src/core/hle/service/hid/controllers/console_six_axis.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/vector_math.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Core::HID { +class EmulatedConsole; +} // namespace Core::HID + +namespace Service::HID { +class ConsoleSixAxis final : public ControllerBase { +public: + explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~ConsoleSixAxis() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + +private: + // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat + struct ConsoleSharedMemory { + u64 sampling_number{}; + bool is_seven_six_axis_sensor_at_rest{}; + INSERT_PADDING_BYTES(3); // padding + f32 verticalization_error{}; + Common::Vec3f gyro_bias{}; + INSERT_PADDING_BYTES(4); // padding + }; + static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); + + ConsoleSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index c58d67d7d..0bcd87062 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp @@ -8,12 +8,17 @@ namespace Service::HID { ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} ControllerBase::~ControllerBase() = default; -void ControllerBase::ActivateController() { +Result ControllerBase::Activate() { if (is_activated) { - return; + return ResultSuccess; } is_activated = true; OnInit(); + return ResultSuccess; +} + +Result ControllerBase::Activate(u64 aruid) { + return Activate(); } void ControllerBase::DeactivateController() { diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index d6f7a5073..9a44ee41e 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -4,6 +4,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/result.h" namespace Core::Timing { class CoreTiming; @@ -31,7 +32,8 @@ public: // When the controller is requesting a motion update for the shared memory virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} - void ActivateController(); + Result Activate(); + Result Activate(u64 aruid); void DeactivateController(); diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 8ec9f4a95..9de19ebfc 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -13,7 +13,7 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; -Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, "DebugPadSharedMemory is bigger than the shared memory"); @@ -22,13 +22,13 @@ Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_ controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); } -Controller_DebugPad::~Controller_DebugPad() = default; +DebugPad::~DebugPad() = default; -void Controller_DebugPad::OnInit() {} +void DebugPad::OnInit() {} -void Controller_DebugPad::OnRelease() {} +void DebugPad::OnRelease() {} -void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { shared_memory->debug_pad_lifo.buffer_count = 0; shared_memory->debug_pad_lifo.buffer_tail = 0; diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 68ff0ea79..5566dba77 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -15,10 +15,10 @@ struct AnalogStickState; } // namespace Core::HID namespace Service::HID { -class Controller_DebugPad final : public ControllerBase { +class DebugPad final : public ControllerBase { public: - explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); - ~Controller_DebugPad() override; + explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~DebugPad() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 63eecd42b..59b2ec73c 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) { return static_cast<f32>(num * num); } -Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase(hid_core_) { static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, "GestureSharedMemory is bigger than the shared memory"); @@ -31,17 +31,17 @@ Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_sh reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); console = hid_core.GetEmulatedConsole(); } -Controller_Gesture::~Controller_Gesture() = default; +Gesture::~Gesture() = default; -void Controller_Gesture::OnInit() { +void Gesture::OnInit() { shared_memory->gesture_lifo.buffer_count = 0; shared_memory->gesture_lifo.buffer_tail = 0; force_update = true; } -void Controller_Gesture::OnRelease() {} +void Gesture::OnRelease() {} -void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { shared_memory->gesture_lifo.buffer_count = 0; shared_memory->gesture_lifo.buffer_tail = 0; @@ -64,7 +64,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { UpdateGestureSharedMemory(gesture, time_difference); } -void Controller_Gesture::ReadTouchInput() { +void Gesture::ReadTouchInput() { if (!Settings::values.touchscreen.enabled) { fingers = {}; return; @@ -76,8 +76,7 @@ void Controller_Gesture::ReadTouchInput() { } } -bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, - f32 time_difference) { +bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { const auto& last_entry = GetLastGestureEntry(); if (force_update) { force_update = false; @@ -100,8 +99,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, return false; } -void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, - f32 time_difference) { +void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { GestureType type = GestureType::Idle; GestureAttribute attributes{}; @@ -138,8 +136,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, shared_memory->gesture_lifo.WriteNextEntry(next_state); } -void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, - GestureAttribute& attributes) { +void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, + GestureAttribute& attributes) { const auto& last_entry = GetLastGestureEntry(); gesture.detection_count++; @@ -152,8 +150,8 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& typ } } -void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, - f32 time_difference) { +void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, + f32 time_difference) { const auto& last_entry = GetLastGestureEntry(); // Promote to pan type if touch moved @@ -186,9 +184,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu } } -void Controller_Gesture::EndGesture(GestureProperties& gesture, - GestureProperties& last_gesture_props, GestureType& type, - GestureAttribute& attributes, f32 time_difference) { +void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, GestureAttribute& attributes, f32 time_difference) { const auto& last_entry = GetLastGestureEntry(); if (last_gesture_props.active_points != 0) { @@ -222,9 +219,8 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture, } } -void Controller_Gesture::SetTapEvent(GestureProperties& gesture, - GestureProperties& last_gesture_props, GestureType& type, - GestureAttribute& attributes) { +void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, GestureAttribute& attributes) { type = GestureType::Tap; gesture = last_gesture_props; force_update = true; @@ -236,9 +232,8 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture, } } -void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, - GestureProperties& last_gesture_props, GestureType& type, - f32 time_difference) { +void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, f32 time_difference) { const auto& last_entry = GetLastGestureEntry(); next_state.delta = gesture.mid_point - last_entry.pos; @@ -263,9 +258,8 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, } } -void Controller_Gesture::EndPanEvent(GestureProperties& gesture, - GestureProperties& last_gesture_props, GestureType& type, - f32 time_difference) { +void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, f32 time_difference) { const auto& last_entry = GetLastGestureEntry(); next_state.vel_x = static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); @@ -287,8 +281,8 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture, force_update = true; } -void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, - GestureProperties& last_gesture_props, GestureType& type) { +void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type) { const auto& last_entry = GetLastGestureEntry(); type = GestureType::Swipe; @@ -311,11 +305,11 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, next_state.direction = GestureDirection::Up; } -const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { +const Gesture::GestureState& Gesture::GetLastGestureEntry() const { return shared_memory->gesture_lifo.ReadCurrentEntry().state; } -Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { +Gesture::GestureProperties Gesture::GetGestureProperties() { GestureProperties gesture; std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 0d6099ea0..4c6f8ee07 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -12,10 +12,10 @@ #include "core/hle/service/hid/ring_lifo.h" namespace Service::HID { -class Controller_Gesture final : public ControllerBase { +class Gesture final : public ControllerBase { public: - explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); - ~Controller_Gesture() override; + explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~Gesture() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 117d87433..ddb1b0ba4 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -12,7 +12,7 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; -Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, "KeyboardSharedMemory is bigger than the shared memory"); @@ -21,13 +21,13 @@ Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_ emulated_devices = hid_core.GetEmulatedDevices(); } -Controller_Keyboard::~Controller_Keyboard() = default; +Keyboard::~Keyboard() = default; -void Controller_Keyboard::OnInit() {} +void Keyboard::OnInit() {} -void Controller_Keyboard::OnRelease() {} +void Keyboard::OnRelease() {} -void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { shared_memory->keyboard_lifo.buffer_count = 0; shared_memory->keyboard_lifo.buffer_tail = 0; diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 7532f53c6..172ec1309 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -14,10 +14,10 @@ struct KeyboardKey; } // namespace Core::HID namespace Service::HID { -class Controller_Keyboard final : public ControllerBase { +class Keyboard final : public ControllerBase { public: - explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); - ~Controller_Keyboard() override; + explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~Keyboard() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 0afc66681..6e5a04e34 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -12,8 +12,7 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; -Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) - : ControllerBase{hid_core_} { +Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, "MouseSharedMemory is bigger than the shared memory"); shared_memory = std::construct_at( @@ -21,12 +20,12 @@ Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared emulated_devices = hid_core.GetEmulatedDevices(); } -Controller_Mouse::~Controller_Mouse() = default; +Mouse::~Mouse() = default; -void Controller_Mouse::OnInit() {} -void Controller_Mouse::OnRelease() {} +void Mouse::OnInit() {} +void Mouse::OnRelease() {} -void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { shared_memory->mouse_lifo.buffer_count = 0; shared_memory->mouse_lifo.buffer_tail = 0; diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 733d00577..a80f3823f 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -14,10 +14,10 @@ struct AnalogStickState; } // namespace Core::HID namespace Service::HID { -class Controller_Mouse final : public ControllerBase { +class Mouse final : public ControllerBase { public: - explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); - ~Controller_Mouse() override; + explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~Mouse() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 21695bda2..08ee9de9c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -18,6 +18,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_util.h" #include "core/hle/service/kernel_helpers.h" namespace Service::HID { @@ -29,60 +30,8 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{ Core::HID::NpadIdType::Handheld, }; -bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { - switch (npad_id) { - case Core::HID::NpadIdType::Player1: - case Core::HID::NpadIdType::Player2: - case Core::HID::NpadIdType::Player3: - case Core::HID::NpadIdType::Player4: - case Core::HID::NpadIdType::Player5: - case Core::HID::NpadIdType::Player6: - case Core::HID::NpadIdType::Player7: - case Core::HID::NpadIdType::Player8: - case Core::HID::NpadIdType::Other: - case Core::HID::NpadIdType::Handheld: - return true; - default: - LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); - return false; - } -} - -Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { - const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); - const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; - const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; - - if (!npad_type) { - return VibrationInvalidStyleIndex; - } - if (!npad_id) { - return VibrationInvalidNpadId; - } - if (!device_index) { - return VibrationDeviceIndexOutOfRange; - } - - return ResultSuccess; -} - -Result Controller_NPad::VerifyValidSixAxisSensorHandle( - const Core::HID::SixAxisSensorHandle& device_handle) { - const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); - const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; - - if (!npad_id) { - return InvalidNpadId; - } - if (!device_index) { - return NpadDeviceIndexOutOfRange; - } - - return ResultSuccess; -} - -Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, - KernelHelpers::ServiceContext& service_context_) +NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_) : ControllerBase{hid_core_}, service_context{service_context_} { static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); for (std::size_t i = 0; i < controller_data.size(); ++i) { @@ -103,7 +52,7 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_m } } -Controller_NPad::~Controller_NPad() { +NPad::~NPad() { for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; controller.device->DeleteCallback(controller.callback_key); @@ -111,8 +60,7 @@ Controller_NPad::~Controller_NPad() { OnRelease(); } -void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, - std::size_t controller_idx) { +void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { if (type == Core::HID::ControllerTriggerType::All) { ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); @@ -150,7 +98,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, } } -void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { +void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { auto& controller = GetControllerFromNpadIdType(npad_id); if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { return; @@ -344,12 +292,13 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, Common::Input::PollingMode::Active); } + SignalStyleSetChangedEvent(npad_id); WriteEmptyEntry(controller.shared_memory); hid_core.SetLastActiveController(npad_id); } -void Controller_NPad::OnInit() { +void NPad::OnInit() { if (!IsControllerActivated()) { return; } @@ -383,7 +332,7 @@ void Controller_NPad::OnInit() { } } -void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { +void NPad::WriteEmptyEntry(NpadInternalState* npad) { NPadGenericState dummy_pad_state{}; NpadGcTriggerState dummy_gc_state{}; dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; @@ -404,7 +353,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); } -void Controller_NPad::OnRelease() { +void NPad::OnRelease() { is_controller_initialized = false; for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; @@ -415,7 +364,7 @@ void Controller_NPad::OnRelease() { } } -void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { +void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { std::scoped_lock lock{mutex}; auto& controller = GetControllerFromNpadIdType(npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); @@ -457,12 +406,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { pad_entry.l_stick = stick_state.left; } - if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { + if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft || + controller_type == Core::HID::NpadStyleIndex::JoyconDual) { pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); } - if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { + if (controller_type == Core::HID::NpadStyleIndex::JoyconRight || + controller_type == Core::HID::NpadStyleIndex::JoyconDual) { pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); } @@ -482,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { } } -void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { return; } @@ -612,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { } } -void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!IsControllerActivated()) { - return; - } - - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - - const auto& controller_type = controller.device->GetNpadStyleIndex(); - - if (controller_type == Core::HID::NpadStyleIndex::None || - !controller.device->IsConnected()) { - continue; - } - - auto* npad = controller.shared_memory; - const auto& motion_state = controller.device->GetMotions(); - auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; - auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; - auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; - auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; - auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; - auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; - - // Clear previous state - sixaxis_fullkey_state = {}; - sixaxis_handheld_state = {}; - sixaxis_dual_left_state = {}; - sixaxis_dual_right_state = {}; - sixaxis_left_lifo_state = {}; - sixaxis_right_lifo_state = {}; - - if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { - controller.sixaxis_at_rest = true; - for (std::size_t e = 0; e < motion_state.size(); ++e) { - controller.sixaxis_at_rest = - controller.sixaxis_at_rest && motion_state[e].is_at_rest; - } - } - - const auto set_motion_state = [&](SixAxisSensorState& state, - const Core::HID::ControllerMotion& hid_state) { - using namespace std::literals::chrono_literals; - static constexpr SixAxisSensorState default_motion_state = { - .delta_time = std::chrono::nanoseconds(5ms).count(), - .accel = {0, 0, -1.0f}, - .orientation = - { - Common::Vec3f{1.0f, 0, 0}, - Common::Vec3f{0, 1.0f, 0}, - Common::Vec3f{0, 0, 1.0f}, - }, - .attribute = {1}, - }; - if (!controller.sixaxis_sensor_enabled) { - state = default_motion_state; - return; - } - if (!Settings::values.motion_enabled.GetValue()) { - state = default_motion_state; - return; - } - state.attribute.is_connected.Assign(1); - state.delta_time = std::chrono::nanoseconds(5ms).count(); - state.accel = hid_state.accel; - state.gyro = hid_state.gyro; - state.rotation = hid_state.rotation; - state.orientation = hid_state.orientation; - }; - - switch (controller_type) { - case Core::HID::NpadStyleIndex::None: - ASSERT(false); - break; - case Core::HID::NpadStyleIndex::ProController: - set_motion_state(sixaxis_fullkey_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::Handheld: - set_motion_state(sixaxis_handheld_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::JoyconDual: - set_motion_state(sixaxis_dual_left_state, motion_state[0]); - set_motion_state(sixaxis_dual_right_state, motion_state[1]); - break; - case Core::HID::NpadStyleIndex::JoyconLeft: - set_motion_state(sixaxis_left_lifo_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::JoyconRight: - set_motion_state(sixaxis_right_lifo_state, motion_state[1]); - break; - case Core::HID::NpadStyleIndex::Pokeball: - using namespace std::literals::chrono_literals; - set_motion_state(sixaxis_fullkey_state, motion_state[0]); - sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); - break; - default: - break; - } - - sixaxis_fullkey_state.sampling_number = - npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_handheld_state.sampling_number = - npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_dual_left_state.sampling_number = - npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_dual_right_state.sampling_number = - npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_left_lifo_state.sampling_number = - npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_right_lifo_state.sampling_number = - npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; - - if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { - // This buffer only is updated on handheld on HW - npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); - } else { - // Handheld doesn't update this buffer on HW - npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); - } - - npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); - npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); - npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); - npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); - } -} - -void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { +void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { hid_core.SetSupportedStyleTag(style_set); if (is_controller_initialized) { @@ -750,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { is_controller_initialized = true; } -Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { +Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const { if (!is_controller_initialized) { return {Core::HID::NpadStyleSet::None}; } return hid_core.GetSupportedStyleTag(); } -Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { +Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { constexpr std::size_t max_number_npad_ids = 0xa; const auto length = data.size(); ASSERT(length > 0 && (length % sizeof(u32)) == 0); @@ -773,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { return ResultSuccess; } -void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { +void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); ASSERT(max_length <= copy_amount); std::memcpy(data, supported_npad_id_types.data(), copy_amount); } -std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { +std::size_t NPad::GetSupportedNpadIdTypesSize() const { return supported_npad_id_types.size(); } -void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { +void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { if (joy_hold_type != NpadJoyHoldType::Horizontal && joy_hold_type != NpadJoyHoldType::Vertical) { LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", @@ -793,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { hold_type = joy_hold_type; } -Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { +NPad::NpadJoyHoldType NPad::GetHoldType() const { return hold_type; } -void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { +void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); return; @@ -806,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a handheld_activation_mode = activation_mode; } -Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { +NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { return handheld_activation_mode; } -void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { +void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { communication_mode = communication_mode_; } -Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { +NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const { return communication_mode; } -bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, - NpadJoyDeviceType npad_device_type, - NpadJoyAssignmentMode assignment_mode) { +bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, + NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return false; @@ -889,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID: return true; } -bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, - std::size_t device_index, - const Core::HID::VibrationValue& vibration_value) { +bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, + const Core::HID::VibrationValue& vibration_value) { auto& controller = GetControllerFromNpadIdType(npad_id); if (!controller.device->IsConnected()) { return false; @@ -935,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, return controller.device->SetVibration(device_index, vibration); } -void Controller_NPad::VibrateController( - const Core::HID::VibrationDeviceHandle& vibration_device_handle, - const Core::HID::VibrationValue& vibration_value) { - if (IsDeviceHandleValid(vibration_device_handle).IsError()) { +void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, + const Core::HID::VibrationValue& vibration_value) { + if (IsVibrationHandleValid(vibration_device_handle).IsError()) { return; } @@ -982,7 +803,7 @@ void Controller_NPad::VibrateController( } } -void Controller_NPad::VibrateControllers( +void NPad::VibrateControllers( std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, std::span<const Core::HID::VibrationValue> vibration_values) { if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { @@ -999,9 +820,9 @@ void Controller_NPad::VibrateControllers( } } -Core::HID::VibrationValue Controller_NPad::GetLastVibration( +Core::HID::VibrationValue NPad::GetLastVibration( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (IsDeviceHandleValid(vibration_device_handle).IsError()) { + if (IsVibrationHandleValid(vibration_device_handle).IsError()) { return {}; } @@ -1010,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( return controller.vibration[device_index].latest_vibration_value; } -void Controller_NPad::InitializeVibrationDevice( +void NPad::InitializeVibrationDevice( const Core::HID::VibrationDeviceHandle& vibration_device_handle) { - if (IsDeviceHandleValid(vibration_device_handle).IsError()) { + if (IsVibrationHandleValid(vibration_device_handle).IsError()) { return; } @@ -1021,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice( InitializeVibrationDeviceAtIndex(npad_index, device_index); } -void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, - std::size_t device_index) { +void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, + std::size_t device_index) { auto& controller = GetControllerFromNpadIdType(npad_id); if (!Settings::values.vibration_enabled.GetValue()) { controller.vibration[device_index].device_mounted = false; @@ -1033,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa controller.device->IsVibrationEnabled(device_index); } -void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { +void NPad::SetPermitVibrationSession(bool permit_vibration_session) { permit_vibration_session_enabled = permit_vibration_session; } -bool Controller_NPad::IsVibrationDeviceMounted( +bool NPad::IsVibrationDeviceMounted( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (IsDeviceHandleValid(vibration_device_handle).IsError()) { + if (IsVibrationHandleValid(vibration_device_handle).IsError()) { return false; } @@ -1048,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted( return controller.vibration[device_index].device_mounted; } -Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { +Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); // Fallback to player 1 @@ -1060,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad return controller.styleset_changed_event->GetReadableEvent(); } -void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { +void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { const auto& controller = GetControllerFromNpadIdType(npad_id); controller.styleset_changed_event->Signal(); } -void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, - Core::HID::NpadIdType npad_id) { +void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { UpdateControllerAt(controller, npad_id, true); } -void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, - Core::HID::NpadIdType npad_id, bool connected) { +void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, + bool connected) { auto& controller = GetControllerFromNpadIdType(npad_id); if (!connected) { DisconnectNpad(npad_id); @@ -1082,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, InitNewlyAddedController(npad_id); } -Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { +Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return InvalidNpadId; @@ -1131,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { return ResultSuccess; } -Result Controller_NPad::SetGyroscopeZeroDriftMode( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode drift_mode) { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - auto& controller = GetControllerFromHandle(sixaxis_handle); - sixaxis.gyroscope_zero_drift_mode = drift_mode; - controller.device->SetGyroscopeZeroDriftMode(drift_mode); - - return ResultSuccess; -} - -Result Controller_NPad::GetGyroscopeZeroDriftMode( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode& drift_mode) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - drift_mode = sixaxis.gyroscope_zero_drift_mode; - - return ResultSuccess; -} - -Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_at_rest) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& controller = GetControllerFromHandle(sixaxis_handle); - is_at_rest = controller.sixaxis_at_rest; - return ResultSuccess; -} - -Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( +Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); if (is_valid.IsError()) { LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); return is_valid; @@ -1189,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( return ResultSuccess; } -Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - sixaxis.unaltered_passtrough = is_enabled; - return ResultSuccess; -} - -Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - is_enabled = sixaxis.unaltered_passtrough; - return ResultSuccess; -} - -Result Controller_NPad::LoadSixAxisSensorCalibrationParameter( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorCalibrationParameter& calibration) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - // TODO: Request this data to the controller. On error return 0xd8ca - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - calibration = sixaxis.calibration; - return ResultSuccess; -} - -Result Controller_NPad::GetSixAxisSensorIcInformation( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorIcInformation& ic_information) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - // TODO: Request this data to the controller. On error return 0xd8ca - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - ic_information = sixaxis.ic_information; - return ResultSuccess; -} - -Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( +Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( const Core::HID::SixAxisSensorHandle& sixaxis_handle) { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); if (is_valid.IsError()) { LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); return is_valid; @@ -1259,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( return ResultSuccess; } -Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool sixaxis_status) { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_sensor_enabled = sixaxis_status; - return ResultSuccess; +NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) { + return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo; } -Result Controller_NPad::IsSixAxisSensorFusionEnabled( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - is_fusion_enabled = sixaxis.is_fusion_enabled; - - return ResultSuccess; +NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) { + return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo; } -Result Controller_NPad::SetSixAxisFusionEnabled( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - auto& sixaxis = GetSixaxisState(sixaxis_handle); - sixaxis.is_fusion_enabled = is_fusion_enabled; - - return ResultSuccess; +NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) { + return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo; } -Result Controller_NPad::SetSixAxisFusionParameters( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto param1 = sixaxis_fusion_parameters.parameter1; - if (param1 < 0.0f || param1 > 1.0f) { - return InvalidSixAxisFusionRange; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - sixaxis.fusion = sixaxis_fusion_parameters; - - return ResultSuccess; +NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) { + return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo; } -Result Controller_NPad::GetSixAxisFusionParameters( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters& parameters) const { - const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - parameters = sixaxis.fusion; +NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) { + return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo; +} - return ResultSuccess; +NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) { + return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo; } -Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); @@ -1397,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, return ResultSuccess; } -void Controller_NPad::StartLRAssignmentMode() { +void NPad::StartLRAssignmentMode() { // Nothing internally is used for lr assignment mode. Since we have the ability to set the // controller types from boot, it doesn't really matter about showing a selection screen is_in_lr_assignment_mode = true; } -void Controller_NPad::StopLRAssignmentMode() { +void NPad::StopLRAssignmentMode() { is_in_lr_assignment_mode = false; } -Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); @@ -1439,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, return ResultSuccess; } -Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, - Core::HID::LedPattern& pattern) const { +Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return InvalidNpadId; @@ -1450,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, return ResultSuccess; } -Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, - bool& is_valid) const { +Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, + bool& is_valid) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return InvalidNpadId; @@ -1461,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID:: return ResultSuccess; } -Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( - bool is_protection_enabled, Core::HID::NpadIdType npad_id) { +Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, + Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return InvalidNpadId; @@ -1472,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( return ResultSuccess; } -void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { +void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { analog_stick_use_center_clamp = use_center_clamp; } -void Controller_NPad::ClearAllConnectedControllers() { +void NPad::ClearAllConnectedControllers() { for (auto& controller : controller_data) { if (controller.device->IsConnected() && controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { @@ -1486,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() { } } -void Controller_NPad::DisconnectAllConnectedControllers() { +void NPad::DisconnectAllConnectedControllers() { for (auto& controller : controller_data) { controller.device->Disconnect(); } } -void Controller_NPad::ConnectAllDisconnectedControllers() { +void NPad::ConnectAllDisconnectedControllers() { for (auto& controller : controller_data) { if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && !controller.device->IsConnected()) { @@ -1501,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() { } } -void Controller_NPad::ClearAllControllers() { +void NPad::ClearAllControllers() { for (auto& controller : controller_data) { controller.device->Disconnect(); controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); } } -Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { +Core::HID::NpadButton NPad::GetAndResetPressState() { return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); } -void Controller_NPad::ApplyNpadSystemCommonPolicy() { +void NPad::ApplyNpadSystemCommonPolicy() { Core::HID::NpadStyleTag styletag{}; styletag.fullkey.Assign(1); styletag.handheld.Assign(1); @@ -1537,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() { supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; } -bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { +bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { if (controller == Core::HID::NpadStyleIndex::Handheld) { const bool support_handheld = std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), @@ -1588,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller return false; } -Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) { +NPad::NpadControllerData& NPad::GetControllerFromHandle( + const Core::HID::VibrationDeviceHandle& device_handle) { const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); return GetControllerFromNpadIdType(npad_id); } -const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) const { +const NPad::NpadControllerData& NPad::GetControllerFromHandle( + const Core::HID::VibrationDeviceHandle& device_handle) const { const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); return GetControllerFromNpadIdType(npad_id); } -Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( - const Core::HID::VibrationDeviceHandle& device_handle) { +NPad::NpadControllerData& NPad::GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle) { const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); return GetControllerFromNpadIdType(npad_id); } -const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( - const Core::HID::VibrationDeviceHandle& device_handle) const { +const NPad::NpadControllerData& NPad::GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle) const { const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); return GetControllerFromNpadIdType(npad_id); } -Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( - Core::HID::NpadIdType npad_id) { +NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); npad_id = Core::HID::NpadIdType::Player1; } - const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); + const auto npad_index = NpadIdTypeToIndex(npad_id); return controller_data[npad_index]; } -const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( +const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( Core::HID::NpadIdType npad_id) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); npad_id = Core::HID::NpadIdType::Player1; } - const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); + const auto npad_index = NpadIdTypeToIndex(npad_id); return controller_data[npad_index]; } -Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( +Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( const Core::HID::SixAxisSensorHandle& sixaxis_handle) { auto& controller = GetControllerFromHandle(sixaxis_handle); switch (sixaxis_handle.npad_type) { @@ -1655,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( } } -const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( +const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { const auto& controller = GetControllerFromHandle(sixaxis_handle); switch (sixaxis_handle.npad_type) { @@ -1678,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( } } -Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) { - auto& controller = GetControllerFromHandle(sixaxis_handle); - switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: - return controller.sixaxis_fullkey; - case Core::HID::NpadStyleIndex::Handheld: - return controller.sixaxis_handheld; - case Core::HID::NpadStyleIndex::JoyconDual: - if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { - return controller.sixaxis_dual_left; - } - return controller.sixaxis_dual_right; - case Core::HID::NpadStyleIndex::JoyconLeft: - return controller.sixaxis_left; - case Core::HID::NpadStyleIndex::JoyconRight: - return controller.sixaxis_right; - default: - return controller.sixaxis_unknown; - } -} +NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { + const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; -const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { - const auto& controller = GetControllerFromHandle(sixaxis_handle); - switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: - return controller.sixaxis_fullkey; - case Core::HID::NpadStyleIndex::Handheld: - return controller.sixaxis_handheld; - case Core::HID::NpadStyleIndex::JoyconDual: - if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { - return controller.sixaxis_dual_left; - } - return controller.sixaxis_dual_right; - case Core::HID::NpadStyleIndex::JoyconLeft: - return controller.sixaxis_left; - case Core::HID::NpadStyleIndex::JoyconRight: - return controller.sixaxis_right; - default: - return controller.sixaxis_unknown; - } + return { + .ui_variant = 0, + .footer = shared_memory->applet_footer_type, + }; } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 949e58a4c..9167c93f0 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -10,7 +10,6 @@ #include "common/bit_field.h" #include "common/common_types.h" -#include "common/vector_math.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" @@ -34,11 +33,11 @@ union Result; namespace Service::HID { -class Controller_NPad final : public ControllerBase { +class NPad final : public ControllerBase { public: - explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, - KernelHelpers::ServiceContext& service_context_); - ~Controller_NPad() override; + explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_); + ~NPad() override; // Called when the controller is initialized void OnInit() override; @@ -49,9 +48,6 @@ public: // When the controller is requesting an update for the shared memory void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - // When the controller is requesting a motion update for the shared memory - void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override; - // This is nn::hid::NpadJoyHoldType enum class NpadJoyHoldType : u64 { Vertical = 0, @@ -78,6 +74,46 @@ public: MaxActivationMode = 3, }; + // This is nn::hid::system::AppletFooterUiAttributesSet + struct AppletFooterUiAttributes { + INSERT_PADDING_BYTES(0x4); + }; + + // This is nn::hid::system::AppletFooterUiType + enum class AppletFooterUiType : u8 { + None = 0, + HandheldNone = 1, + HandheldJoyConLeftOnly = 2, + HandheldJoyConRightOnly = 3, + HandheldJoyConLeftJoyConRight = 4, + JoyDual = 5, + JoyDualLeftOnly = 6, + JoyDualRightOnly = 7, + JoyLeftHorizontal = 8, + JoyLeftVertical = 9, + JoyRightHorizontal = 10, + JoyRightVertical = 11, + SwitchProController = 12, + CompatibleProController = 13, + CompatibleJoyCon = 14, + LarkHvc1 = 15, + LarkHvc2 = 16, + LarkNesLeft = 17, + LarkNesRight = 18, + Lucia = 19, + Verification = 20, + Lagon = 21, + }; + + using AppletFooterUiVariant = u8; + + // This is "nn::hid::system::AppletDetailedUiType". + struct AppletDetailedUiType { + AppletFooterUiVariant ui_variant; + INSERT_PADDING_BYTES(0x2); + AppletFooterUiType footer; + }; + static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size"); // This is nn::hid::NpadCommunicationMode enum class NpadCommunicationMode : u64 { Mode_5ms = 0, @@ -86,6 +122,15 @@ public: Default = 3, }; + enum class NpadRevision : u32 { + Revision0 = 0, + Revision1 = 1, + Revision2 = 2, + Revision3 = 3, + }; + + using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>; + void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); Core::HID::NpadStyleTag GetSupportedStyleSet() const; @@ -138,37 +183,18 @@ public: Result DisconnectNpad(Core::HID::NpadIdType npad_id); - Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode drift_mode); - Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode& drift_mode) const; - Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_at_rest) const; Result IsFirmwareUpdateAvailableForSixAxisSensor( const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; - Result EnableSixAxisSensorUnalteredPassthrough( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); - Result IsSixAxisSensorUnalteredPassthroughEnabled( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; - Result LoadSixAxisSensorCalibrationParameter( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorCalibrationParameter& calibration) const; - Result GetSixAxisSensorIcInformation( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorIcInformation& ic_information) const; Result ResetIsSixAxisSensorDeviceNewlyAssigned( const Core::HID::SixAxisSensorHandle& sixaxis_handle); - Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool sixaxis_status); - Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_fusion_enabled) const; - Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool is_fusion_enabled); - Result SetSixAxisFusionParameters( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); - Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters& parameters) const; + + SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id); + SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id); + SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id); + SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id); + SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id); + SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id); + Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, bool& is_enabled) const; @@ -192,10 +218,7 @@ public: void ApplyNpadSystemCommonPolicy(); - static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); - static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); - static Result VerifyValidSixAxisSensorHandle( - const Core::HID::SixAxisSensorHandle& device_handle); + AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); private: static constexpr std::size_t NPAD_COUNT = 10; @@ -254,29 +277,6 @@ private: }; static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); - // This is nn::hid::SixAxisSensorAttribute - struct SixAxisSensorAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> is_connected; - BitField<1, 1, u32> is_interpolated; - }; - }; - static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); - - // This is nn::hid::SixAxisSensorState - struct SixAxisSensorState { - s64 delta_time{}; - s64 sampling_number{}; - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - std::array<Common::Vec3f, 3> orientation{}; - SixAxisSensorAttribute attribute{}; - INSERT_PADDING_BYTES(4); // Reserved - }; - static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); - // This is nn::hid::server::NpadGcTriggerState struct NpadGcTriggerState { s64 sampling_number{}; @@ -353,37 +353,6 @@ private: static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, "NfcXcdDeviceHandleStateImpl is an invalid size"); - // This is nn::hid::system::AppletFooterUiAttributesSet - struct AppletFooterUiAttributes { - INSERT_PADDING_BYTES(0x4); - }; - - // This is nn::hid::system::AppletFooterUiType - enum class AppletFooterUiType : u8 { - None = 0, - HandheldNone = 1, - HandheldJoyConLeftOnly = 2, - HandheldJoyConRightOnly = 3, - HandheldJoyConLeftJoyConRight = 4, - JoyDual = 5, - JoyDualLeftOnly = 6, - JoyDualRightOnly = 7, - JoyLeftHorizontal = 8, - JoyLeftVertical = 9, - JoyRightHorizontal = 10, - JoyRightVertical = 11, - SwitchProController = 12, - CompatibleProController = 13, - CompatibleJoyCon = 14, - LarkHvc1 = 15, - LarkHvc2 = 16, - LarkNesLeft = 17, - LarkNesRight = 18, - Lucia = 19, - Verification = 20, - Lagon = 21, - }; - // This is nn::hid::NpadLarkType enum class NpadLarkType : u32 { Invalid, @@ -427,12 +396,12 @@ private: Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; + Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; + Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; + Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; + Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; + Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; + Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; DeviceType device_type{}; INSERT_PADDING_BYTES(0x4); // Reserved NPadSystemProperties system_properties{}; @@ -466,16 +435,6 @@ private: std::chrono::steady_clock::time_point last_vibration_timepoint{}; }; - struct SixaxisParameters { - bool is_fusion_enabled{true}; - bool unaltered_passtrough{false}; - Core::HID::SixAxisSensorFusionParameters fusion{}; - Core::HID::SixAxisSensorCalibrationParameter calibration{}; - Core::HID::SixAxisSensorIcInformation ic_information{}; - Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ - Core::HID::GyroscopeZeroDriftMode::Standard}; - }; - struct NpadControllerData { Kernel::KEvent* styleset_changed_event{}; NpadInternalState* shared_memory = nullptr; @@ -489,27 +448,10 @@ private: bool is_dual_left_connected{true}; bool is_dual_right_connected{true}; - // Motion parameters - bool sixaxis_at_rest{true}; - bool sixaxis_sensor_enabled{true}; - SixaxisParameters sixaxis_fullkey{}; - SixaxisParameters sixaxis_handheld{}; - SixaxisParameters sixaxis_dual_left{}; - SixaxisParameters sixaxis_dual_right{}; - SixaxisParameters sixaxis_left{}; - SixaxisParameters sixaxis_right{}; - SixaxisParameters sixaxis_unknown{}; - // Current pad state NPadGenericState npad_pad_state{}; NPadGenericState npad_libnx_state{}; NpadGcTriggerState npad_trigger_state{}; - SixAxisSensorState sixaxis_fullkey_state{}; - SixAxisSensorState sixaxis_handheld_state{}; - SixAxisSensorState sixaxis_dual_left_state{}; - SixAxisSensorState sixaxis_dual_right_state{}; - SixAxisSensorState sixaxis_left_lifo_state{}; - SixAxisSensorState sixaxis_right_lifo_state{}; int callback_key{}; }; @@ -520,13 +462,13 @@ private: void WriteEmptyEntry(NpadInternalState* npad); NpadControllerData& GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle); - const NpadControllerData& GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) const; - NpadControllerData& GetControllerFromHandle( const Core::HID::VibrationDeviceHandle& device_handle); const NpadControllerData& GetControllerFromHandle( const Core::HID::VibrationDeviceHandle& device_handle) const; + NpadControllerData& GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle); + const NpadControllerData& GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle) const; NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; @@ -534,9 +476,6 @@ private: const Core::HID::SixAxisSensorHandle& device_handle); const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( const Core::HID::SixAxisSensorHandle& device_handle) const; - SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); - const SixaxisParameters& GetSixaxisState( - const Core::HID::SixAxisSensorHandle& device_handle) const; std::atomic<u64> press_state{}; diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp index 73a2a2b91..588ff9d62 100644 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -12,43 +12,43 @@ namespace Service::HID { -Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, - KernelHelpers::ServiceContext& service_context_) +Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_) : ControllerBase{hid_core_}, service_context{service_context_} { controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); } -Controller_Palma::~Controller_Palma() { +Palma::~Palma() { service_context.CloseEvent(operation_complete_event); }; -void Controller_Palma::OnInit() {} +void Palma::OnInit() {} -void Controller_Palma::OnRelease() {} +void Palma::OnRelease() {} -void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { return; } } -Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, - PalmaConnectionHandle& handle) { +Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, + PalmaConnectionHandle& handle) { active_handle.npad_id = npad_id; handle = active_handle; return ResultSuccess; } -Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { +Result Palma::InitializePalma(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } - ActivateController(); + Activate(); return ResultSuccess; } -Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( +Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent( const PalmaConnectionHandle& handle) const { if (handle.npad_id != active_handle.npad_id) { LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); @@ -56,9 +56,9 @@ Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( return operation_complete_event->GetReadableEvent(); } -Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, - PalmaOperationType& operation_type, - PalmaOperationData& data) const { +Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -67,8 +67,7 @@ Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& hand return ResultSuccess; } -Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, - u64 palma_activity) { +Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -79,8 +78,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, return ResultSuccess; } -Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, - PalmaFrModeType fr_mode_) { +Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -88,7 +86,7 @@ Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, return ResultSuccess; } -Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { +Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -99,25 +97,25 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { return ResultSuccess; } -Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { +Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } return ResultSuccess; } -Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { +Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } return ResultSuccess; } -void Controller_Palma::ReadPalmaApplicationSection() {} +void Palma::ReadPalmaApplicationSection() {} -void Controller_Palma::WritePalmaApplicationSection() {} +void Palma::WritePalmaApplicationSection() {} -Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { +Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -128,7 +126,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle return ResultSuccess; } -Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { +Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -139,10 +137,9 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& return ResultSuccess; } -void Controller_Palma::WritePalmaActivityEntry() {} +void Palma::WritePalmaActivityEntry() {} -Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, - u64 unknown) { +Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -153,8 +150,8 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl return ResultSuccess; } -Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, - Common::ProcessAddress t_mem, u64 size) { +Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, + Common::ProcessAddress t_mem, u64 size) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -165,8 +162,8 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle return ResultSuccess; } -Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, - s32 database_id_version_) { +Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -178,8 +175,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec return ResultSuccess; } -Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( - const PalmaConnectionHandle& handle) { +Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -191,26 +187,26 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( return ResultSuccess; } -void Controller_Palma::SuspendPalmaFeature() {} +void Palma::SuspendPalmaFeature() {} -Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { +Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } return operation.result; } -void Controller_Palma::ReadPalmaPlayLog() {} +void Palma::ReadPalmaPlayLog() {} -void Controller_Palma::ResetPalmaPlayLog() {} +void Palma::ResetPalmaPlayLog() {} -void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { +void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { // If true controllers are able to be paired is_connectable = is_all_connectable; } -void Controller_Palma::SetIsPalmaPairedConnectable() {} +void Palma::SetIsPalmaPairedConnectable() {} -Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { +Result Palma::PairPalma(const PalmaConnectionHandle& handle) { if (handle.npad_id != active_handle.npad_id) { return InvalidPalmaHandle; } @@ -218,14 +214,14 @@ Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { return ResultSuccess; } -void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} +void Palma::SetPalmaBoostMode(bool boost_mode) {} -void Controller_Palma::CancelWritePalmaWaveEntry() {} +void Palma::CancelWritePalmaWaveEntry() {} -void Controller_Palma::EnablePalmaBoostMode() {} +void Palma::EnablePalmaBoostMode() {} -void Controller_Palma::GetPalmaBluetoothAddress() {} +void Palma::GetPalmaBluetoothAddress() {} -void Controller_Palma::SetDisallowedPalmaConnection() {} +void Palma::SetDisallowedPalmaConnection() {} } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h index a0491a819..a6047f36a 100644 --- a/src/core/hle/service/hid/controllers/palma.h +++ b/src/core/hle/service/hid/controllers/palma.h @@ -23,7 +23,7 @@ class EmulatedController; } // namespace Core::HID namespace Service::HID { -class Controller_Palma final : public ControllerBase { +class Palma final : public ControllerBase { public: using PalmaOperationData = std::array<u8, 0x140>; @@ -97,9 +97,9 @@ public: static_assert(sizeof(PalmaConnectionHandle) == 0x8, "PalmaConnectionHandle has incorrect size."); - explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, - KernelHelpers::ServiceContext& service_context_); - ~Controller_Palma() override; + explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_); + ~Palma() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/seven_six_axis.cpp index bcb272eaf..495568484 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/seven_six_axis.cpp @@ -1,32 +1,29 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +#include <cstring> +#include "common/common_types.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/frontend/emu_window.h" #include "core/hid/emulated_console.h" +#include "core/hid/emulated_devices.h" #include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/console_sixaxis.h" +#include "core/hle/service/hid/controllers/seven_six_axis.h" #include "core/memory.h" namespace Service::HID { -constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; - -Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_) +SevenSixAxis::SevenSixAxis(Core::System& system_) : ControllerBase{system_.HIDCore()}, system{system_} { console = hid_core.GetEmulatedConsole(); - static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, - "ConsoleSharedMemory is bigger than the shared memory"); - shared_memory = std::construct_at( - reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); } -Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; - -void Controller_ConsoleSixAxis::OnInit() {} +SevenSixAxis::~SevenSixAxis() = default; -void Controller_ConsoleSixAxis::OnRelease() {} +void SevenSixAxis::OnInit() {} +void SevenSixAxis::OnRelease() {} -void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated() || transfer_memory == 0) { seven_sixaxis_lifo.buffer_count = 0; seven_sixaxis_lifo.buffer_tail = 0; @@ -53,22 +50,17 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti -motion_status.quaternion.xyz.z, }; - shared_memory->sampling_number++; - shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; - shared_memory->verticalization_error = motion_status.verticalization_error; - shared_memory->gyro_bias = motion_status.gyro_bias; - - // Update seven six axis transfer memory seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo)); } -void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { +void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { transfer_memory = t_mem; } -void Controller_ConsoleSixAxis::ResetTimestamp() { +void SevenSixAxis::ResetTimestamp() { last_saved_timestamp = last_global_timestamp; } + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/seven_six_axis.h index 7015d924c..40e3f5d12 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/seven_six_axis.h @@ -1,10 +1,9 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once -#include <array> - +#include "common/common_types.h" #include "common/quaternion.h" #include "common/typed_address.h" #include "core/hle/service/hid/controllers/controller_base.h" @@ -19,10 +18,10 @@ class EmulatedConsole; } // namespace Core::HID namespace Service::HID { -class Controller_ConsoleSixAxis final : public ControllerBase { +class SevenSixAxis final : public ControllerBase { public: - explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_); - ~Controller_ConsoleSixAxis() override; + explicit SevenSixAxis(Core::System& system_); + ~SevenSixAxis() override; // Called when the controller is initialized void OnInit() override; @@ -51,28 +50,16 @@ private: }; static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); - // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat - struct ConsoleSharedMemory { - u64 sampling_number{}; - bool is_seven_six_axis_sensor_at_rest{}; - INSERT_PADDING_BYTES(3); // padding - f32 verticalization_error{}; - Common::Vec3f gyro_bias{}; - INSERT_PADDING_BYTES(4); // padding - }; - static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); - Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); + u64 last_saved_timestamp{}; + u64 last_global_timestamp{}; + SevenSixAxisState next_seven_sixaxis_state{}; Common::ProcessAddress transfer_memory{}; - ConsoleSharedMemory* shared_memory = nullptr; Core::HID::EmulatedConsole* console = nullptr; - u64 last_saved_timestamp{}; - u64 last_global_timestamp{}; - Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp new file mode 100644 index 000000000..3d24a5c04 --- /dev/null +++ b/src/core/hle/service/hid/controllers/six_axis.cpp @@ -0,0 +1,413 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/six_axis.h" +#include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_util.h" + +namespace Service::HID { + +SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_) + : ControllerBase{hid_core_}, npad{npad_} { + for (std::size_t i = 0; i < controller_data.size(); ++i) { + auto& controller = controller_data[i]; + controller.device = hid_core.GetEmulatedControllerByIndex(i); + } +} + +SixAxis::~SixAxis() = default; + +void SixAxis::OnInit() {} +void SixAxis::OnRelease() {} + +void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + if (!IsControllerActivated()) { + return; + } + + for (std::size_t i = 0; i < controller_data.size(); ++i) { + auto& controller = controller_data[i]; + + const auto npad_id = IndexToNpadIdType(i); + const auto& controller_type = controller.device->GetNpadStyleIndex(); + + if (controller_type == Core::HID::NpadStyleIndex::None || + !controller.device->IsConnected()) { + continue; + } + + const auto& motion_state = controller.device->GetMotions(); + auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; + auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; + auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; + auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; + auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; + auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; + + auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id); + auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id); + auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id); + auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id); + auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id); + auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id); + + // Clear previous state + sixaxis_fullkey_state = {}; + sixaxis_handheld_state = {}; + sixaxis_dual_left_state = {}; + sixaxis_dual_right_state = {}; + sixaxis_left_lifo_state = {}; + sixaxis_right_lifo_state = {}; + + if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { + controller.sixaxis_at_rest = true; + for (std::size_t e = 0; e < motion_state.size(); ++e) { + controller.sixaxis_at_rest = + controller.sixaxis_at_rest && motion_state[e].is_at_rest; + } + } + + const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, + const Core::HID::ControllerMotion& hid_state) { + using namespace std::literals::chrono_literals; + static constexpr Core::HID::SixAxisSensorState default_motion_state = { + .delta_time = std::chrono::nanoseconds(5ms).count(), + .accel = {0, 0, -1.0f}, + .orientation = + { + Common::Vec3f{1.0f, 0, 0}, + Common::Vec3f{0, 1.0f, 0}, + Common::Vec3f{0, 0, 1.0f}, + }, + .attribute = {1}, + }; + if (!controller.sixaxis_sensor_enabled) { + state = default_motion_state; + return; + } + if (!Settings::values.motion_enabled.GetValue()) { + state = default_motion_state; + return; + } + state.attribute.is_connected.Assign(1); + state.delta_time = std::chrono::nanoseconds(5ms).count(); + state.accel = hid_state.accel; + state.gyro = hid_state.gyro; + state.rotation = hid_state.rotation; + state.orientation = hid_state.orientation; + }; + + switch (controller_type) { + case Core::HID::NpadStyleIndex::None: + ASSERT(false); + break; + case Core::HID::NpadStyleIndex::ProController: + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + break; + case Core::HID::NpadStyleIndex::Handheld: + set_motion_state(sixaxis_handheld_state, motion_state[0]); + break; + case Core::HID::NpadStyleIndex::JoyconDual: + set_motion_state(sixaxis_dual_left_state, motion_state[0]); + set_motion_state(sixaxis_dual_right_state, motion_state[1]); + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + set_motion_state(sixaxis_left_lifo_state, motion_state[0]); + break; + case Core::HID::NpadStyleIndex::JoyconRight: + set_motion_state(sixaxis_right_lifo_state, motion_state[1]); + break; + case Core::HID::NpadStyleIndex::Pokeball: + using namespace std::literals::chrono_literals; + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); + break; + default: + break; + } + + sixaxis_fullkey_state.sampling_number = + sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_handheld_state.sampling_number = + sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_dual_left_state.sampling_number = + sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_dual_right_state.sampling_number = + sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_left_lifo_state.sampling_number = + sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_right_lifo_state.sampling_number = + sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + + if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { + // This buffer only is updated on handheld on HW + sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); + } else { + // Handheld doesn't update this buffer on HW + sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); + } + + sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); + sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); + sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); + sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); + } +} + +Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::GyroscopeZeroDriftMode drift_mode) { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + auto& controller = GetControllerFromHandle(sixaxis_handle); + sixaxis.gyroscope_zero_drift_mode = drift_mode; + controller.device->SetGyroscopeZeroDriftMode(drift_mode); + + return ResultSuccess; +} + +Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::GyroscopeZeroDriftMode& drift_mode) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + drift_mode = sixaxis.gyroscope_zero_drift_mode; + + return ResultSuccess; +} + +Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_at_rest) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& controller = GetControllerFromHandle(sixaxis_handle); + is_at_rest = controller.sixaxis_at_rest; + return ResultSuccess; +} + +Result SixAxis::LoadSixAxisSensorCalibrationParameter( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorCalibrationParameter& calibration) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + // TODO: Request this data to the controller. On error return 0xd8ca + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + calibration = sixaxis.calibration; + return ResultSuccess; +} + +Result SixAxis::GetSixAxisSensorIcInformation( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorIcInformation& ic_information) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + // TODO: Request this data to the controller. On error return 0xd8ca + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + ic_information = sixaxis.ic_information; + return ResultSuccess; +} + +Result SixAxis::EnableSixAxisSensorUnalteredPassthrough( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.unaltered_passtrough = is_enabled; + return ResultSuccess; +} + +Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + is_enabled = sixaxis.unaltered_passtrough; + return ResultSuccess; +} + +Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool sixaxis_status) { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& controller = GetControllerFromHandle(sixaxis_handle); + controller.sixaxis_sensor_enabled = sixaxis_status; + return ResultSuccess; +} + +Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_fusion_enabled) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + is_fusion_enabled = sixaxis.is_fusion_enabled; + + return ResultSuccess; +} +Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool is_fusion_enabled) { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.is_fusion_enabled = is_fusion_enabled; + + return ResultSuccess; +} + +Result SixAxis::SetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto param1 = sixaxis_fusion_parameters.parameter1; + if (param1 < 0.0f || param1 > 1.0f) { + return InvalidSixAxisFusionRange; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.fusion = sixaxis_fusion_parameters; + + return ResultSuccess; +} + +Result SixAxis::GetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const { + const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + parameters = sixaxis.fusion; + + return ResultSuccess; +} + +SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.sixaxis_fullkey; + case Core::HID::NpadStyleIndex::Handheld: + return controller.sixaxis_handheld; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.sixaxis_dual_left; + } + return controller.sixaxis_dual_right; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.sixaxis_left; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.sixaxis_right; + default: + return controller.sixaxis_unknown; + } +} + +const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { + const auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.sixaxis_fullkey; + case Core::HID::NpadStyleIndex::Handheld: + return controller.sixaxis_handheld; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.sixaxis_dual_left; + } + return controller.sixaxis_dual_right; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.sixaxis_left; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.sixaxis_right; + default: + return controller.sixaxis_unknown; + } +} + +SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle) { + const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); + return GetControllerFromNpadIdType(npad_id); +} + +const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle) const { + const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); + return GetControllerFromNpadIdType(npad_id); +} + +SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { + if (!IsNpadIdValid(npad_id)) { + LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); + npad_id = Core::HID::NpadIdType::Player1; + } + const auto npad_index = NpadIdTypeToIndex(npad_id); + return controller_data[npad_index]; +} + +const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType( + Core::HID::NpadIdType npad_id) const { + if (!IsNpadIdValid(npad_id)) { + LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); + npad_id = Core::HID::NpadIdType::Player1; + } + const auto npad_index = NpadIdTypeToIndex(npad_id); + return controller_data[npad_index]; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/six_axis.h b/src/core/hle/service/hid/controllers/six_axis.h new file mode 100644 index 000000000..4c4f5dc7b --- /dev/null +++ b/src/core/hle/service/hid/controllers/six_axis.h @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/hid_types.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/ring_lifo.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { +class NPad; + +class SixAxis final : public ControllerBase { +public: + explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_); + ~SixAxis() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + + Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::GyroscopeZeroDriftMode drift_mode); + Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::GyroscopeZeroDriftMode& drift_mode) const; + Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_at_rest) const; + Result EnableSixAxisSensorUnalteredPassthrough( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); + Result IsSixAxisSensorUnalteredPassthroughEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; + Result LoadSixAxisSensorCalibrationParameter( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorCalibrationParameter& calibration) const; + Result GetSixAxisSensorIcInformation( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorIcInformation& ic_information) const; + Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool sixaxis_status); + Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_fusion_enabled) const; + Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool is_fusion_enabled); + Result SetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); + Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const; + +private: + static constexpr std::size_t NPAD_COUNT = 10; + + struct SixaxisParameters { + bool is_fusion_enabled{true}; + bool unaltered_passtrough{false}; + Core::HID::SixAxisSensorFusionParameters fusion{}; + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + Core::HID::SixAxisSensorIcInformation ic_information{}; + Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ + Core::HID::GyroscopeZeroDriftMode::Standard}; + }; + + struct NpadControllerData { + Core::HID::EmulatedController* device = nullptr; + + // Motion parameters + bool sixaxis_at_rest{true}; + bool sixaxis_sensor_enabled{true}; + SixaxisParameters sixaxis_fullkey{}; + SixaxisParameters sixaxis_handheld{}; + SixaxisParameters sixaxis_dual_left{}; + SixaxisParameters sixaxis_dual_right{}; + SixaxisParameters sixaxis_left{}; + SixaxisParameters sixaxis_right{}; + SixaxisParameters sixaxis_unknown{}; + + // Current pad state + Core::HID::SixAxisSensorState sixaxis_fullkey_state{}; + Core::HID::SixAxisSensorState sixaxis_handheld_state{}; + Core::HID::SixAxisSensorState sixaxis_dual_left_state{}; + Core::HID::SixAxisSensorState sixaxis_dual_right_state{}; + Core::HID::SixAxisSensorState sixaxis_left_lifo_state{}; + Core::HID::SixAxisSensorState sixaxis_right_lifo_state{}; + int callback_key{}; + }; + + SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); + const SixaxisParameters& GetSixaxisState( + const Core::HID::SixAxisSensorHandle& device_handle) const; + + NpadControllerData& GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle); + const NpadControllerData& GetControllerFromHandle( + const Core::HID::SixAxisSensorHandle& device_handle) const; + NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); + const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; + + std::shared_ptr<NPad> npad; + std::array<NpadControllerData, NPAD_COUNT> controller_data{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 3ef91df4b..3bcf0ee9f 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -15,8 +15,7 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; -Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, - u8* raw_shared_memory_) +TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, "TouchSharedMemory is bigger than the shared memory"); @@ -25,13 +24,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, console = hid_core.GetEmulatedConsole(); } -Controller_Touchscreen::~Controller_Touchscreen() = default; +TouchScreen::~TouchScreen() = default; -void Controller_Touchscreen::OnInit() {} +void TouchScreen::OnInit() {} -void Controller_Touchscreen::OnRelease() {} +void TouchScreen::OnRelease() {} -void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index dd00921fd..cd342ce91 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -14,10 +14,10 @@ class EmulatedConsole; } // namespace Core::HID namespace Service::HID { -class Controller_Touchscreen final : public ControllerBase { +class TouchScreen final : public ControllerBase { public: - explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); - ~Controller_Touchscreen() override; + explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~TouchScreen() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 62119e2c5..0aaed1fa7 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -10,20 +10,19 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; -Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) - : ControllerBase{hid_core_} { +XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, "XpadSharedMemory is bigger than the shared memory"); shared_memory = std::construct_at( reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); } -Controller_XPad::~Controller_XPad() = default; +XPad::~XPad() = default; -void Controller_XPad::OnInit() {} +void XPad::OnInit() {} -void Controller_XPad::OnRelease() {} +void XPad::OnRelease() {} -void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { shared_memory->basic_xpad_lifo.buffer_count = 0; shared_memory->basic_xpad_lifo.buffer_tail = 0; diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index d01dee5fc..9e63a317a 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -10,10 +10,10 @@ #include "core/hle/service/hid/ring_lifo.h" namespace Service::HID { -class Controller_XPad final : public ControllerBase { +class XPad final : public ControllerBase { public: - explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); - ~Controller_XPad() override; + explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); + ~XPad() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1d4101be9..1b7381d8d 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1,2862 +1,39 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <array> -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hid/hid_core.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_debug_server.h" +#include "core/hle/service/hid/hid_firmware_settings.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/hid_system_server.h" #include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/irs.h" +#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/hid/xcd.h" -#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" -#include "core/memory.h" - -#include "core/hle/service/hid/controllers/console_sixaxis.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/debug_pad.h" -#include "core/hle/service/hid/controllers/gesture.h" -#include "core/hle/service/hid/controllers/keyboard.h" -#include "core/hle/service/hid/controllers/mouse.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/palma.h" -#include "core/hle/service/hid/controllers/stubbed.h" -#include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/hle/service/hid/controllers/xpad.h" namespace Service::HID { -// Updating period for each HID device. -// Period time is obtained by measuring the number of samples in a second on HW using a homebrew -// Correct npad_update_ns is 4ms this is overclocked to lower input lag -constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) -constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) -constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) -constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) - -IAppletResource::IAppletResource(Core::System& system_, - KernelHelpers::ServiceContext& service_context_) - : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} { - static const FunctionInfo functions[] = { - {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, - }; - RegisterHandlers(functions); - u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); - MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); - MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); - MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); - MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); - MakeController<Controller_XPad>(HidController::XPad, shared_memory); - MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); - MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); - MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); - MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); - MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); - MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); - MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); - MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); - MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory); - MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); - - // Homebrew doesn't try to activate some controllers, so we activate them by default - GetController<Controller_NPad>(HidController::NPad).ActivateController(); - GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController(); - - GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); - GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); - GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); - GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200); - GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); - GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00); - - // Register update callbacks - npad_update_event = Core::Timing::CreateEvent( - "HID::UpdatePadCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - UpdateNpad(user_data, ns_late); - return std::nullopt; - }); - default_update_event = Core::Timing::CreateEvent( - "HID::UpdateDefaultCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - UpdateControllers(user_data, ns_late); - return std::nullopt; - }); - mouse_keyboard_update_event = Core::Timing::CreateEvent( - "HID::UpdateMouseKeyboardCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - UpdateMouseKeyboard(user_data, ns_late); - return std::nullopt; - }); - motion_update_event = Core::Timing::CreateEvent( - "HID::UpdateMotionCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - UpdateMotion(user_data, ns_late); - return std::nullopt; - }); - - system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); - system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, - default_update_event); - system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, - mouse_keyboard_update_event); - system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, - motion_update_event); - - system.HIDCore().ReloadInputDevices(); -} - -void IAppletResource::ActivateController(HidController controller) { - controllers[static_cast<size_t>(controller)]->ActivateController(); -} - -void IAppletResource::DeactivateController(HidController controller) { - controllers[static_cast<size_t>(controller)]->DeactivateController(); -} - -IAppletResource::~IAppletResource() { - system.CoreTiming().UnscheduleEvent(npad_update_event, 0); - system.CoreTiming().UnscheduleEvent(default_update_event, 0); - system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); - system.CoreTiming().UnscheduleEvent(motion_update_event, 0); -} - -void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); -} - -void IAppletResource::UpdateControllers(std::uintptr_t user_data, - std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - for (const auto& controller : controllers) { - // Keyboard has it's own update event - if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) { - continue; - } - // Mouse has it's own update event - if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { - continue; - } - // Npad has it's own update event - if (controller == controllers[static_cast<size_t>(HidController::NPad)]) { - continue; - } - controller->OnUpdate(core_timing); - } -} - -void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing); -} - -void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, - std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); - controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); -} - -void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); -} - -class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { -public: - explicit IActiveVibrationDeviceList(Core::System& system_, - std::shared_ptr<IAppletResource> applet_resource_) - : ServiceFramework{system_, "IActiveVibrationDeviceList"}, - applet_resource(applet_resource_) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void InitializeVibrationDevice(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; - - if (applet_resource != nullptr) { - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .InitializeVibrationDevice(vibration_device_handle); - } - - LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", - vibration_device_handle.npad_type, vibration_device_handle.npad_id, - vibration_device_handle.device_index); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - std::shared_ptr<IAppletResource> applet_resource; -}; - -std::shared_ptr<IAppletResource> Hid::GetAppletResource() { - if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system, service_context); - } - - return applet_resource; -} - -Hid::Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_) - : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{ - system_, - service_name} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &Hid::CreateAppletResource, "CreateAppletResource"}, - {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, - {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, - {21, &Hid::ActivateMouse, "ActivateMouse"}, - {26, nullptr, "ActivateDebugMouse"}, - {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, - {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"}, - {40, nullptr, "AcquireXpadIdEventHandle"}, - {41, nullptr, "ReleaseXpadIdEventHandle"}, - {51, &Hid::ActivateXpad, "ActivateXpad"}, - {55, &Hid::GetXpadIDs, "GetXpadIds"}, - {56, nullptr, "ActivateJoyXpad"}, - {58, nullptr, "GetJoyXpadLifoHandle"}, - {59, nullptr, "GetJoyXpadIds"}, - {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, - {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, - {62, nullptr, "GetSixAxisSensorLifoHandle"}, - {63, nullptr, "ActivateJoySixAxisSensor"}, - {64, nullptr, "DeactivateJoySixAxisSensor"}, - {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, - {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, - {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, - {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, - {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, - {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, - {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, - {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"}, - {73, nullptr, "SetAccelerometerParameters"}, - {74, nullptr, "GetAccelerometerParameters"}, - {75, nullptr, "ResetAccelerometerParameters"}, - {76, nullptr, "SetAccelerometerPlayMode"}, - {77, nullptr, "GetAccelerometerPlayMode"}, - {78, nullptr, "ResetAccelerometerPlayMode"}, - {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, - {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, - {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, - {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, - {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, - {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, - {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, - {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, - {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, - {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, - {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, - {91, &Hid::ActivateGesture, "ActivateGesture"}, - {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, - {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, - {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, - {103, &Hid::ActivateNpad, "ActivateNpad"}, - {104, &Hid::DeactivateNpad, "DeactivateNpad"}, - {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, - {107, &Hid::DisconnectNpad, "DisconnectNpad"}, - {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, - {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, - {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, - {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, - {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, - {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"}, - {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, - {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, - {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"}, - {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"}, - {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, - {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, - {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, - {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, - {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, - {133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"}, - {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, - {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, - {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, - {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, - {201, &Hid::SendVibrationValue, "SendVibrationValue"}, - {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, - {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, - {204, &Hid::PermitVibration, "PermitVibration"}, - {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, - {206, &Hid::SendVibrationValues, "SendVibrationValues"}, - {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, - {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, - {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, - {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, - {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, - {212, nullptr, "SendVibrationValueInBool"}, - {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, - {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, - {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, - {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, - {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, - {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, - {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, - {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, - {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, - {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, - {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, - {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, - {401, nullptr, "EnableUsbFullKeyController"}, - {402, nullptr, "IsUsbFullKeyControllerConnected"}, - {403, nullptr, "HasBattery"}, - {404, nullptr, "HasLeftRightBattery"}, - {405, nullptr, "GetNpadInterfaceType"}, - {406, nullptr, "GetNpadLeftRightInterfaceType"}, - {407, nullptr, "GetNpadOfHighestBatteryLevel"}, - {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, - {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, - {501, &Hid::InitializePalma, "InitializePalma"}, - {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, - {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, - {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, - {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, - {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, - {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, - {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, - {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, - {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, - {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, - {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, - {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, - {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, - {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, - {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, - {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, - {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, - {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, - {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, - {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, - {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, - {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, - {524, &Hid::PairPalma, "PairPalma"}, - {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, - {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, - {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, - {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, - {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, - {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, - {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, - {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, - {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, - {2000, nullptr, "ActivateDigitizer"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -Hid::~Hid() = default; - -void Hid::CreateAppletResource(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system, service_context); - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAppletResource>(applet_resource); -} - -void Hid::ActivateDebugPad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::DebugPad); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateTouchScreen(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::Touchscreen); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateMouse(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::Mouse); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateKeyboard(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::Keyboard); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto flags{rp.Pop<u32>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateXpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 basic_xpad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - applet_resource->ActivateController(HidController::XPad); - - LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", - parameters.basic_xpad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetXpadIDs(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void Hid::ActivateSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 basic_xpad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - // This function does nothing on 10.0.0+ - - LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", - parameters.basic_xpad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::DeactivateSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 basic_xpad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - // This function does nothing on 10.0.0+ - - LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", - parameters.basic_xpad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StartSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::StopSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - bool is_enabled{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(is_enabled); -} - -void Hid::EnableSixAxisSensorFusion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool enable_sixaxis_sensor_fusion; - INSERT_PADDING_BYTES_NOINIT(3); - Core::HID::SixAxisSensorHandle sixaxis_handle; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, - parameters.enable_sixaxis_sensor_fusion); - - LOG_DEBUG(Service_HID, - "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " - "device_index={}, applet_resource_user_id={}", - parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, - parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " - "parameter2={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, - parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; - const auto& controller = - GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.PushRaw(fusion_parameters); -} - -void Hid::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - // Since these parameters are unknown just use what HW outputs - const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ - .parameter1 = 0.03f, - .parameter2 = 0.4f, - }; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result1 = - controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); - const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - if (result1.IsError()) { - rb.Push(result1); - return; - } - rb.Push(result2); -} - -void Hid::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()}; - const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " - "applet_resource_user_id={}", - sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, - drift_mode, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.PushEnum(drift_mode); -} - -void Hid::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsSixAxisSensorAtRest(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - bool is_at_rest{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_at_rest); -} - -void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - bool is_firmware_available{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, - is_firmware_available); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_firmware_available); -} - -void Hid::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool enabled; - Core::HID::SixAxisSensorHandle sixaxis_handle; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( - parameters.sixaxis_handle, parameters.enabled); - - LOG_DEBUG(Service_HID, - "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " - "applet_resource_user_id={}", - parameters.enabled, parameters.sixaxis_handle.npad_type, - parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - bool is_unaltered_sisxaxis_enabled{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( - parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); - - LOG_DEBUG( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(is_unaltered_sisxaxis_enabled); -} - -void Hid::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::SixAxisSensorCalibrationParameter calibration{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - if (result.IsSuccess()) { - ctx.WriteBuffer(calibration); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::SixAxisSensorIcInformation ic_information{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - if (result.IsSuccess()) { - ctx.WriteBuffer(ic_information); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ActivateGesture(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 unknown; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - applet_resource->ActivateController(HidController::Gesture); - - LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", - parameters.unknown, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetSupportedNpadStyleSet(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadStyleSet supported_styleset; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSupportedStyleSet({parameters.supported_styleset}); - - LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", - parameters.supported_styleset, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetSupportedNpadStyleSet(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetSupportedStyleSet() - .raw); -} - -void Hid::SetSupportedNpadIdType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSupportedNpadIdTypes(ctx.ReadBuffer()); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ActivateNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::NPad); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::DeactivateNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->DeactivateController(HidController::NPad); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - u64 unknown; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", - parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); - - // Games expect this event to be signaled after calling this function - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SignalStyleSetChangedEvent(parameters.npad_id); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetStyleSetChangedEvent(parameters.npad_id)); -} - -void Hid::DisconnectNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - controller.DisconnectNpad(parameters.npad_id); - - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPlayerLedPattern(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; - - Core::HID::LedPattern pattern{0, 0, 0, 0}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.GetLedPattern(npad_id, pattern); - - LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(pattern.raw); -} - -void Hid::ActivateNpadWithRevision(HLERequestContext& ctx) { - // Should have no effect with how our npad sets up the data - IPC::RequestParser rp{ctx}; - struct Parameters { - s32 revision; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - applet_resource->ActivateController(HidController::NPad); - - LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadJoyHoldType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", - applet_resource_user_id, hold_type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetNpadJoyHoldType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType()); -} - -void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - controller.SetNpadMode(new_npad_id, parameters.npad_id, - Controller_NPad::NpadJoyDeviceType::Left, - Controller_NPad::NpadJoyAssignmentMode::Single); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Controller_NPad::NpadJoyDeviceType npad_joy_device_type; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - parameters.npad_id, parameters.applet_resource_user_id, - parameters.npad_joy_device_type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, - Controller_NPad::NpadJoyAssignmentMode::Dual); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; - const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); - - LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", - npad_id_1, npad_id_2, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::StartLrAssignmentMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode(); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StopLrAssignmentMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode(); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadHandheldActivationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadHandheldActivationMode(activation_mode); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", - applet_resource_user_id, activation_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetNpadHandheldActivationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetNpadHandheldActivationMode()); -} - -void Hid::SwapNpadAssignment(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; - const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); - - LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", - npad_id_1, npad_id_2, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - bool is_enabled = false; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); - - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", - parameters.npad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(is_enabled); -} - -void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool is_enabled; - INSERT_PADDING_BYTES_NOINIT(3); - Core::HID::NpadIdType npad_id; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( - parameters.is_enabled, parameters.npad_id); - - LOG_DEBUG(Service_HID, - "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", - parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Controller_NPad::NpadJoyDeviceType npad_joy_device_type; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto is_reassigned = - controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - parameters.npad_id, parameters.applet_resource_user_id, - parameters.npad_joy_device_type); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(is_reassigned); - rb.PushEnum(new_npad_id); -} - -void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool analog_stick_use_center_clamp; - INSERT_PADDING_BYTES_NOINIT(7); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - GetAppletResource() - ->GetController<Controller_NPad>(HidController::NPad) - .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); - - LOG_WARNING(Service_HID, - "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", - parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadStyleSet npad_styleset; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::HID::NpadButton button; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", - parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetVibrationDeviceInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; - const auto& controller = - GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - - Core::HID::VibrationDeviceInfo vibration_device_info; - bool check_device_index = false; - - switch (vibration_device_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Handheld: - case Core::HID::NpadStyleIndex::JoyconDual: - case Core::HID::NpadStyleIndex::JoyconLeft: - case Core::HID::NpadStyleIndex::JoyconRight: - vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; - check_device_index = true; - break; - case Core::HID::NpadStyleIndex::GameCube: - vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; - break; - case Core::HID::NpadStyleIndex::N64: - vibration_device_info.type = Core::HID::VibrationDeviceType::N64; - break; - default: - vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; - break; - } - - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - if (check_device_index) { - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - ASSERT_MSG(false, "DeviceIndex should never be None!"); - break; - } - } - - LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", - vibration_device_info.type, vibration_device_info.position); - - const auto result = controller.IsDeviceHandleValid(vibration_device_handle); - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(vibration_device_info); -} - -void Hid::SendVibrationValue(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - Core::HID::VibrationValue vibration_value; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetActualVibrationValue(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetLastVibration(parameters.vibration_device_handle)); -} - -void Hid::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IActiveVibrationDeviceList>(system, applet_resource); -} - -void Hid::PermitVibration(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto can_vibrate{rp.Pop<bool>()}; - - // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value - // by converting it to a bool - Settings::values.vibration_enabled.SetValue(can_vibrate); - - LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsVibrationPermitted(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - // nnSDK checks if a float is greater than zero. We return the bool we stored earlier - const auto is_enabled = Settings::values.vibration_enabled.GetValue(); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_enabled); -} - -void Hid::SendVibrationValues(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - const auto handle_data = ctx.ReadBuffer(0); - const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0); - const auto vibration_data = ctx.ReadBuffer(1); - const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1); - - auto vibration_device_handles = - std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()), - handle_count); - auto vibration_values = std::span( - reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .VibrateControllers(vibration_device_handles, vibration_values); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SendVibrationGcErmCommand(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::HID::VibrationGcErmCommand gc_erm_command; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - /** - * Note: This uses yuzu-specific behavior such that the StopHard command produces - * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, - * in order to differentiate between Stop and StopHard commands. - * This is done to reuse the controller vibration functions made for regular controllers. - */ - const auto vibration_value = [parameters] { - switch (parameters.gc_erm_command) { - case Core::HID::VibrationGcErmCommand::Stop: - return Core::HID::VibrationValue{ - .low_amplitude = 0.0f, - .low_frequency = 160.0f, - .high_amplitude = 0.0f, - .high_frequency = 320.0f, - }; - case Core::HID::VibrationGcErmCommand::Start: - return Core::HID::VibrationValue{ - .low_amplitude = 1.0f, - .low_frequency = 160.0f, - .high_amplitude = 1.0f, - .high_frequency = 320.0f, - }; - case Core::HID::VibrationGcErmCommand::StopHard: - return Core::HID::VibrationValue{ - .low_amplitude = 0.0f, - .low_frequency = 0.0f, - .high_amplitude = 0.0f, - .high_frequency = 0.0f, - }; - default: - return Core::HID::DEFAULT_VIBRATION_VALUE; - } - }(); - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .VibrateController(parameters.vibration_device_handle, vibration_value); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " - "gc_erm_command={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, - parameters.gc_erm_command); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - - const auto parameters{rp.PopRaw<Parameters>()}; - - const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetLastVibration(parameters.vibration_device_handle); - - const auto gc_erm_command = [last_vibration] { - if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { - return Core::HID::VibrationGcErmCommand::Start; - } - - /** - * Note: This uses yuzu-specific behavior such that the StopHard command produces - * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function - * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. - * This is done to reuse the controller vibration functions made for regular controllers. - */ - if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { - return Core::HID::VibrationGcErmCommand::StopHard; - } - - return Core::HID::VibrationGcErmCommand::Stop; - }(); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(gc_erm_command); -} - -void Hid::BeginPermitVibrationSession(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetPermitVibrationSession(true); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::EndPermitVibrationSession(HLERequestContext& ctx) { - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetPermitVibrationSession(false); - - LOG_DEBUG(Service_HID, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsVibrationDeviceMounted(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .IsVibrationDeviceMounted(parameters.vibration_device_handle)); -} - -void Hid::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::ConsoleSixAxisSensor); - - LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StartConsoleSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", - parameters.console_sixaxis_handle.unknown_1, - parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StopConsoleSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", - parameters.console_sixaxis_handle.unknown_1, - parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->ActivateController(HidController::ConsoleSixAxisSensor); - - LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StartSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StopSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto t_mem_1_size{rp.Pop<u64>()}; - const auto t_mem_2_size{rp.Pop<u64>()}; - const auto t_mem_1_handle{ctx.GetCopyHandle(0)}; - const auto t_mem_2_handle{ctx.GetCopyHandle(1)}; - - ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); - ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); - - auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( - t_mem_1_handle); - - if (t_mem_1.IsNull()) { - LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( - t_mem_2_handle); - - if (t_mem_2.IsNull()) { - LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size"); - ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); - - // Activate console six axis controller - applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) - .ActivateController(); - - applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) - .SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); - - LOG_WARNING(Service_HID, - "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " - "applet_resource_user_id={}", - t_mem_1_handle, t_mem_2_handle, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) - .ResetTimestamp(); - - LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); -} - -void Hid::GetPalmaConnectionHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", - parameters.npad_id, parameters.applet_resource_user_id); - - Controller_Palma::PalmaConnectionHandle handle; - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.PushRaw(handle); -} - -void Hid::InitializePalma(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.InitializePalma(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); -} - -void Hid::GetPalmaOperationInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - Controller_Palma::PalmaOperationType operation_type; - Controller_Palma::PalmaOperationData data; - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); - - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - ctx.WriteBuffer(data); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(static_cast<u64>(operation_type)); -} - -void Hid::PlayPalmaActivity(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - const auto palma_activity{rp.Pop<u64>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", - connection_handle.npad_id, palma_activity); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::SetPalmaFrModeType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", - connection_handle.npad_id, fr_mode); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ReadPalmaStep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.ReadPalmaStep(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::EnablePalmaStep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool is_enabled; - INSERT_PADDING_WORDS_NOINIT(1); - Controller_Palma::PalmaConnectionHandle connection_handle; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", - parameters.connection_handle.npad_id, parameters.is_enabled); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = - controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ResetPalmaStep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); - const auto result = controller.ResetPalmaStep(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ReadPalmaApplicationSection(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaApplicationSection(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ReadPalmaUniqueCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .ReadPalmaUniqueCode(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .SetPalmaUniqueCodeInvalid(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaActivityEntry(HLERequestContext& ctx) { - LOG_CRITICAL(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - const auto unknown{rp.Pop<u64>()}; - - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", - connection_handle.npad_id, unknown); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .WritePalmaRgbLedPatternEntry(connection_handle, unknown); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaWaveEntry(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; - const auto unknown{rp.Pop<u64>()}; - const auto t_mem_size{rp.Pop<u64>()}; - const auto t_mem_handle{ctx.GetCopyHandle(0)}; - const auto size{rp.Pop<u64>()}; - - ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); - - auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( - t_mem_handle); - - if (t_mem.IsNull()) { - LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); - - LOG_WARNING(Service_HID, - "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " - "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", - connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - s32 database_id_version; - INSERT_PADDING_WORDS_NOINIT(1); - Controller_Palma::PalmaConnectionHandle connection_handle; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", - parameters.connection_handle.npad_id, parameters.database_id_version); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, - parameters.database_id_version); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .GetPalmaDataBaseIdentificationVersion(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SuspendPalmaFeature(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPalmaOperationResult(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma) - .GetPalmaOperationResult(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ReadPalmaPlayLog(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ResetPalmaPlayLog(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetIsPalmaAllConnectable(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool is_palma_all_connectable; - INSERT_PADDING_BYTES_NOINIT(7); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", - parameters.is_palma_all_connectable, parameters.applet_resource_user_id); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::PairPalma(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .PairPalma(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetPalmaBoostMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto palma_boost_mode{rp.Pop<bool>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); - - applet_resource->GetController<Controller_Palma>(HidController::Palma) - .SetPalmaBoostMode(palma_boost_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::CancelWritePalmaWaveEntry(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::EnablePalmaBoostMode(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPalmaBluetoothAddress(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetDisallowedPalmaConnection(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadCommunicationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()}; - - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadCommunicationMode(communication_mode); - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", - applet_resource_user_id, communication_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetNpadCommunicationMode()); -} - -void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", - touchscreen_mode.mode, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - s32 unknown; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", - parameters.unknown, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); -} - -class HidDbg final : public ServiceFramework<HidDbg> { -public: - explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "DeactivateDebugPad"}, - {1, nullptr, "SetDebugPadAutoPilotState"}, - {2, nullptr, "UnsetDebugPadAutoPilotState"}, - {10, nullptr, "DeactivateTouchScreen"}, - {11, nullptr, "SetTouchScreenAutoPilotState"}, - {12, nullptr, "UnsetTouchScreenAutoPilotState"}, - {13, nullptr, "GetTouchScreenConfiguration"}, - {14, nullptr, "ProcessTouchScreenAutoTune"}, - {15, nullptr, "ForceStopTouchScreenManagement"}, - {16, nullptr, "ForceRestartTouchScreenManagement"}, - {17, nullptr, "IsTouchScreenManaged"}, - {20, nullptr, "DeactivateMouse"}, - {21, nullptr, "SetMouseAutoPilotState"}, - {22, nullptr, "UnsetMouseAutoPilotState"}, - {25, nullptr, "SetDebugMouseAutoPilotState"}, - {26, nullptr, "UnsetDebugMouseAutoPilotState"}, - {30, nullptr, "DeactivateKeyboard"}, - {31, nullptr, "SetKeyboardAutoPilotState"}, - {32, nullptr, "UnsetKeyboardAutoPilotState"}, - {50, nullptr, "DeactivateXpad"}, - {51, nullptr, "SetXpadAutoPilotState"}, - {52, nullptr, "UnsetXpadAutoPilotState"}, - {53, nullptr, "DeactivateJoyXpad"}, - {60, nullptr, "ClearNpadSystemCommonPolicy"}, - {61, nullptr, "DeactivateNpad"}, - {62, nullptr, "ForceDisconnectNpad"}, - {91, nullptr, "DeactivateGesture"}, - {110, nullptr, "DeactivateHomeButton"}, - {111, nullptr, "SetHomeButtonAutoPilotState"}, - {112, nullptr, "UnsetHomeButtonAutoPilotState"}, - {120, nullptr, "DeactivateSleepButton"}, - {121, nullptr, "SetSleepButtonAutoPilotState"}, - {122, nullptr, "UnsetSleepButtonAutoPilotState"}, - {123, nullptr, "DeactivateInputDetector"}, - {130, nullptr, "DeactivateCaptureButton"}, - {131, nullptr, "SetCaptureButtonAutoPilotState"}, - {132, nullptr, "UnsetCaptureButtonAutoPilotState"}, - {133, nullptr, "SetShiftAccelerometerCalibrationValue"}, - {134, nullptr, "GetShiftAccelerometerCalibrationValue"}, - {135, nullptr, "SetShiftGyroscopeCalibrationValue"}, - {136, nullptr, "GetShiftGyroscopeCalibrationValue"}, - {140, nullptr, "DeactivateConsoleSixAxisSensor"}, - {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, - {142, nullptr, "DeactivateSevenSixAxisSensor"}, - {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, - {144, nullptr, "GetAccelerometerFsr"}, - {145, nullptr, "SetAccelerometerFsr"}, - {146, nullptr, "GetAccelerometerOdr"}, - {147, nullptr, "SetAccelerometerOdr"}, - {148, nullptr, "GetGyroscopeFsr"}, - {149, nullptr, "SetGyroscopeFsr"}, - {150, nullptr, "GetGyroscopeOdr"}, - {151, nullptr, "SetGyroscopeOdr"}, - {152, nullptr, "GetWhoAmI"}, - {201, nullptr, "ActivateFirmwareUpdate"}, - {202, nullptr, "DeactivateFirmwareUpdate"}, - {203, nullptr, "StartFirmwareUpdate"}, - {204, nullptr, "GetFirmwareUpdateStage"}, - {205, nullptr, "GetFirmwareVersion"}, - {206, nullptr, "GetDestinationFirmwareVersion"}, - {207, nullptr, "DiscardFirmwareInfoCacheForRevert"}, - {208, nullptr, "StartFirmwareUpdateForRevert"}, - {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, - {210, nullptr, "IsFirmwareUpdatingDevice"}, - {211, nullptr, "StartFirmwareUpdateIndividual"}, - {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, - {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, - {221, nullptr, "UpdateControllerColor"}, - {222, nullptr, "ConnectUsbPadsAsync"}, - {223, nullptr, "DisconnectUsbPadsAsync"}, - {224, nullptr, "UpdateDesignInfo"}, - {225, nullptr, "GetUniquePadDriverState"}, - {226, nullptr, "GetSixAxisSensorDriverStates"}, - {227, nullptr, "GetRxPacketHistory"}, - {228, nullptr, "AcquireOperationEventHandle"}, - {229, nullptr, "ReadSerialFlash"}, - {230, nullptr, "WriteSerialFlash"}, - {231, nullptr, "GetOperationResult"}, - {232, nullptr, "EnableShipmentMode"}, - {233, nullptr, "ClearPairingInfo"}, - {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, - {235, nullptr, "EnableAnalogStickPower"}, - {236, nullptr, "RequestKuinaUartClockCal"}, - {237, nullptr, "GetKuinaUartClockCal"}, - {238, nullptr, "SetKuinaUartClockTrim"}, - {239, nullptr, "KuinaLoopbackTest"}, - {240, nullptr, "RequestBatteryVoltage"}, - {241, nullptr, "GetBatteryVoltage"}, - {242, nullptr, "GetUniquePadPowerInfo"}, - {243, nullptr, "RebootUniquePad"}, - {244, nullptr, "RequestKuinaFirmwareVersion"}, - {245, nullptr, "GetKuinaFirmwareVersion"}, - {246, nullptr, "GetVidPid"}, - {247, nullptr, "GetAnalogStickCalibrationValue"}, - {248, nullptr, "GetUniquePadIdsFull"}, - {249, nullptr, "ConnectUniquePad"}, - {250, nullptr, "IsVirtual"}, - {251, nullptr, "GetAnalogStickModuleParam"}, - {301, nullptr, "GetAbstractedPadHandles"}, - {302, nullptr, "GetAbstractedPadState"}, - {303, nullptr, "GetAbstractedPadsState"}, - {321, nullptr, "SetAutoPilotVirtualPadState"}, - {322, nullptr, "UnsetAutoPilotVirtualPadState"}, - {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, - {324, nullptr, "AttachHdlsWorkBuffer"}, - {325, nullptr, "ReleaseHdlsWorkBuffer"}, - {326, nullptr, "DumpHdlsNpadAssignmentState"}, - {327, nullptr, "DumpHdlsStates"}, - {328, nullptr, "ApplyHdlsNpadAssignmentState"}, - {329, nullptr, "ApplyHdlsStateList"}, - {330, nullptr, "AttachHdlsVirtualDevice"}, - {331, nullptr, "DetachHdlsVirtualDevice"}, - {332, nullptr, "SetHdlsState"}, - {350, nullptr, "AddRegisteredDevice"}, - {400, nullptr, "DisableExternalMcuOnNxDevice"}, - {401, nullptr, "DisableRailDeviceFiltering"}, - {402, nullptr, "EnableWiredPairing"}, - {403, nullptr, "EnableShipmentModeAutoClear"}, - {404, nullptr, "SetRailEnabled"}, - {500, nullptr, "SetFactoryInt"}, - {501, nullptr, "IsFactoryBootEnabled"}, - {550, nullptr, "SetAnalogStickModelDataTemporarily"}, - {551, nullptr, "GetAnalogStickModelData"}, - {552, nullptr, "ResetAnalogStickModelData"}, - {600, nullptr, "ConvertPadState"}, - {650, nullptr, "AddButtonPlayData"}, - {651, nullptr, "StartButtonPlayData"}, - {652, nullptr, "StopButtonPlayData"}, - {2000, nullptr, "DeactivateDigitizer"}, - {2001, nullptr, "SetDigitizerAutoPilotState"}, - {2002, nullptr, "UnsetDigitizerAutoPilotState"}, - {2002, nullptr, "ReloadFirmwareDebugSettings"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class HidSys final : public ServiceFramework<HidSys> { -public: - explicit HidSys(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_) - : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"}, - applet_resource{applet_resource_} { - // clang-format off - static const FunctionInfo functions[] = { - {31, nullptr, "SendKeyboardLockKeyEvent"}, - {101, nullptr, "AcquireHomeButtonEventHandle"}, - {111, nullptr, "ActivateHomeButton"}, - {121, nullptr, "AcquireSleepButtonEventHandle"}, - {131, nullptr, "ActivateSleepButton"}, - {141, nullptr, "AcquireCaptureButtonEventHandle"}, - {151, nullptr, "ActivateCaptureButton"}, - {161, nullptr, "GetPlatformConfig"}, - {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, - {211, nullptr, "GetNpadsWithNfc"}, - {212, nullptr, "AcquireNfcActivateEventHandle"}, - {213, nullptr, "ActivateNfc"}, - {214, nullptr, "GetXcdHandleForNpadWithNfc"}, - {215, nullptr, "IsNfcActivated"}, - {230, nullptr, "AcquireIrSensorEventHandle"}, - {231, nullptr, "ActivateIrSensor"}, - {232, nullptr, "GetIrSensorState"}, - {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, - {301, nullptr, "ActivateNpadSystem"}, - {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, - {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, - {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, - {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"}, - {307, nullptr, "GetNpadSystemExtStyle"}, - {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, - {309, nullptr, "GetNpadFullKeyGripColor"}, - {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, - {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, - {312, nullptr, "SetSupportedNpadStyleSetAll"}, - {313, nullptr, "GetNpadCaptureButtonAssignment"}, - {314, nullptr, "GetAppletFooterUiType"}, - {315, nullptr, "GetAppletDetailedUiType"}, - {316, nullptr, "GetNpadInterfaceType"}, - {317, nullptr, "GetNpadLeftRightInterfaceType"}, - {318, nullptr, "HasBattery"}, - {319, nullptr, "HasLeftRightBattery"}, - {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, - {322, nullptr, "GetIrSensorState"}, - {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, - {324, nullptr, "GetUniquePadButtonSet"}, - {325, nullptr, "GetUniquePadColor"}, - {326, nullptr, "GetUniquePadAppletDetailedUiType"}, - {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, - {328, nullptr, "AttachAbstractedPadToNpad"}, - {329, nullptr, "DetachAbstractedPadAll"}, - {330, nullptr, "CheckAbstractedPadConnection"}, - {500, nullptr, "SetAppletResourceUserId"}, - {501, nullptr, "RegisterAppletResourceUserId"}, - {502, nullptr, "UnregisterAppletResourceUserId"}, - {503, nullptr, "EnableAppletToGetInput"}, - {504, nullptr, "SetAruidValidForVibration"}, - {505, nullptr, "EnableAppletToGetSixAxisSensor"}, - {506, nullptr, "EnableAppletToGetPadInput"}, - {507, nullptr, "EnableAppletToGetTouchScreen"}, - {510, nullptr, "SetVibrationMasterVolume"}, - {511, nullptr, "GetVibrationMasterVolume"}, - {512, nullptr, "BeginPermitVibrationSession"}, - {513, nullptr, "EndPermitVibrationSession"}, - {514, nullptr, "Unknown514"}, - {520, nullptr, "EnableHandheldHids"}, - {521, nullptr, "DisableHandheldHids"}, - {522, nullptr, "SetJoyConRailEnabled"}, - {523, nullptr, "IsJoyConRailEnabled"}, - {524, nullptr, "IsHandheldHidsEnabled"}, - {525, nullptr, "IsJoyConAttachedOnAllRail"}, - {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, - {541, nullptr, "GetPlayReportControllerUsages"}, - {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, - {543, nullptr, "GetRegisteredDevicesOld"}, - {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"}, - {545, nullptr, "SendConnectionTrigger"}, - {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, - {547, nullptr, "GetAllowedBluetoothLinksCount"}, - {548, nullptr, "GetRegisteredDevices"}, - {549, nullptr, "GetConnectableRegisteredDevices"}, - {700, nullptr, "ActivateUniquePad"}, - {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, - {703, nullptr, "GetUniquePadIds"}, - {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, - {800, nullptr, "ListSixAxisSensorHandles"}, - {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, - {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, - {803, nullptr, "StartSixAxisSensorUserCalibration"}, - {804, nullptr, "CancelSixAxisSensorUserCalibration"}, - {805, nullptr, "GetUniquePadBluetoothAddress"}, - {806, nullptr, "DisconnectUniquePad"}, - {807, nullptr, "GetUniquePadType"}, - {808, nullptr, "GetUniquePadInterface"}, - {809, nullptr, "GetUniquePadSerialNumber"}, - {810, nullptr, "GetUniquePadControllerNumber"}, - {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, - {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, - {821, nullptr, "StartAnalogStickManualCalibration"}, - {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, - {823, nullptr, "CancelAnalogStickManualCalibration"}, - {824, nullptr, "ResetAnalogStickManualCalibration"}, - {825, nullptr, "GetAnalogStickState"}, - {826, nullptr, "GetAnalogStickManualCalibrationStage"}, - {827, nullptr, "IsAnalogStickButtonPressed"}, - {828, nullptr, "IsAnalogStickInReleasePosition"}, - {829, nullptr, "IsAnalogStickInCircumference"}, - {830, nullptr, "SetNotificationLedPattern"}, - {831, nullptr, "SetNotificationLedPatternWithTimeout"}, - {832, nullptr, "PrepareHidsForNotificationWake"}, - {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, - {851, nullptr, "EnableUsbFullKeyController"}, - {852, nullptr, "IsUsbConnected"}, - {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, - {900, nullptr, "ActivateInputDetector"}, - {901, nullptr, "NotifyInputDetector"}, - {1000, nullptr, "InitializeFirmwareUpdate"}, - {1001, nullptr, "GetFirmwareVersion"}, - {1002, nullptr, "GetAvailableFirmwareVersion"}, - {1003, nullptr, "IsFirmwareUpdateAvailable"}, - {1004, nullptr, "CheckFirmwareUpdateRequired"}, - {1005, nullptr, "StartFirmwareUpdate"}, - {1006, nullptr, "AbortFirmwareUpdate"}, - {1007, nullptr, "GetFirmwareUpdateState"}, - {1008, nullptr, "ActivateAudioControl"}, - {1009, nullptr, "AcquireAudioControlEventHandle"}, - {1010, nullptr, "GetAudioControlStates"}, - {1011, nullptr, "DeactivateAudioControl"}, - {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, - {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, - {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, - {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, - {1100, nullptr, "GetHidbusSystemServiceObject"}, - {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, - {1130, nullptr, "InitializeUsbFirmwareUpdate"}, - {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, - {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, - {1133, nullptr, "StartUsbFirmwareUpdate"}, - {1134, nullptr, "GetUsbFirmwareUpdateState"}, - {1150, nullptr, "SetTouchScreenMagnification"}, - {1151, nullptr, "GetTouchScreenFirmwareVersion"}, - {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, - {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, - {1154, nullptr, "IsFirmwareAvailableForNotification"}, - {1155, nullptr, "SetForceHandheldStyleVibration"}, - {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, - {1157, nullptr, "CancelConnectionTrigger"}, - {1200, nullptr, "IsButtonConfigSupported"}, - {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, - {1202, nullptr, "DeleteButtonConfig"}, - {1203, nullptr, "DeleteButtonConfigEmbedded"}, - {1204, nullptr, "SetButtonConfigEnabled"}, - {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, - {1206, nullptr, "IsButtonConfigEnabled"}, - {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, - {1208, nullptr, "SetButtonConfigEmbedded"}, - {1209, nullptr, "SetButtonConfigFull"}, - {1210, nullptr, "SetButtonConfigLeft"}, - {1211, nullptr, "SetButtonConfigRight"}, - {1212, nullptr, "GetButtonConfigEmbedded"}, - {1213, nullptr, "GetButtonConfigFull"}, - {1214, nullptr, "GetButtonConfigLeft"}, - {1215, nullptr, "GetButtonConfigRight"}, - {1250, nullptr, "IsCustomButtonConfigSupported"}, - {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, - {1252, nullptr, "IsDefaultButtonConfigFull"}, - {1253, nullptr, "IsDefaultButtonConfigLeft"}, - {1254, nullptr, "IsDefaultButtonConfigRight"}, - {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, - {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, - {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, - {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, - {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, - {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, - {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, - {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, - {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, - {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, - {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, - {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, - {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, - {1268, nullptr, "DeleteButtonConfigStorageFull"}, - {1269, nullptr, "DeleteButtonConfigStorageLeft"}, - {1270, nullptr, "DeleteButtonConfigStorageRight"}, - {1271, nullptr, "IsUsingCustomButtonConfig"}, - {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, - {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, - {1274, nullptr, "SetDefaultButtonConfig"}, - {1275, nullptr, "SetAllDefaultButtonConfig"}, - {1276, nullptr, "SetHidButtonConfigEmbedded"}, - {1277, nullptr, "SetHidButtonConfigFull"}, - {1278, nullptr, "SetHidButtonConfigLeft"}, - {1279, nullptr, "SetHidButtonConfigRight"}, - {1280, nullptr, "GetHidButtonConfigEmbedded"}, - {1281, nullptr, "GetHidButtonConfigFull"}, - {1282, nullptr, "GetHidButtonConfigLeft"}, - {1283, nullptr, "GetHidButtonConfigRight"}, - {1284, nullptr, "GetButtonConfigStorageEmbedded"}, - {1285, nullptr, "GetButtonConfigStorageFull"}, - {1286, nullptr, "GetButtonConfigStorageLeft"}, - {1287, nullptr, "GetButtonConfigStorageRight"}, - {1288, nullptr, "SetButtonConfigStorageEmbedded"}, - {1289, nullptr, "SetButtonConfigStorageFull"}, - {1290, nullptr, "DeleteButtonConfigStorageRight"}, - {1291, nullptr, "DeleteButtonConfigStorageRight"}, - }; - // clang-format on - - RegisterHandlers(functions); - - joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); - } - - ~HidSys() { - service_context.CloseEvent(joy_detach_event); - }; - -private: - void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "called"); - - GetAppletResource() - ->GetController<Controller_NPad>(HidController::NPad) - .ApplyNpadSystemCommonPolicy(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetLastActiveNpad(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(system.HIDCore().GetLastActiveController()); - } - - void GetUniquePadsFromNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); - - const std::vector<Core::HID::UniquePadId> unique_pads{}; - - ctx.WriteBuffer(unique_pads); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(unique_pads.size())); - } - - void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(joy_detach_event->GetReadableEvent()); - } - - void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { - const bool is_enabled = false; - - LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_enabled); - } - - void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - Core::HID::TouchScreenConfigurationForNx touchscreen_config{ - .mode = Core::HID::TouchScreenModeForNx::Finger, - }; - - if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && - touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { - touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(touchscreen_config); - } - - std::shared_ptr<IAppletResource> GetAppletResource() { - if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system, service_context); - } - - return applet_resource; - } - - Kernel::KEvent* joy_detach_event; - KernelHelpers::ServiceContext service_context; - std::shared_ptr<IAppletResource> applet_resource; -}; - void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - std::shared_ptr<IAppletResource> applet_resource; + std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system); + std::shared_ptr<HidFirmwareSettings> firmware_settings = + std::make_shared<HidFirmwareSettings>(); + + server_manager->RegisterNamedService( + "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings)); + server_manager->RegisterNamedService( + "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager)); + server_manager->RegisterNamedService( + "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager)); - server_manager->RegisterNamedService("hid", std::make_shared<Hid>(system, applet_resource)); server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); - server_manager->RegisterNamedService("hid:dbg", std::make_shared<HidDbg>(system)); - server_manager->RegisterNamedService("hid:sys", - std::make_shared<HidSys>(system, applet_resource)); - server_manager->RegisterNamedService("irs", std::make_shared<Service::IRS::IRS>(system)); - server_manager->RegisterNamedService("irs:sys", - std::make_shared<Service::IRS::IRS_SYS>(system)); + server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system)); + server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system)); server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system)); + system.RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 0ca43de93..ec5463f4e 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -3,220 +3,12 @@ #pragma once -#include <chrono> - -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Core::Timing { -struct EventType; -} - -namespace Service::SM { -class ServiceManager; +namespace Core { +class System; } namespace Service::HID { -enum class HidController : std::size_t { - DebugPad, - Touchscreen, - Mouse, - Keyboard, - XPad, - HomeButton, - SleepButton, - CaptureButton, - InputDetector, - UniquePad, - NPad, - Gesture, - ConsoleSixAxisSensor, - DebugMouse, - Palma, - - MaxControllers, -}; - -class IAppletResource final : public ServiceFramework<IAppletResource> { -public: - explicit IAppletResource(Core::System& system_, - KernelHelpers::ServiceContext& service_context_); - ~IAppletResource() override; - - void ActivateController(HidController controller); - void DeactivateController(HidController controller); - - template <typename T> - T& GetController(HidController controller) { - return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); - } - - template <typename T> - const T& GetController(HidController controller) const { - return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); - } - -private: - template <typename T> - void MakeController(HidController controller, u8* shared_memory) { - if constexpr (std::is_constructible_v<T, Core::System&, u8*>) { - controllers[static_cast<std::size_t>(controller)] = - std::make_unique<T>(system, shared_memory); - } else { - controllers[static_cast<std::size_t>(controller)] = - std::make_unique<T>(system.HIDCore(), shared_memory); - } - } - - template <typename T> - void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { - controllers[static_cast<std::size_t>(controller)] = - std::make_unique<T>(system.HIDCore(), shared_memory, service_context); - } - - void GetSharedMemoryHandle(HLERequestContext& ctx); - void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - - KernelHelpers::ServiceContext& service_context; - - std::shared_ptr<Core::Timing::EventType> npad_update_event; - std::shared_ptr<Core::Timing::EventType> default_update_event; - std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; - std::shared_ptr<Core::Timing::EventType> motion_update_event; - - std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> - controllers{}; -}; - -class Hid final : public ServiceFramework<Hid> { -public: - explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_); - ~Hid() override; - - std::shared_ptr<IAppletResource> GetAppletResource(); - -private: - void CreateAppletResource(HLERequestContext& ctx); - void ActivateDebugPad(HLERequestContext& ctx); - void ActivateTouchScreen(HLERequestContext& ctx); - void ActivateMouse(HLERequestContext& ctx); - void ActivateKeyboard(HLERequestContext& ctx); - void SendKeyboardLockKeyEvent(HLERequestContext& ctx); - void ActivateXpad(HLERequestContext& ctx); - void GetXpadIDs(HLERequestContext& ctx); - void ActivateSixAxisSensor(HLERequestContext& ctx); - void DeactivateSixAxisSensor(HLERequestContext& ctx); - void StartSixAxisSensor(HLERequestContext& ctx); - void StopSixAxisSensor(HLERequestContext& ctx); - void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx); - void EnableSixAxisSensorFusion(HLERequestContext& ctx); - void SetSixAxisSensorFusionParameters(HLERequestContext& ctx); - void GetSixAxisSensorFusionParameters(HLERequestContext& ctx); - void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx); - void SetGyroscopeZeroDriftMode(HLERequestContext& ctx); - void GetGyroscopeZeroDriftMode(HLERequestContext& ctx); - void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx); - void IsSixAxisSensorAtRest(HLERequestContext& ctx); - void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx); - void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx); - void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx); - void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx); - void GetSixAxisSensorIcInformation(HLERequestContext& ctx); - void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx); - void ActivateGesture(HLERequestContext& ctx); - void SetSupportedNpadStyleSet(HLERequestContext& ctx); - void GetSupportedNpadStyleSet(HLERequestContext& ctx); - void SetSupportedNpadIdType(HLERequestContext& ctx); - void ActivateNpad(HLERequestContext& ctx); - void DeactivateNpad(HLERequestContext& ctx); - void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx); - void DisconnectNpad(HLERequestContext& ctx); - void GetPlayerLedPattern(HLERequestContext& ctx); - void ActivateNpadWithRevision(HLERequestContext& ctx); - void SetNpadJoyHoldType(HLERequestContext& ctx); - void GetNpadJoyHoldType(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx); - void MergeSingleJoyAsDualJoy(HLERequestContext& ctx); - void StartLrAssignmentMode(HLERequestContext& ctx); - void StopLrAssignmentMode(HLERequestContext& ctx); - void SetNpadHandheldActivationMode(HLERequestContext& ctx); - void GetNpadHandheldActivationMode(HLERequestContext& ctx); - void SwapNpadAssignment(HLERequestContext& ctx); - void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx); - void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx); - void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx); - void SetNpadCaptureButtonAssignment(HLERequestContext& ctx); - void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx); - void GetVibrationDeviceInfo(HLERequestContext& ctx); - void SendVibrationValue(HLERequestContext& ctx); - void GetActualVibrationValue(HLERequestContext& ctx); - void CreateActiveVibrationDeviceList(HLERequestContext& ctx); - void PermitVibration(HLERequestContext& ctx); - void IsVibrationPermitted(HLERequestContext& ctx); - void SendVibrationValues(HLERequestContext& ctx); - void SendVibrationGcErmCommand(HLERequestContext& ctx); - void GetActualVibrationGcErmCommand(HLERequestContext& ctx); - void BeginPermitVibrationSession(HLERequestContext& ctx); - void EndPermitVibrationSession(HLERequestContext& ctx); - void IsVibrationDeviceMounted(HLERequestContext& ctx); - void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); - void StartConsoleSixAxisSensor(HLERequestContext& ctx); - void StopConsoleSixAxisSensor(HLERequestContext& ctx); - void ActivateSevenSixAxisSensor(HLERequestContext& ctx); - void StartSevenSixAxisSensor(HLERequestContext& ctx); - void StopSevenSixAxisSensor(HLERequestContext& ctx); - void InitializeSevenSixAxisSensor(HLERequestContext& ctx); - void FinalizeSevenSixAxisSensor(HLERequestContext& ctx); - void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx); - void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); - void GetPalmaConnectionHandle(HLERequestContext& ctx); - void InitializePalma(HLERequestContext& ctx); - void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx); - void GetPalmaOperationInfo(HLERequestContext& ctx); - void PlayPalmaActivity(HLERequestContext& ctx); - void SetPalmaFrModeType(HLERequestContext& ctx); - void ReadPalmaStep(HLERequestContext& ctx); - void EnablePalmaStep(HLERequestContext& ctx); - void ResetPalmaStep(HLERequestContext& ctx); - void ReadPalmaApplicationSection(HLERequestContext& ctx); - void WritePalmaApplicationSection(HLERequestContext& ctx); - void ReadPalmaUniqueCode(HLERequestContext& ctx); - void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx); - void WritePalmaActivityEntry(HLERequestContext& ctx); - void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx); - void WritePalmaWaveEntry(HLERequestContext& ctx); - void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); - void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); - void SuspendPalmaFeature(HLERequestContext& ctx); - void GetPalmaOperationResult(HLERequestContext& ctx); - void ReadPalmaPlayLog(HLERequestContext& ctx); - void ResetPalmaPlayLog(HLERequestContext& ctx); - void SetIsPalmaAllConnectable(HLERequestContext& ctx); - void SetIsPalmaPairedConnectable(HLERequestContext& ctx); - void PairPalma(HLERequestContext& ctx); - void SetPalmaBoostMode(HLERequestContext& ctx); - void CancelWritePalmaWaveEntry(HLERequestContext& ctx); - void EnablePalmaBoostMode(HLERequestContext& ctx); - void GetPalmaBluetoothAddress(HLERequestContext& ctx); - void SetDisallowedPalmaConnection(HLERequestContext& ctx); - void SetNpadCommunicationMode(HLERequestContext& ctx); - void GetNpadCommunicationMode(HLERequestContext& ctx); - void SetTouchScreenConfiguration(HLERequestContext& ctx); - void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); - - std::shared_ptr<IAppletResource> applet_resource; - - KernelHelpers::ServiceContext service_context; -}; - void LoopProcess(Core::System& system); } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp new file mode 100644 index 000000000..6294f3dfb --- /dev/null +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/hid_debug_server.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::HID { + +IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) + : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "DeactivateDebugPad"}, + {1, nullptr, "SetDebugPadAutoPilotState"}, + {2, nullptr, "UnsetDebugPadAutoPilotState"}, + {10, nullptr, "DeactivateTouchScreen"}, + {11, nullptr, "SetTouchScreenAutoPilotState"}, + {12, nullptr, "UnsetTouchScreenAutoPilotState"}, + {13, nullptr, "GetTouchScreenConfiguration"}, + {14, nullptr, "ProcessTouchScreenAutoTune"}, + {15, nullptr, "ForceStopTouchScreenManagement"}, + {16, nullptr, "ForceRestartTouchScreenManagement"}, + {17, nullptr, "IsTouchScreenManaged"}, + {20, nullptr, "DeactivateMouse"}, + {21, nullptr, "SetMouseAutoPilotState"}, + {22, nullptr, "UnsetMouseAutoPilotState"}, + {25, nullptr, "SetDebugMouseAutoPilotState"}, + {26, nullptr, "UnsetDebugMouseAutoPilotState"}, + {30, nullptr, "DeactivateKeyboard"}, + {31, nullptr, "SetKeyboardAutoPilotState"}, + {32, nullptr, "UnsetKeyboardAutoPilotState"}, + {50, nullptr, "DeactivateXpad"}, + {51, nullptr, "SetXpadAutoPilotState"}, + {52, nullptr, "UnsetXpadAutoPilotState"}, + {53, nullptr, "DeactivateJoyXpad"}, + {60, nullptr, "ClearNpadSystemCommonPolicy"}, + {61, nullptr, "DeactivateNpad"}, + {62, nullptr, "ForceDisconnectNpad"}, + {91, nullptr, "DeactivateGesture"}, + {110, nullptr, "DeactivateHomeButton"}, + {111, nullptr, "SetHomeButtonAutoPilotState"}, + {112, nullptr, "UnsetHomeButtonAutoPilotState"}, + {120, nullptr, "DeactivateSleepButton"}, + {121, nullptr, "SetSleepButtonAutoPilotState"}, + {122, nullptr, "UnsetSleepButtonAutoPilotState"}, + {123, nullptr, "DeactivateInputDetector"}, + {130, nullptr, "DeactivateCaptureButton"}, + {131, nullptr, "SetCaptureButtonAutoPilotState"}, + {132, nullptr, "UnsetCaptureButtonAutoPilotState"}, + {133, nullptr, "SetShiftAccelerometerCalibrationValue"}, + {134, nullptr, "GetShiftAccelerometerCalibrationValue"}, + {135, nullptr, "SetShiftGyroscopeCalibrationValue"}, + {136, nullptr, "GetShiftGyroscopeCalibrationValue"}, + {140, nullptr, "DeactivateConsoleSixAxisSensor"}, + {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, + {142, nullptr, "DeactivateSevenSixAxisSensor"}, + {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, + {144, nullptr, "GetAccelerometerFsr"}, + {145, nullptr, "SetAccelerometerFsr"}, + {146, nullptr, "GetAccelerometerOdr"}, + {147, nullptr, "SetAccelerometerOdr"}, + {148, nullptr, "GetGyroscopeFsr"}, + {149, nullptr, "SetGyroscopeFsr"}, + {150, nullptr, "GetGyroscopeOdr"}, + {151, nullptr, "SetGyroscopeOdr"}, + {152, nullptr, "GetWhoAmI"}, + {201, nullptr, "ActivateFirmwareUpdate"}, + {202, nullptr, "DeactivateFirmwareUpdate"}, + {203, nullptr, "StartFirmwareUpdate"}, + {204, nullptr, "GetFirmwareUpdateStage"}, + {205, nullptr, "GetFirmwareVersion"}, + {206, nullptr, "GetDestinationFirmwareVersion"}, + {207, nullptr, "DiscardFirmwareInfoCacheForRevert"}, + {208, nullptr, "StartFirmwareUpdateForRevert"}, + {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, + {210, nullptr, "IsFirmwareUpdatingDevice"}, + {211, nullptr, "StartFirmwareUpdateIndividual"}, + {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, + {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, + {221, nullptr, "UpdateControllerColor"}, + {222, nullptr, "ConnectUsbPadsAsync"}, + {223, nullptr, "DisconnectUsbPadsAsync"}, + {224, nullptr, "UpdateDesignInfo"}, + {225, nullptr, "GetUniquePadDriverState"}, + {226, nullptr, "GetSixAxisSensorDriverStates"}, + {227, nullptr, "GetRxPacketHistory"}, + {228, nullptr, "AcquireOperationEventHandle"}, + {229, nullptr, "ReadSerialFlash"}, + {230, nullptr, "WriteSerialFlash"}, + {231, nullptr, "GetOperationResult"}, + {232, nullptr, "EnableShipmentMode"}, + {233, nullptr, "ClearPairingInfo"}, + {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, + {235, nullptr, "EnableAnalogStickPower"}, + {236, nullptr, "RequestKuinaUartClockCal"}, + {237, nullptr, "GetKuinaUartClockCal"}, + {238, nullptr, "SetKuinaUartClockTrim"}, + {239, nullptr, "KuinaLoopbackTest"}, + {240, nullptr, "RequestBatteryVoltage"}, + {241, nullptr, "GetBatteryVoltage"}, + {242, nullptr, "GetUniquePadPowerInfo"}, + {243, nullptr, "RebootUniquePad"}, + {244, nullptr, "RequestKuinaFirmwareVersion"}, + {245, nullptr, "GetKuinaFirmwareVersion"}, + {246, nullptr, "GetVidPid"}, + {247, nullptr, "GetAnalogStickCalibrationValue"}, + {248, nullptr, "GetUniquePadIdsFull"}, + {249, nullptr, "ConnectUniquePad"}, + {250, nullptr, "IsVirtual"}, + {251, nullptr, "GetAnalogStickModuleParam"}, + {301, nullptr, "GetAbstractedPadHandles"}, + {302, nullptr, "GetAbstractedPadState"}, + {303, nullptr, "GetAbstractedPadsState"}, + {321, nullptr, "SetAutoPilotVirtualPadState"}, + {322, nullptr, "UnsetAutoPilotVirtualPadState"}, + {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, + {324, nullptr, "AttachHdlsWorkBuffer"}, + {325, nullptr, "ReleaseHdlsWorkBuffer"}, + {326, nullptr, "DumpHdlsNpadAssignmentState"}, + {327, nullptr, "DumpHdlsStates"}, + {328, nullptr, "ApplyHdlsNpadAssignmentState"}, + {329, nullptr, "ApplyHdlsStateList"}, + {330, nullptr, "AttachHdlsVirtualDevice"}, + {331, nullptr, "DetachHdlsVirtualDevice"}, + {332, nullptr, "SetHdlsState"}, + {350, nullptr, "AddRegisteredDevice"}, + {400, nullptr, "DisableExternalMcuOnNxDevice"}, + {401, nullptr, "DisableRailDeviceFiltering"}, + {402, nullptr, "EnableWiredPairing"}, + {403, nullptr, "EnableShipmentModeAutoClear"}, + {404, nullptr, "SetRailEnabled"}, + {500, nullptr, "SetFactoryInt"}, + {501, nullptr, "IsFactoryBootEnabled"}, + {550, nullptr, "SetAnalogStickModelDataTemporarily"}, + {551, nullptr, "GetAnalogStickModelData"}, + {552, nullptr, "ResetAnalogStickModelData"}, + {600, nullptr, "ConvertPadState"}, + {650, nullptr, "AddButtonPlayData"}, + {651, nullptr, "StartButtonPlayData"}, + {652, nullptr, "StopButtonPlayData"}, + {2000, nullptr, "DeactivateDigitizer"}, + {2001, nullptr, "SetDigitizerAutoPilotState"}, + {2002, nullptr, "UnsetDigitizerAutoPilotState"}, + {2002, nullptr, "ReloadFirmwareDebugSettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IHidDebugServer::~IHidDebugServer() = default; + +std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { + resource_manager->Initialize(); + return resource_manager; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h new file mode 100644 index 000000000..406db2211 --- /dev/null +++ b/src/core/hle/service/hid/hid_debug_server.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::HID { +class ResourceManager; + +class IHidDebugServer final : public ServiceFramework<IHidDebugServer> { +public: + explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); + ~IHidDebugServer() override; + +private: + std::shared_ptr<ResourceManager> GetResourceManager(); + + std::shared_ptr<ResourceManager> resource_manager; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp new file mode 100644 index 000000000..59bd6825c --- /dev/null +++ b/src/core/hle/service/hid/hid_firmware_settings.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/hid_firmware_settings.h" + +namespace Service::HID { + +HidFirmwareSettings::HidFirmwareSettings() { + LoadSettings(true); +} + +void HidFirmwareSettings::Reload() { + LoadSettings(true); +} + +void HidFirmwareSettings::LoadSettings(bool reload_config) { + if (is_initalized && !reload_config) { + return; + } + + // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values + + is_debug_pad_enabled = true; + is_device_managed = true; + is_touch_i2c_managed = is_device_managed; + is_future_devices_emulated = false; + is_mcu_hardware_error_emulated = false; + is_rail_enabled = true; + is_firmware_update_failure_emulated = false; + is_firmware_update_failure = {}; + is_ble_disabled = false; + is_dscale_disabled = false; + is_handheld_forced = true; + features_per_id_disabled = {}; + is_touch_firmware_auto_update_disabled = false; + is_initalized = true; +} + +bool HidFirmwareSettings::IsDebugPadEnabled() { + LoadSettings(false); + return is_debug_pad_enabled; +} + +bool HidFirmwareSettings::IsDeviceManaged() { + LoadSettings(false); + return is_device_managed; +} + +bool HidFirmwareSettings::IsEmulateFutureDevice() { + LoadSettings(false); + return is_future_devices_emulated; +} + +bool HidFirmwareSettings::IsTouchI2cManaged() { + LoadSettings(false); + return is_touch_i2c_managed; +} + +bool HidFirmwareSettings::IsHandheldForced() { + LoadSettings(false); + return is_handheld_forced; +} + +bool HidFirmwareSettings::IsRailEnabled() { + LoadSettings(false); + return is_rail_enabled; +} + +bool HidFirmwareSettings::IsHardwareErrorEmulated() { + LoadSettings(false); + return is_mcu_hardware_error_emulated; +} + +bool HidFirmwareSettings::IsBleDisabled() { + LoadSettings(false); + return is_ble_disabled; +} + +bool HidFirmwareSettings::IsDscaleDisabled() { + LoadSettings(false); + return is_dscale_disabled; +} + +bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() { + LoadSettings(false); + return is_touch_firmware_auto_update_disabled; +} + +HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() { + LoadSettings(false); + return is_firmware_update_failure; +} + +HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() { + LoadSettings(false); + return features_per_id_disabled; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h new file mode 100644 index 000000000..6c10c440b --- /dev/null +++ b/src/core/hle/service/hid/hid_firmware_settings.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::HID { + +/// Loads firmware config from nn::settings::fwdbg +class HidFirmwareSettings { +public: + using FirmwareSetting = std::array<u8, 4>; + using FeaturesPerId = std::array<bool, 0xA8>; + + HidFirmwareSettings(); + + void Reload(); + void LoadSettings(bool reload_config); + + bool IsDebugPadEnabled(); + bool IsDeviceManaged(); + bool IsEmulateFutureDevice(); + bool IsTouchI2cManaged(); + bool IsHandheldForced(); + bool IsRailEnabled(); + bool IsHardwareErrorEmulated(); + bool IsBleDisabled(); + bool IsDscaleDisabled(); + bool IsTouchAutoUpdateDisabled(); + + FirmwareSetting GetFirmwareUpdateFailure(); + FeaturesPerId FeaturesDisabledPerId(); + +private: + bool is_initalized{}; + + // Debug settings + bool is_debug_pad_enabled{}; + bool is_device_managed{}; + bool is_touch_i2c_managed{}; + bool is_future_devices_emulated{}; + bool is_mcu_hardware_error_emulated{}; + bool is_rail_enabled{}; + bool is_firmware_update_failure_emulated{}; + bool is_ble_disabled{}; + bool is_dscale_disabled{}; + bool is_handheld_forced{}; + bool is_touch_firmware_auto_update_disabled{}; + FirmwareSetting is_firmware_update_failure{}; + FeaturesPerId features_per_id_disabled{}; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp new file mode 100644 index 000000000..583142e35 --- /dev/null +++ b/src/core/hle/service/hid/hid_server.cpp @@ -0,0 +1,2371 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <array> +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_firmware_settings.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/hid_util.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/memory.h" + +#include "core/hle/service/hid/controllers/console_six_axis.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/controllers/debug_pad.h" +#include "core/hle/service/hid/controllers/gesture.h" +#include "core/hle/service/hid/controllers/keyboard.h" +#include "core/hle/service/hid/controllers/mouse.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/hid/controllers/seven_six_axis.h" +#include "core/hle/service/hid/controllers/six_axis.h" +#include "core/hle/service/hid/controllers/touchscreen.h" + +namespace Service::HID { + +class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { +public: + explicit IActiveVibrationDeviceList(Core::System& system_, + std::shared_ptr<ResourceManager> resource) + : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void InitializeVibrationDevice(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; + + if (resource_manager != nullptr) { + resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle); + } + + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", + vibration_device_handle.npad_type, vibration_device_handle.npad_id, + vibration_device_handle.device_index); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + std::shared_ptr<ResourceManager> resource_manager; +}; + +IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings) + : ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IHidServer::CreateAppletResource, "CreateAppletResource"}, + {1, &IHidServer::ActivateDebugPad, "ActivateDebugPad"}, + {11, &IHidServer::ActivateTouchScreen, "ActivateTouchScreen"}, + {21, &IHidServer::ActivateMouse, "ActivateMouse"}, + {26, nullptr, "ActivateDebugMouse"}, + {31, &IHidServer::ActivateKeyboard, "ActivateKeyboard"}, + {32, &IHidServer::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"}, + {40, &IHidServer::AcquireXpadIdEventHandle, "AcquireXpadIdEventHandle"}, + {41, &IHidServer::ReleaseXpadIdEventHandle, "ReleaseXpadIdEventHandle"}, + {51, &IHidServer::ActivateXpad, "ActivateXpad"}, + {55, &IHidServer::GetXpadIds, "GetXpadIds"}, + {56, &IHidServer::ActivateJoyXpad, "ActivateJoyXpad"}, + {58, &IHidServer::GetJoyXpadLifoHandle, "GetJoyXpadLifoHandle"}, + {59, &IHidServer::GetJoyXpadIds, "GetJoyXpadIds"}, + {60, &IHidServer::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, + {61, &IHidServer::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, + {62, &IHidServer::GetSixAxisSensorLifoHandle, "GetSixAxisSensorLifoHandle"}, + {63, &IHidServer::ActivateJoySixAxisSensor, "ActivateJoySixAxisSensor"}, + {64, &IHidServer::DeactivateJoySixAxisSensor, "DeactivateJoySixAxisSensor"}, + {65, &IHidServer::GetJoySixAxisSensorLifoHandle, "GetJoySixAxisSensorLifoHandle"}, + {66, &IHidServer::StartSixAxisSensor, "StartSixAxisSensor"}, + {67, &IHidServer::StopSixAxisSensor, "StopSixAxisSensor"}, + {68, &IHidServer::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, + {69, &IHidServer::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, + {70, &IHidServer::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, + {71, &IHidServer::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, + {72, &IHidServer::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"}, + {73, nullptr, "SetAccelerometerParameters"}, + {74, nullptr, "GetAccelerometerParameters"}, + {75, nullptr, "ResetAccelerometerParameters"}, + {76, nullptr, "SetAccelerometerPlayMode"}, + {77, nullptr, "GetAccelerometerPlayMode"}, + {78, nullptr, "ResetAccelerometerPlayMode"}, + {79, &IHidServer::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, + {80, &IHidServer::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, + {81, &IHidServer::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, + {82, &IHidServer::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, + {83, &IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, + {84, &IHidServer::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, + {85, &IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, + {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, + {87, &IHidServer::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, + {88, &IHidServer::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, + {89, &IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, + {91, &IHidServer::ActivateGesture, "ActivateGesture"}, + {100, &IHidServer::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, + {101, &IHidServer::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, + {102, &IHidServer::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, + {103, &IHidServer::ActivateNpad, "ActivateNpad"}, + {104, &IHidServer::DeactivateNpad, "DeactivateNpad"}, + {106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, + {107, &IHidServer::DisconnectNpad, "DisconnectNpad"}, + {108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"}, + {109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, + {120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, + {121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, + {122, &IHidServer::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, + {123, &IHidServer::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"}, + {124, &IHidServer::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, + {125, &IHidServer::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, + {126, &IHidServer::StartLrAssignmentMode, "StartLrAssignmentMode"}, + {127, &IHidServer::StopLrAssignmentMode, "StopLrAssignmentMode"}, + {128, &IHidServer::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, + {129, &IHidServer::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, + {130, &IHidServer::SwapNpadAssignment, "SwapNpadAssignment"}, + {131, &IHidServer::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, + {132, &IHidServer::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, + {133, &IHidServer::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"}, + {134, &IHidServer::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, + {135, &IHidServer::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, + {136, &IHidServer::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, + {200, &IHidServer::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, + {201, &IHidServer::SendVibrationValue, "SendVibrationValue"}, + {202, &IHidServer::GetActualVibrationValue, "GetActualVibrationValue"}, + {203, &IHidServer::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, + {204, &IHidServer::PermitVibration, "PermitVibration"}, + {205, &IHidServer::IsVibrationPermitted, "IsVibrationPermitted"}, + {206, &IHidServer::SendVibrationValues, "SendVibrationValues"}, + {207, &IHidServer::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, + {208, &IHidServer::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, + {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, + {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"}, + {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, + {212, nullptr, "SendVibrationValueInBool"}, + {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, + {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, + {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, + {303, &IHidServer::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, + {304, &IHidServer::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, + {305, &IHidServer::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, + {306, &IHidServer::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, + {307, &IHidServer::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, + {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, + {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, + {310, &IHidServer::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, + {400, &IHidServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, + {401, nullptr, "EnableUsbFullKeyController"}, + {402, nullptr, "IsUsbFullKeyControllerConnected"}, + {403, nullptr, "HasBattery"}, + {404, nullptr, "HasLeftRightBattery"}, + {405, nullptr, "GetNpadInterfaceType"}, + {406, nullptr, "GetNpadLeftRightInterfaceType"}, + {407, nullptr, "GetNpadOfHighestBatteryLevel"}, + {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, + {500, &IHidServer::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, + {501, &IHidServer::InitializePalma, "InitializePalma"}, + {502, &IHidServer::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, + {503, &IHidServer::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, + {504, &IHidServer::PlayPalmaActivity, "PlayPalmaActivity"}, + {505, &IHidServer::SetPalmaFrModeType, "SetPalmaFrModeType"}, + {506, &IHidServer::ReadPalmaStep, "ReadPalmaStep"}, + {507, &IHidServer::EnablePalmaStep, "EnablePalmaStep"}, + {508, &IHidServer::ResetPalmaStep, "ResetPalmaStep"}, + {509, &IHidServer::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, + {510, &IHidServer::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, + {511, &IHidServer::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, + {512, &IHidServer::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, + {513, &IHidServer::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, + {514, &IHidServer::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, + {515, &IHidServer::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, + {516, &IHidServer::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, + {517, &IHidServer::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, + {518, &IHidServer::SuspendPalmaFeature, "SuspendPalmaFeature"}, + {519, &IHidServer::GetPalmaOperationResult, "GetPalmaOperationResult"}, + {520, &IHidServer::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, + {521, &IHidServer::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, + {522, &IHidServer::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, + {523, &IHidServer::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, + {524, &IHidServer::PairPalma, "PairPalma"}, + {525, &IHidServer::SetPalmaBoostMode, "SetPalmaBoostMode"}, + {526, &IHidServer::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, + {527, &IHidServer::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, + {528, &IHidServer::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, + {529, &IHidServer::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, + {1000, &IHidServer::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, + {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, + {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, + {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, + {2000, nullptr, "ActivateDigitizer"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IHidServer::~IHidServer() = default; + +void IHidServer::CreateAppletResource(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IAppletResource>(system, resource_manager); +} + +void IHidServer::ActivateDebugPad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + Result result = ResultSuccess; + auto debug_pad = GetResourceManager()->GetDebugPad(); + + if (!firmware_settings->IsDeviceManaged()) { + result = debug_pad->Activate(); + } + + if (result.IsSuccess()) { + result = debug_pad->Activate(applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + Result result = ResultSuccess; + auto touch_screen = GetResourceManager()->GetTouchScreen(); + + if (!firmware_settings->IsDeviceManaged()) { + result = touch_screen->Activate(); + } + + if (result.IsSuccess()) { + result = touch_screen->Activate(applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateMouse(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + Result result = ResultSuccess; + auto mouse = GetResourceManager()->GetMouse(); + + if (!firmware_settings->IsDeviceManaged()) { + result = mouse->Activate(); + } + + if (result.IsSuccess()) { + result = mouse->Activate(applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateKeyboard(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + Result result = ResultSuccess; + auto keyboard = GetResourceManager()->GetKeyboard(); + + if (!firmware_settings->IsDeviceManaged()) { + result = keyboard->Activate(); + } + + if (result.IsSuccess()) { + result = keyboard->Activate(applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flags{rp.Pop<u32>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::AcquireXpadIdEventHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + // Handle returned is null here +} + +void IHidServer::ReleaseXpadIdEventHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateXpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 basic_xpad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", + parameters.basic_xpad_id, parameters.applet_resource_user_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetXpadIds(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + // This function has been hardcoded since 10.0.0+ + const std::array<u32, 4> basic_xpad_id{0, 1, 2, 3}; + ctx.WriteBuffer(basic_xpad_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<s64>(basic_xpad_id.size()); +} + +void IHidServer::ActivateJoyXpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetJoyXpadLifoHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + // Handle returned is null here +} + +void IHidServer::GetJoyXpadIds(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + // This function has been hardcoded since 10.0.0+ + const s64 basic_xpad_id_count{}; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(basic_xpad_id_count); +} + +void IHidServer::ActivateSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::DeactivateSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetSixAxisSensorLifoHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateJoySixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::DeactivateJoySixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto joy_xpad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); + + // This function has been stubbed since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + // Handle returned is null here +} + +void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_enabled{}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = + six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); +} + +void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enable_sixaxis_sensor_fusion; + INSERT_PADDING_BYTES_NOINIT(3); + Core::HID::SixAxisSensorHandle sixaxis_handle; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, + parameters.enable_sixaxis_sensor_fusion); + + LOG_DEBUG(Service_HID, + "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " + "device_index={}, applet_resource_user_id={}", + parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = + six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " + "parameter2={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, + parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = + six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(fusion_parameters); +} + +void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + // Since these parameters are unknown just use what HW outputs + const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ + .parameter1 = 0.03f, + .parameter2 = 0.4f, + }; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result1 = + six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + if (result1.IsError()) { + rb.Push(result1); + return; + } + rb.Push(result2); +} + +void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()}; + const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " + "applet_resource_user_id={}", + sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, + drift_mode, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.PushEnum(drift_mode); +} + +void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_at_rest{}; + auto six_axis = GetResourceManager()->GetSixAxis(); + six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_at_rest); +} + +void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_firmware_available{}; + auto controller = GetResourceManager()->GetNpad(); + controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, + is_firmware_available); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_firmware_available); +} + +void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enabled; + Core::HID::SixAxisSensorHandle sixaxis_handle; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle, + parameters.enabled); + + LOG_DEBUG(Service_HID, + "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " + "applet_resource_user_id={}", + parameters.enabled, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_unaltered_sisxaxis_enabled{}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled( + parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); + + LOG_DEBUG( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_unaltered_sisxaxis_enabled); +} + +void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = + six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(calibration); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorIcInformation ic_information{}; + auto six_axis = GetResourceManager()->GetSixAxis(); + const auto result = + six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(ic_information); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto controller = GetResourceManager()->GetNpad(); + const auto result = + controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateGesture(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 basic_gesture_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", + parameters.basic_gesture_id, parameters.applet_resource_user_id); + + Result result = ResultSuccess; + auto gesture = GetResourceManager()->GetGesture(); + + if (!firmware_settings->IsDeviceManaged()) { + result = gesture->Activate(); + } + + if (result.IsSuccess()) { + // TODO: Use gesture id here + result = gesture->Activate(parameters.applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadStyleSet supported_styleset; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset}); + + LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", + parameters.supported_styleset, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw); +} + +void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer()); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + auto npad = GetResourceManager()->GetNpad(); + + // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); + const Result result = npad->Activate(applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::DeactivateNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + // This function does nothing since 10.0.0+ + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + u64 unknown; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", + parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); + + // Games expect this event to be signaled after calling this function + GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects( + GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id)); +} + +void IHidServer::DisconnectNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto controller = GetResourceManager()->GetNpad(); + controller->DisconnectNpad(parameters.npad_id); + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; + + Core::HID::LedPattern pattern{0, 0, 0, 0}; + auto controller = GetResourceManager()->GetNpad(); + const auto result = controller->GetLedPattern(npad_id, pattern); + + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(pattern.raw); +} + +void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + NPad::NpadRevision revision; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, + parameters.applet_resource_user_id); + + auto npad = GetResourceManager()->GetNpad(); + + // TODO: npad->SetRevision(applet_resource_user_id, revision); + const auto result = npad->Activate(parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()}; + + GetResourceManager()->GetNpad()->SetHoldType(hold_type); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", + applet_resource_user_id, hold_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType()); +} + +void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::NpadIdType new_npad_id{}; + auto controller = GetResourceManager()->GetNpad(); + controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left, + NPad::NpadJoyAssignmentMode::Single); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + NPad::NpadJoyDeviceType npad_joy_device_type; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::NpadIdType new_npad_id{}; + auto controller = GetResourceManager()->GetNpad(); + controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, + NPad::NpadJoyAssignmentMode::Single); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", + parameters.npad_id, parameters.applet_resource_user_id, + parameters.npad_joy_device_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::NpadIdType new_npad_id{}; + auto controller = GetResourceManager()->GetNpad(); + controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual); + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); // Spams a lot when controller applet is open + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; + const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + auto controller = GetResourceManager()->GetNpad(); + const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + GetResourceManager()->GetNpad()->StartLRAssignmentMode(); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + GetResourceManager()->GetNpad()->StopLRAssignmentMode(); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()}; + + GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", + applet_resource_user_id, activation_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode()); +} + +void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; + const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + auto controller = GetResourceManager()->GetNpad(); + const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2); + + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_enabled = false; + auto controller = GetResourceManager()->GetNpad(); + const auto result = + controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); +} + +void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_BYTES_NOINIT(3); + Core::HID::NpadIdType npad_id; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto controller = GetResourceManager()->GetNpad(); + const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled( + parameters.is_enabled, parameters.npad_id); + + LOG_DEBUG(Service_HID, + "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + NPad::NpadJoyDeviceType npad_joy_device_type; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::NpadIdType new_npad_id{}; + auto controller = GetResourceManager()->GetNpad(); + const auto is_reassigned = + controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, + NPad::NpadJoyAssignmentMode::Single); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", + parameters.npad_id, parameters.applet_resource_user_id, + parameters.npad_joy_device_type); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(is_reassigned); + rb.PushEnum(new_npad_id); +} + +void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool analog_stick_use_center_clamp; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp( + parameters.analog_stick_use_center_clamp); + + LOG_WARNING(Service_HID, + "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", + parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadStyleSet npad_styleset; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::HID::NpadButton button; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", + parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; + const auto controller = GetResourceManager()->GetNpad(); + + Core::HID::VibrationDeviceInfo vibration_device_info; + bool check_device_index = false; + + switch (vibration_device_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Handheld: + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::JoyconLeft: + case Core::HID::NpadStyleIndex::JoyconRight: + vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; + check_device_index = true; + break; + case Core::HID::NpadStyleIndex::GameCube: + vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; + break; + case Core::HID::NpadStyleIndex::N64: + vibration_device_info.type = Core::HID::VibrationDeviceType::N64; + break; + default: + vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; + break; + } + + vibration_device_info.position = Core::HID::VibrationDevicePosition::None; + if (check_device_index) { + switch (vibration_device_handle.device_index) { + case Core::HID::DeviceIndex::Left: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; + break; + case Core::HID::DeviceIndex::Right: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; + break; + case Core::HID::DeviceIndex::None: + default: + ASSERT_MSG(false, "DeviceIndex should never be None!"); + break; + } + } + + LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", + vibration_device_info.type, vibration_device_info.position); + + const auto result = IsVibrationHandleValid(vibration_device_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(vibration_device_info); +} + +void IHidServer::SendVibrationValue(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + Core::HID::VibrationValue vibration_value; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, + parameters.vibration_value); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw( + GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle)); +} + +void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager()); +} + +void IHidServer::PermitVibration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto can_vibrate{rp.Pop<bool>()}; + + // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value + // by converting it to a bool + Settings::values.vibration_enabled.SetValue(can_vibrate); + + LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + // nnSDK checks if a float is greater than zero. We return the bool we stored earlier + const auto is_enabled = Settings::values.vibration_enabled.GetValue(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_enabled); +} + +void IHidServer::SendVibrationValues(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + const auto handle_data = ctx.ReadBuffer(0); + const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0); + const auto vibration_data = ctx.ReadBuffer(1); + const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1); + + auto vibration_device_handles = + std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()), + handle_count); + auto vibration_values = std::span( + reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); + + GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::HID::VibrationGcErmCommand gc_erm_command; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + /** + * Note: This uses yuzu-specific behavior such that the StopHard command produces + * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, + * in order to differentiate between Stop and StopHard commands. + * This is done to reuse the controller vibration functions made for regular controllers. + */ + const auto vibration_value = [parameters] { + switch (parameters.gc_erm_command) { + case Core::HID::VibrationGcErmCommand::Stop: + return Core::HID::VibrationValue{ + .low_amplitude = 0.0f, + .low_frequency = 160.0f, + .high_amplitude = 0.0f, + .high_frequency = 320.0f, + }; + case Core::HID::VibrationGcErmCommand::Start: + return Core::HID::VibrationValue{ + .low_amplitude = 1.0f, + .low_frequency = 160.0f, + .high_amplitude = 1.0f, + .high_frequency = 320.0f, + }; + case Core::HID::VibrationGcErmCommand::StopHard: + return Core::HID::VibrationValue{ + .low_amplitude = 0.0f, + .low_frequency = 0.0f, + .high_amplitude = 0.0f, + .high_frequency = 0.0f, + }; + default: + return Core::HID::DEFAULT_VIBRATION_VALUE; + } + }(); + + GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, + vibration_value); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " + "gc_erm_command={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, + parameters.gc_erm_command); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + const auto last_vibration = + GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle); + + const auto gc_erm_command = [last_vibration] { + if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { + return Core::HID::VibrationGcErmCommand::Start; + } + + /** + * Note: This uses yuzu-specific behavior such that the StopHard command produces + * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function + * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. + * This is done to reuse the controller vibration functions made for regular controllers. + */ + if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { + return Core::HID::VibrationGcErmCommand::StopHard; + } + + return Core::HID::VibrationGcErmCommand::Stop; + }(); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(gc_erm_command); +} + +void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + GetResourceManager()->GetNpad()->SetPermitVibrationSession(true); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { + GetResourceManager()->GetNpad()->SetPermitVibrationSession(false); + + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted( + parameters.vibration_device_handle)); +} + +void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + Result result = ResultSuccess; + auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis(); + + if (!firmware_settings->IsDeviceManaged()) { + result = console_sixaxis->Activate(); + } + + if (result.IsSuccess()) { + result = console_sixaxis->Activate(applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", + parameters.console_sixaxis_handle.unknown_1, + parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StopConsoleSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", + parameters.console_sixaxis_handle.unknown_1, + parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + Result result = ResultSuccess; + auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis(); + + if (!firmware_settings->IsDeviceManaged()) { + result = seven_sixaxis->Activate(); + } + + if (result.IsSuccess()) { + seven_sixaxis->Activate(applet_resource_user_id); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StartSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StopSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto t_mem_1_size{rp.Pop<u64>()}; + const auto t_mem_2_size{rp.Pop<u64>()}; + const auto t_mem_1_handle{ctx.GetCopyHandle(0)}; + const auto t_mem_2_handle{ctx.GetCopyHandle(1)}; + + ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); + ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); + + auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( + t_mem_1_handle); + + if (t_mem_1.IsNull()) { + LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( + t_mem_2_handle); + + if (t_mem_2.IsNull()) { + LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size"); + ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); + + // Activate console six axis controller + GetResourceManager()->GetConsoleSixAxis()->Activate(); + GetResourceManager()->GetSevenSixAxis()->Activate(); + + GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); + + LOG_WARNING(Service_HID, + "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " + "applet_resource_user_id={}", + t_mem_1_handle, t_mem_2_handle, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + GetResourceManager()->GetSevenSixAxis()->ResetTimestamp(); + + LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + Palma::PalmaConnectionHandle handle; + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(handle); +} + +void IHidServer::InitializePalma(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->InitializePalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto controller = GetResourceManager()->GetPalma(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle)); +} + +void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + Palma::PalmaOperationType operation_type; + Palma::PalmaOperationData data; + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data); + + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(static_cast<u64>(operation_type)); +} + +void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + const auto palma_activity{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", + connection_handle.npad_id, palma_activity); + + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", + connection_handle.npad_id, fr_mode); + + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ReadPalmaStep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->ReadPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::EnablePalmaStep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", + parameters.connection_handle.npad_id, parameters.is_enabled); + + auto controller = GetResourceManager()->GetPalma(); + const auto result = + controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ResetPalmaStep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto controller = GetResourceManager()->GetPalma(); + const auto result = controller->ResetPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ReadPalmaApplicationSection(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) { + LOG_CRITICAL(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + const auto unknown{rp.Pop<u64>()}; + + [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", + connection_handle.npad_id, unknown); + + GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()}; + const auto unknown{rp.Pop<u64>()}; + const auto t_mem_size{rp.Pop<u64>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto size{rp.Pop<u64>()}; + + ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); + + auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( + t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); + + LOG_WARNING(Service_HID, + "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " + "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", + connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); + + GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set, + t_mem->GetSourceAddress(), t_mem_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 database_id_version; + INSERT_PADDING_WORDS_NOINIT(1); + Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", + parameters.connection_handle.npad_id, parameters.database_id_version); + + GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion( + parameters.connection_handle, parameters.database_id_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + const auto result = + GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ReadPalmaPlayLog(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ResetPalmaPlayLog(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_palma_all_connectable; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", + parameters.is_palma_all_connectable, parameters.applet_resource_user_id); + + GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::PairPalma(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager()->GetPalma()->PairPalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto palma_boost_mode{rp.Pop<bool>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); + + GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::CancelWritePalmaWaveEntry(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::EnablePalmaBoostMode(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPalmaBluetoothAddress(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()}; + + GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode); + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", + applet_resource_user_id, communication_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode()); +} + +void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", + touchscreen_mode.mode, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", + parameters.unknown, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() { + resource_manager->Initialize(); + return resource_manager; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h new file mode 100644 index 000000000..eb2e8e7f4 --- /dev/null +++ b/src/core/hle/service/hid/hid_server.h @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::HID { +class ResourceManager; +class HidFirmwareSettings; + +class IHidServer final : public ServiceFramework<IHidServer> { +public: + explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings); + ~IHidServer() override; + + std::shared_ptr<ResourceManager> GetResourceManager(); + +private: + void CreateAppletResource(HLERequestContext& ctx); + void ActivateDebugPad(HLERequestContext& ctx); + void ActivateTouchScreen(HLERequestContext& ctx); + void ActivateMouse(HLERequestContext& ctx); + void ActivateKeyboard(HLERequestContext& ctx); + void SendKeyboardLockKeyEvent(HLERequestContext& ctx); + void AcquireXpadIdEventHandle(HLERequestContext& ctx); + void ReleaseXpadIdEventHandle(HLERequestContext& ctx); + void ActivateXpad(HLERequestContext& ctx); + void GetXpadIds(HLERequestContext& ctx); + void ActivateJoyXpad(HLERequestContext& ctx); + void GetJoyXpadLifoHandle(HLERequestContext& ctx); + void GetJoyXpadIds(HLERequestContext& ctx); + void ActivateSixAxisSensor(HLERequestContext& ctx); + void DeactivateSixAxisSensor(HLERequestContext& ctx); + void GetSixAxisSensorLifoHandle(HLERequestContext& ctx); + void ActivateJoySixAxisSensor(HLERequestContext& ctx); + void DeactivateJoySixAxisSensor(HLERequestContext& ctx); + void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx); + void StartSixAxisSensor(HLERequestContext& ctx); + void StopSixAxisSensor(HLERequestContext& ctx); + void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx); + void EnableSixAxisSensorFusion(HLERequestContext& ctx); + void SetSixAxisSensorFusionParameters(HLERequestContext& ctx); + void GetSixAxisSensorFusionParameters(HLERequestContext& ctx); + void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx); + void SetGyroscopeZeroDriftMode(HLERequestContext& ctx); + void GetGyroscopeZeroDriftMode(HLERequestContext& ctx); + void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx); + void IsSixAxisSensorAtRest(HLERequestContext& ctx); + void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx); + void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx); + void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx); + void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx); + void GetSixAxisSensorIcInformation(HLERequestContext& ctx); + void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx); + void ActivateGesture(HLERequestContext& ctx); + void SetSupportedNpadStyleSet(HLERequestContext& ctx); + void GetSupportedNpadStyleSet(HLERequestContext& ctx); + void SetSupportedNpadIdType(HLERequestContext& ctx); + void ActivateNpad(HLERequestContext& ctx); + void DeactivateNpad(HLERequestContext& ctx); + void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx); + void DisconnectNpad(HLERequestContext& ctx); + void GetPlayerLedPattern(HLERequestContext& ctx); + void ActivateNpadWithRevision(HLERequestContext& ctx); + void SetNpadJoyHoldType(HLERequestContext& ctx); + void GetNpadJoyHoldType(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx); + void MergeSingleJoyAsDualJoy(HLERequestContext& ctx); + void StartLrAssignmentMode(HLERequestContext& ctx); + void StopLrAssignmentMode(HLERequestContext& ctx); + void SetNpadHandheldActivationMode(HLERequestContext& ctx); + void GetNpadHandheldActivationMode(HLERequestContext& ctx); + void SwapNpadAssignment(HLERequestContext& ctx); + void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx); + void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx); + void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx); + void SetNpadCaptureButtonAssignment(HLERequestContext& ctx); + void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx); + void GetVibrationDeviceInfo(HLERequestContext& ctx); + void SendVibrationValue(HLERequestContext& ctx); + void GetActualVibrationValue(HLERequestContext& ctx); + void CreateActiveVibrationDeviceList(HLERequestContext& ctx); + void PermitVibration(HLERequestContext& ctx); + void IsVibrationPermitted(HLERequestContext& ctx); + void SendVibrationValues(HLERequestContext& ctx); + void SendVibrationGcErmCommand(HLERequestContext& ctx); + void GetActualVibrationGcErmCommand(HLERequestContext& ctx); + void BeginPermitVibrationSession(HLERequestContext& ctx); + void EndPermitVibrationSession(HLERequestContext& ctx); + void IsVibrationDeviceMounted(HLERequestContext& ctx); + void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); + void StartConsoleSixAxisSensor(HLERequestContext& ctx); + void StopConsoleSixAxisSensor(HLERequestContext& ctx); + void ActivateSevenSixAxisSensor(HLERequestContext& ctx); + void StartSevenSixAxisSensor(HLERequestContext& ctx); + void StopSevenSixAxisSensor(HLERequestContext& ctx); + void InitializeSevenSixAxisSensor(HLERequestContext& ctx); + void FinalizeSevenSixAxisSensor(HLERequestContext& ctx); + void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx); + void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void GetPalmaConnectionHandle(HLERequestContext& ctx); + void InitializePalma(HLERequestContext& ctx); + void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx); + void GetPalmaOperationInfo(HLERequestContext& ctx); + void PlayPalmaActivity(HLERequestContext& ctx); + void SetPalmaFrModeType(HLERequestContext& ctx); + void ReadPalmaStep(HLERequestContext& ctx); + void EnablePalmaStep(HLERequestContext& ctx); + void ResetPalmaStep(HLERequestContext& ctx); + void ReadPalmaApplicationSection(HLERequestContext& ctx); + void WritePalmaApplicationSection(HLERequestContext& ctx); + void ReadPalmaUniqueCode(HLERequestContext& ctx); + void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx); + void WritePalmaActivityEntry(HLERequestContext& ctx); + void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx); + void WritePalmaWaveEntry(HLERequestContext& ctx); + void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); + void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); + void SuspendPalmaFeature(HLERequestContext& ctx); + void GetPalmaOperationResult(HLERequestContext& ctx); + void ReadPalmaPlayLog(HLERequestContext& ctx); + void ResetPalmaPlayLog(HLERequestContext& ctx); + void SetIsPalmaAllConnectable(HLERequestContext& ctx); + void SetIsPalmaPairedConnectable(HLERequestContext& ctx); + void PairPalma(HLERequestContext& ctx); + void SetPalmaBoostMode(HLERequestContext& ctx); + void CancelWritePalmaWaveEntry(HLERequestContext& ctx); + void EnablePalmaBoostMode(HLERequestContext& ctx); + void GetPalmaBluetoothAddress(HLERequestContext& ctx); + void SetDisallowedPalmaConnection(HLERequestContext& ctx); + void SetNpadCommunicationMode(HLERequestContext& ctx); + void GetNpadCommunicationMode(HLERequestContext& ctx); + void SetTouchScreenConfiguration(HLERequestContext& ctx); + void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); + + std::shared_ptr<ResourceManager> resource_manager; + std::shared_ptr<HidFirmwareSettings> firmware_settings; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp new file mode 100644 index 000000000..b56d0347a --- /dev/null +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -0,0 +1,539 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_system_server.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::HID { + +IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) + : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, + resource_manager{resource} { + // clang-format off + static const FunctionInfo functions[] = { + {31, nullptr, "SendKeyboardLockKeyEvent"}, + {101, nullptr, "AcquireHomeButtonEventHandle"}, + {111, nullptr, "ActivateHomeButton"}, + {121, nullptr, "AcquireSleepButtonEventHandle"}, + {131, nullptr, "ActivateSleepButton"}, + {141, nullptr, "AcquireCaptureButtonEventHandle"}, + {151, nullptr, "ActivateCaptureButton"}, + {161, nullptr, "GetPlatformConfig"}, + {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, + {211, nullptr, "GetNpadsWithNfc"}, + {212, nullptr, "AcquireNfcActivateEventHandle"}, + {213, nullptr, "ActivateNfc"}, + {214, nullptr, "GetXcdHandleForNpadWithNfc"}, + {215, nullptr, "IsNfcActivated"}, + {230, nullptr, "AcquireIrSensorEventHandle"}, + {231, nullptr, "ActivateIrSensor"}, + {232, nullptr, "GetIrSensorState"}, + {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, + {301, nullptr, "ActivateNpadSystem"}, + {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, + {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"}, + {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"}, + {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"}, + {307, nullptr, "GetNpadSystemExtStyle"}, + {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"}, + {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"}, + {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"}, + {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, + {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"}, + {313, nullptr, "GetNpadCaptureButtonAssignment"}, + {314, nullptr, "GetAppletFooterUiType"}, + {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"}, + {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"}, + {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"}, + {318, &IHidSystemServer::HasBattery, "HasBattery"}, + {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"}, + {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, + {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"}, + {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, + {324, nullptr, "GetUniquePadButtonSet"}, + {325, nullptr, "GetUniquePadColor"}, + {326, nullptr, "GetUniquePadAppletDetailedUiType"}, + {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, + {328, nullptr, "AttachAbstractedPadToNpad"}, + {329, nullptr, "DetachAbstractedPadAll"}, + {330, nullptr, "CheckAbstractedPadConnection"}, + {500, nullptr, "SetAppletResourceUserId"}, + {501, nullptr, "RegisterAppletResourceUserId"}, + {502, nullptr, "UnregisterAppletResourceUserId"}, + {503, nullptr, "EnableAppletToGetInput"}, + {504, nullptr, "SetAruidValidForVibration"}, + {505, nullptr, "EnableAppletToGetSixAxisSensor"}, + {506, nullptr, "EnableAppletToGetPadInput"}, + {507, nullptr, "EnableAppletToGetTouchScreen"}, + {510, nullptr, "SetVibrationMasterVolume"}, + {511, nullptr, "GetVibrationMasterVolume"}, + {512, nullptr, "BeginPermitVibrationSession"}, + {513, nullptr, "EndPermitVibrationSession"}, + {514, nullptr, "Unknown514"}, + {520, nullptr, "EnableHandheldHids"}, + {521, nullptr, "DisableHandheldHids"}, + {522, nullptr, "SetJoyConRailEnabled"}, + {523, nullptr, "IsJoyConRailEnabled"}, + {524, nullptr, "IsHandheldHidsEnabled"}, + {525, nullptr, "IsJoyConAttachedOnAllRail"}, + {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, + {541, nullptr, "GetPlayReportControllerUsages"}, + {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, + {543, nullptr, "GetRegisteredDevicesOld"}, + {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"}, + {545, nullptr, "SendConnectionTrigger"}, + {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"}, + {547, nullptr, "GetAllowedBluetoothLinksCount"}, + {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"}, + {549, nullptr, "GetConnectableRegisteredDevices"}, + {700, nullptr, "ActivateUniquePad"}, + {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"}, + {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"}, + {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, + {800, nullptr, "ListSixAxisSensorHandles"}, + {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, + {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, + {803, nullptr, "StartSixAxisSensorUserCalibration"}, + {804, nullptr, "CancelSixAxisSensorUserCalibration"}, + {805, nullptr, "GetUniquePadBluetoothAddress"}, + {806, nullptr, "DisconnectUniquePad"}, + {807, nullptr, "GetUniquePadType"}, + {808, nullptr, "GetUniquePadInterface"}, + {809, nullptr, "GetUniquePadSerialNumber"}, + {810, nullptr, "GetUniquePadControllerNumber"}, + {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, + {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, + {821, nullptr, "StartAnalogStickManualCalibration"}, + {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, + {823, nullptr, "CancelAnalogStickManualCalibration"}, + {824, nullptr, "ResetAnalogStickManualCalibration"}, + {825, nullptr, "GetAnalogStickState"}, + {826, nullptr, "GetAnalogStickManualCalibrationStage"}, + {827, nullptr, "IsAnalogStickButtonPressed"}, + {828, nullptr, "IsAnalogStickInReleasePosition"}, + {829, nullptr, "IsAnalogStickInCircumference"}, + {830, nullptr, "SetNotificationLedPattern"}, + {831, nullptr, "SetNotificationLedPatternWithTimeout"}, + {832, nullptr, "PrepareHidsForNotificationWake"}, + {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, + {851, nullptr, "EnableUsbFullKeyController"}, + {852, nullptr, "IsUsbConnected"}, + {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"}, + {900, nullptr, "ActivateInputDetector"}, + {901, nullptr, "NotifyInputDetector"}, + {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"}, + {1001, nullptr, "GetFirmwareVersion"}, + {1002, nullptr, "GetAvailableFirmwareVersion"}, + {1003, nullptr, "IsFirmwareUpdateAvailable"}, + {1004, nullptr, "CheckFirmwareUpdateRequired"}, + {1005, nullptr, "StartFirmwareUpdate"}, + {1006, nullptr, "AbortFirmwareUpdate"}, + {1007, nullptr, "GetFirmwareUpdateState"}, + {1008, nullptr, "ActivateAudioControl"}, + {1009, nullptr, "AcquireAudioControlEventHandle"}, + {1010, nullptr, "GetAudioControlStates"}, + {1011, nullptr, "DeactivateAudioControl"}, + {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, + {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, + {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, + {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, + {1100, nullptr, "GetHidbusSystemServiceObject"}, + {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, + {1130, nullptr, "InitializeUsbFirmwareUpdate"}, + {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, + {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, + {1133, nullptr, "StartUsbFirmwareUpdate"}, + {1134, nullptr, "GetUsbFirmwareUpdateState"}, + {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, + {1150, nullptr, "SetTouchScreenMagnification"}, + {1151, nullptr, "GetTouchScreenFirmwareVersion"}, + {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, + {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, + {1154, nullptr, "IsFirmwareAvailableForNotification"}, + {1155, nullptr, "SetForceHandheldStyleVibration"}, + {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, + {1157, nullptr, "CancelConnectionTrigger"}, + {1200, nullptr, "IsButtonConfigSupported"}, + {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, + {1202, nullptr, "DeleteButtonConfig"}, + {1203, nullptr, "DeleteButtonConfigEmbedded"}, + {1204, nullptr, "SetButtonConfigEnabled"}, + {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, + {1206, nullptr, "IsButtonConfigEnabled"}, + {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, + {1208, nullptr, "SetButtonConfigEmbedded"}, + {1209, nullptr, "SetButtonConfigFull"}, + {1210, nullptr, "SetButtonConfigLeft"}, + {1211, nullptr, "SetButtonConfigRight"}, + {1212, nullptr, "GetButtonConfigEmbedded"}, + {1213, nullptr, "GetButtonConfigFull"}, + {1214, nullptr, "GetButtonConfigLeft"}, + {1215, nullptr, "GetButtonConfigRight"}, + {1250, nullptr, "IsCustomButtonConfigSupported"}, + {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, + {1252, nullptr, "IsDefaultButtonConfigFull"}, + {1253, nullptr, "IsDefaultButtonConfigLeft"}, + {1254, nullptr, "IsDefaultButtonConfigRight"}, + {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, + {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, + {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, + {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, + {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, + {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, + {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, + {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, + {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, + {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, + {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, + {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, + {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, + {1268, nullptr, "DeleteButtonConfigStorageFull"}, + {1269, nullptr, "DeleteButtonConfigStorageLeft"}, + {1270, nullptr, "DeleteButtonConfigStorageRight"}, + {1271, nullptr, "IsUsingCustomButtonConfig"}, + {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, + {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, + {1274, nullptr, "SetDefaultButtonConfig"}, + {1275, nullptr, "SetAllDefaultButtonConfig"}, + {1276, nullptr, "SetHidButtonConfigEmbedded"}, + {1277, nullptr, "SetHidButtonConfigFull"}, + {1278, nullptr, "SetHidButtonConfigLeft"}, + {1279, nullptr, "SetHidButtonConfigRight"}, + {1280, nullptr, "GetHidButtonConfigEmbedded"}, + {1281, nullptr, "GetHidButtonConfigFull"}, + {1282, nullptr, "GetHidButtonConfigLeft"}, + {1283, nullptr, "GetHidButtonConfigRight"}, + {1284, nullptr, "GetButtonConfigStorageEmbedded"}, + {1285, nullptr, "GetButtonConfigStorageFull"}, + {1286, nullptr, "GetButtonConfigStorageLeft"}, + {1287, nullptr, "GetButtonConfigStorageRight"}, + {1288, nullptr, "SetButtonConfigStorageEmbedded"}, + {1289, nullptr, "SetButtonConfigStorageFull"}, + {1290, nullptr, "DeleteButtonConfigStorageRight"}, + {1291, nullptr, "DeleteButtonConfigStorageRight"}, + }; + // clang-format on + + RegisterHandlers(functions); + + joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent"); + acquire_device_registered_event = + service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent"); + acquire_connection_trigger_timeout_event = + service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent"); + unique_pad_connection_event = + service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle"); +} + +IHidSystemServer::~IHidSystemServer() { + service_context.CloseEvent(joy_detach_event); + service_context.CloseEvent(acquire_device_registered_event); + service_context.CloseEvent(acquire_connection_trigger_timeout_event); + service_context.CloseEvent(unique_pad_connection_event); +}; + +void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "called"); + + GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(system.HIDCore().GetLastActiveController()); +} + +void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "called"); + + GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + Core::HID::NpadColor left_color{}; + Core::HID::NpadColor right_color{}; + // TODO: Get colors from Npad + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(left_color); + rb.PushRaw(right_color); +} + +void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_INFO(Service_HID, "(STUBBED) called"); + + Core::HID::NpadStyleSet supported_styleset = + GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(supported_styleset); +} + +void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_INFO(Service_HID, "(STUBBED) called"); + + Core::HID::NpadStyleSet supported_styleset = + GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(supported_styleset); +} + +void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + const NPad::AppletDetailedUiType detailed_ui_type = + GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(detailed_ui_type); +} + +void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); +} + +void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); + rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); +} + +void IHidSystemServer::HasBattery(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + struct LeftRightBattery { + bool left; + bool right; + }; + + LeftRightBattery left_right_battery{ + .left = false, + .right = false, + }; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(left_right_battery); +} + +void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + const std::vector<Core::HID::UniquePadId> unique_pads{}; + + if (!unique_pads.empty()) { + ctx.WriteBuffer(unique_pads); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(unique_pads.size())); +} + +void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent()); +} + +void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) { + LOG_INFO(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent()); +} + +void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + struct RegisterData { + std::array<u8, 0x68> data; + }; + static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size"); + std::vector<RegisterData> registered_devices{}; + + if (!registered_devices.empty()) { + ctx.WriteBuffer(registered_devices); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(registered_devices.size()); +} + +void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent()); + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(0); +} + +void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(joy_detach_event->GetReadableEvent()); +} + +void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { + const bool is_enabled = false; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_enabled); +} + +void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { + const bool button_pressed = false; + + LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", + button_pressed); // Spams a lot when controller applet is open + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(button_pressed); +} + +void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + Core::HID::TouchScreenConfigurationForNx touchscreen_config{ + .mode = Core::HID::TouchScreenModeForNx::Finger, + }; + + if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && + touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { + touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(touchscreen_config); +} + +std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() { + resource_manager->Initialize(); + return resource_manager; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h new file mode 100644 index 000000000..822d5e5b9 --- /dev/null +++ b/src/core/hle/service/hid/hid_system_server.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KEvent; +} + +namespace Service::HID { +class ResourceManager; + +class IHidSystemServer final : public ServiceFramework<IHidSystemServer> { +public: + explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); + ~IHidSystemServer() override; + +private: + void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); + void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); + void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); + void GetLastActiveNpad(HLERequestContext& ctx); + void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx); + void GetNpadFullKeyGripColor(HLERequestContext& ctx); + void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx); + void SetSupportedNpadStyleSetAll(HLERequestContext& ctx); + void GetAppletDetailedUiType(HLERequestContext& ctx); + void GetNpadInterfaceType(HLERequestContext& ctx); + void GetNpadLeftRightInterfaceType(HLERequestContext& ctx); + void HasBattery(HLERequestContext& ctx); + void HasLeftRightBattery(HLERequestContext& ctx); + void GetUniquePadsFromNpad(HLERequestContext& ctx); + void GetIrSensorState(HLERequestContext& ctx); + void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); + void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); + void GetRegisteredDevices(HLERequestContext& ctx); + void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx); + void GetUniquePadIds(HLERequestContext& ctx); + void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); + void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); + void InitializeFirmwareUpdate(HLERequestContext& ctx); + void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); + void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); + + std::shared_ptr<ResourceManager> GetResourceManager(); + + Kernel::KEvent* acquire_connection_trigger_timeout_event; + Kernel::KEvent* acquire_device_registered_event; + Kernel::KEvent* joy_detach_event; + Kernel::KEvent* unique_pad_connection_event; + KernelHelpers::ServiceContext service_context; + std::shared_ptr<ResourceManager> resource_manager; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h new file mode 100644 index 000000000..b87cc10e3 --- /dev/null +++ b/src/core/hle/service/hid/hid_util.h @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hid/hid_types.h" +#include "core/hle/service/hid/errors.h" + +namespace Service::HID { + +constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) { + switch (npad_id) { + case Core::HID::NpadIdType::Player1: + case Core::HID::NpadIdType::Player2: + case Core::HID::NpadIdType::Player3: + case Core::HID::NpadIdType::Player4: + case Core::HID::NpadIdType::Player5: + case Core::HID::NpadIdType::Player6: + case Core::HID::NpadIdType::Player7: + case Core::HID::NpadIdType::Player8: + case Core::HID::NpadIdType::Other: + case Core::HID::NpadIdType::Handheld: + return true; + default: + return false; + } +} + +constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) { + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id)); + const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + + if (!npad_id) { + return InvalidNpadId; + } + if (!device_index) { + return NpadDeviceIndexOutOfRange; + } + + return ResultSuccess; +} + +constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { + switch (handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Handheld: + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::JoyconLeft: + case Core::HID::NpadStyleIndex::JoyconRight: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::N64: + case Core::HID::NpadStyleIndex::SystemExt: + case Core::HID::NpadStyleIndex::System: + // These support vibration + break; + default: + return VibrationInvalidStyleIndex; + } + + if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) { + return VibrationInvalidNpadId; + } + + if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) { + return VibrationDeviceIndexOutOfRange; + } + + return ResultSuccess; +} + +/// Converts a Core::HID::NpadIdType to an array index. +constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) { + switch (npad_id_type) { + case Core::HID::NpadIdType::Player1: + return 0; + case Core::HID::NpadIdType::Player2: + return 1; + case Core::HID::NpadIdType::Player3: + return 2; + case Core::HID::NpadIdType::Player4: + return 3; + case Core::HID::NpadIdType::Player5: + return 4; + case Core::HID::NpadIdType::Player6: + return 5; + case Core::HID::NpadIdType::Player7: + return 6; + case Core::HID::NpadIdType::Player8: + return 7; + case Core::HID::NpadIdType::Handheld: + return 8; + case Core::HID::NpadIdType::Other: + return 9; + default: + return 8; + } +} + +/// Converts an array index to a Core::HID::NpadIdType +constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) { + switch (index) { + case 0: + return Core::HID::NpadIdType::Player1; + case 1: + return Core::HID::NpadIdType::Player2; + case 2: + return Core::HID::NpadIdType::Player3; + case 3: + return Core::HID::NpadIdType::Player4; + case 4: + return Core::HID::NpadIdType::Player5; + case 5: + return Core::HID::NpadIdType::Player6; + case 6: + return Core::HID::NpadIdType::Player7; + case 7: + return Core::HID::NpadIdType::Player8; + case 8: + return Core::HID::NpadIdType::Handheld; + case 9: + return Core::HID::NpadIdType::Other; + default: + return Core::HID::NpadIdType::Invalid; + } +} + +constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) { + switch (index) { + case 0: + return Core::HID::NpadStyleSet::Fullkey; + case 1: + return Core::HID::NpadStyleSet::Handheld; + case 2: + return Core::HID::NpadStyleSet::JoyDual; + case 3: + return Core::HID::NpadStyleSet::JoyLeft; + case 4: + return Core::HID::NpadStyleSet::JoyRight; + case 5: + return Core::HID::NpadStyleSet::Palma; + default: + return Core::HID::NpadStyleSet::None; + } +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index d383a266d..39b9a4474 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -12,6 +12,7 @@ #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_util.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/irsensor/clustering_processor.h" #include "core/hle/service/hid/irsensor/image_transfer_processor.h" @@ -320,7 +321,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { } Core::IrSensor::IrCameraHandle camera_handle{ - .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), + .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), .npad_type = Core::HID::NpadStyleIndex::None, }; @@ -545,7 +546,7 @@ void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { if (camera_handle.npad_id > - static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { + static_cast<u8>(HID::NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { return InvalidIrCameraHandle; } if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index a8fa19025..c8e6dab17 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -3,15 +3,12 @@ #pragma once +#include "core/core.h" #include "core/hid/hid_types.h" #include "core/hid/irs_types.h" #include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/service.h" -namespace Core { -class System; -} - namespace Core::HID { class EmulatedController; } // namespace Core::HID diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp new file mode 100644 index 000000000..e76d4eea9 --- /dev/null +++ b/src/core/hle/service/hid/resource_manager.cpp @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" + +#include "core/hle/service/hid/controllers/console_six_axis.h" +#include "core/hle/service/hid/controllers/debug_pad.h" +#include "core/hle/service/hid/controllers/gesture.h" +#include "core/hle/service/hid/controllers/keyboard.h" +#include "core/hle/service/hid/controllers/mouse.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/hid/controllers/seven_six_axis.h" +#include "core/hle/service/hid/controllers/six_axis.h" +#include "core/hle/service/hid/controllers/stubbed.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/hle/service/hid/controllers/xpad.h" + +namespace Service::HID { + +// Updating period for each HID device. +// Period time is obtained by measuring the number of samples in a second on HW using a homebrew +// Correct npad_update_ns is 4ms this is overclocked to lower input lag +constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) +constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) +constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) +constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) + +ResourceManager::ResourceManager(Core::System& system_) + : system{system_}, service_context{system_, "hid"} {} + +ResourceManager::~ResourceManager() = default; + +void ResourceManager::Initialize() { + if (is_initialized) { + return; + } + + u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); + debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory); + mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory); + debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory); + keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory); + unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory); + npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context); + gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory); + touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory); + xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory); + + palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context); + + home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory); + sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory); + capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory); + + six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad); + console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory); + seven_six_axis = std::make_shared<SevenSixAxis>(system); + + home_button->SetCommonHeaderOffset(0x4C00); + sleep_button->SetCommonHeaderOffset(0x4E00); + capture_button->SetCommonHeaderOffset(0x5000); + unique_pad->SetCommonHeaderOffset(0x5A00); + debug_mouse->SetCommonHeaderOffset(0x3DC00); + + // Homebrew doesn't try to activate some controllers, so we activate them by default + npad->Activate(); + six_axis->Activate(); + touch_screen->Activate(); + + system.HIDCore().ReloadInputDevices(); + is_initialized = true; +} +std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const { + return capture_button; +} + +std::shared_ptr<ConsoleSixAxis> ResourceManager::GetConsoleSixAxis() const { + return console_six_axis; +} + +std::shared_ptr<DebugMouse> ResourceManager::GetDebugMouse() const { + return debug_mouse; +} + +std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const { + return debug_pad; +} + +std::shared_ptr<Gesture> ResourceManager::GetGesture() const { + return gesture; +} + +std::shared_ptr<HomeButton> ResourceManager::GetHomeButton() const { + return home_button; +} + +std::shared_ptr<Keyboard> ResourceManager::GetKeyboard() const { + return keyboard; +} + +std::shared_ptr<Mouse> ResourceManager::GetMouse() const { + return mouse; +} + +std::shared_ptr<NPad> ResourceManager::GetNpad() const { + return npad; +} + +std::shared_ptr<Palma> ResourceManager::GetPalma() const { + return palma; +} + +std::shared_ptr<SevenSixAxis> ResourceManager::GetSevenSixAxis() const { + return seven_six_axis; +} + +std::shared_ptr<SixAxis> ResourceManager::GetSixAxis() const { + return six_axis; +} + +std::shared_ptr<SleepButton> ResourceManager::GetSleepButton() const { + return sleep_button; +} + +std::shared_ptr<TouchScreen> ResourceManager::GetTouchScreen() const { + return touch_screen; +} + +std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const { + return unique_pad; +} + +void ResourceManager::UpdateControllers(std::uintptr_t user_data, + std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + debug_pad->OnUpdate(core_timing); + unique_pad->OnUpdate(core_timing); + gesture->OnUpdate(core_timing); + touch_screen->OnUpdate(core_timing); + palma->OnUpdate(core_timing); + home_button->OnUpdate(core_timing); + sleep_button->OnUpdate(core_timing); + capture_button->OnUpdate(core_timing); + xpad->OnUpdate(core_timing); +} + +void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + npad->OnUpdate(core_timing); +} + +void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, + std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + mouse->OnUpdate(core_timing); + debug_mouse->OnUpdate(core_timing); + keyboard->OnUpdate(core_timing); +} + +void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + six_axis->OnUpdate(core_timing); + seven_six_axis->OnUpdate(core_timing); + console_six_axis->OnUpdate(core_timing); +} + +IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource) + : ServiceFramework{system_, "IAppletResource"} { + static const FunctionInfo functions[] = { + {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, + }; + RegisterHandlers(functions); + + resource->Initialize(); + + // Register update callbacks + npad_update_event = Core::Timing::CreateEvent( + "HID::UpdatePadCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional<std::chrono::nanoseconds> { + const auto guard = LockService(); + resource->UpdateNpad(user_data, ns_late); + return std::nullopt; + }); + default_update_event = Core::Timing::CreateEvent( + "HID::UpdateDefaultCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional<std::chrono::nanoseconds> { + const auto guard = LockService(); + resource->UpdateControllers(user_data, ns_late); + return std::nullopt; + }); + mouse_keyboard_update_event = Core::Timing::CreateEvent( + "HID::UpdateMouseKeyboardCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional<std::chrono::nanoseconds> { + const auto guard = LockService(); + resource->UpdateMouseKeyboard(user_data, ns_late); + return std::nullopt; + }); + motion_update_event = Core::Timing::CreateEvent( + "HID::UpdateMotionCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional<std::chrono::nanoseconds> { + const auto guard = LockService(); + resource->UpdateMotion(user_data, ns_late); + return std::nullopt; + }); + + system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); + system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, + default_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); +} + +IAppletResource::~IAppletResource() { + system.CoreTiming().UnscheduleEvent(npad_update_event, 0); + system.CoreTiming().UnscheduleEvent(default_update_event, 0); + system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); + system.CoreTiming().UnscheduleEvent(motion_update_event, 0); +} + +void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h new file mode 100644 index 000000000..2b6a9b5e6 --- /dev/null +++ b/src/core/hle/service/hid/resource_manager.h @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core::Timing { +struct EventType; +} + +namespace Service::HID { +class Controller_Stubbed; +class ConsoleSixAxis; +class DebugPad; +class Gesture; +class Keyboard; +class Mouse; +class NPad; +class Palma; +class SevenSixAxis; +class SixAxis; +class TouchScreen; +class XPad; + +using CaptureButton = Controller_Stubbed; +using DebugMouse = Controller_Stubbed; +using HomeButton = Controller_Stubbed; +using SleepButton = Controller_Stubbed; +using UniquePad = Controller_Stubbed; + +class ResourceManager { + +public: + explicit ResourceManager(Core::System& system_); + ~ResourceManager(); + + void Initialize(); + + std::shared_ptr<CaptureButton> GetCaptureButton() const; + std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const; + std::shared_ptr<DebugMouse> GetDebugMouse() const; + std::shared_ptr<DebugPad> GetDebugPad() const; + std::shared_ptr<Gesture> GetGesture() const; + std::shared_ptr<HomeButton> GetHomeButton() const; + std::shared_ptr<Keyboard> GetKeyboard() const; + std::shared_ptr<Mouse> GetMouse() const; + std::shared_ptr<NPad> GetNpad() const; + std::shared_ptr<Palma> GetPalma() const; + std::shared_ptr<SevenSixAxis> GetSevenSixAxis() const; + std::shared_ptr<SixAxis> GetSixAxis() const; + std::shared_ptr<SleepButton> GetSleepButton() const; + std::shared_ptr<TouchScreen> GetTouchScreen() const; + std::shared_ptr<UniquePad> GetUniquePad() const; + + void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + +private: + bool is_initialized{false}; + + std::shared_ptr<CaptureButton> capture_button = nullptr; + std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; + std::shared_ptr<DebugMouse> debug_mouse = nullptr; + std::shared_ptr<DebugPad> debug_pad = nullptr; + std::shared_ptr<Gesture> gesture = nullptr; + std::shared_ptr<HomeButton> home_button = nullptr; + std::shared_ptr<Keyboard> keyboard = nullptr; + std::shared_ptr<Mouse> mouse = nullptr; + std::shared_ptr<NPad> npad = nullptr; + std::shared_ptr<Palma> palma = nullptr; + std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr; + std::shared_ptr<SixAxis> six_axis = nullptr; + std::shared_ptr<SleepButton> sleep_button = nullptr; + std::shared_ptr<TouchScreen> touch_screen = nullptr; + std::shared_ptr<UniquePad> unique_pad = nullptr; + std::shared_ptr<XPad> xpad = nullptr; + + // TODO: Create these resources + // std::shared_ptr<AudioControl> audio_control = nullptr; + // std::shared_ptr<ButtonConfig> button_config = nullptr; + // std::shared_ptr<Config> config = nullptr; + // std::shared_ptr<Connection> connection = nullptr; + // std::shared_ptr<CustomConfig> custom_config = nullptr; + // std::shared_ptr<Digitizer> digitizer = nullptr; + // std::shared_ptr<Hdls> hdls = nullptr; + // std::shared_ptr<PlayReport> play_report = nullptr; + // std::shared_ptr<Rail> rail = nullptr; + + Core::System& system; + KernelHelpers::ServiceContext service_context; +}; + +class IAppletResource final : public ServiceFramework<IAppletResource> { +public: + explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource); + ~IAppletResource() override; + +private: + void GetSharedMemoryHandle(HLERequestContext& ctx); + + std::shared_ptr<Core::Timing::EventType> npad_update_event; + std::shared_ptr<Core::Timing::EventType> default_update_event; + std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; + std::shared_ptr<Core::Timing::EventType> motion_update_event; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 7927f8264..961f89a14 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -115,12 +115,20 @@ public: {400, nullptr, "InitializeSystem"}, {401, nullptr, "FinalizeSystem"}, {402, nullptr, "SetOperationMode"}, - {403, nullptr, "InitializeSystem2"}, + {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"}, }; // clang-format on RegisterHandlers(functions); } + +private: + void InitializeSystem2(HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; class IUserLocalCommunicationService final diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index a71d26157..ad534177d 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -7,6 +7,7 @@ #include "core/core.h" #include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/service/hid/hid_util.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/common/device_manager.h" @@ -24,7 +25,7 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex for (u32 device_index = 0; device_index < devices.size(); device_index++) { devices[device_index] = - std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system, + std::make_shared<NfcDevice>(HID::IndexToNpadIdType(device_index), system, service_context, availability_change_event); } diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h index 3da8cc3aa..7fd808f54 100644 --- a/src/core/hle/service/nvnflinger/buffer_item.h +++ b/src/core/hle/service/nvnflinger/buffer_item.h @@ -15,7 +15,7 @@ namespace Service::android { -struct GraphicBuffer; +class GraphicBuffer; class BufferItem final { public: diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index 51291539d..d91886bed 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -5,7 +5,6 @@ // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp #include "common/logging/log.h" -#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" @@ -14,9 +13,8 @@ namespace Service::android { -BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, - Service::Nvidia::NvCore::NvMap& nvmap_) - : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) + : core{std::move(core_)}, slots{core->slots} {} BufferQueueConsumer::~BufferQueueConsumer() = default; @@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc slots[slot].buffer_state = BufferState::Free; - nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); - listener = core->connected_producer_listener; LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); @@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_ return Status::NoError; } +Status BufferQueueConsumer::Disconnect() { + LOG_DEBUG(Service_Nvnflinger, "called"); + + std::scoped_lock lock{core->mutex}; + + if (core->consumer_listener == nullptr) { + LOG_ERROR(Service_Nvnflinger, "no consumer is connected"); + return Status::BadValue; + } + + core->is_abandoned = true; + core->consumer_listener = nullptr; + core->queue.clear(); + core->FreeAllBuffersLocked(); + core->SignalDequeueCondition(); + + return Status::NoError; +} + Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { if (out_slot_mask == nullptr) { LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h index 50ed0bb5f..0a61e8dbd 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h @@ -13,10 +13,6 @@ #include "core/hle/service/nvnflinger/buffer_queue_defs.h" #include "core/hle/service/nvnflinger/status.h" -namespace Service::Nvidia::NvCore { -class NvMap; -} // namespace Service::Nvidia::NvCore - namespace Service::android { class BufferItem; @@ -25,19 +21,18 @@ class IConsumerListener; class BufferQueueConsumer final { public: - explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, - Service::Nvidia::NvCore::NvMap& nvmap_); + explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); ~BufferQueueConsumer(); Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); + Status Disconnect(); Status GetReleasedBuffers(u64* out_slot_mask); private: std::shared_ptr<BufferQueueCore> core; BufferQueueDefs::SlotsType& slots; - Service::Nvidia::NvCore::NvMap& nvmap; }; } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index ed66f6f5b..4ed5e5978 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default; BufferQueueCore::~BufferQueueCore() = default; -void BufferQueueCore::NotifyShutdown() { - std::scoped_lock lock{mutex}; - - is_shutting_down = true; - - SignalDequeueCondition(); -} - void BufferQueueCore::SignalDequeueCondition() { dequeue_possible.store(true); dequeue_condition.notify_all(); } bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { - if (is_shutting_down) { - return false; - } - dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); dequeue_possible.store(false); diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index 9164f08a0..e513d183b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -34,8 +34,6 @@ public: BufferQueueCore(); ~BufferQueueCore(); - void NotifyShutdown(); - private: void SignalDequeueCondition(); bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); @@ -74,7 +72,6 @@ private: u32 transform_hint{}; bool is_allocating{}; mutable std::condition_variable_any is_allocating_condition; - bool is_shutting_down{}; }; } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 6e7a49658..5d8762d25 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -13,7 +13,6 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/hle_ipc.h" #include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" #include "core/hle/service/nvnflinger/buffer_queue_producer.h" #include "core/hle/service/nvnflinger/consumer_listener.h" @@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; - nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); - sticky_transform = sticky_transform_; if (core->queue.empty()) { @@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { return Status::NoError; } - // HACK: We are not Android. Remove handle for items in queue, and clear queue. - // Allows synchronous destruction of nvmap handles. - for (auto& item : core->queue) { - nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); - } - core->queue.clear(); - switch (api) { case NativeWindowApi::Egl: case NativeWindowApi::Cpu: case NativeWindowApi::Media: case NativeWindowApi::Camera: if (core->connected_api == api) { + core->queue.clear(); core->FreeAllBuffersLocked(); core->connected_producer_listener = nullptr; core->connected_api = NativeWindowApi::NoConnectedApi; @@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { } Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, - const std::shared_ptr<GraphicBuffer>& buffer) { + const std::shared_ptr<NvGraphicBuffer>& buffer) { LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { @@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, slots[slot] = {}; slots[slot].fence = Fence::NoFence(); - slots[slot].graphic_buffer = buffer; + slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer); slots[slot].frame_number = 0; // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for @@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u } case TransactionId::SetPreallocatedBuffer: { const auto slot = parcel_in.Read<s32>(); - const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); + const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>(); status = SetPreallocatedBuffer(slot, buffer); break; @@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u status = RequestBuffer(slot, &buf); - parcel_out.WriteFlattenedObject(buf); + parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get()); break; } case TransactionId::QueueBuffer: { diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index d4201c104..64c17d56c 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -38,6 +38,7 @@ namespace Service::android { class BufferQueueCore; class IProducerListener; +struct NvGraphicBuffer; class BufferQueueProducer final : public IBinder { public: @@ -65,7 +66,7 @@ public: bool producer_controlled_by_app, QueueBufferOutput* output); Status Disconnect(NativeWindowApi api); - Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); + Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer); private: BufferQueueProducer(const BufferQueueProducer&) = delete; diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index d8c9dec3b..d25bca049 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -13,7 +13,7 @@ namespace Service::android { -struct GraphicBuffer; +class GraphicBuffer; enum class BufferState : u32 { Free = 0, diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp index 4dcda8dac..1059e72bf 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.cpp +++ b/src/core/hle/service/nvnflinger/consumer_base.cpp @@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) { consumer->Connect(shared_from_this(), controlled_by_app); } +void ConsumerBase::Abandon() { + LOG_DEBUG(Service_Nvnflinger, "called"); + + std::scoped_lock lock{mutex}; + + if (!is_abandoned) { + this->AbandonLocked(); + is_abandoned = true; + } +} + +void ConsumerBase::AbandonLocked() { + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + this->FreeBufferLocked(i); + } + // disconnect from the BufferQueue + consumer->Disconnect(); + consumer = nullptr; +} + void ConsumerBase::FreeBufferLocked(s32 slot_index) { LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h index 264829414..ea3e9e97a 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.h +++ b/src/core/hle/service/nvnflinger/consumer_base.h @@ -24,6 +24,7 @@ class BufferQueueConsumer; class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { public: void Connect(bool controlled_by_app); + void Abandon(); protected: explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); @@ -34,6 +35,7 @@ protected: void OnBuffersReleased() override; void OnSidebandStreamChanged() override; + void AbandonLocked(); void FreeBufferLocked(s32 slot_index); Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index 6dc327b8b..d7db24f42 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp @@ -166,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { }(); void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { - auto buffer = std::make_shared<android::GraphicBuffer>(); + auto buffer = std::make_shared<android::NvGraphicBuffer>(); buffer->width = SharedBufferWidth; buffer->height = SharedBufferHeight; buffer->stride = SharedBufferBlockLinearStride; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index bebb45eae..0745434c5 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) { vsync_signal.Wait(); const auto lock_guard = Lock(); - Compose(); + + if (!is_abandoned) { + Compose(); + } } } @@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() { } ShutdownLayers(); - vsync_thread = {}; if (nvdrv) { nvdrv->Close(disp_fd); @@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() { } void Nvnflinger::ShutdownLayers() { - const auto lock_guard = Lock(); - for (auto& display : displays) { - for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { - display.GetLayer(layer).Core().NotifyShutdown(); + // Abandon consumers. + { + const auto lock_guard = Lock(); + for (auto& display : displays) { + for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { + display.GetLayer(layer).GetConsumer().Abandon(); + } } + + is_abandoned = true; } + + // Join the vsync thread, if it exists. + vsync_thread = {}; } void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 959d8b46b..f5d73acdb 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -140,6 +140,8 @@ private: s32 swap_interval = 1; + bool is_abandoned = false; + /// Event that handles screen composition. std::shared_ptr<Core::Timing::EventType> multi_composition_event; std::shared_ptr<Core::Timing::EventType> single_composition_event; diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h index 7af166c40..3fa0fe15b 100644 --- a/src/core/hle/service/nvnflinger/status.h +++ b/src/core/hle/service/nvnflinger/status.h @@ -19,7 +19,7 @@ enum class Status : s32 { Busy = -16, NoInit = -19, BadValue = -22, - InvalidOperation = -37, + InvalidOperation = -38, BufferNeedsReallocation = 1, ReleaseAllBuffers = 2, }; diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp new file mode 100644 index 000000000..ce70946ec --- /dev/null +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" + +namespace Service::android { + +static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) { + if (buffer) { + return *buffer; + } else { + return {}; + } +} + +GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) + : NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {} + +GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap, + std::shared_ptr<NvGraphicBuffer> buffer) + : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) { + if (this->BufferId() > 0) { + m_nvmap->DuplicateHandle(this->BufferId(), true); + } +} + +GraphicBuffer::~GraphicBuffer() { + if (m_nvmap != nullptr && this->BufferId() > 0) { + m_nvmap->FreeHandle(this->BufferId(), true); + } +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h index 3eac5cedd..da430aa75 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h @@ -6,16 +6,22 @@ #pragma once +#include <memory> + #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hle/service/nvnflinger/pixel_format.h" +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::android { -struct GraphicBuffer final { - constexpr GraphicBuffer() = default; +struct NvGraphicBuffer { + constexpr NvGraphicBuffer() = default; - constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) + constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, usage{static_cast<s32>(usage_)} {} @@ -93,6 +99,17 @@ struct GraphicBuffer final { u32 offset{}; INSERT_PADDING_WORDS(60); }; -static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); +static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size"); + +class GraphicBuffer final : public NvGraphicBuffer { +public: + explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage); + explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap, + std::shared_ptr<NvGraphicBuffer> buffer); + ~GraphicBuffer(); + +private: + Service::Nvidia::NvCore::NvMap* m_nvmap{}; +}; } // namespace Service::android diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index ec3af80af..f5edfdc8b 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -19,19 +19,8 @@ namespace Service::Set { -namespace { -constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; - -enum class GetFirmwareVersionType { - Version1, - Version2, -}; - -void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, - GetFirmwareVersionType type) { - ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100, - "FirmwareVersion output buffer must be 0x100 bytes in size!"); - +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type) { constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; auto& fsc = system.GetFileSystemController(); @@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); } - const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { + const auto early_exit_failure = [](std::string_view desc, Result code) { LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", desc); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(code); + return code; }; const auto ver_file = romfs->GetFile("file"); if (ver_file == nullptr) { - early_exit_failure("The system version archive didn't contain the file 'file'.", - FileSys::ERROR_INVALID_ARGUMENT); - return; + return early_exit_failure("The system version archive didn't contain the file 'file'.", + FileSys::ERROR_INVALID_ARGUMENT); } auto data = ver_file->ReadAllBytes(); - if (data.size() != 0x100) { - early_exit_failure("The system version file 'file' was not the correct size.", - FileSys::ERROR_OUT_OF_BOUNDS); - return; + if (data.size() != sizeof(FirmwareVersionFormat)) { + return early_exit_failure("The system version file 'file' was not the correct size.", + FileSys::ERROR_OUT_OF_BOUNDS); } + std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); + // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will // zero out the REVISION_MINOR field. if (type == GetFirmwareVersionType::Version1) { - data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; + out_firmware.revision_minor = 0; } - ctx.WriteBuffer(data); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + return ResultSuccess; } -} // Anonymous namespace void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); - GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); - GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { @@ -431,8 +435,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { u8 battery_percentage_flag{1}; - LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", - battery_percentage_flag); + LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -492,6 +495,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); } +void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "(STUBBED) called"); + + const HomeMenuScheme default_color = { + .main = 0xFF323232, + .back = 0xFF323232, + .sub = 0xFFFFFFFF, + .bezel = 0xFFFFFFFF, + .extra = 0xFF000000, + }; + + IPC::ResponseBuilder rb{ctx, 7}; + rb.Push(ResultSuccess); + rb.PushRaw(default_color); +} + +void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); @@ -674,7 +700,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {171, nullptr, "SetChineseTraditionalInputMethod"}, {172, nullptr, "GetPtmCycleCountReliability"}, {173, nullptr, "SetPtmCycleCountReliability"}, - {174, nullptr, "GetHomeMenuScheme"}, + {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"}, {175, nullptr, "GetThemeSettings"}, {176, nullptr, "SetThemeSettings"}, {177, nullptr, "GetThemeKey"}, @@ -685,7 +711,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {182, nullptr, "SetT"}, {183, nullptr, "GetPlatformRegion"}, {184, nullptr, "SetPlatformRegion"}, - {185, nullptr, "GetHomeMenuSchemeModel"}, + {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, {186, nullptr, "GetMemoryUsageRateFlag"}, {187, nullptr, "GetTouchScreenMode"}, {188, nullptr, "SetTouchScreenMode"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index c7dba2a9e..5f770fd32 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -4,6 +4,7 @@ #pragma once #include "common/uuid.h" +#include "core/hle/result.h" #include "core/hle/service/service.h" #include "core/hle/service/time/clock_types.h" @@ -12,6 +13,29 @@ class System; } namespace Service::Set { +enum class LanguageCode : u64; +enum class GetFirmwareVersionType { + Version1, + Version2, +}; + +struct FirmwareVersionFormat { + u8 major; + u8 minor; + u8 micro; + INSERT_PADDING_BYTES(1); + u8 revision_major; + u8 revision_minor; + INSERT_PADDING_BYTES(2); + std::array<char, 0x20> platform; + std::array<u8, 0x40> version_hash; + std::array<char, 0x18> display_version; + std::array<char, 0x80> display_title; +}; +static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size"); + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type); class SET_SYS final : public ServiceFramework<SET_SYS> { public: @@ -269,6 +293,16 @@ private: }; static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); + /// This is nn::settings::system::HomeMenuScheme + struct HomeMenuScheme { + u32 main; + u32 back; + u32 sub; + u32 bezel; + u32 extra; + }; + static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"); + void SetLanguageCode(HLERequestContext& ctx); void GetFirmwareVersion(HLERequestContext& ctx); void GetFirmwareVersion2(HLERequestContext& ctx); @@ -305,6 +339,8 @@ private: void GetKeyboardLayout(HLERequestContext& ctx); void GetChineseTraditionalInputMethod(HLERequestContext& ctx); void GetFieldTestingFlag(HLERequestContext& ctx); + void GetHomeMenuScheme(HLERequestContext& ctx); + void GetHomeMenuSchemeModel(HLERequestContext& ctx); AccountSettings account_settings{ .flags = {}, diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index 9fc01ea90..7149fffeb 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -11,6 +11,11 @@ #include "core/hle/service/time/errors.h" #include "core/hle/service/time/time_zone_types.h" +// Defined by WinBase.h on Windows +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + namespace Service::Time::Clock { enum class TimeType : u8 { diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index f0b5eff8a..d30f49877 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont return { buffer_queue_core, std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), - std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; + std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; } Display::Display(u64 id, std::string name_, diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 84b60a928..a3431772a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,8 +1,10 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-FileCopyrightText: 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> +#include <mutex> #include <span> #include "common/assert.h" @@ -10,6 +12,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/page_table.h" +#include "common/scope_exit.h" #include "common/settings.h" #include "common/swap.h" #include "core/core.h" @@ -318,7 +321,7 @@ struct Memory::Impl { [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { if constexpr (!UNSAFE) { - system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); + HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); } std::memcpy(host_ptr, src_buffer, copy_amount); }, @@ -351,7 +354,7 @@ struct Memory::Impl { }, [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { - system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); + HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); std::memset(host_ptr, 0, copy_amount); }, [](const std::size_t copy_amount) {}); @@ -420,7 +423,7 @@ struct Memory::Impl { const std::size_t block_size) { // dc cvac: Store to point of coherency // CPU flush -> GPU invalidate - system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); + HandleRasterizerWrite(GetInteger(current_vaddr), block_size); }; return PerformCacheOperation(dest_addr, size, on_rasterizer); } @@ -430,7 +433,7 @@ struct Memory::Impl { const std::size_t block_size) { // dc civac: Store to point of coherency, and invalidate from cache // CPU flush -> GPU invalidate - system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); + HandleRasterizerWrite(GetInteger(current_vaddr), block_size); }; return PerformCacheOperation(dest_addr, size, on_rasterizer); } @@ -767,7 +770,18 @@ struct Memory::Impl { } void HandleRasterizerWrite(VAddr address, size_t size) { - const size_t core = system.GetCurrentHostThreadID(); + constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; + const size_t core = std::min(system.GetCurrentHostThreadID(), + sys_core); // any other calls threads go to syscore. + // Guard on sys_core; + if (core == sys_core) [[unlikely]] { + sys_core_guard.lock(); + } + SCOPE_EXIT({ + if (core == sys_core) [[unlikely]] { + sys_core_guard.unlock(); + } + }); auto& current_area = rasterizer_write_areas[core]; VAddr subaddress = address >> YUZU_PAGEBITS; bool do_collection = current_area.last_address == subaddress; @@ -799,6 +813,7 @@ struct Memory::Impl { rasterizer_read_areas{}; std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; + std::mutex sys_core_guard; }; Memory::Memory(Core::System& system_) : system{system_} { diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 53a89cc8f..db30ba598 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -10,7 +10,8 @@ #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/sm/sm.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" @@ -54,23 +55,20 @@ void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) } u64 StandardVmCallbacks::HidKeysDown() { - const auto hid = system.ServiceManager().GetService<Service::HID::Hid>("hid"); + const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid"); if (hid == nullptr) { LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!"); return 0; } - const auto applet_resource = hid->GetAppletResource(); + const auto applet_resource = hid->GetResourceManager(); if (applet_resource == nullptr) { LOG_WARNING(CheatEngine, "Attempted to read input state, but applet resource is not initialized!"); return 0; } - const auto press_state = - applet_resource - ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) - .GetAndResetPressState(); + const auto press_state = applet_resource->GetNpad()->GetAndResetPressState(); return static_cast<u64>(press_state & HID::NpadButton::All); } diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt new file mode 100644 index 000000000..22e9337c4 --- /dev/null +++ b/src/frontend_common/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +add_library(frontend_common STATIC + config.cpp + config.h +) + +create_target_directory_groups(frontend_common) +target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers) diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp new file mode 100644 index 000000000..7474cb0f9 --- /dev/null +++ b/src/frontend_common/config.cpp @@ -0,0 +1,1008 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <array> +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/settings.h" +#include "common/settings_common.h" +#include "common/settings_enums.h" +#include "config.h" +#include "core/core.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "network/network.h" + +#include <boost/algorithm/string/replace.hpp> + +#include "common/string_util.h" + +namespace FS = Common::FS; + +Config::Config(const ConfigType config_type) + : type(config_type), global{config_type == ConfigType::GlobalConfig} {} + +void Config::Initialize(const std::string& config_name) { + const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); + const auto config_file = fmt::format("{}.ini", config_name); + + switch (type) { + case ConfigType::GlobalConfig: + config_loc = FS::PathToUTF8String(fs_config_loc / config_file); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + Reload(); + break; + case ConfigType::PerGameConfig: + config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + Reload(); + break; + case ConfigType::InputProfile: + config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + break; + } +} + +void Config::Initialize(const std::optional<std::string> config_path) { + const std::filesystem::path default_sdl_config_path = + FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; + config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path)); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + Reload(); +} + +void Config::WriteToIni() const { + FILE* fp = nullptr; +#ifdef _WIN32 + fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb"); +#else + fp = fopen(config_loc.c_str(), "wb"); +#endif + + if (fp == nullptr) { + LOG_ERROR(Frontend, "Config file could not be saved!"); + return; + } + + CSimpleIniA::FileWriter writer(fp); + const SI_Error rc = config->Save(writer, false); + if (rc < 0) { + LOG_ERROR(Frontend, "Config file could not be saved!"); + } + fclose(fp); +} + +void Config::SetUpIni() { + config = std::make_unique<CSimpleIniA>(); + config->SetUnicode(true); + config->SetSpaces(false); + + FILE* fp = nullptr; +#ifdef _WIN32 + _wfopen_s(&fp, Common::UTF8ToUTF16W(config_loc).data(), L"rb, ccs=UTF-8"); + if (fp == nullptr) { + fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb, ccs=UTF-8"); + } +#else + fp = fopen(config_loc.c_str(), "rb"); + if (fp == nullptr) { + fp = fopen(config_loc.c_str(), "wb"); + } +#endif + + if (fp == nullptr) { + LOG_ERROR(Frontend, "Config file could not be loaded!"); + return; + } + + if (SI_Error rc = config->LoadFile(fp); rc < 0) { + LOG_ERROR(Frontend, "Config file could not be loaded!"); + } + fclose(fp); +} + +bool Config::IsCustomConfig() const { + return type == ConfigType::PerGameConfig; +} + +void Config::ReadPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + player.profile_name = profile_name; + } + + if (player_prefix.empty() && Settings::IsConfiguringGlobal()) { + const auto controller = static_cast<Settings::ControllerType>( + ReadIntegerSetting(std::string(player_prefix).append("type"), + static_cast<u8>(Settings::ControllerType::ProController))); + + if (controller == Settings::ControllerType::LeftJoycon || + controller == Settings::ControllerType::RightJoycon) { + player.controller_type = controller; + } + } else { + std::string connected_key = player_prefix; + player.connected = ReadBooleanSetting(connected_key.append("connected"), + std::make_optional(player_index == 0)); + + player.controller_type = static_cast<Settings::ControllerType>( + ReadIntegerSetting(std::string(player_prefix).append("type"), + static_cast<u8>(Settings::ControllerType::ProController))); + + player.vibration_enabled = ReadBooleanSetting( + std::string(player_prefix).append("vibration_enabled"), std::make_optional(true)); + + player.vibration_strength = static_cast<int>( + ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100)); + + player.body_color_left = static_cast<u32>(ReadIntegerSetting( + std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE)); + player.body_color_right = static_cast<u32>(ReadIntegerSetting( + std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED)); + player.button_color_left = static_cast<u32>( + ReadIntegerSetting(std::string(player_prefix).append("button_color_left"), + Settings::JOYCON_BUTTONS_NEON_BLUE)); + player.button_color_right = static_cast<u32>( + ReadIntegerSetting(std::string(player_prefix).append("button_color_right"), + Settings::JOYCON_BUTTONS_NEON_RED)); + } +} + +void Config::ReadTouchscreenValues() { + Settings::values.touchscreen.enabled = + ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true)); + Settings::values.touchscreen.rotation_angle = + static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); + Settings::values.touchscreen.diameter_x = + static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); + Settings::values.touchscreen.diameter_y = + static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); +} + +void Config::ReadAudioValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); + + ReadCategory(Settings::Category::Audio); + ReadCategory(Settings::Category::UiAudio); + + EndGroup(); +} + +void Config::ReadControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + ReadCategory(Settings::Category::Controls); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadPlayerValues(p); + } + + // Disable docked mode if handheld is selected + const auto controller_type = Settings::values.players.GetValue()[0].controller_type; + if (controller_type == Settings::ControllerType::Handheld) { + Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); + Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); + } + + if (IsCustomConfig()) { + EndGroup(); + return; + } + ReadTouchscreenValues(); + ReadMotionTouchValues(); + + EndGroup(); +} + +void Config::ReadMotionTouchValues() { + int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps")); + + if (num_touch_from_button_maps > 0) { + for (int i = 0; i < num_touch_from_button_maps; ++i) { + SetArrayIndex(i); + + Settings::TouchFromButtonMap map; + map.name = ReadStringSetting(std::string("name"), std::string("default")); + + const int num_touch_maps = BeginArray(std::string("entries")); + map.buttons.reserve(num_touch_maps); + for (int j = 0; j < num_touch_maps; j++) { + SetArrayIndex(j); + std::string touch_mapping = ReadStringSetting(std::string("bind")); + map.buttons.emplace_back(std::move(touch_mapping)); + } + EndArray(); // entries + Settings::values.touch_from_button_maps.emplace_back(std::move(map)); + } + } else { + Settings::values.touch_from_button_maps.emplace_back( + Settings::TouchFromButtonMap{"default", {}}); + num_touch_from_button_maps = 1; + } + EndArray(); // touch_from_button_maps + + Settings::values.touch_from_button_map_index = std::clamp( + Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); +} + +void Config::ReadCoreValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); + + ReadCategory(Settings::Category::Core); + + EndGroup(); +} + +void Config::ReadDataStorageValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); + + FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory"))); + FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory"))); + FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory"))); + FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory"))); + FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory"))); + + ReadCategory(Settings::Category::DataStorage); + + EndGroup(); +} + +void Config::ReadDebuggingValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); + + // Intentionally not using the QT default setting as this is intended to be changed in the ini + Settings::values.record_frame_times = + ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false)); + + ReadCategory(Settings::Category::Debugging); + ReadCategory(Settings::Category::DebuggingGraphics); + + EndGroup(); +} + +void Config::ReadServiceValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); + + ReadCategory(Settings::Category::Services); + + EndGroup(); +} + +void Config::ReadDisabledAddOnValues() { + // Custom config section + BeginGroup(std::string("DisabledAddOns")); + + const int size = BeginArray(std::string("")); + for (int i = 0; i < size; ++i) { + SetArrayIndex(i); + const auto title_id = ReadUnsignedIntegerSetting(std::string("title_id"), 0); + std::vector<std::string> out; + const int d_size = BeginArray("disabled"); + for (int j = 0; j < d_size; ++j) { + SetArrayIndex(j); + out.push_back(ReadStringSetting(std::string("d"), std::string(""))); + } + EndArray(); // d + Settings::values.disabled_addons.insert_or_assign(title_id, out); + } + EndArray(); // Base disabled addons array - Has no base key + + EndGroup(); +} + +void Config::ReadMiscellaneousValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); + + ReadCategory(Settings::Category::Miscellaneous); + + EndGroup(); +} + +void Config::ReadCpuValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); + + ReadCategory(Settings::Category::Cpu); + ReadCategory(Settings::Category::CpuDebug); + ReadCategory(Settings::Category::CpuUnsafe); + + EndGroup(); +} + +void Config::ReadRendererValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); + + ReadCategory(Settings::Category::Renderer); + ReadCategory(Settings::Category::RendererAdvanced); + ReadCategory(Settings::Category::RendererDebug); + + EndGroup(); +} + +void Config::ReadScreenshotValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); + + ReadCategory(Settings::Category::Screenshots); + FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir, + ReadStringSetting(std::string("screenshot_path"))); + + EndGroup(); +} + +void Config::ReadSystemValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::System)); + + ReadCategory(Settings::Category::System); + ReadCategory(Settings::Category::SystemAudio); + + EndGroup(); +} + +void Config::ReadWebServiceValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); + + ReadCategory(Settings::Category::WebService); + + EndGroup(); +} + +void Config::ReadNetworkValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); + + ReadCategory(Settings::Category::Network); + + EndGroup(); +} + +void Config::ReadValues() { + if (global) { + ReadDataStorageValues(); + ReadDebuggingValues(); + ReadDisabledAddOnValues(); + ReadNetworkValues(); + ReadServiceValues(); + ReadWebServiceValues(); + ReadMiscellaneousValues(); + } + ReadControlValues(); + ReadCoreValues(); + ReadCpuValues(); + ReadRendererValues(); + ReadAudioValues(); + ReadSystemValues(); +} + +void Config::SavePlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + if (player.profile_name.empty()) { + // No custom profile selected + return; + } + WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name, + std::make_optional(std::string(""))); + } + + WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type), + std::make_optional(static_cast<u8>(Settings::ControllerType::ProController))); + + if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { + WriteSetting(std::string(player_prefix).append("connected"), player.connected, + std::make_optional(player_index == 0)); + WriteSetting(std::string(player_prefix).append("vibration_enabled"), + player.vibration_enabled, std::make_optional(true)); + WriteSetting(std::string(player_prefix).append("vibration_strength"), + player.vibration_strength, std::make_optional(100)); + WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left, + std::make_optional(Settings::JOYCON_BODY_NEON_BLUE)); + WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right, + std::make_optional(Settings::JOYCON_BODY_NEON_RED)); + WriteSetting(std::string(player_prefix).append("button_color_left"), + player.button_color_left, + std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE)); + WriteSetting(std::string(player_prefix).append("button_color_right"), + player.button_color_right, + std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED)); + } +} + +void Config::SaveTouchscreenValues() { + const auto& touchscreen = Settings::values.touchscreen; + + WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true)); + + WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, + std::make_optional(static_cast<u32>(0))); + WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, + std::make_optional(static_cast<u32>(15))); + WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, + std::make_optional(static_cast<u32>(15))); +} + +void Config::SaveMotionTouchValues() { + BeginArray(std::string("touch_from_button_maps")); + for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { + SetArrayIndex(static_cast<int>(p)); + WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, + std::make_optional(std::string("default"))); + + BeginArray(std::string("entries")); + for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); + ++q) { + SetArrayIndex(static_cast<int>(q)); + WriteSetting(std::string("bind"), + Settings::values.touch_from_button_maps[p].buttons[q]); + } + EndArray(); // entries + } + EndArray(); // touch_from_button_maps +} + +void Config::SaveValues() { + if (global) { + SaveDataStorageValues(); + SaveDebuggingValues(); + SaveDisabledAddOnValues(); + SaveNetworkValues(); + SaveWebServiceValues(); + SaveMiscellaneousValues(); + } + SaveControlValues(); + SaveCoreValues(); + SaveCpuValues(); + SaveRendererValues(); + SaveAudioValues(); + SaveSystemValues(); + + WriteToIni(); +} + +void Config::SaveAudioValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); + + WriteCategory(Settings::Category::Audio); + WriteCategory(Settings::Category::UiAudio); + + EndGroup(); +} + +void Config::SaveControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + WriteCategory(Settings::Category::Controls); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SavePlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + SaveTouchscreenValues(); + SaveMotionTouchValues(); + + EndGroup(); +} + +void Config::SaveCoreValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); + + WriteCategory(Settings::Category::Core); + + EndGroup(); +} + +void Config::SaveDataStorageValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); + + WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); + WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); + WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); + WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); + WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); + + WriteCategory(Settings::Category::DataStorage); + + EndGroup(); +} + +void Config::SaveDebuggingValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); + + // Intentionally not using the QT default setting as this is intended to be changed in the ini + WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times); + + WriteCategory(Settings::Category::Debugging); + WriteCategory(Settings::Category::DebuggingGraphics); + + EndGroup(); +} + +void Config::SaveNetworkValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); + + WriteCategory(Settings::Category::Network); + + EndGroup(); +} + +void Config::SaveDisabledAddOnValues() { + // Custom config section + BeginGroup(std::string("DisabledAddOns")); + + int i = 0; + BeginArray(std::string("")); + for (const auto& elem : Settings::values.disabled_addons) { + SetArrayIndex(i); + WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0))); + BeginArray(std::string("disabled")); + for (std::size_t j = 0; j < elem.second.size(); ++j) { + SetArrayIndex(static_cast<int>(j)); + WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string(""))); + } + EndArray(); // disabled + ++i; + } + EndArray(); // Base disabled addons array - Has no base key + + EndGroup(); +} + +void Config::SaveMiscellaneousValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); + + WriteCategory(Settings::Category::Miscellaneous); + + EndGroup(); +} + +void Config::SaveCpuValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); + + WriteCategory(Settings::Category::Cpu); + WriteCategory(Settings::Category::CpuDebug); + WriteCategory(Settings::Category::CpuUnsafe); + + EndGroup(); +} + +void Config::SaveRendererValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); + + WriteCategory(Settings::Category::Renderer); + WriteCategory(Settings::Category::RendererAdvanced); + WriteCategory(Settings::Category::RendererDebug); + + EndGroup(); +} + +void Config::SaveScreenshotValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); + + WriteSetting(std::string("screenshot_path"), + FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)); + WriteCategory(Settings::Category::Screenshots); + + EndGroup(); +} + +void Config::SaveSystemValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::System)); + + WriteCategory(Settings::Category::System); + WriteCategory(Settings::Category::SystemAudio); + + EndGroup(); +} + +void Config::SaveWebServiceValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); + + WriteCategory(Settings::Category::WebService); + + EndGroup(); +} + +bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false); + } + + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), false)) { + return static_cast<bool>(default_value.value()); + } else { + return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), + static_cast<bool>(default_value.value())); + } +} + +s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + try { + return std::stoll( + std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); + } catch (...) { + return 0; + } + } + + s64 result = 0; + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + try { + result = std::stoll(std::string(config->GetValue( + GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); + } catch (...) { + result = default_value.value(); + } + } + return result; +} + +u64 Config::ReadUnsignedIntegerSetting(const std::string& key, + const std::optional<u64> default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + try { + return std::stoull( + std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); + } catch (...) { + return 0; + } + } + + u64 result = 0; + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + try { + result = std::stoull(std::string(config->GetValue( + GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); + } catch (...) { + result = default_value.value(); + } + } + return result; +} + +double Config::ReadDoubleSetting(const std::string& key, + const std::optional<double> default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0); + } + + double result; + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + result = + config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value()); + } + return result; +} + +std::string Config::ReadStringSetting(const std::string& key, + const std::optional<std::string> default_value) { + std::string result; + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + result = config->GetValue(GetSection().c_str(), full_key.c_str(), ""); + boost::replace_all(result, "\"", ""); + return result; + } + + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + result = + config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str()); + } + boost::replace_all(result, "\"", ""); + boost::replace_all(result, "//", "/"); + return result; +} + +bool Config::Exists(const std::string& section, const std::string& key) const { + const std::string value = config->GetValue(section.c_str(), key.c_str(), ""); + return !value.empty(); +} + +template <typename Type> +void Config::WriteSetting(const std::string& key, const Type& value, + const std::optional<Type>& default_value, + const std::optional<bool>& use_global) { + std::string full_key = GetFullKey(key, false); + + std::string saved_value; + std::string string_default; + if constexpr (std::is_same_v<Type, std::string>) { + saved_value.append(AdjustOutputString(value)); + if (default_value.has_value()) { + string_default.append(AdjustOutputString(default_value.value())); + } + } else { + saved_value.append(AdjustOutputString(ToString(value))); + if (default_value.has_value()) { + string_default.append(ToString(default_value.value())); + } + } + + if (default_value.has_value() && use_global.has_value()) { + if (!global) { + WriteSettingInternal(std::string(full_key).append("\\global"), + ToString(use_global.value())); + } + if (global || use_global.value() == false) { + WriteSettingInternal(std::string(full_key).append("\\default"), + ToString(string_default == saved_value)); + WriteSettingInternal(full_key, saved_value); + } + } else if (default_value.has_value() && !use_global.has_value()) { + WriteSettingInternal(std::string(full_key).append("\\default"), + ToString(string_default == saved_value)); + WriteSettingInternal(full_key, saved_value); + } else { + WriteSettingInternal(full_key, saved_value); + } +} + +void Config::WriteSettingInternal(const std::string& key, const std::string& value) { + config->SetValue(GetSection().c_str(), key.c_str(), value.c_str()); +} + +void Config::Reload() { + ReadValues(); + // To apply default value changes + SaveValues(); +} + +void Config::Save() { + SaveValues(); +} + +void Config::ClearControlPlayerValues() const { + // If key is an empty string, all keys in the current group() are removed. + const char* section = Settings::TranslateCategory(Settings::Category::Controls); + CSimpleIniA::TNamesDepend keys; + config->GetAllKeys(section, keys); + for (const auto& key : keys) { + if (std::string(config->GetValue(section, key.pItem)).empty()) { + config->Delete(section, key.pItem); + } + } +} + +const std::string& Config::GetConfigFilePath() const { + return config_loc; +} + +void Config::ReadCategory(const Settings::Category category) { + const auto& settings = FindRelevantList(category); + std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); }); +} + +void Config::WriteCategory(const Settings::Category category) { + const auto& settings = FindRelevantList(category); + std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); }); +} + +void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { + if (!setting->Save() || (!setting->Switchable() && !global)) { + return; + } + + const std::string key = AdjustKey(setting->GetLabel()); + const std::string default_value(setting->DefaultToString()); + + bool use_global = true; + if (setting->Switchable() && !global) { + use_global = + ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true)); + setting->SetGlobal(use_global); + } + + if (global || !use_global) { + const bool is_default = + ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true)); + if (!is_default) { + const std::string setting_string = ReadStringSetting(key, default_value); + setting->LoadString(setting_string); + } else { + // Empty string resets the Setting to default + setting->LoadString(""); + } + } +} + +void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) { + if (!setting->Save()) { + return; + } + + std::string key = AdjustKey(setting->GetLabel()); + if (setting->Switchable()) { + if (!global) { + WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); + } + if (global || !setting->UsingGlobal()) { + WriteSetting(std::string(key).append("\\default"), + setting->ToString() == setting->DefaultToString()); + WriteSetting(key, setting->ToString()); + } + } else if (global) { + WriteSetting(std::string(key).append("\\default"), + setting->ToString() == setting->DefaultToString()); + WriteSetting(key, setting->ToString()); + } +} + +void Config::BeginGroup(const std::string& group) { + // You can't begin a group while reading/writing from a config array + ASSERT(array_stack.empty()); + + key_stack.push_back(AdjustKey(group)); +} + +void Config::EndGroup() { + // You can't end a group if you haven't started one yet + ASSERT(!key_stack.empty()); + + // You can't end a group when reading/writing from a config array + ASSERT(array_stack.empty()); + + key_stack.pop_back(); +} + +std::string Config::GetSection() { + if (key_stack.empty()) { + return std::string{""}; + } + + return key_stack.front(); +} + +std::string Config::GetGroup() const { + if (key_stack.size() <= 1) { + return std::string{""}; + } + + std::string key; + for (size_t i = 1; i < key_stack.size(); ++i) { + key.append(key_stack[i]).append("\\"); + } + return key; +} + +std::string Config::AdjustKey(const std::string& key) { + std::string adjusted_key(key); + boost::replace_all(adjusted_key, "/", "\\"); + boost::replace_all(adjusted_key, " ", "%20"); + return adjusted_key; +} + +std::string Config::AdjustOutputString(const std::string& string) { + std::string adjusted_string(string); + boost::replace_all(adjusted_string, "\\", "/"); + + // Windows requires that two forward slashes are used at the start of a path for unmapped + // network drives so we have to watch for that here + if (string.substr(0, 2) == "//") { + boost::replace_all(adjusted_string, "//", "/"); + adjusted_string.insert(0, "/"); + } else { + boost::replace_all(adjusted_string, "//", "/"); + } + + // Needed for backwards compatibility with QSettings deserialization + for (const auto& special_character : special_characters) { + if (adjusted_string.find(special_character) != std::string::npos) { + adjusted_string.insert(0, "\""); + adjusted_string.append("\""); + break; + } + } + return adjusted_string; +} + +std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) { + if (array_stack.empty()) { + return std::string(GetGroup()).append(AdjustKey(key)); + } + + std::string array_key; + for (size_t i = 0; i < array_stack.size(); ++i) { + if (!array_stack[i].name.empty()) { + array_key.append(array_stack[i].name).append("\\"); + } + + if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) { + array_key.append(ToString(array_stack[i].index)).append("\\"); + } + } + std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key)); + return final_key; +} + +int Config::BeginArray(const std::string& array) { + array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0}); + const int size = config->GetLongValue(GetSection().c_str(), + GetFullKey(std::string("size"), true).c_str(), 0); + array_stack.back().size = size; + return size; +} + +void Config::EndArray() { + // You can't end a config array before starting one + ASSERT(!array_stack.empty()); + + // Set the array size to 0 if the array is ended without changing the index + int size = 0; + if (array_stack.back().index != 0) { + size = array_stack.back().size; + } + + // Write out the size to config + if (key_stack.size() == 1 && array_stack.back().name.empty()) { + // Edge-case where the first array created doesn't have a name + config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str()); + } else { + const auto key = GetFullKey(std::string("size"), true); + config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str()); + } + + array_stack.pop_back(); +} + +void Config::SetArrayIndex(const int index) { + // You can't set the array index if you haven't started one yet + ASSERT(!array_stack.empty()); + + const int array_index = index + 1; + + // You can't exceed the known max size of the array by more than 1 + ASSERT(array_stack.front().size + 1 >= array_index); + + // Change the config array size to the current index since you may want + // to reduce the number of elements that you read back from the config + // in the future. + array_stack.back().size = array_index; + array_stack.back().index = array_index; +} diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h new file mode 100644 index 000000000..b3812af17 --- /dev/null +++ b/src/frontend_common/config.h @@ -0,0 +1,211 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <string> +#include "common/settings.h" + +#define SI_NO_CONVERSION +#include <SimpleIni.h> +#include <boost/algorithm/string/replace.hpp> + +// Workaround for conflicting definition in libloaderapi.h caused by SimpleIni +#undef LoadString +#undef CreateFile +#undef DeleteFile +#undef CopyFile +#undef CreateDirectory +#undef MoveFile + +namespace Core { +class System; +} + +class Config { +public: + enum class ConfigType { + GlobalConfig, + PerGameConfig, + InputProfile, + }; + + virtual ~Config() = default; + + void ClearControlPlayerValues() const; + + [[nodiscard]] const std::string& GetConfigFilePath() const; + + [[nodiscard]] bool Exists(const std::string& section, const std::string& key) const; + +protected: + explicit Config(ConfigType config_type = ConfigType::GlobalConfig); + + void Initialize(const std::string& config_name = "config"); + void Initialize(std::optional<std::string> config_path); + + void WriteToIni() const; + + void SetUpIni(); + [[nodiscard]] bool IsCustomConfig() const; + + void Reload(); + void Save(); + + /** + * Derived config classes must implement this so they can reload all platform-specific + * values and global ones. + */ + virtual void ReloadAllValues() = 0; + + /** + * Derived config classes must implement this so they can save all platform-specific + * and global values. + */ + virtual void SaveAllValues() = 0; + + void ReadValues(); + void ReadPlayerValues(std::size_t player_index); + + void ReadTouchscreenValues(); + void ReadMotionTouchValues(); + + // Read functions bases off the respective config section names. + void ReadAudioValues(); + void ReadControlValues(); + void ReadCoreValues(); + void ReadDataStorageValues(); + void ReadDebuggingValues(); + void ReadServiceValues(); + void ReadDisabledAddOnValues(); + void ReadMiscellaneousValues(); + void ReadCpuValues(); + void ReadRendererValues(); + void ReadScreenshotValues(); + void ReadSystemValues(); + void ReadWebServiceValues(); + void ReadNetworkValues(); + + // Read platform specific sections + virtual void ReadHidbusValues() = 0; + virtual void ReadDebugControlValues() = 0; + virtual void ReadPathValues() = 0; + virtual void ReadShortcutValues() = 0; + virtual void ReadUIValues() = 0; + virtual void ReadUIGamelistValues() = 0; + virtual void ReadUILayoutValues() = 0; + virtual void ReadMultiplayerValues() = 0; + + void SaveValues(); + void SavePlayerValues(std::size_t player_index); + void SaveTouchscreenValues(); + void SaveMotionTouchValues(); + + // Save functions based off the respective config section names. + void SaveAudioValues(); + void SaveControlValues(); + void SaveCoreValues(); + void SaveDataStorageValues(); + void SaveDebuggingValues(); + void SaveNetworkValues(); + void SaveDisabledAddOnValues(); + void SaveMiscellaneousValues(); + void SaveCpuValues(); + void SaveRendererValues(); + void SaveScreenshotValues(); + void SaveSystemValues(); + void SaveWebServiceValues(); + + // Save platform specific sections + virtual void SaveHidbusValues() = 0; + virtual void SaveDebugControlValues() = 0; + virtual void SavePathValues() = 0; + virtual void SaveShortcutValues() = 0; + virtual void SaveUIValues() = 0; + virtual void SaveUIGamelistValues() = 0; + virtual void SaveUILayoutValues() = 0; + virtual void SaveMultiplayerValues() = 0; + + virtual std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) = 0; + + /** + * Reads a setting from the qt_config. + * + * @param key The setting's identifier + * @param default_value The value to use when the setting is not already present in the config + */ + bool ReadBooleanSetting(const std::string& key, + std::optional<bool> default_value = std::nullopt); + s64 ReadIntegerSetting(const std::string& key, std::optional<s64> default_value = std::nullopt); + u64 ReadUnsignedIntegerSetting(const std::string& key, + std::optional<u64> default_value = std::nullopt); + double ReadDoubleSetting(const std::string& key, + std::optional<double> default_value = std::nullopt); + std::string ReadStringSetting(const std::string& key, + std::optional<std::string> default_value = std::nullopt); + + /** + * Writes a setting to the qt_config. + * + * @param key The setting's idetentifier + * @param value Value of the setting + * @param default_value Default of the setting if not present in config + * @param use_global Specifies if the custom or global config should be in use, for custom + * configs + */ + template <typename Type = int> + void WriteSetting(const std::string& key, const Type& value, + const std::optional<Type>& default_value = std::nullopt, + const std::optional<bool>& use_global = std::nullopt); + void WriteSettingInternal(const std::string& key, const std::string& value); + + void ReadCategory(Settings::Category category); + void WriteCategory(Settings::Category category); + void ReadSettingGeneric(Settings::BasicSetting* setting); + void WriteSettingGeneric(const Settings::BasicSetting* setting); + + template <typename T> + [[nodiscard]] std::string ToString(const T& value_) { + if constexpr (std::is_same_v<T, std::string>) { + return value_; + } else if constexpr (std::is_same_v<T, std::optional<u32>>) { + return value_.has_value() ? std::to_string(*value_) : "none"; + } else if constexpr (std::is_same_v<T, bool>) { + return value_ ? "true" : "false"; + } else if constexpr (std::is_same_v<T, u64>) { + return std::to_string(static_cast<u64>(value_)); + } else { + return std::to_string(static_cast<s64>(value_)); + } + } + + void BeginGroup(const std::string& group); + void EndGroup(); + std::string GetSection(); + [[nodiscard]] std::string GetGroup() const; + static std::string AdjustKey(const std::string& key); + static std::string AdjustOutputString(const std::string& string); + std::string GetFullKey(const std::string& key, bool skipArrayIndex); + int BeginArray(const std::string& array); + void EndArray(); + void SetArrayIndex(int index); + + const ConfigType type; + std::unique_ptr<CSimpleIniA> config; + std::string config_loc; + const bool global; + +private: + inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*', + '|', ';', '\'', '\"', ',', '<', '.', + '>', '?', '`', '~', '='}; + + struct ConfigArray { + std::string name; + int size; + int index; + }; + std::vector<ConfigArray> array_stack; + std::vector<std::string> key_stack; +}; diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 3ad34884d..1ff296af5 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -415,7 +415,7 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p // This list is missing ZL/ZR since those are not considered buttons. // We will add those afterwards // This list also excludes any button that can't be really mapped - static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> + static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 14> switch_to_gcadapter_button = { std::pair{Settings::NativeButton::A, PadButton::ButtonA}, {Settings::NativeButton::B, PadButton::ButtonB}, @@ -426,8 +426,10 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p {Settings::NativeButton::DUp, PadButton::ButtonUp}, {Settings::NativeButton::DRight, PadButton::ButtonRight}, {Settings::NativeButton::DDown, PadButton::ButtonDown}, - {Settings::NativeButton::SL, PadButton::TriggerL}, - {Settings::NativeButton::SR, PadButton::TriggerR}, + {Settings::NativeButton::SLLeft, PadButton::TriggerL}, + {Settings::NativeButton::SRLeft, PadButton::TriggerR}, + {Settings::NativeButton::SLRight, PadButton::TriggerL}, + {Settings::NativeButton::SRRight, PadButton::TriggerR}, {Settings::NativeButton::R, PadButton::TriggerZ}, }; if (!params.Has("port")) { diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 0aca5a3a3..72d2951f3 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -680,8 +680,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par Common::ParamPackage sr_button_params = button_params; sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); - mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); - mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SLLeft, std::move(sl_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SRLeft, std::move(sr_button_params)); } // Map SL and SR buttons for right joycons @@ -693,8 +693,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par Common::ParamPackage sr_button_params = button_params; sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); - mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); - mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SLRight, std::move(sl_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SRRight, std::move(sr_button_params)); } return mapping; diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 66e3ae9af..78f458afe 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -828,16 +828,18 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p ButtonBindings SDLDriver::GetDefaultButtonBinding( const std::shared_ptr<SDLJoystick>& joystick) const { // Default SL/SR mapping for other controllers - auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; if (joystick->IsJoyconLeft()) { - sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; - sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; + sll_button = SDL_CONTROLLER_BUTTON_PADDLE2; + srl_button = SDL_CONTROLLER_BUTTON_PADDLE4; } if (joystick->IsJoyconRight()) { - sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; - sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; + slr_button = SDL_CONTROLLER_BUTTON_PADDLE3; + srr_button = SDL_CONTROLLER_BUTTON_PADDLE1; } return { @@ -855,8 +857,10 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding( {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, - {Settings::NativeButton::SL, sl_button}, - {Settings::NativeButton::SR, sr_button}, + {Settings::NativeButton::SLLeft, sll_button}, + {Settings::NativeButton::SRLeft, srl_button}, + {Settings::NativeButton::SLRight, slr_button}, + {Settings::NativeButton::SRRight, srr_button}, {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, }; diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fcba4e3c6..08e49a0da 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -24,7 +24,7 @@ namespace InputCommon { class SDLJoystick; using ButtonBindings = - std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>; + std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 20>; using ZButtonBindings = std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 77db60e92..60821b31a 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -396,7 +396,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const { ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { // This list excludes any button that can't be really mapped - static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 20> + static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 22> switch_to_dsu_button = { std::pair{Settings::NativeButton::A, PadButton::Circle}, {Settings::NativeButton::B, PadButton::Cross}, @@ -412,8 +412,10 @@ ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& p {Settings::NativeButton::R, PadButton::R1}, {Settings::NativeButton::ZL, PadButton::L2}, {Settings::NativeButton::ZR, PadButton::R2}, - {Settings::NativeButton::SL, PadButton::L2}, - {Settings::NativeButton::SR, PadButton::R2}, + {Settings::NativeButton::SLLeft, PadButton::L2}, + {Settings::NativeButton::SRLeft, PadButton::R2}, + {Settings::NativeButton::SLRight, PadButton::L2}, + {Settings::NativeButton::SRRight, PadButton::R2}, {Settings::NativeButton::LStick, PadButton::L3}, {Settings::NativeButton::RStick, PadButton::R3}, {Settings::NativeButton::Home, PadButton::Home}, diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 83b763447..19db17c6d 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -231,6 +231,7 @@ add_library(shader_recompiler STATIC ir_opt/rescaling_pass.cpp ir_opt/ssa_rewrite_pass.cpp ir_opt/texture_pass.cpp + ir_opt/vendor_workaround_pass.cpp ir_opt/verification_pass.cpp object_pool.h precompiled_headers.h diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp index d0e308124..64e7bad75 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp @@ -559,12 +559,12 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, const IR::Value& offset, const IR::Value& lod_clamp) { const auto info{inst.Flags<IR::TextureInstInfo>()}; ScopedRegister dpdx, dpdy, coords; - const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; + const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp}; if (multi_component) { // Allocate this early to avoid aliasing other registers dpdx = ScopedRegister{ctx.reg_alloc}; dpdy = ScopedRegister{ctx.reg_alloc}; - if (info.num_derivates >= 3) { + if (info.num_derivatives >= 3) { coords = ScopedRegister{ctx.reg_alloc}; } } @@ -584,7 +584,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec, dpdy.reg, derivatives_vec); Register final_coord; - if (info.num_derivates >= 3) { + if (info.num_derivatives >= 3) { ctx.Add("MOV.F {}.z,{}.x;" "MOV.F {}.z,{}.y;", dpdx.reg, coord_vec, dpdy.reg, coord_vec); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index d9872ecc2..6e940bd5a 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp @@ -548,15 +548,15 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, if (sparse_inst) { throw NotImplementedException("EmitImageGradient Sparse"); } - if (!offset.IsEmpty() && info.num_derivates <= 2) { + if (!offset.IsEmpty() && info.num_derivatives <= 2) { throw NotImplementedException("EmitImageGradient offset"); } const auto texture{Texture(ctx, info, index)}; const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; - const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; + const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp}; const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)}; if (multi_component) { - if (info.num_derivates >= 3) { + if (info.num_derivatives >= 3) { const auto offset_vec{ctx.var_alloc.Consume(offset)}; ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 34592a01f..0031fa5fb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -407,7 +407,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct } ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); } - if (info.stores[IR::Attribute::ViewportIndex]) { + if (info.stores[IR::Attribute::ViewportIndex] && profile.support_multi_viewport) { ctx.AddCapability(spv::Capability::MultiViewport); } if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 1d77426e0..e5a78a914 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -84,6 +84,10 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { } return std::nullopt; case IR::Attribute::ViewportIndex: + if (!ctx.profile.support_multi_viewport) { + LOG_WARNING(Shader, "Ignoring viewport index store on non-supporting driver"); + return std::nullopt; + } if (ctx.profile.support_viewport_index_layer_non_geometry || ctx.stage == Shader::Stage::Geometry) { return OutAttr{ctx.viewport_index, ctx.U32[1]}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 8decdf399..22ceca19c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -67,22 +67,22 @@ public: } } - explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates, - Id offset, Id lod_clamp) { - if (!Sirit::ValidId(derivates)) { - throw LogicError("Derivates must be present"); + explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives, + u32 num_derivatives, Id offset, Id lod_clamp) { + if (!Sirit::ValidId(derivatives)) { + throw LogicError("Derivatives must be present"); } boost::container::static_vector<Id, 3> deriv_x_accum; boost::container::static_vector<Id, 3> deriv_y_accum; - for (u32 i = 0; i < num_derivates; ++i) { - deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2)); - deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1)); + for (u32 i = 0; i < num_derivatives; ++i) { + deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2)); + deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2 + 1)); } - const Id derivates_X{ctx.OpCompositeConstruct( - ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; - const Id derivates_Y{ctx.OpCompositeConstruct( - ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; - Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y); + const Id derivatives_X{ctx.OpCompositeConstruct( + ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; + const Id derivatives_Y{ctx.OpCompositeConstruct( + ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; + Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y); if (Sirit::ValidId(offset)) { Add(spv::ImageOperandsMask::Offset, offset); } @@ -91,26 +91,26 @@ public: } } - explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, + explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2, Id offset, Id lod_clamp) { - if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { - throw LogicError("Derivates must be present"); + if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) { + throw LogicError("Derivatives must be present"); } boost::container::static_vector<Id, 3> deriv_1_accum{ - ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), - ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), - ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), + ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 0), + ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 2), + ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 0), }; boost::container::static_vector<Id, 3> deriv_2_accum{ - ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), - ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), - ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), + ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 1), + ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 3), + ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 1), }; - const Id derivates_id1{ctx.OpCompositeConstruct( + const Id derivatives_id1{ctx.OpCompositeConstruct( ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; - const Id derivates_id2{ctx.OpCompositeConstruct( + const Id derivatives_id2{ctx.OpCompositeConstruct( ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; - Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); + Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2); if (Sirit::ValidId(offset)) { Add(spv::ImageOperandsMask::Offset, offset); } @@ -548,12 +548,12 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I } Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, - Id derivates, Id offset, Id lod_clamp) { + Id derivatives, Id offset, Id lod_clamp) { const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto operands = - info.num_derivates == 3 - ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) - : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, + info.num_derivatives == 3 + ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp) + : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset, lod_clamp); return Emit(&EmitContext::OpImageSparseSampleExplicitLod, &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index a440b557d..7d34575c8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i const IR::Value& skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, - Id derivates, Id offset, Id lod_clamp); + Id derivatives, Id offset, Id lod_clamp); Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index b7caa4246..49171c470 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp @@ -1864,11 +1864,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture return Inst(op, Flags{info}, handle, coords); } -Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivates, +Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives, const Value& offset, const F32& lod_clamp, TextureInstInfo info) { const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient : Opcode::BindlessImageGradient}; - return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); + return Inst(op, Flags{info}, handle, coords, derivatives, offset, lod_clamp); } Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index f3c81dbe1..6c30897f4 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -335,7 +335,7 @@ public: [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, const U32& lod, const U32& multisampling, TextureInstInfo info); [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, - const Value& derivates, const Value& offset, + const Value& derivatives, const Value& offset, const F32& lod_clamp, TextureInstInfo info); [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); void ImageWrite(const Value& handle, const Value& coords, const Value& color, diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h index 1e9e8c8f5..c20c2401f 100644 --- a/src/shader_recompiler/frontend/ir/modifiers.h +++ b/src/shader_recompiler/frontend/ir/modifiers.h @@ -40,7 +40,7 @@ union TextureInstInfo { BitField<21, 1, u32> has_lod_clamp; BitField<22, 1, u32> relaxed_precision; BitField<23, 2, u32> gather_component; - BitField<25, 2, u32> num_derivates; + BitField<25, 2, u32> num_derivatives; BitField<27, 3, ImageFormat> image_format; BitField<30, 1, u32> ndv_is_active; }; diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp index dd34507bc..4ce3dd0cd 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp @@ -59,7 +59,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { BitField<51, 3, IR::Pred> sparse_pred; BitField<0, 8, IR::Reg> dest_reg; BitField<8, 8, IR::Reg> coord_reg; - BitField<20, 8, IR::Reg> derivate_reg; + BitField<20, 8, IR::Reg> derivative_reg; BitField<28, 3, TextureType> type; BitField<31, 4, u64> mask; BitField<36, 13, u64> cbuf_offset; @@ -71,7 +71,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { } IR::Value coords; - u32 num_derivates{}; + u32 num_derivatives{}; IR::Reg base_reg{txd.coord_reg}; IR::Reg last_reg; IR::Value handle; @@ -90,42 +90,42 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { switch (txd.type) { case TextureType::_1D: { coords = v.F(base_reg); - num_derivates = 1; + num_derivatives = 1; last_reg = base_reg + 1; break; } case TextureType::ARRAY_1D: { last_reg = base_reg + 1; coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); - num_derivates = 1; + num_derivatives = 1; break; } case TextureType::_2D: { last_reg = base_reg + 2; coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); - num_derivates = 2; + num_derivatives = 2; break; } case TextureType::ARRAY_2D: { last_reg = base_reg + 2; coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); - num_derivates = 2; + num_derivatives = 2; break; } default: throw NotImplementedException("Invalid texture type"); } - const IR::Reg derivate_reg{txd.derivate_reg}; - IR::Value derivates; - switch (num_derivates) { + const IR::Reg derivative_reg{txd.derivative_reg}; + IR::Value derivatives; + switch (num_derivatives) { case 1: { - derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1)); + derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1)); break; } case 2: { - derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1), - v.F(derivate_reg + 2), v.F(derivate_reg + 3)); + derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1), + v.F(derivative_reg + 2), v.F(derivative_reg + 3)); break; } default: @@ -150,9 +150,10 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { IR::TextureInstInfo info{}; info.type.Assign(GetType(txd.type)); - info.num_derivates.Assign(num_derivates); + info.num_derivatives.Assign(num_derivatives); info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); - const IR::Value sample{v.ir.ImageGradient(handle, coords, derivates, offset, lod_clamp, info)}; + const IR::Value sample{ + v.ir.ImageGradient(handle, coords, derivatives, offset, lod_clamp, info)}; IR::Reg dest_reg{txd.dest_reg}; for (size_t element = 0; element < 4; ++element) { diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 928b35561..8fac6bad3 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -310,6 +310,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo } Optimization::CollectShaderInfoPass(env, program); Optimization::LayerPass(program, host_info); + Optimization::VendorWorkaroundPass(program); CollectInterpolationInfo(env, program); AddNVNStorageBuffers(program); diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp index f46e55122..ec12c843a 100644 --- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp @@ -428,7 +428,7 @@ void FoldFPAdd32(IR::Inst& inst) { } } -bool FoldDerivateYFromCorrection(IR::Inst& inst) { +bool FoldDerivativeYFromCorrection(IR::Inst& inst) { const IR::Value lhs_value{inst.Arg(0)}; const IR::Value rhs_value{inst.Arg(1)}; IR::Inst* const lhs_op{lhs_value.InstRecursive()}; @@ -464,7 +464,7 @@ void FoldFPMul32(IR::Inst& inst) { if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) { return; } - if (FoldDerivateYFromCorrection(inst)) { + if (FoldDerivativeYFromCorrection(inst)) { return; } IR::Inst* const lhs_op{lhs_value.InstRecursive()}; @@ -699,7 +699,7 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) { } } -bool FindGradient3DDerivates(std::array<IR::Value, 3>& results, IR::Value coord) { +bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coord) { if (coord.IsImmediate()) { return false; } @@ -834,7 +834,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { IR::Inst* const inst2 = coords.InstRecursive(); std::array<std::array<IR::Value, 3>, 3> results_matrix; for (size_t i = 0; i < 3; i++) { - if (!FindGradient3DDerivates(results_matrix[i], inst2->Arg(i).Resolve())) { + if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) { return; } } @@ -852,7 +852,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], results_matrix[1][1], results_matrix[1][2]); IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]); - info.num_derivates.Assign(3); + info.num_derivatives.Assign(3); IR::Value new_gradient_instruction = ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info); IR::Inst* const new_inst = new_gradient_instruction.InstRecursive(); diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 629d18fa1..d4d5285e5 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -26,6 +26,7 @@ void SsaRewritePass(IR::Program& program); void PositionPass(Environment& env, IR::Program& program); void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); +void VendorWorkaroundPass(IR::Program& program); void VerificationPass(const IR::Program& program); // Dual Vertex diff --git a/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp new file mode 100644 index 000000000..08c658cb8 --- /dev/null +++ b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/ir_opt/passes.h" + +namespace Shader::Optimization { + +namespace { +void AddingByteSwapsWorkaround(IR::Block& block, IR::Inst& inst) { + /* + * Workaround for an NVIDIA bug seen in Super Mario RPG + * + * We are looking for this pattern: + * %lhs_bfe = BitFieldUExtract %factor_a, #0, #16 + * %lhs_mul = IMul32 %lhs_bfe, %factor_b // potentially optional? + * %lhs_shl = ShiftLeftLogical32 %lhs_mul, #16 + * %rhs_bfe = BitFieldUExtract %factor_a, #16, #16 + * %result = IAdd32 %lhs_shl, %rhs_bfe + * + * And replacing the IAdd32 with a BitwiseOr32 + * %result = BitwiseOr32 %lhs_shl, %rhs_bfe + * + */ + IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()}; + IR::Inst* const rhs_bfe{inst.Arg(1).TryInstRecursive()}; + if (!lhs_shl || !rhs_bfe) { + return; + } + if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || + lhs_shl->Arg(1) != IR::Value{16U}) { + return; + } + if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract || rhs_bfe->Arg(1) != IR::Value{16U} || + rhs_bfe->Arg(2) != IR::Value{16U}) { + return; + } + IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()}; + if (!lhs_mul) { + return; + } + const bool lhs_mul_optional{lhs_mul->GetOpcode() == IR::Opcode::BitFieldUExtract}; + if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 && + lhs_mul->GetOpcode() != IR::Opcode::BitFieldUExtract) { + return; + } + IR::Inst* const lhs_bfe{lhs_mul_optional ? lhs_mul : lhs_mul->Arg(0).TryInstRecursive()}; + if (!lhs_bfe) { + return; + } + if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) { + return; + } + if (lhs_bfe->Arg(1) != IR::Value{0U} || lhs_bfe->Arg(2) != IR::Value{16U}) { + return; + } + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + inst.ReplaceUsesWith(ir.BitwiseOr(IR::U32{inst.Arg(0)}, IR::U32{inst.Arg(1)})); +} + +} // Anonymous namespace + +void VendorWorkaroundPass(IR::Program& program) { + for (IR::Block* const block : program.post_order_blocks) { + for (IR::Inst& inst : block->Instructions()) { + switch (inst.GetOpcode()) { + case IR::Opcode::IAdd32: + AddingByteSwapsWorkaround(*block, inst); + break; + default: + break; + } + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 38d820db2..a9de9f4a9 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -43,6 +43,7 @@ struct Profile { bool support_gl_sparse_textures{}; bool support_gl_derivative_control{}; bool support_scaled_attributes{}; + bool support_multi_viewport{}; bool warp_size_potentially_larger_than_guest{}; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 336532e0b..c22c7631c 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(host_shaders) if(LIBVA_FOUND) - set_source_files_properties(host1x/codecs/codec.cpp + set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) endif() @@ -67,6 +67,8 @@ add_library(video_core STATIC host1x/codecs/vp9.cpp host1x/codecs/vp9.h host1x/codecs/vp9_types.h + host1x/ffmpeg/ffmpeg.cpp + host1x/ffmpeg/ffmpeg.h host1x/control.cpp host1x/control.h host1x/host1x.cpp diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 813b68963..90dbd352f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1209,11 +1209,6 @@ void BufferCache<P>::UpdateDrawIndirect() { .size = static_cast<u32>(size), .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), }; - VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64); - VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64); - IntervalType interval{cpu_addr_start, cpu_addr_end}; - ClearDownload(interval); - common_ranges.subtract(interval); }; if (current_draw_indirect->include_count) { update(current_draw_indirect->count_start_address, sizeof(u32), diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 02e161270..91f10aec2 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -72,7 +72,7 @@ void Fermi2D::Blit() { UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); const auto& args = regs.pixels_from_memory; - constexpr s64 null_derivate = 1ULL << 32; + constexpr s64 null_derivative = 1ULL << 32; Surface src = regs.src; const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && @@ -89,7 +89,7 @@ void Fermi2D::Blit() { .operation = regs.operation, .filter = args.sample_mode.filter, .must_accelerate = - args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu, + args.du_dx != null_derivative || args.dv_dy != null_derivative || delegate_to_gpu, .dst_x0 = args.dst_x0, .dst_y0 = args.dst_y0, .dst_x1 = args.dst_x0 + args.dst_width, diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 32d767d85..592c28ba3 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -268,7 +268,7 @@ size_t Maxwell3D::EstimateIndexBufferSize() { std::numeric_limits<u32>::max()}; const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); const size_t log2_byte_size = Common::Log2Ceil64(byte_size); - const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; + const size_t cap{GetMaxCurrentVertices() * 4 * byte_size}; const size_t lower_cap = std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); return std::min<size_t>( diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index c0e6471fe..805a89900 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -86,10 +86,7 @@ public: uncommitted_operations.emplace_back(std::move(func)); } pending_operations.emplace_back(std::move(uncommitted_operations)); - { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - QueueFence(new_fence); - } + QueueFence(new_fence); if (!delay_fence) { func(); } diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index dbcf508e5..1030db681 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp @@ -1,11 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <algorithm> -#include <fstream> -#include <vector> #include "common/assert.h" -#include "common/scope_exit.h" #include "common/settings.h" #include "video_core/host1x/codecs/codec.h" #include "video_core/host1x/codecs/h264.h" @@ -14,242 +10,17 @@ #include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" -extern "C" { -#include <libavfilter/buffersink.h> -#include <libavfilter/buffersrc.h> -#include <libavutil/opt.h> -#ifdef LIBVA_FOUND -// for querying VAAPI driver information -#include <libavutil/hwcontext_vaapi.h> -#endif -} - namespace Tegra { -namespace { -constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; -constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; -constexpr std::array PREFERRED_GPU_DECODERS = { - AV_HWDEVICE_TYPE_CUDA, -#ifdef _WIN32 - AV_HWDEVICE_TYPE_D3D11VA, - AV_HWDEVICE_TYPE_DXVA2, -#elif defined(__unix__) - AV_HWDEVICE_TYPE_VAAPI, - AV_HWDEVICE_TYPE_VDPAU, -#endif - // last resort for Linux Flatpak (w/ NVIDIA) - AV_HWDEVICE_TYPE_VULKAN, -}; - -void AVPacketDeleter(AVPacket* ptr) { - av_packet_free(&ptr); -} - -using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>; - -AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { - for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { - if (*p == av_codec_ctx->pix_fmt) { - return av_codec_ctx->pix_fmt; - } - } - LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); - av_buffer_unref(&av_codec_ctx->hw_device_ctx); - av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; - return PREFERRED_CPU_FMT; -} - -// List all the currently available hwcontext in ffmpeg -std::vector<AVHWDeviceType> ListSupportedContexts() { - std::vector<AVHWDeviceType> contexts{}; - AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; - do { - current_device_type = av_hwdevice_iterate_types(current_device_type); - contexts.push_back(current_device_type); - } while (current_device_type != AV_HWDEVICE_TYPE_NONE); - return contexts; -} - -} // namespace - -void AVFrameDeleter(AVFrame* ptr) { - av_frame_free(&ptr); -} Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} -Codec::~Codec() { - if (!initialized) { - return; - } - // Free libav memory - avcodec_free_context(&av_codec_ctx); - av_buffer_unref(&av_gpu_decoder); - - if (filters_initialized) { - avfilter_graph_free(&av_filter_graph); - } -} - -bool Codec::CreateGpuAvDevice() { - static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; - static const auto supported_contexts = ListSupportedContexts(); - for (const auto& type : PREFERRED_GPU_DECODERS) { - if (std::none_of(supported_contexts.begin(), supported_contexts.end(), - [&type](const auto& context) { return context == type; })) { - LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); - continue; - } - // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create - av_buffer_unref(&av_gpu_decoder); - const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); - if (hwdevice_res < 0) { - LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", - av_hwdevice_get_type_name(type), hwdevice_res); - continue; - } -#ifdef LIBVA_FOUND - if (type == AV_HWDEVICE_TYPE_VAAPI) { - // we need to determine if this is an impersonated VAAPI driver - AVHWDeviceContext* hwctx = - static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data)); - AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); - const char* vendor_name = vaQueryVendorString(vactx->display); - if (strstr(vendor_name, "VDPAU backend")) { - // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them - LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver"); - continue; - } else { - // according to some user testing, certain vaapi driver (Intel?) could be buggy - // so let's log the driver name which may help the developers/supporters - LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name); - } - } -#endif - for (int i = 0;; i++) { - const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); - if (!config) { - LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.", - av_codec->name, av_hwdevice_get_type_name(type)); - break; - } - if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { - LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); - av_codec_ctx->pix_fmt = config->pix_fmt; - return true; - } - } - } - return false; -} - -void Codec::InitializeAvCodecContext() { - av_codec_ctx = avcodec_alloc_context3(av_codec); - av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); - av_codec_ctx->thread_count = 0; - av_codec_ctx->thread_type &= ~FF_THREAD_FRAME; -} - -void Codec::InitializeGpuDecoder() { - if (!CreateGpuAvDevice()) { - av_buffer_unref(&av_gpu_decoder); - return; - } - auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder); - ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); - av_codec_ctx->hw_device_ctx = hw_device_ctx; - av_codec_ctx->get_format = GetGpuFormat; -} - -void Codec::InitializeAvFilters(AVFrame* frame) { - const AVFilter* buffer_src = avfilter_get_by_name("buffer"); - const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); - AVFilterInOut* inputs = avfilter_inout_alloc(); - AVFilterInOut* outputs = avfilter_inout_alloc(); - SCOPE_EXIT({ - avfilter_inout_free(&inputs); - avfilter_inout_free(&outputs); - }); - - // Don't know how to get the accurate time_base but it doesn't matter for yadif filter - // so just use 1/1 to make buffer filter happy - std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width, - frame->height, frame->format); - - av_filter_graph = avfilter_graph_alloc(); - int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(), - nullptr, av_filter_graph); - if (ret < 0) { - LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret); - return; - } - - ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr, - av_filter_graph); - if (ret < 0) { - LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret); - return; - } - - inputs->name = av_strdup("out"); - inputs->filter_ctx = av_filter_sink_ctx; - inputs->pad_idx = 0; - inputs->next = nullptr; - - outputs->name = av_strdup("in"); - outputs->filter_ctx = av_filter_src_ctx; - outputs->pad_idx = 0; - outputs->next = nullptr; - - const char* description = "yadif=1:-1:0"; - ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr); - if (ret < 0) { - LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret); - return; - } - - ret = avfilter_graph_config(av_filter_graph, nullptr); - if (ret < 0) { - LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret); - return; - } - - filters_initialized = true; -} +Codec::~Codec() = default; void Codec::Initialize() { - const AVCodecID codec = [&] { - switch (current_codec) { - case Host1x::NvdecCommon::VideoCodec::H264: - return AV_CODEC_ID_H264; - case Host1x::NvdecCommon::VideoCodec::VP8: - return AV_CODEC_ID_VP8; - case Host1x::NvdecCommon::VideoCodec::VP9: - return AV_CODEC_ID_VP9; - default: - UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); - return AV_CODEC_ID_NONE; - } - }(); - av_codec = avcodec_find_decoder(codec); - - InitializeAvCodecContext(); - if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { - InitializeGpuDecoder(); - } - if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { - LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); - avcodec_free_context(&av_codec_ctx); - av_buffer_unref(&av_gpu_decoder); - return; - } - if (!av_codec_ctx->hw_device_ctx) { - LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); - } - initialized = true; + initialized = decode_api.Initialize(current_codec); } void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { @@ -264,14 +35,18 @@ void Codec::Decode() { if (is_first_frame) { Initialize(); } + if (!initialized) { return; } + + // Assemble bitstream. bool vp9_hidden_frame = false; - const auto& frame_data = [&]() { + size_t configuration_size = 0; + const auto packet_data = [&]() { switch (current_codec) { case Tegra::Host1x::NvdecCommon::VideoCodec::H264: - return h264_decoder->ComposeFrame(state, is_first_frame); + return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame); case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: return vp8_decoder->ComposeFrame(state); case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: @@ -283,89 +58,35 @@ void Codec::Decode() { return std::span<const u8>{}; } }(); - AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; - if (!packet) { - LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); - return; - } - packet->data = const_cast<u8*>(frame_data.data()); - packet->size = static_cast<s32>(frame_data.size()); - if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) { - LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res); + + // Send assembled bitstream to decoder. + if (!decode_api.SendPacket(packet_data, configuration_size)) { return; } - // Only receive/store visible frames + + // Only receive/store visible frames. if (vp9_hidden_frame) { return; } - AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter}; - AVFramePtr final_frame{nullptr, AVFrameDeleter}; - ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed"); - if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) { - LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); - return; - } - if (initial_frame->width == 0 || initial_frame->height == 0) { - LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); - return; - } - bool is_interlaced = initial_frame->interlaced_frame != 0; - if (av_codec_ctx->hw_device_ctx) { - final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; - ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); - // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp - // because Intel drivers crash unless using AV_PIX_FMT_NV12 - final_frame->format = PREFERRED_GPU_FMT; - const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); - ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); - } else { - final_frame = std::move(initial_frame); - } - if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) { - UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); - return; - } - if (!is_interlaced) { - av_frames.push(std::move(final_frame)); - } else { - if (!filters_initialized) { - InitializeAvFilters(final_frame.get()); - } - if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(), - AV_BUFFERSRC_FLAG_KEEP_REF); - ret) { - LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret); - return; - } - while (true) { - auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; - int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get()); + // Receive output frames from decoder. + decode_api.ReceiveFrames(frames); - if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) - break; - if (ret < 0) { - LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret); - return; - } - - av_frames.push(std::move(filter_frame)); - } - } - while (av_frames.size() > 10) { - LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); - av_frames.pop(); + while (frames.size() > 10) { + LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame"); + frames.pop(); } } -AVFramePtr Codec::GetCurrentFrame() { +std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() { // Sometimes VIC will request more frames than have been decoded. - // in this case, return a nullptr and don't overwrite previous frame data - if (av_frames.empty()) { - return AVFramePtr{nullptr, AVFrameDeleter}; + // in this case, return a blank frame and don't overwrite previous data. + if (frames.empty()) { + return {}; } - AVFramePtr frame = std::move(av_frames.front()); - av_frames.pop(); + + auto frame = std::move(frames.front()); + frames.pop(); return frame; } diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h index 06fe00a4b..f700ae129 100644 --- a/src/video_core/host1x/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h @@ -4,28 +4,15 @@ #pragma once #include <memory> +#include <optional> #include <string_view> #include <queue> #include "common/common_types.h" +#include "video_core/host1x/ffmpeg/ffmpeg.h" #include "video_core/host1x/nvdec_common.h" -extern "C" { -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif -#include <libavcodec/avcodec.h> -#include <libavfilter/avfilter.h> -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif -} - namespace Tegra { -void AVFrameDeleter(AVFrame* ptr); -using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>; - namespace Decoder { class H264; class VP8; @@ -51,7 +38,7 @@ public: void Decode(); /// Returns next decoded frame - [[nodiscard]] AVFramePtr GetCurrentFrame(); + [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame(); /// Returns the value of current_codec [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; @@ -60,25 +47,9 @@ public: [[nodiscard]] std::string_view GetCurrentCodecName() const; private: - void InitializeAvCodecContext(); - - void InitializeAvFilters(AVFrame* frame); - - void InitializeGpuDecoder(); - - bool CreateGpuAvDevice(); - bool initialized{}; - bool filters_initialized{}; Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; - - const AVCodec* av_codec{nullptr}; - AVCodecContext* av_codec_ctx{nullptr}; - AVBufferRef* av_gpu_decoder{nullptr}; - - AVFilterContext* av_filter_src_ctx{nullptr}; - AVFilterContext* av_filter_sink_ctx{nullptr}; - AVFilterGraph* av_filter_graph{nullptr}; + FFmpeg::DecodeApi decode_api; Host1x::Host1x& host1x; const Host1x::NvdecCommon::NvdecRegisters& state; @@ -86,7 +57,7 @@ private: std::unique_ptr<Decoder::VP8> vp8_decoder; std::unique_ptr<Decoder::VP9> vp9_decoder; - std::queue<AVFramePtr> av_frames{}; + std::queue<std::unique_ptr<FFmpeg::Frame>> frames{}; }; } // namespace Tegra diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index ece79b1e2..309a7f1d5 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} H264::~H264() = default; std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, - bool is_first_frame) { + size_t* out_configuration_size, bool is_first_frame) { H264DecoderContext context; host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); @@ -39,6 +39,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters if (!is_first_frame && frame_number != 0) { frame.resize_destructive(context.stream_len); host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); + *out_configuration_size = 0; return frame; } @@ -157,6 +158,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters frame.resize(encoded_header.size() + context.stream_len); std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); + *out_configuration_size = encoded_header.size(); host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(), context.stream_len); diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h index d6b556322..1deaf4632 100644 --- a/src/video_core/host1x/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h @@ -67,6 +67,7 @@ public: /// Compose the H264 frame for FFmpeg decoding [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, + size_t* out_configuration_size, bool is_first_frame = false); private: diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp new file mode 100644 index 000000000..dcd07e6d2 --- /dev/null +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -0,0 +1,419 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/scope_exit.h" +#include "common/settings.h" +#include "video_core/host1x/ffmpeg/ffmpeg.h" + +extern "C" { +#ifdef LIBVA_FOUND +// for querying VAAPI driver information +#include <libavutil/hwcontext_vaapi.h> +#endif +} + +namespace FFmpeg { + +namespace { + +constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; +constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; +constexpr std::array PreferredGpuDecoders = { + AV_HWDEVICE_TYPE_CUDA, +#ifdef _WIN32 + AV_HWDEVICE_TYPE_D3D11VA, + AV_HWDEVICE_TYPE_DXVA2, +#elif defined(__unix__) + AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_VDPAU, +#endif + // last resort for Linux Flatpak (w/ NVIDIA) + AV_HWDEVICE_TYPE_VULKAN, +}; + +AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) { + for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { + if (*p == codec_context->pix_fmt) { + return codec_context->pix_fmt; + } + } + + LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU"); + av_buffer_unref(&codec_context->hw_device_ctx); + + codec_context->pix_fmt = PreferredCpuFormat; + return codec_context->pix_fmt; +} + +std::string AVError(int errnum) { + char errbuf[AV_ERROR_MAX_STRING_SIZE] = {}; + av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum); + return errbuf; +} + +} // namespace + +Packet::Packet(std::span<const u8> data) { + m_packet = av_packet_alloc(); + m_packet->data = const_cast<u8*>(data.data()); + m_packet->size = static_cast<s32>(data.size()); +} + +Packet::~Packet() { + av_packet_free(&m_packet); +} + +Frame::Frame() { + m_frame = av_frame_alloc(); +} + +Frame::~Frame() { + av_frame_free(&m_frame); +} + +Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { + const AVCodecID av_codec = [&] { + switch (codec) { + case Tegra::Host1x::NvdecCommon::VideoCodec::H264: + return AV_CODEC_ID_H264; + case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: + return AV_CODEC_ID_VP8; + case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: + return AV_CODEC_ID_VP9; + default: + UNIMPLEMENTED_MSG("Unknown codec {}", codec); + return AV_CODEC_ID_NONE; + } + }(); + + m_codec = avcodec_find_decoder(av_codec); +} + +bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const { + for (int i = 0;; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i); + if (!config) { + LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, + av_hwdevice_get_type_name(type)); + break; + } + if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 && + config->device_type == type) { + LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); + *out_pix_fmt = config->pix_fmt; + return true; + } + } + + return false; +} + +std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() { + std::vector<AVHWDeviceType> types; + AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; + + while (true) { + current_device_type = av_hwdevice_iterate_types(current_device_type); + if (current_device_type == AV_HWDEVICE_TYPE_NONE) { + return types; + } + + types.push_back(current_device_type); + } +} + +HardwareContext::~HardwareContext() { + av_buffer_unref(&m_gpu_decoder); +} + +bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, + const Decoder& decoder) { + const auto supported_types = GetSupportedDeviceTypes(); + for (const auto type : PreferredGpuDecoders) { + AVPixelFormat hw_pix_fmt; + + if (std::ranges::find(supported_types, type) == supported_types.end()) { + LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); + continue; + } + + if (!this->InitializeWithType(type)) { + continue; + } + + if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) { + decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt); + return true; + } + } + + return false; +} + +bool HardwareContext::InitializeWithType(AVHWDeviceType type) { + av_buffer_unref(&m_gpu_decoder); + + if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); + ret < 0) { + LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), + AVError(ret)); + return false; + } + +#ifdef LIBVA_FOUND + if (type == AV_HWDEVICE_TYPE_VAAPI) { + // We need to determine if this is an impersonated VAAPI driver. + auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data); + auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); + const char* vendor_name = vaQueryVendorString(vactx->display); + if (strstr(vendor_name, "VDPAU backend")) { + // VDPAU impersonated VAAPI impls are super buggy, we need to skip them. + LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver"); + return false; + } else { + // According to some user testing, certain VAAPI drivers (Intel?) could be buggy. + // Log the driver name just in case. + LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name); + } + } +#endif + + return true; +} + +DecoderContext::DecoderContext(const Decoder& decoder) { + m_codec_context = avcodec_alloc_context3(decoder.GetCodec()); + av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0); + m_codec_context->thread_count = 0; + m_codec_context->thread_type &= ~FF_THREAD_FRAME; +} + +DecoderContext::~DecoderContext() { + av_buffer_unref(&m_codec_context->hw_device_ctx); + avcodec_free_context(&m_codec_context); +} + +void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, + AVPixelFormat hw_pix_fmt) { + m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef()); + m_codec_context->get_format = GetGpuFormat; + m_codec_context->pix_fmt = hw_pix_fmt; +} + +bool DecoderContext::OpenContext(const Decoder& decoder) { + if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) { + LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret)); + return false; + } + + if (!m_codec_context->hw_device_ctx) { + LOG_INFO(HW_GPU, "Using FFmpeg software decoding"); + } + + return true; +} + +bool DecoderContext::SendPacket(const Packet& packet) { + if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) { + LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); + return false; + } + + return true; +} + +std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) { + auto dst_frame = std::make_unique<Frame>(); + + const auto ReceiveImpl = [&](AVFrame* frame) { + if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) { + LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret)); + return false; + } + + *out_is_interlaced = frame->interlaced_frame != 0; + return true; + }; + + if (m_codec_context->hw_device_ctx) { + // If we have a hardware context, make a separate frame here to receive the + // hardware result before sending it to the output. + Frame intermediate_frame; + + if (!ReceiveImpl(intermediate_frame.GetFrame())) { + return {}; + } + + dst_frame->SetFormat(PreferredGpuFormat); + if (const int ret = + av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0); + ret < 0) { + LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret)); + return {}; + } + } else { + // Otherwise, decode the frame as normal. + if (!ReceiveImpl(dst_frame->GetFrame())) { + return {}; + } + } + + return dst_frame; +} + +DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) { + const AVFilter* buffer_src = avfilter_get_by_name("buffer"); + const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); + AVFilterInOut* inputs = avfilter_inout_alloc(); + AVFilterInOut* outputs = avfilter_inout_alloc(); + SCOPE_EXIT({ + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + }); + + // Don't know how to get the accurate time_base but it doesn't matter for yadif filter + // so just use 1/1 to make buffer filter happy + std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(), + frame.GetHeight(), static_cast<int>(frame.GetPixelFormat())); + + m_filter_graph = avfilter_graph_alloc(); + int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(), + nullptr, m_filter_graph); + if (ret < 0) { + LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret)); + return; + } + + ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr, + m_filter_graph); + if (ret < 0) { + LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret)); + return; + } + + inputs->name = av_strdup("out"); + inputs->filter_ctx = m_sink_context; + inputs->pad_idx = 0; + inputs->next = nullptr; + + outputs->name = av_strdup("in"); + outputs->filter_ctx = m_source_context; + outputs->pad_idx = 0; + outputs->next = nullptr; + + const char* description = "yadif=1:-1:0"; + ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr); + if (ret < 0) { + LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret)); + return; + } + + ret = avfilter_graph_config(m_filter_graph, nullptr); + if (ret < 0) { + LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret)); + return; + } + + m_initialized = true; +} + +bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) { + if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(), + AV_BUFFERSRC_FLAG_KEEP_REF); + ret < 0) { + LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret)); + return false; + } + + return true; +} + +std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() { + auto dst_frame = std::make_unique<Frame>(); + const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame()); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) { + return {}; + } + + if (ret < 0) { + LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret)); + return {}; + } + + return dst_frame; +} + +DeinterlaceFilter::~DeinterlaceFilter() { + avfilter_graph_free(&m_filter_graph); +} + +void DecodeApi::Reset() { + m_deinterlace_filter.reset(); + m_hardware_context.reset(); + m_decoder_context.reset(); + m_decoder.reset(); +} + +bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) { + this->Reset(); + m_decoder.emplace(codec); + m_decoder_context.emplace(*m_decoder); + + // Enable GPU decoding if requested. + if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { + m_hardware_context.emplace(); + m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder); + } + + // Open the decoder context. + if (!m_decoder_context->OpenContext(*m_decoder)) { + this->Reset(); + return false; + } + + return true; +} + +bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) { + FFmpeg::Packet packet(packet_data); + return m_decoder_context->SendPacket(packet); +} + +void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) { + // Receive raw frame from decoder. + bool is_interlaced; + auto frame = m_decoder_context->ReceiveFrame(&is_interlaced); + if (!frame) { + return; + } + + if (!is_interlaced) { + // If the frame is not interlaced, we can pend it now. + frame_queue.push(std::move(frame)); + } else { + // Create the deinterlacer if needed. + if (!m_deinterlace_filter) { + m_deinterlace_filter.emplace(*frame); + } + + // Add the frame we just received. + if (!m_deinterlace_filter->AddSourceFrame(*frame)) { + return; + } + + // Pend output fields. + while (true) { + auto filter_frame = m_deinterlace_filter->DrainSinkFrame(); + if (!filter_frame) { + break; + } + + frame_queue.push(std::move(filter_frame)); + } + } +} + +} // namespace FFmpeg diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h new file mode 100644 index 000000000..1de0bbd83 --- /dev/null +++ b/src/video_core/host1x/ffmpeg/ffmpeg.h @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <optional> +#include <span> +#include <vector> +#include <queue> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "video_core/host1x/nvdec_common.h" + +extern "C" { +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif + +#include <libavcodec/avcodec.h> +#include <libavfilter/avfilter.h> +#include <libavfilter/buffersink.h> +#include <libavfilter/buffersrc.h> +#include <libavutil/avutil.h> +#include <libavutil/opt.h> + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +namespace FFmpeg { + +class Packet; +class Frame; +class Decoder; +class HardwareContext; +class DecoderContext; +class DeinterlaceFilter; + +// Wraps an AVPacket, a container for compressed bitstream data. +class Packet { +public: + YUZU_NON_COPYABLE(Packet); + YUZU_NON_MOVEABLE(Packet); + + explicit Packet(std::span<const u8> data); + ~Packet(); + + AVPacket* GetPacket() const { + return m_packet; + } + +private: + AVPacket* m_packet{}; +}; + +// Wraps an AVFrame, a container for audio and video stream data. +class Frame { +public: + YUZU_NON_COPYABLE(Frame); + YUZU_NON_MOVEABLE(Frame); + + explicit Frame(); + ~Frame(); + + int GetWidth() const { + return m_frame->width; + } + + int GetHeight() const { + return m_frame->height; + } + + AVPixelFormat GetPixelFormat() const { + return static_cast<AVPixelFormat>(m_frame->format); + } + + int GetStride(int plane) const { + return m_frame->linesize[plane]; + } + + int* GetStrides() const { + return m_frame->linesize; + } + + u8* GetData(int plane) const { + return m_frame->data[plane]; + } + + u8** GetPlanes() const { + return m_frame->data; + } + + void SetFormat(int format) { + m_frame->format = format; + } + + AVFrame* GetFrame() const { + return m_frame; + } + +private: + AVFrame* m_frame{}; +}; + +// Wraps an AVCodec, a type containing information about a codec. +class Decoder { +public: + YUZU_NON_COPYABLE(Decoder); + YUZU_NON_MOVEABLE(Decoder); + + explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec); + ~Decoder() = default; + + bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const; + + const AVCodec* GetCodec() const { + return m_codec; + } + +private: + const AVCodec* m_codec{}; +}; + +// Wraps AVBufferRef for an accelerated decoder. +class HardwareContext { +public: + YUZU_NON_COPYABLE(HardwareContext); + YUZU_NON_MOVEABLE(HardwareContext); + + static std::vector<AVHWDeviceType> GetSupportedDeviceTypes(); + + explicit HardwareContext() = default; + ~HardwareContext(); + + bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder); + + AVBufferRef* GetBufferRef() const { + return m_gpu_decoder; + } + +private: + bool InitializeWithType(AVHWDeviceType type); + + AVBufferRef* m_gpu_decoder{}; +}; + +// Wraps an AVCodecContext. +class DecoderContext { +public: + YUZU_NON_COPYABLE(DecoderContext); + YUZU_NON_MOVEABLE(DecoderContext); + + explicit DecoderContext(const Decoder& decoder); + ~DecoderContext(); + + void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt); + bool OpenContext(const Decoder& decoder); + bool SendPacket(const Packet& packet); + std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced); + + AVCodecContext* GetCodecContext() const { + return m_codec_context; + } + +private: + AVCodecContext* m_codec_context{}; +}; + +// Wraps an AVFilterGraph. +class DeinterlaceFilter { +public: + YUZU_NON_COPYABLE(DeinterlaceFilter); + YUZU_NON_MOVEABLE(DeinterlaceFilter); + + explicit DeinterlaceFilter(const Frame& frame); + ~DeinterlaceFilter(); + + bool AddSourceFrame(const Frame& frame); + std::unique_ptr<Frame> DrainSinkFrame(); + +private: + AVFilterGraph* m_filter_graph{}; + AVFilterContext* m_source_context{}; + AVFilterContext* m_sink_context{}; + bool m_initialized{}; +}; + +class DecodeApi { +public: + YUZU_NON_COPYABLE(DecodeApi); + YUZU_NON_MOVEABLE(DecodeApi); + + DecodeApi() = default; + ~DecodeApi() = default; + + bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec); + void Reset(); + + bool SendPacket(std::span<const u8> packet_data, size_t configuration_size); + void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue); + +private: + std::optional<FFmpeg::Decoder> m_decoder; + std::optional<FFmpeg::DecoderContext> m_decoder_context; + std::optional<FFmpeg::HardwareContext> m_hardware_context; + std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter; +}; + +} // namespace FFmpeg diff --git a/src/video_core/host1x/nvdec.cpp b/src/video_core/host1x/nvdec.cpp index a4bd5b79f..b8f5866d3 100644 --- a/src/video_core/host1x/nvdec.cpp +++ b/src/video_core/host1x/nvdec.cpp @@ -28,7 +28,7 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) { } } -AVFramePtr Nvdec::GetFrame() { +std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() { return codec->GetCurrentFrame(); } diff --git a/src/video_core/host1x/nvdec.h b/src/video_core/host1x/nvdec.h index 3949d5181..ddddb8d28 100644 --- a/src/video_core/host1x/nvdec.h +++ b/src/video_core/host1x/nvdec.h @@ -23,7 +23,7 @@ public: void ProcessMethod(u32 method, u32 argument); /// Return most recently decoded frame - [[nodiscard]] AVFramePtr GetFrame(); + [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame(); private: /// Invoke codec to decode a frame diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index 10d7ef884..2a5eba415 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -82,27 +82,26 @@ void Vic::Execute() { return; } const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)}; - const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); - const auto* frame = frame_ptr.get(); + auto frame = nvdec_processor->GetFrame(); if (!frame) { return; } const u64 surface_width = config.surface_width_minus1 + 1; const u64 surface_height = config.surface_height_minus1 + 1; - if (static_cast<u64>(frame->width) != surface_width || - static_cast<u64>(frame->height) != surface_height) { + if (static_cast<u64>(frame->GetWidth()) != surface_width || + static_cast<u64>(frame->GetHeight()) != surface_height) { // TODO: Properly support multiple video streams with differing frame dimensions LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}", - frame->width, frame->height, surface_width, surface_height); + frame->GetWidth(), frame->GetHeight(), surface_width, surface_height); } switch (config.pixel_format) { case VideoPixelFormat::RGBA8: case VideoPixelFormat::BGRA8: case VideoPixelFormat::RGBX8: - WriteRGBFrame(frame, config); + WriteRGBFrame(std::move(frame), config); break; case VideoPixelFormat::YUV420: - WriteYUVFrame(frame, config); + WriteYUVFrame(std::move(frame), config); break; default: UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value()); @@ -110,10 +109,14 @@ void Vic::Execute() { } } -void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { +void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) { LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); - if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) { + const auto frame_width = frame->GetWidth(); + const auto frame_height = frame->GetHeight(); + const auto frame_format = frame->GetPixelFormat(); + + if (!scaler_ctx || frame_width != scaler_width || frame_height != scaler_height) { const AVPixelFormat target_format = [pixel_format = config.pixel_format]() { switch (pixel_format) { case VideoPixelFormat::RGBA8: @@ -129,27 +132,26 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { sws_freeContext(scaler_ctx); // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format - scaler_ctx = sws_getContext(frame->width, frame->height, - static_cast<AVPixelFormat>(frame->format), frame->width, - frame->height, target_format, 0, nullptr, nullptr, nullptr); - scaler_width = frame->width; - scaler_height = frame->height; + scaler_ctx = sws_getContext(frame_width, frame_height, frame_format, frame_width, + frame_height, target_format, 0, nullptr, nullptr, nullptr); + scaler_width = frame_width; + scaler_height = frame_height; converted_frame_buffer.reset(); } if (!converted_frame_buffer) { - const size_t frame_size = frame->width * frame->height * 4; + const size_t frame_size = frame_width * frame_height * 4; converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free}; } - const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0}; + const std::array<int, 4> converted_stride{frame_width * 4, frame_height * 4, 0, 0}; u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; - sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr, - converted_stride.data()); + sws_scale(scaler_ctx, frame->GetPlanes(), frame->GetStrides(), 0, frame_height, + &converted_frame_buf_addr, converted_stride.data()); // Use the minimum of surface/frame dimensions to avoid buffer overflow. const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1; const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1; - const u32 width = std::min(surface_width, static_cast<u32>(frame->width)); - const u32 height = std::min(surface_height, static_cast<u32>(frame->height)); + const u32 width = std::min(surface_width, static_cast<u32>(frame_width)); + const u32 height = std::min(surface_height, static_cast<u32>(frame_height)); const u32 blk_kind = static_cast<u32>(config.block_linear_kind); if (blk_kind != 0) { // swizzle pitch linear to block linear @@ -169,23 +171,23 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { } } -void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { +void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) { LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); const std::size_t surface_width = config.surface_width_minus1 + 1; const std::size_t surface_height = config.surface_height_minus1 + 1; const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; // Use the minimum of surface/frame dimensions to avoid buffer overflow. - const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); - const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); + const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->GetWidth())); + const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->GetHeight())); - const auto stride = static_cast<size_t>(frame->linesize[0]); + const auto stride = static_cast<size_t>(frame->GetStride(0)); luma_buffer.resize_destructive(aligned_width * surface_height); chroma_buffer.resize_destructive(aligned_width * surface_height / 2); // Populate luma buffer - const u8* luma_src = frame->data[0]; + const u8* luma_src = frame->GetData(0); for (std::size_t y = 0; y < frame_height; ++y) { const std::size_t src = y * stride; const std::size_t dst = y * aligned_width; @@ -196,16 +198,16 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { // Chroma const std::size_t half_height = frame_height / 2; - const auto half_stride = static_cast<size_t>(frame->linesize[1]); + const auto half_stride = static_cast<size_t>(frame->GetStride(1)); - switch (frame->format) { + switch (frame->GetPixelFormat()) { case AV_PIX_FMT_YUV420P: { // Frame from FFmpeg software // Populate chroma buffer from both channels with interleaving. const std::size_t half_width = frame_width / 2; u8* chroma_buffer_data = chroma_buffer.data(); - const u8* chroma_b_src = frame->data[1]; - const u8* chroma_r_src = frame->data[2]; + const u8* chroma_b_src = frame->GetData(1); + const u8* chroma_r_src = frame->GetData(2); for (std::size_t y = 0; y < half_height; ++y) { const std::size_t src = y * half_stride; const std::size_t dst = y * aligned_width; @@ -219,7 +221,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { case AV_PIX_FMT_NV12: { // Frame from VA-API hardware // This is already interleaved so just copy - const u8* chroma_src = frame->data[1]; + const u8* chroma_src = frame->GetData(1); for (std::size_t y = 0; y < half_height; ++y) { const std::size_t src = y * stride; const std::size_t dst = y * aligned_width; diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h index 3d9753047..6c868f062 100644 --- a/src/video_core/host1x/vic.h +++ b/src/video_core/host1x/vic.h @@ -39,9 +39,9 @@ public: private: void Execute(); - void WriteRGBFrame(const AVFrame* frame, const VicConfig& config); + void WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config); - void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); + void WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config); Host1x& host1x; std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor; diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index 78b42b518..efa9adf7a 100644 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h @@ -266,7 +266,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type return; } if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] { - UNREACHABLE(); + ASSERT(false); return; } query_base->value += streamer->GetAmmendValue(); diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 44a771d65..af0a453ee 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -559,7 +559,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { } void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { - glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS); + const GLenum buffer_mode = + num_xfb_buffers_active == 1 ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS; + glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), buffer_mode); } void GraphicsPipeline::GenerateTransformFeedbackState() { @@ -567,12 +569,14 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { // when this is required. GLint* cursor{xfb_attribs.data()}; + num_xfb_buffers_active = 0; for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { const auto& layout = key.xfb_state.layouts[feedback]; UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); if (layout.varying_count == 0) { continue; } + num_xfb_buffers_active++; const auto& locations = key.xfb_state.varyings[feedback]; std::optional<u32> current_index; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 74fc9cc3d..2f70c1ae9 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -154,6 +154,7 @@ private: static constexpr std::size_t XFB_ENTRY_STRIDE = 3; GLsizei num_xfb_attribs{}; + u32 num_xfb_buffers_active{}; std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; std::mutex built_mutex; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 27e2de1bf..9995b6dd4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -555,7 +555,7 @@ void RasterizerOpenGL::OnCacheInvalidation(VAddr addr, u64 size) { } { std::scoped_lock lock{buffer_cache.mutex}; - buffer_cache.CachedWriteMemory(addr, size); + buffer_cache.WriteMemory(addr, size); } shader_cache.InvalidateRegion(addr, size); } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7e7a80740..c4c30d807 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -132,16 +132,12 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { const bool use_accelerated = rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); const bool is_srgb = use_accelerated && screen_info.is_srgb; + RenderScreenshot(*framebuffer, use_accelerated); - { - std::scoped_lock lock{rasterizer.LockCaches()}; - RenderScreenshot(*framebuffer, use_accelerated); - - Frame* frame = present_manager.GetRenderFrame(); - blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); - scheduler.Flush(*frame->render_ready); - present_manager.Present(frame); - } + Frame* frame = present_manager.GetRenderFrame(); + blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); + scheduler.Flush(*frame->render_ready); + present_manager.Present(frame); gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 22bf8cc77..89b455bff 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -263,6 +263,22 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program info.y_negate = key.state.y_negate != 0; return info; } + +size_t GetTotalPipelineWorkers() { + const size_t max_core_threads = + std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL; +#ifdef ANDROID + // Leave at least a few cores free in android + constexpr size_t free_cores = 3ULL; + if (max_core_threads <= free_cores) { + return 1ULL; + } + return max_core_threads - free_cores; +#else + return max_core_threads; +#endif +} + } // Anonymous namespace size_t ComputePipelineCacheKey::Hash() const noexcept { @@ -294,11 +310,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, -#ifdef ANDROID - workers(1, "VkPipelineBuilder"), -#else - workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), -#endif + workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(), + "VkPipelineBuilder"), serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; const VkDriverId driver_id{device.GetDriverID()}; @@ -338,6 +351,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_native_ndc = device.IsExtDepthClipControlSupported(), .support_scaled_attributes = !device.MustEmulateScaledFormats(), + .support_multi_viewport = device.SupportsMultiViewport(), .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 66c03bf17..078777cdd 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -211,6 +211,13 @@ public: return; } PauseCounter(); + const auto driver_id = device.GetDriverID(); + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + pending_sync.clear(); + sync_values_stash.clear(); + return; + } sync_values_stash.clear(); sync_values_stash.emplace_back(); std::vector<HostSyncValues>* sync_values = &sync_values_stash.back(); @@ -1378,6 +1385,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku return true; } + auto driver_id = impl->device.GetDriverID(); + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + return true; + } + for (size_t i = 0; i < 2; i++) { is_null[i] = !is_in_ac[i] && check_value(objects[i]->address); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index c0e8431e4..e0ab1eaac 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -199,7 +199,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { if (!pipeline) { return; } - std::scoped_lock lock{LockCaches()}; + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); @@ -621,7 +621,7 @@ void RasterizerVulkan::OnCacheInvalidation(VAddr addr, u64 size) { } { std::scoped_lock lock{buffer_cache.mutex}; - buffer_cache.CachedWriteMemory(addr, size); + buffer_cache.WriteMemory(addr, size); } pipeline_cache.InvalidateRegion(addr, size); } @@ -710,7 +710,6 @@ void RasterizerVulkan::TiledCacheBarrier() { } void RasterizerVulkan::FlushCommands() { - std::scoped_lock lock{LockCaches()}; if (draw_counter == 0) { return; } @@ -808,7 +807,6 @@ void RasterizerVulkan::FlushWork() { if ((++draw_counter & 7) != 7) { return; } - std::scoped_lock lock{LockCaches()}; if (draw_counter < DRAWS_TO_DISPATCH) { // Send recorded tasks to the worker thread scheduler.DispatchWork(); @@ -1507,7 +1505,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { CreateChannel(channel); { - std::scoped_lock lock{LockCaches()}; + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; texture_cache.CreateChannel(channel); buffer_cache.CreateChannel(channel); } @@ -1520,7 +1518,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { const s32 channel_id = channel.bind_id; BindToChannel(channel_id); { - std::scoped_lock lock{LockCaches()}; + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; texture_cache.BindToChannel(channel_id); buffer_cache.BindToChannel(channel_id); } @@ -1533,7 +1531,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { void RasterizerVulkan::ReleaseChannel(s32 channel_id) { EraseChannel(channel_id); { - std::scoped_lock lock{LockCaches()}; + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; texture_cache.EraseChannel(channel_id); buffer_cache.EraseChannel(channel_id); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index ce3dfbaab..ad069556c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -133,10 +133,6 @@ public: void ReleaseChannel(s32 channel_id) override; - std::scoped_lock<std::recursive_mutex, std::recursive_mutex> LockCaches() { - return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex}; - } - private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index de34f6d49..5dbec2e62 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1785,8 +1785,22 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} -ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params) - : VideoCommon::ImageViewBase{params} {} +ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params) + : VideoCommon::ImageViewBase{params}, device{&runtime.device} { + if (device->HasNullDescriptor()) { + return; + } + + // Handle fallback for devices without nullDescriptor + ImageInfo info{}; + info.format = PixelFormat::A8B8G8R8_UNORM; + + null_image = MakeImage(*device, runtime.memory_allocator, info, {}); + image_handle = *null_image; + for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) { + image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT); + } +} ImageView::~ImageView() = default; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 7a0807709..edf5d7635 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -267,6 +267,7 @@ private: vk::ImageView depth_view; vk::ImageView stencil_view; vk::ImageView color_view; + vk::Image null_image; VkImage image_handle = VK_NULL_HANDLE; VkImageView render_target = VK_NULL_HANDLE; VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index e518756d2..fde36a49c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -635,6 +635,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR has_broken_cube_compatibility = true; } } + if (is_qualcomm) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) { + has_broken_parallel_compiling = true; + } + } if (extensions.sampler_filter_minmax && is_amd) { // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. if (!features.shader_float16_int8.shaderFloat16) { @@ -863,7 +869,8 @@ bool Device::ShouldBoostClocks() const { driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; - const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; + const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) || + (vendor_id == 0x1002 && device_id == 0x1435); const bool is_debugging = this->HasDebuggingToolAttached(); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index b213ed7dd..4f3846345 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -102,6 +102,7 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ @@ -599,6 +600,11 @@ public: return has_broken_cube_compatibility; } + /// Returns true if parallel shader compiling has issues with the current driver. + bool HasBrokenParallelShaderCompiling() const { + return has_broken_parallel_compiling; + } + /// Returns the vendor name reported from Vulkan. std::string_view GetVendorName() const { return properties.driver.driverName; @@ -663,6 +669,10 @@ public: return supports_conditional_barriers; } + bool SupportsMultiViewport() const { + return features2.features.multiViewport; + } + [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id, u32 driver_version) { if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { @@ -794,6 +804,7 @@ private: bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. bool has_broken_compute{}; ///< Compute shaders can cause crashes bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit + bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling. bool has_renderdoc{}; ///< Has RenderDoc attached bool has_nsight_graphics{}; ///< Has Nsight Graphics attached bool supports_d24_depth{}; ///< Supports D24 depth buffers. diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 33e1fb663..90278052a 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -38,8 +38,6 @@ add_executable(yuzu compatdb.ui compatibility_list.cpp compatibility_list.h - configuration/config.cpp - configuration/config.h configuration/configuration_shared.cpp configuration/configuration_shared.h configuration/configure.ui @@ -147,6 +145,8 @@ add_executable(yuzu configuration/shared_translation.h configuration/shared_widget.cpp configuration/shared_widget.h + configuration/qt_config.cpp + configuration/qt_config.h debugger/console.cpp debugger/console.h debugger/controller.cpp @@ -252,6 +252,7 @@ file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) if (ENABLE_QT_TRANSLATION) set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) + option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF) # Update source TS file if enabled if (GENERATE_QT_TRANSLATION) @@ -259,19 +260,51 @@ if (ENABLE_QT_TRANSLATION) # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") - qt_create_translation(QM_FILES - ${SRCS} - ${UIS} - ${YUZU_QT_LANGUAGES}/en.ts - OPTIONS - -source-language en_US - -target-language en_US - ) + if (WORKAROUND_BROKEN_LUPDATE) + add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts + COMMAND lupdate + -source-language en_US + -target-language en_US + ${SRCS} + ${UIS} + -ts ${YUZU_QT_LANGUAGES}/en.ts + DEPENDS + ${SRCS} + ${UIS} + WORKING_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + qt_create_translation(QM_FILES + ${SRCS} + ${UIS} + ${YUZU_QT_LANGUAGES}/en.ts + OPTIONS + -source-language en_US + -target-language en_US + ) + endif() # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") - qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) + if (WORKAROUND_BROKEN_LUPDATE) + add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE} + COMMAND lupdate + -source-language en_US + -target-language en_US + ${SRCS} + ${UIS} + -ts ${GENERATED_PLURALS_FILE} + DEPENDS + ${SRCS} + ${UIS} + WORKING_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) + endif() add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) endif() @@ -344,7 +377,7 @@ endif() create_target_directory_groups(yuzu) -target_link_libraries(yuzu PRIVATE common core input_common network video_core) +target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core) target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 515cb7ce6..9e5319716 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -13,7 +13,6 @@ #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" #include "core/hle/service/sm/sm.h" #include "ui_qt_controller.h" #include "yuzu/applets/qt_controller.h" diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp deleted file mode 100644 index baa3e55f3..000000000 --- a/src/yuzu/configuration/config.cpp +++ /dev/null @@ -1,1306 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <array> -#include <QKeySequence> -#include <QSettings> -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/settings.h" -#include "common/settings_common.h" -#include "common/settings_enums.h" -#include "core/core.h" -#include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "input_common/main.h" -#include "network/network.h" -#include "yuzu/configuration/config.h" - -namespace FS = Common::FS; - -Config::Config(const std::string& config_name, ConfigType config_type) - : type(config_type), global{config_type == ConfigType::GlobalConfig} { - Initialize(config_name); -} - -Config::~Config() { - if (global) { - Save(); - } -} - -const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { - Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, - Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, - Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, - Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, -}; - -const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = { - Qt::Key_7, - Qt::Key_8, -}; - -const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ - { - Qt::Key_W, - Qt::Key_S, - Qt::Key_A, - Qt::Key_D, - }, - { - Qt::Key_I, - Qt::Key_K, - Qt::Key_J, - Qt::Key_L, - }, -}}; - -const std::array<int, 2> Config::default_stick_mod = { - Qt::Key_Shift, - 0, -}; - -const std::array<int, 2> Config::default_ringcon_analogs{{ - Qt::Key_A, - Qt::Key_D, -}}; - -const std::map<Settings::AntiAliasing, QString> Config::anti_aliasing_texts_map = { - {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, - {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, - {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, -}; - -const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_map = { - {Settings::ScalingFilter::NearestNeighbor, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, - {Settings::ScalingFilter::Bilinear, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, - {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, - {Settings::ScalingFilter::Gaussian, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, - {Settings::ScalingFilter::ScaleForce, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, - {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, -}; - -const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = { - {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, - {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, -}; - -const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { - {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, - {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, - {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, -}; - -const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = { - {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, - {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, - {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, -}; - -const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = { - {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, - {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, - {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, -}; - -// This shouldn't have anything except static initializers (no functions). So -// QKeySequence(...).toString() is NOT ALLOWED HERE. -// This must be in alphabetical order according to action name as it must have the same order as -// UISetting::values.shortcuts, which is alphabetically ordered. -// clang-format off -const std::array<UISettings::Shortcut, 23> Config::default_hotkeys{{ - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}}, -}}; -// clang-format on - -void Config::Initialize(const std::string& config_name) { - const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); - const auto config_file = fmt::format("{}.ini", config_name); - - switch (type) { - case ConfigType::GlobalConfig: - qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file); - void(FS::CreateParentDir(qt_config_loc)); - qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - Reload(); - break; - case ConfigType::PerGameConfig: - qt_config_loc = - FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); - void(FS::CreateParentDir(qt_config_loc)); - qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - Reload(); - break; - case ConfigType::InputProfile: - qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); - void(FS::CreateParentDir(qt_config_loc)); - qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - break; - } -} - -bool Config::IsCustomConfig() { - return type == ConfigType::PerGameConfig; -} - -void Config::ReadPlayerValue(std::size_t player_index) { - const QString player_prefix = [this, player_index] { - if (type == ConfigType::InputProfile) { - return QString{}; - } else { - return QStringLiteral("player_%1_").arg(player_index); - } - }(); - - auto& player = Settings::values.players.GetValue()[player_index]; - if (IsCustomConfig()) { - const auto profile_name = - qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{}) - .toString() - .toStdString(); - if (profile_name.empty()) { - // Use the global input config - player = Settings::values.players.GetValue(true)[player_index]; - return; - } - player.profile_name = profile_name; - } - - if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) { - const auto controller = static_cast<Settings::ControllerType>( - qt_config - ->value(QStringLiteral("%1type").arg(player_prefix), - static_cast<u8>(Settings::ControllerType::ProController)) - .toUInt()); - - if (controller == Settings::ControllerType::LeftJoycon || - controller == Settings::ControllerType::RightJoycon) { - player.controller_type = controller; - } - } else { - player.connected = - ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) - .toBool(); - - player.controller_type = static_cast<Settings::ControllerType>( - qt_config - ->value(QStringLiteral("%1type").arg(player_prefix), - static_cast<u8>(Settings::ControllerType::ProController)) - .toUInt()); - - player.vibration_enabled = - qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) - .toBool(); - - player.vibration_strength = - qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) - .toInt(); - - player.body_color_left = qt_config - ->value(QStringLiteral("%1body_color_left").arg(player_prefix), - Settings::JOYCON_BODY_NEON_BLUE) - .toUInt(); - player.body_color_right = - qt_config - ->value(QStringLiteral("%1body_color_right").arg(player_prefix), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - player.button_color_left = - qt_config - ->value(QStringLiteral("%1button_color_left").arg(player_prefix), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); - player.button_color_right = - qt_config - ->value(QStringLiteral("%1button_color_right").arg(player_prefix), - Settings::JOYCON_BUTTONS_NEON_RED) - .toUInt(); - } - - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& player_buttons = player.buttons[i]; - - player_buttons = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_buttons.empty()) { - player_buttons = default_param; - } - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - auto& player_analogs = player.analogs[i]; - - player_analogs = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_analogs.empty()) { - player_analogs = default_param; - } - } - - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeMotion::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_motions.empty()) { - player_motions = default_param; - } - } -} - -void Config::ReadDebugValues() { - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; - - debug_pad_buttons = qt_config - ->value(QStringLiteral("debug_pad_") + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (debug_pad_buttons.empty()) { - debug_pad_buttons = default_param; - } - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; - - debug_pad_analogs = qt_config - ->value(QStringLiteral("debug_pad_") + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (debug_pad_analogs.empty()) { - debug_pad_analogs = default_param; - } - } -} - -void Config::ReadTouchscreenValues() { - Settings::values.touchscreen.enabled = - ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); - - Settings::values.touchscreen.rotation_angle = - ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); - Settings::values.touchscreen.diameter_x = - ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt(); - Settings::values.touchscreen.diameter_y = - ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); -} - -void Config::ReadHidbusValues() { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); - auto& ringcon_analogs = Settings::values.ringcon_analogs; - - ringcon_analogs = - qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (ringcon_analogs.empty()) { - ringcon_analogs = default_param; - } -} - -void Config::ReadAudioValues() { - qt_config->beginGroup(QStringLiteral("Audio")); - - ReadCategory(Settings::Category::Audio); - - qt_config->endGroup(); -} - -void Config::ReadControlValues() { - qt_config->beginGroup(QStringLiteral("Controls")); - - ReadCategory(Settings::Category::Controls); - - Settings::values.players.SetGlobal(!IsCustomConfig()); - for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { - ReadPlayerValue(p); - } - - // Disable docked mode if handheld is selected - const auto controller_type = Settings::values.players.GetValue()[0].controller_type; - if (controller_type == Settings::ControllerType::Handheld) { - Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); - Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); - } - - if (IsCustomConfig()) { - qt_config->endGroup(); - return; - } - ReadDebugValues(); - ReadTouchscreenValues(); - ReadMotionTouchValues(); - ReadHidbusValues(); - - qt_config->endGroup(); -} - -void Config::ReadMotionTouchValues() { - int num_touch_from_button_maps = - qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); - - if (num_touch_from_button_maps > 0) { - const auto append_touch_from_button_map = [this] { - Settings::TouchFromButtonMap map; - map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) - .toString() - .toStdString(); - const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); - map.buttons.reserve(num_touch_maps); - for (int i = 0; i < num_touch_maps; i++) { - qt_config->setArrayIndex(i); - std::string touch_mapping = - ReadSetting(QStringLiteral("bind")).toString().toStdString(); - map.buttons.emplace_back(std::move(touch_mapping)); - } - qt_config->endArray(); // entries - Settings::values.touch_from_button_maps.emplace_back(std::move(map)); - }; - - for (int i = 0; i < num_touch_from_button_maps; ++i) { - qt_config->setArrayIndex(i); - append_touch_from_button_map(); - } - } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); - num_touch_from_button_maps = 1; - } - qt_config->endArray(); - - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); -} - -void Config::ReadCoreValues() { - qt_config->beginGroup(QStringLiteral("Core")); - - ReadCategory(Settings::Category::Core); - - qt_config->endGroup(); -} - -void Config::ReadDataStorageValues() { - qt_config->beginGroup(QStringLiteral("Data Storage")); - - FS::SetYuzuPath( - FS::YuzuPath::NANDDir, - qt_config - ->value(QStringLiteral("nand_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))) - .toString() - .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::SDMCDir, - qt_config - ->value(QStringLiteral("sdmc_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))) - .toString() - .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::LoadDir, - qt_config - ->value(QStringLiteral("load_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))) - .toString() - .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::DumpDir, - qt_config - ->value(QStringLiteral("dump_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) - .toString() - .toStdString()); - FS::SetYuzuPath(FS::YuzuPath::TASDir, - qt_config - ->value(QStringLiteral("tas_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))) - .toString() - .toStdString()); - - ReadCategory(Settings::Category::DataStorage); - - qt_config->endGroup(); -} - -void Config::ReadDebuggingValues() { - qt_config->beginGroup(QStringLiteral("Debugging")); - - // Intentionally not using the QT default setting as this is intended to be changed in the ini - Settings::values.record_frame_times = - qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); - - ReadCategory(Settings::Category::Debugging); - ReadCategory(Settings::Category::DebuggingGraphics); - - qt_config->endGroup(); -} - -void Config::ReadServiceValues() { - qt_config->beginGroup(QStringLiteral("Services")); - - ReadCategory(Settings::Category::Services); - - qt_config->endGroup(); -} - -void Config::ReadDisabledAddOnValues() { - const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); - - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong(); - std::vector<std::string> out; - const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); - for (int j = 0; j < d_size; ++j) { - qt_config->setArrayIndex(j); - out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString()); - } - qt_config->endArray(); - Settings::values.disabled_addons.insert_or_assign(title_id, out); - } - - qt_config->endArray(); -} - -void Config::ReadMiscellaneousValues() { - qt_config->beginGroup(QStringLiteral("Miscellaneous")); - - ReadCategory(Settings::Category::Miscellaneous); - - qt_config->endGroup(); -} - -void Config::ReadPathValues() { - qt_config->beginGroup(QStringLiteral("Paths")); - - UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); - UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); - UISettings::values.game_dir_deprecated = - ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); - UISettings::values.game_dir_deprecated_deepscan = - ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); - const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); - for (int i = 0; i < gamedirs_size; ++i) { - qt_config->setArrayIndex(i); - UISettings::GameDir game_dir; - game_dir.path = ReadSetting(QStringLiteral("path")).toString(); - game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); - game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); - UISettings::values.game_dirs.append(game_dir); - } - qt_config->endArray(); - // create NAND and SD card directories if empty, these are not removable through the UI, - // also carries over old game list settings if present - if (UISettings::values.game_dirs.isEmpty()) { - UISettings::GameDir game_dir; - game_dir.path = QStringLiteral("SDMC"); - game_dir.expanded = true; - UISettings::values.game_dirs.append(game_dir); - game_dir.path = QStringLiteral("UserNAND"); - UISettings::values.game_dirs.append(game_dir); - game_dir.path = QStringLiteral("SysNAND"); - UISettings::values.game_dirs.append(game_dir); - if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { - game_dir.path = UISettings::values.game_dir_deprecated; - game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; - UISettings::values.game_dirs.append(game_dir); - } - } - UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); - UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); - - qt_config->endGroup(); -} - -void Config::ReadCpuValues() { - qt_config->beginGroup(QStringLiteral("Cpu")); - - ReadCategory(Settings::Category::Cpu); - ReadCategory(Settings::Category::CpuDebug); - ReadCategory(Settings::Category::CpuUnsafe); - - qt_config->endGroup(); -} - -void Config::ReadRendererValues() { - qt_config->beginGroup(QStringLiteral("Renderer")); - - ReadCategory(Settings::Category::Renderer); - ReadCategory(Settings::Category::RendererAdvanced); - ReadCategory(Settings::Category::RendererDebug); - - qt_config->endGroup(); -} - -void Config::ReadScreenshotValues() { - qt_config->beginGroup(QStringLiteral("Screenshots")); - - ReadCategory(Settings::Category::Screenshots); - FS::SetYuzuPath( - FS::YuzuPath::ScreenshotsDir, - qt_config - ->value(QStringLiteral("screenshot_path"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))) - .toString() - .toStdString()); - - qt_config->endGroup(); -} - -void Config::ReadShortcutValues() { - qt_config->beginGroup(QStringLiteral("Shortcuts")); - - for (const auto& [name, group, shortcut] : default_hotkeys) { - qt_config->beginGroup(group); - qt_config->beginGroup(name); - // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 - // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open - // a file dialog in windowed mode - UISettings::values.shortcuts.push_back( - {name, - group, - {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), - ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) - .toString(), - shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}}); - qt_config->endGroup(); - qt_config->endGroup(); - } - - qt_config->endGroup(); -} - -void Config::ReadSystemValues() { - qt_config->beginGroup(QStringLiteral("System")); - - ReadCategory(Settings::Category::System); - ReadCategory(Settings::Category::SystemAudio); - - qt_config->endGroup(); -} - -void Config::ReadUIValues() { - qt_config->beginGroup(QStringLiteral("UI")); - - UISettings::values.theme = - ReadSetting( - QStringLiteral("theme"), - QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)) - .toString(); - - ReadUIGamelistValues(); - ReadUILayoutValues(); - ReadPathValues(); - ReadScreenshotValues(); - ReadShortcutValues(); - ReadMultiplayerValues(); - - ReadCategory(Settings::Category::Ui); - ReadCategory(Settings::Category::UiGeneral); - - qt_config->endGroup(); -} - -void Config::ReadUIGamelistValues() { - qt_config->beginGroup(QStringLiteral("UIGameList")); - - ReadCategory(Settings::Category::UiGameList); - - const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); - for (int i = 0; i < favorites_size; i++) { - qt_config->setArrayIndex(i); - UISettings::values.favorited_ids.append( - ReadSetting(QStringLiteral("program_id")).toULongLong()); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -void Config::ReadUILayoutValues() { - qt_config->beginGroup(QStringLiteral("UILayout")); - - UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray(); - UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray(); - UISettings::values.renderwindow_geometry = - ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray(); - UISettings::values.gamelist_header_state = - ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); - UISettings::values.microprofile_geometry = - ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); - - ReadCategory(Settings::Category::UiLayout); - - qt_config->endGroup(); -} - -void Config::ReadWebServiceValues() { - qt_config->beginGroup(QStringLiteral("WebService")); - - ReadCategory(Settings::Category::WebService); - - qt_config->endGroup(); -} - -void Config::ReadMultiplayerValues() { - qt_config->beginGroup(QStringLiteral("Multiplayer")); - - ReadCategory(Settings::Category::Multiplayer); - - // Read ban list back - int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); - UISettings::values.multiplayer_ban_list.first.resize(size); - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - UISettings::values.multiplayer_ban_list.first[i] = - ReadSetting(QStringLiteral("username")).toString().toStdString(); - } - qt_config->endArray(); - size = qt_config->beginReadArray(QStringLiteral("ip_ban_list")); - UISettings::values.multiplayer_ban_list.second.resize(size); - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - UISettings::values.multiplayer_ban_list.second[i] = - ReadSetting(QStringLiteral("ip")).toString().toStdString(); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -void Config::ReadNetworkValues() { - qt_config->beginGroup(QString::fromStdString("Services")); - - ReadCategory(Settings::Category::Network); - - qt_config->endGroup(); -} - -void Config::ReadValues() { - if (global) { - ReadDataStorageValues(); - ReadDebuggingValues(); - ReadDisabledAddOnValues(); - ReadNetworkValues(); - ReadServiceValues(); - ReadUIValues(); - ReadWebServiceValues(); - ReadMiscellaneousValues(); - } - ReadControlValues(); - ReadCoreValues(); - ReadCpuValues(); - ReadRendererValues(); - ReadAudioValues(); - ReadSystemValues(); -} - -void Config::SavePlayerValue(std::size_t player_index) { - const QString player_prefix = [this, player_index] { - if (type == ConfigType::InputProfile) { - return QString{}; - } else { - return QStringLiteral("player_%1_").arg(player_index); - } - }(); - - const auto& player = Settings::values.players.GetValue()[player_index]; - if (IsCustomConfig()) { - if (player.profile_name.empty()) { - // No custom profile selected - return; - } - WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix), - QString::fromStdString(player.profile_name), QString{}); - } - - WriteSetting(QStringLiteral("%1type").arg(player_prefix), - static_cast<u8>(player.controller_type), - static_cast<u8>(Settings::ControllerType::ProController)); - - if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) { - WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, - player_index == 0); - WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), - player.vibration_enabled, true); - WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), - player.vibration_strength, 100); - WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, - Settings::JOYCON_BODY_NEON_BLUE); - WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), - player.body_color_right, Settings::JOYCON_BODY_NEON_RED); - WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), - player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); - WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), - player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); - } - - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(player.buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(player.analogs[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeMotion::mapping[i]), - QString::fromStdString(player.motions[i]), - QString::fromStdString(default_param)); - } -} - -void Config::SaveDebugValues() { - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("debug_pad_") + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(Settings::values.debug_pad_buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - WriteSetting(QStringLiteral("debug_pad_") + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(Settings::values.debug_pad_analogs[i]), - QString::fromStdString(default_param)); - } -} - -void Config::SaveTouchscreenValues() { - const auto& touchscreen = Settings::values.touchscreen; - - WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); - - WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); - WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); - WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); -} - -void Config::SaveMotionTouchValues() { - qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); - for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { - qt_config->setArrayIndex(static_cast<int>(p)); - WriteSetting(QStringLiteral("name"), - QString::fromStdString(Settings::values.touch_from_button_maps[p].name), - QStringLiteral("default")); - qt_config->beginWriteArray(QStringLiteral("entries")); - for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); - ++q) { - qt_config->setArrayIndex(static_cast<int>(q)); - WriteSetting( - QStringLiteral("bind"), - QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q])); - } - qt_config->endArray(); - } - qt_config->endArray(); -} - -void Config::SaveHidbusValues() { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); - WriteSetting(QStringLiteral("ring_controller"), - QString::fromStdString(Settings::values.ringcon_analogs), - QString::fromStdString(default_param)); -} - -void Config::SaveValues() { - if (global) { - SaveDataStorageValues(); - SaveDebuggingValues(); - SaveDisabledAddOnValues(); - SaveNetworkValues(); - SaveUIValues(); - SaveWebServiceValues(); - SaveMiscellaneousValues(); - } - SaveControlValues(); - SaveCoreValues(); - SaveCpuValues(); - SaveRendererValues(); - SaveAudioValues(); - SaveSystemValues(); - - qt_config->sync(); -} - -void Config::SaveAudioValues() { - qt_config->beginGroup(QStringLiteral("Audio")); - - WriteCategory(Settings::Category::Audio); - - qt_config->endGroup(); -} - -void Config::SaveControlValues() { - qt_config->beginGroup(QStringLiteral("Controls")); - - WriteCategory(Settings::Category::Controls); - - Settings::values.players.SetGlobal(!IsCustomConfig()); - for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { - SavePlayerValue(p); - } - if (IsCustomConfig()) { - qt_config->endGroup(); - return; - } - SaveDebugValues(); - SaveTouchscreenValues(); - SaveMotionTouchValues(); - SaveHidbusValues(); - - qt_config->endGroup(); -} - -void Config::SaveCoreValues() { - qt_config->beginGroup(QStringLiteral("Core")); - - WriteCategory(Settings::Category::Core); - - qt_config->endGroup(); -} - -void Config::SaveDataStorageValues() { - qt_config->beginGroup(QStringLiteral("Data Storage")); - - WriteSetting(QStringLiteral("nand_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); - WriteSetting(QStringLiteral("sdmc_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); - WriteSetting(QStringLiteral("load_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); - WriteSetting(QStringLiteral("dump_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - WriteSetting(QStringLiteral("tas_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); - - WriteCategory(Settings::Category::DataStorage); - - qt_config->endGroup(); -} - -void Config::SaveDebuggingValues() { - qt_config->beginGroup(QStringLiteral("Debugging")); - - // Intentionally not using the QT default setting as this is intended to be changed in the ini - qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); - - WriteCategory(Settings::Category::Debugging); - WriteCategory(Settings::Category::DebuggingGraphics); - - qt_config->endGroup(); -} - -void Config::SaveNetworkValues() { - qt_config->beginGroup(QStringLiteral("Services")); - - WriteCategory(Settings::Category::Network); - - qt_config->endGroup(); -} - -void Config::SaveDisabledAddOnValues() { - qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); - - int i = 0; - for (const auto& elem : Settings::values.disabled_addons) { - qt_config->setArrayIndex(i); - WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0); - qt_config->beginWriteArray(QStringLiteral("disabled")); - for (std::size_t j = 0; j < elem.second.size(); ++j) { - qt_config->setArrayIndex(static_cast<int>(j)); - WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{}); - } - qt_config->endArray(); - ++i; - } - - qt_config->endArray(); -} - -void Config::SaveMiscellaneousValues() { - qt_config->beginGroup(QStringLiteral("Miscellaneous")); - - WriteCategory(Settings::Category::Miscellaneous); - - qt_config->endGroup(); -} - -void Config::SavePathValues() { - qt_config->beginGroup(QStringLiteral("Paths")); - - WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); - WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); - qt_config->beginWriteArray(QStringLiteral("gamedirs")); - for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { - qt_config->setArrayIndex(i); - const auto& game_dir = UISettings::values.game_dirs[i]; - WriteSetting(QStringLiteral("path"), game_dir.path); - WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); - WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); - } - qt_config->endArray(); - WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); - WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); - - qt_config->endGroup(); -} - -void Config::SaveCpuValues() { - qt_config->beginGroup(QStringLiteral("Cpu")); - - WriteCategory(Settings::Category::Cpu); - WriteCategory(Settings::Category::CpuDebug); - WriteCategory(Settings::Category::CpuUnsafe); - - qt_config->endGroup(); -} - -void Config::SaveRendererValues() { - qt_config->beginGroup(QStringLiteral("Renderer")); - - WriteCategory(Settings::Category::Renderer); - WriteCategory(Settings::Category::RendererAdvanced); - WriteCategory(Settings::Category::RendererDebug); - - qt_config->endGroup(); -} - -void Config::SaveScreenshotValues() { - qt_config->beginGroup(QStringLiteral("Screenshots")); - - WriteSetting(QStringLiteral("screenshot_path"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); - WriteCategory(Settings::Category::Screenshots); - - qt_config->endGroup(); -} - -void Config::SaveShortcutValues() { - qt_config->beginGroup(QStringLiteral("Shortcuts")); - - // Lengths of UISettings::values.shortcuts & default_hotkeys are same. - // However, their ordering must also be the same. - for (std::size_t i = 0; i < default_hotkeys.size(); i++) { - const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; - const auto& default_hotkey = default_hotkeys[i].shortcut; - - qt_config->beginGroup(group); - qt_config->beginGroup(name); - WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq); - WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, - default_hotkey.controller_keyseq); - WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); - WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat); - qt_config->endGroup(); - qt_config->endGroup(); - } - - qt_config->endGroup(); -} - -void Config::SaveSystemValues() { - qt_config->beginGroup(QStringLiteral("System")); - - WriteCategory(Settings::Category::System); - WriteCategory(Settings::Category::SystemAudio); - - qt_config->endGroup(); -} - -void Config::SaveUIValues() { - qt_config->beginGroup(QStringLiteral("UI")); - - WriteCategory(Settings::Category::Ui); - WriteCategory(Settings::Category::UiGeneral); - - WriteSetting(QStringLiteral("theme"), UISettings::values.theme, - QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)); - - SaveUIGamelistValues(); - SaveUILayoutValues(); - SavePathValues(); - SaveScreenshotValues(); - SaveShortcutValues(); - SaveMultiplayerValues(); - - qt_config->endGroup(); -} - -void Config::SaveUIGamelistValues() { - qt_config->beginGroup(QStringLiteral("UIGameList")); - - WriteCategory(Settings::Category::UiGameList); - - qt_config->beginWriteArray(QStringLiteral("favorites")); - for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { - qt_config->setArrayIndex(i); - WriteSetting(QStringLiteral("program_id"), - QVariant::fromValue(UISettings::values.favorited_ids[i])); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -void Config::SaveUILayoutValues() { - qt_config->beginGroup(QStringLiteral("UILayout")); - - WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry); - WriteSetting(QStringLiteral("state"), UISettings::values.state); - WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry); - WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); - WriteSetting(QStringLiteral("microProfileDialogGeometry"), - UISettings::values.microprofile_geometry); - - WriteCategory(Settings::Category::UiLayout); - - qt_config->endGroup(); -} - -void Config::SaveWebServiceValues() { - qt_config->beginGroup(QStringLiteral("WebService")); - - WriteCategory(Settings::Category::WebService); - - qt_config->endGroup(); -} - -void Config::SaveMultiplayerValues() { - qt_config->beginGroup(QStringLiteral("Multiplayer")); - - WriteCategory(Settings::Category::Multiplayer); - - // Write ban list - qt_config->beginWriteArray(QStringLiteral("username_ban_list")); - for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { - qt_config->setArrayIndex(static_cast<int>(i)); - WriteSetting(QStringLiteral("username"), - QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i])); - } - qt_config->endArray(); - qt_config->beginWriteArray(QStringLiteral("ip_ban_list")); - for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { - qt_config->setArrayIndex(static_cast<int>(i)); - WriteSetting(QStringLiteral("ip"), - QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i])); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -QVariant Config::ReadSetting(const QString& name) const { - return qt_config->value(name); -} - -QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { - QVariant result; - if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { - result = default_value; - } else { - result = qt_config->value(name, default_value); - } - return result; -} - -void Config::WriteSetting(const QString& name, const QVariant& value) { - qt_config->setValue(name, value); -} - -void Config::WriteSetting(const QString& name, const QVariant& value, - const QVariant& default_value) { - qt_config->setValue(name + QStringLiteral("/default"), value == default_value); - qt_config->setValue(name, value); -} - -void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, - bool use_global) { - if (!global) { - qt_config->setValue(name + QStringLiteral("/use_global"), use_global); - } - if (global || !use_global) { - qt_config->setValue(name + QStringLiteral("/default"), value == default_value); - qt_config->setValue(name, value); - } -} - -void Config::Reload() { - ReadValues(); - // To apply default value changes - SaveValues(); -} - -void Config::Save() { - SaveValues(); -} - -void Config::ReadControlPlayerValue(std::size_t player_index) { - qt_config->beginGroup(QStringLiteral("Controls")); - ReadPlayerValue(player_index); - qt_config->endGroup(); -} - -void Config::SaveControlPlayerValue(std::size_t player_index) { - qt_config->beginGroup(QStringLiteral("Controls")); - SavePlayerValue(player_index); - qt_config->endGroup(); -} - -void Config::ClearControlPlayerValues() { - qt_config->beginGroup(QStringLiteral("Controls")); - // If key is an empty string, all keys in the current group() are removed. - qt_config->remove(QString{}); - qt_config->endGroup(); -} - -const std::string& Config::GetConfigFilePath() const { - return qt_config_loc; -} - -static auto FindRelevantList(Settings::Category category) { - auto& map = Settings::values.linkage.by_category; - if (map.contains(category)) { - return Settings::values.linkage.by_category[category]; - } - return UISettings::values.linkage.by_category[category]; -} - -void Config::ReadCategory(Settings::Category category) { - const auto& settings = FindRelevantList(category); - std::for_each(settings.begin(), settings.end(), - [&](const auto& setting) { ReadSettingGeneric(setting); }); -} - -void Config::WriteCategory(Settings::Category category) { - const auto& settings = FindRelevantList(category); - std::for_each(settings.begin(), settings.end(), - [&](const auto& setting) { WriteSettingGeneric(setting); }); -} - -void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { - if (!setting->Save() || (!setting->Switchable() && !global)) { - return; - } - const QString name = QString::fromStdString(setting->GetLabel()); - const auto default_value = - QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString())); - - bool use_global = true; - if (setting->Switchable() && !global) { - use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>(); - setting->SetGlobal(use_global); - } - - if (global || !use_global) { - const bool is_default = - qt_config->value(name + QStringLiteral("/default"), true).value<bool>(); - if (!is_default) { - setting->LoadString( - qt_config->value(name, default_value).value<QString>().toStdString()); - } else { - // Empty string resets the Setting to default - setting->LoadString(""); - } - } -} - -void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const { - if (!setting->Save()) { - return; - } - const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString())); - const QVariant default_value = - QVariant::fromValue(QString::fromStdString(setting->DefaultToString())); - const QString label = QString::fromStdString(setting->GetLabel()); - if (setting->Switchable()) { - if (!global) { - qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal()); - } - if (global || !setting->UsingGlobal()) { - qt_config->setValue(label + QStringLiteral("/default"), value == default_value); - qt_config->setValue(label, value); - } - } else if (global) { - qt_config->setValue(label + QStringLiteral("/default"), value == default_value); - qt_config->setValue(label, value); - } -} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h deleted file mode 100644 index 1589ba057..000000000 --- a/src/yuzu/configuration/config.h +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <memory> -#include <string> -#include <QMetaType> -#include <QVariant> -#include "common/settings.h" -#include "common/settings_enums.h" -#include "yuzu/uisettings.h" - -class QSettings; - -namespace Core { -class System; -} - -class Config { -public: - enum class ConfigType { - GlobalConfig, - PerGameConfig, - InputProfile, - }; - - explicit Config(const std::string& config_name = "qt-config", - ConfigType config_type = ConfigType::GlobalConfig); - ~Config(); - - void Reload(); - void Save(); - - void ReadControlPlayerValue(std::size_t player_index); - void SaveControlPlayerValue(std::size_t player_index); - void ClearControlPlayerValues(); - - const std::string& GetConfigFilePath() const; - - static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; - static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; - static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; - static const std::array<int, 2> default_stick_mod; - static const std::array<int, 2> default_ringcon_analogs; - static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> - default_mouse_buttons; - static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; - static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; - static const std::array<UISettings::Shortcut, 23> default_hotkeys; - - static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; - static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; - static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map; - static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; - static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; - static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; - - static constexpr UISettings::Theme default_theme{ -#ifdef _WIN32 - UISettings::Theme::DarkColorful -#else - UISettings::Theme::DefaultColorful -#endif - }; - -private: - void Initialize(const std::string& config_name); - bool IsCustomConfig(); - - void ReadValues(); - void ReadPlayerValue(std::size_t player_index); - void ReadDebugValues(); - void ReadKeyboardValues(); - void ReadMouseValues(); - void ReadTouchscreenValues(); - void ReadMotionTouchValues(); - void ReadHidbusValues(); - void ReadIrCameraValues(); - - // Read functions bases off the respective config section names. - void ReadAudioValues(); - void ReadControlValues(); - void ReadCoreValues(); - void ReadDataStorageValues(); - void ReadDebuggingValues(); - void ReadServiceValues(); - void ReadDisabledAddOnValues(); - void ReadMiscellaneousValues(); - void ReadPathValues(); - void ReadCpuValues(); - void ReadRendererValues(); - void ReadScreenshotValues(); - void ReadShortcutValues(); - void ReadSystemValues(); - void ReadUIValues(); - void ReadUIGamelistValues(); - void ReadUILayoutValues(); - void ReadWebServiceValues(); - void ReadMultiplayerValues(); - void ReadNetworkValues(); - - void SaveValues(); - void SavePlayerValue(std::size_t player_index); - void SaveDebugValues(); - void SaveMouseValues(); - void SaveTouchscreenValues(); - void SaveMotionTouchValues(); - void SaveHidbusValues(); - void SaveIrCameraValues(); - - // Save functions based off the respective config section names. - void SaveAudioValues(); - void SaveControlValues(); - void SaveCoreValues(); - void SaveDataStorageValues(); - void SaveDebuggingValues(); - void SaveNetworkValues(); - void SaveDisabledAddOnValues(); - void SaveMiscellaneousValues(); - void SavePathValues(); - void SaveCpuValues(); - void SaveRendererValues(); - void SaveScreenshotValues(); - void SaveShortcutValues(); - void SaveSystemValues(); - void SaveUIValues(); - void SaveUIGamelistValues(); - void SaveUILayoutValues(); - void SaveWebServiceValues(); - void SaveMultiplayerValues(); - - /** - * Reads a setting from the qt_config. - * - * @param name The setting's identifier - * @param default_value The value to use when the setting is not already present in the config - */ - QVariant ReadSetting(const QString& name) const; - QVariant ReadSetting(const QString& name, const QVariant& default_value) const; - - /** - * Writes a setting to the qt_config. - * - * @param name The setting's idetentifier - * @param value Value of the setting - * @param default_value Default of the setting if not present in qt_config - * @param use_global Specifies if the custom or global config should be in use, for custom - * configs - */ - void WriteSetting(const QString& name, const QVariant& value); - void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); - void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, - bool use_global); - - void ReadCategory(Settings::Category category); - void WriteCategory(Settings::Category category); - void ReadSettingGeneric(Settings::BasicSetting* const setting); - void WriteSettingGeneric(Settings::BasicSetting* const setting) const; - - const ConfigType type; - std::unique_ptr<QSettings> qt_config; - std::string qt_config_loc; - const bool global; -}; - -// These metatype declarations cannot be in common/settings.h because core is devoid of QT -Q_DECLARE_METATYPE(Settings::CpuAccuracy); -Q_DECLARE_METATYPE(Settings::GpuAccuracy); -Q_DECLARE_METATYPE(Settings::FullscreenMode); -Q_DECLARE_METATYPE(Settings::NvdecEmulation); -Q_DECLARE_METATYPE(Settings::ResolutionSetup); -Q_DECLARE_METATYPE(Settings::ScalingFilter); -Q_DECLARE_METATYPE(Settings::AntiAliasing); -Q_DECLARE_METATYPE(Settings::RendererBackend); -Q_DECLARE_METATYPE(Settings::ShaderBackend); -Q_DECLARE_METATYPE(Settings::AstcRecompression); -Q_DECLARE_METATYPE(Settings::AstcDecodeMode); diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 81dd51ad3..9b6ef47a7 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) { std::map<u32, QWidget*> hold; - auto push = [&](Settings::Category category) { + auto push_settings = [&](Settings::Category category) { for (auto* setting : Settings::values.linkage.by_category[category]) { settings.push_back(setting); } + }; + + auto push_ui_settings = [&](Settings::Category category) { for (auto* setting : UISettings::values.linkage.by_category[category]) { settings.push_back(setting); } }; - push(Settings::Category::Audio); - push(Settings::Category::SystemAudio); + push_settings(Settings::Category::Audio); + push_settings(Settings::Category::SystemAudio); + push_ui_settings(Settings::Category::UiAudio); for (auto* setting : settings) { auto* widget = builder.BuildWidget(setting, apply_funcs); diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp index d95e96696..3368f53f3 100644 --- a/src/yuzu/configuration/configure_camera.cpp +++ b/src/yuzu/configuration/configure_camera.cpp @@ -10,10 +10,10 @@ #include <QStandardItemModel> #include <QTimer> +#include "common/settings.h" #include "input_common/drivers/camera.h" #include "input_common/main.h" #include "ui_configure_camera.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_camera.h" ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 0ad95cc02..aab54a1cc 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "ui_configure.h" #include "vk_device_info.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" #include "yuzu/configuration/configure_debug_tab.h" diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 68e21cd84..76fc33e49 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -9,10 +9,11 @@ #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" +#include "frontend_common/config.h" #include "ui_configure_hotkeys.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_hotkeys.h" #include "yuzu/hotkeys.h" +#include "yuzu/uisettings.h" #include "yuzu/util/sequence_dialog/sequence_dialog.h" constexpr int name_column = 0; @@ -62,18 +63,21 @@ ConfigureHotkeys::~ConfigureHotkeys() = default; void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { for (const auto& group : registry.hotkey_groups) { + QString parent_item_data = QString::fromStdString(group.first); auto* parent_item = - new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); + new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(parent_item_data))); parent_item->setEditable(false); - parent_item->setData(group.first); + parent_item->setData(parent_item_data); for (const auto& hotkey : group.second) { - auto* action = - new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); + QString hotkey_action_data = QString::fromStdString(hotkey.first); + auto* action = new QStandardItem( + QCoreApplication::translate("Hotkeys", qPrintable(hotkey_action_data))); auto* keyseq = new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); - auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); + auto* controller_keyseq = + new QStandardItem(QString::fromStdString(hotkey.second.controller_keyseq)); action->setEditable(false); - action->setData(hotkey.first); + action->setData(hotkey_action_data); keyseq->setEditable(false); controller_keyseq->setEditable(false); parent_item->appendRow({action, keyseq, controller_keyseq}); @@ -301,13 +305,13 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { const QStandardItem* controller_keyseq = parent->child(key_column_id, controller_column); for (auto& [group, sub_actions] : registry.hotkey_groups) { - if (group != parent->data()) + if (group != parent->data().toString().toStdString()) continue; for (auto& [action_name, hotkey] : sub_actions) { - if (action_name != action->data()) + if (action_name != action->data().toString().toStdString()) continue; hotkey.keyseq = QKeySequence(keyseq->text()); - hotkey.controller_keyseq = controller_keyseq->text(); + hotkey.controller_keyseq = controller_keyseq->text().toStdString(); } } } @@ -319,7 +323,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { void ConfigureHotkeys::RestoreDefaults() { for (int r = 0; r < model->rowCount(); ++r) { const QStandardItem* parent = model->item(r, 0); - const int hotkey_size = static_cast<int>(Config::default_hotkeys.size()); + const int hotkey_size = static_cast<int>(UISettings::default_hotkeys.size()); if (hotkey_size != parent->rowCount()) { QMessageBox::warning(this, tr("Invalid hotkey settings"), @@ -330,10 +334,11 @@ void ConfigureHotkeys::RestoreDefaults() { for (int r2 = 0; r2 < parent->rowCount(); ++r2) { model->item(r, 0) ->child(r2, hotkey_column) - ->setText(Config::default_hotkeys[r2].shortcut.keyseq); + ->setText(QString::fromStdString(UISettings::default_hotkeys[r2].shortcut.keyseq)); model->item(r, 0) ->child(r2, controller_column) - ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); + ->setText(QString::fromStdString( + UISettings::default_hotkeys[r2].shortcut.controller_keyseq)); } } } @@ -379,7 +384,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { const QString& default_key_sequence = - Config::default_hotkeys[index.row()].shortcut.controller_keyseq; + QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.controller_keyseq); const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); if (key_sequence_used && default_key_sequence != model->data(index).toString()) { @@ -393,7 +398,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { const QKeySequence& default_key_sequence = QKeySequence::fromString( - Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); + QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.keyseq), + QKeySequence::NativeText); const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp index 78e65d468..8d9f65a05 100644 --- a/src/yuzu/configuration/configure_input_per_game.cpp +++ b/src/yuzu/configuration/configure_input_per_game.cpp @@ -5,12 +5,12 @@ #include "core/core.h" #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" +#include "frontend_common/config.h" #include "ui_configure_input_per_game.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/configuration/input_profiles.h" -ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, +ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, QtConfig* config_, QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()), profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} { @@ -110,6 +110,6 @@ void ConfigureInputPerGame::SaveConfiguration() { // Clear all controls from the config in case the user reverted back to globals config->ClearControlPlayerValues(); for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { - config->SaveControlPlayerValue(index); + config->SaveQtControlPlayerValues(index); } } diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h index 660faf574..4420e856c 100644 --- a/src/yuzu/configuration/configure_input_per_game.h +++ b/src/yuzu/configuration/configure_input_per_game.h @@ -9,6 +9,7 @@ #include "ui_configure_input_per_game.h" #include "yuzu/configuration/input_profiles.h" +#include "yuzu/configuration/qt_config.h" class QComboBox; @@ -22,7 +23,7 @@ class ConfigureInputPerGame : public QWidget { Q_OBJECT public: - explicit ConfigureInputPerGame(Core::System& system_, Config* config_, + explicit ConfigureInputPerGame(Core::System& system_, QtConfig* config_, QWidget* parent = nullptr); /// Load and Save configurations to settings file. @@ -41,5 +42,5 @@ private: std::array<QComboBox*, 8> profile_comboboxes; Core::System& system; - Config* config; + QtConfig* config; }; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 576f5b571..0f7b3714e 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -12,15 +12,16 @@ #include <QTimer> #include "common/assert.h" #include "common/param_package.h" +#include "configuration/qt_config.h" #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" +#include "frontend_common/config.h" #include "input_common/drivers/keyboard.h" #include "input_common/drivers/mouse.h" #include "input_common/main.h" #include "ui_configure_input_player.h" #include "yuzu/bootmanager.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_input_player_widget.h" #include "yuzu/configuration/configure_mouse_panning.h" @@ -322,11 +323,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i setFocusPolicy(Qt::ClickFocus); button_map = { - ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, - ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, - ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, - ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, - ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, + ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, + ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, + ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, + ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, + ui->buttonSLLeft, ui->buttonSRLeft, ui->buttonHome, ui->buttonScreenshot, + ui->buttonSLRight, ui->buttonSRRight, }; analog_map_buttons = {{ @@ -1181,10 +1183,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { // List of all the widgets that will be hidden by any of the following layouts that need // "unhidden" after the controller type changes - const std::array<QWidget*, 11> layout_show = { - ui->buttonShoulderButtonsSLSR, + const std::array<QWidget*, 14> layout_show = { + ui->buttonShoulderButtonsSLSRLeft, + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget3, + ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft, ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft, @@ -1202,16 +1207,19 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { std::vector<QWidget*> layout_hidden; switch (layout) { case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: layout_hidden = { - ui->buttonShoulderButtonsSLSR, + ui->buttonShoulderButtonsSLSRLeft, + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget4, }; break; case Core::HID::NpadStyleIndex::JoyconLeft: layout_hidden = { + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget3, ui->buttonShoulderButtonsRight, ui->buttonMiscButtonsPlusHome, ui->bottomRight, @@ -1219,16 +1227,17 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { break; case Core::HID::NpadStyleIndex::JoyconRight: layout_hidden = { - ui->horizontalSpacerShoulderButtonsWidget, - ui->buttonShoulderButtonsLeft, - ui->buttonMiscButtonsMinusScreenshot, - ui->bottomLeft, + ui->buttonShoulderButtonsSLSRLeft, ui->horizontalSpacerShoulderButtonsWidget, + ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft, + ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft, }; break; case Core::HID::NpadStyleIndex::GameCube: layout_hidden = { - ui->buttonShoulderButtonsSLSR, + ui->buttonShoulderButtonsSLSRLeft, + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonMiscButtonsMinusGroup, ui->buttonMiscButtonsScreenshotGroup, }; @@ -1389,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { emulated_controller->SetButtonParam( button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( - Config::default_buttons[button_id])}); + QtConfig::default_buttons[button_id])}); } for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { Common::ParamPackage analog_param{}; for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { Common::ParamPackage params{InputCommon::GenerateKeyboardParam( - Config::default_analogs[analog_id][sub_button_id])}; + QtConfig::default_analogs[analog_id][sub_button_id])}; SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); } analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( - Config::default_stick_mod[analog_id])); + QtConfig::default_stick_mod[analog_id])); emulated_controller->SetStickParam(analog_id, analog_param); } for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { emulated_controller->SetMotionParam( motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( - Config::default_motions[motion_id])}); + QtConfig::default_motions[motion_id])}); } // If mouse is selected we want to override with mappings from the driver diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 611a79477..5518cccd1 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -1208,6 +1208,159 @@ <property name="spacing"> <number>3</number> </property> + <item> + <widget class="QWidget" name="buttonShoulderButtonsSLSRLeft" native="true"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRLeftVerticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonShoulderButtonsSLLeftGroup"> + <property name="title"> + <string>SL</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLLeftVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonSLLeft"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>SL</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonShoulderButtonsSRLeftGroup"> + <property name="title"> + <string>SR</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSRLeftVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonSRLeft"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>SR</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget4" native="true"> + <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget4Layout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <spacer name="horizontalSpacerShoulderButtons5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> <item> <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true"> <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout"> @@ -1830,125 +1983,125 @@ </layout> </widget> </item> - <item> - <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item alignment="Qt::AlignHCenter"> - <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup"> - <property name="title"> - <string>SL</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> - <property name="spacing"> - <number>3</number> - </property> - <property name="leftMargin"> - <number>3</number> - </property> - <property name="topMargin"> - <number>3</number> - </property> - <property name="rightMargin"> - <number>3</number> - </property> - <property name="bottomMargin"> - <number>3</number> - </property> - <item> - <widget class="QPushButton" name="buttonSL"> - <property name="minimumSize"> - <size> - <width>68</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>68</width> - <height>16777215</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true">min-width: 68px;</string> - </property> - <property name="text"> - <string>SL</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item alignment="Qt::AlignHCenter"> - <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup"> - <property name="title"> - <string>SR</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> - <property name="spacing"> - <number>3</number> - </property> - <property name="leftMargin"> - <number>3</number> - </property> - <property name="topMargin"> - <number>3</number> - </property> - <property name="rightMargin"> - <number>3</number> - </property> - <property name="bottomMargin"> - <number>3</number> - </property> - <item> - <widget class="QPushButton" name="buttonSR"> - <property name="minimumSize"> - <size> - <width>68</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>68</width> - <height>16777215</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true">min-width: 68px;</string> - </property> - <property name="text"> - <string>SR</string> - </property> - </widget> - </item> + <item> + <widget class="QWidget" name="buttonShoulderButtonsSLSRRight" native="true"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRRightVerticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonShoulderButtonsSLRightGroup"> + <property name="title"> + <string>SL</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLRightVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonSLRight"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>SL</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonShoulderButtonsSRRightGroup"> + <property name="title"> + <string>SR</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSRRightVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonSRRight"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>SR</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> </layout> - </widget> - </item> - </layout> - </widget> - </item> + </widget> + </item> </layout> </item> <item> diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index a188eef92..550cff9a0 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -297,8 +297,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) // Sideview SL and SR buttons button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left); - DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left); + DrawRoundButton(p, center + QPoint(59, 52), button_values[SRLeft], 5, 12, Direction::Left); + DrawRoundButton(p, center + QPoint(59, -69), button_values[SLLeft], 5, 12, Direction::Left); DrawLeftBody(p, center); @@ -353,8 +353,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) // SR and SL buttons p.setPen(colors.outline); button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4); - DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4); + DrawRoundButton(p, center + QPoint(155, 52), button_values[SRLeft], 5.2f, 12, Direction::None, + 4); + DrawRoundButton(p, center + QPoint(155, -69), button_values[SLLeft], 5.2f, 12, Direction::None, + 4); // SR and SL text p.setPen(colors.transparent); @@ -428,8 +430,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center // Sideview SL and SR buttons button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right); - DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right); + DrawRoundButton(p, center + QPoint(-59, 52), button_values[SLRight], 5, 11, + Direction::Right); + DrawRoundButton(p, center + QPoint(-59, -69), button_values[SRRight], 5, 11, + Direction::Right); DrawRightBody(p, center); @@ -484,8 +488,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center // SR and SL buttons p.setPen(colors.outline); button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f); - DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f); + DrawRoundButton(p, center + QPoint(-155, 52), button_values[SLRight], 5, 12, Direction::None, + 4.0f); + DrawRoundButton(p, center + QPoint(-155, -69), button_values[SRRight], 5, 12, Direction::None, + 4.0f); // SR and SL text p.setPen(colors.transparent); @@ -557,6 +563,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, 1); + // Left SR and SL sideview buttons + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(-20, -62), button_values[SLLeft], 4, 11, + Direction::Left); + DrawRoundButton(p, center + QPoint(-20, 47), button_values[SRLeft], 4, 11, Direction::Left); + + // Right SR and SL sideview buttons + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(20, 47), button_values[SLRight], 4, 11, + Direction::Right); + DrawRoundButton(p, center + QPoint(20, -62), button_values[SRRight], 4, 11, + Direction::Right); + DrawDualBody(p, center); // Right trigger top view @@ -1792,16 +1811,6 @@ void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) { p.setBrush(colors.right); DrawPolygon(p, qright_joycon_topview); - // Right SR and SL sideview buttons - p.setPen(colors.outline); - p.setBrush(colors.slider_button); - DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1); - DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1); - - // Left SR and SL sideview buttons - DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1); - DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1); - // Right Sideview body p.setBrush(colors.slider); DrawPolygon(p, qright_joycon_slider); diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index b91d6ad4a..b274a3321 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -25,8 +25,8 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" +#include "frontend_common/config.h" #include "ui_configure_per_game.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" @@ -50,8 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); - game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); - + game_config = std::make_unique<QtConfig>(config_file_name, Config::ConfigType::PerGameConfig); addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this); cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this); @@ -108,7 +107,7 @@ void ConfigurePerGame::ApplyConfiguration() { system.ApplySettings(); Settings::LogSettings(); - game_config->Save(); + game_config->SaveAllValues(); } void ConfigurePerGame::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index cc2513001..c8ee46c04 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -12,9 +12,10 @@ #include "configuration/shared_widget.h" #include "core/file_sys/vfs_types.h" +#include "frontend_common/config.h" #include "vk_device_info.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/qt_config.h" #include "yuzu/configuration/shared_translation.h" namespace Core { @@ -72,7 +73,7 @@ private: QGraphicsScene* scene; - std::unique_ptr<Config> game_config; + std::unique_ptr<QtConfig> game_config; Core::System& system; std::unique_ptr<ConfigurationShared::Builder> builder; diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 674a75a62..140a7fe5d 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -19,7 +19,6 @@ #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" #include "ui_configure_per_game_addons.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_per_game_addons.h" #include "yuzu/uisettings.h" diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index f83705544..9572ff43c 100644 --- a/src/yuzu/configuration/configure_ringcon.cpp +++ b/src/yuzu/configuration/configure_ringcon.cpp @@ -8,6 +8,7 @@ #include <QTimer> #include <fmt/format.h> +#include "configuration/qt_config.h" #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "input_common/drivers/keyboard.h" @@ -15,7 +16,6 @@ #include "input_common/main.h" #include "ui_configure_ringcon.h" #include "yuzu/bootmanager.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_ringcon.h" const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM> @@ -270,7 +270,7 @@ void ConfigureRingController::LoadConfiguration() { void ConfigureRingController::RestoreDefaults() { const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( - 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); + 0, 0, QtConfig::default_ringcon_analogs[0], QtConfig::default_ringcon_analogs[1], 0, 0.05f); emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); UpdateUI(); } diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 0c8e5c8b4..7cbf43775 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -16,7 +16,6 @@ #include "core/core.h" #include "core/hle/service/time/time_manager.h" #include "ui_configure_system.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_system.h" #include "yuzu/configuration/shared_widget.h" diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp index 5a03e48df..94df6d9d3 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <memory> +#include "common/settings.h" #include "ui_configure_touchscreen_advanced.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_touchscreen_advanced.h" ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 82f3b6e78..dd43f0a0e 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -164,7 +164,7 @@ ConfigureUi::~ConfigureUi() = default; void ConfigureUi::ApplyConfiguration() { UISettings::values.theme = - ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); + ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); UISettings::values.show_compat = ui->show_compat->isChecked(); UISettings::values.show_size = ui->show_size->isChecked(); @@ -191,9 +191,10 @@ void ConfigureUi::RequestGameListUpdate() { } void ConfigureUi::SetConfiguration() { - ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); + ui->theme_combobox->setCurrentIndex( + ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme))); ui->language_combobox->setCurrentIndex( - ui->language_combobox->findData(UISettings::values.language)); + ui->language_combobox->findData(QString::fromStdString(UISettings::values.language))); ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); ui->show_size->setChecked(UISettings::values.show_size.GetValue()); diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index 41ef4250a..716efbccd 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp @@ -5,7 +5,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" -#include "yuzu/configuration/config.h" +#include "frontend_common/config.h" #include "yuzu/configuration/input_profiles.h" namespace FS = Common::FS; @@ -44,7 +44,7 @@ InputProfiles::InputProfiles() { if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { map_profiles.insert_or_assign( name_without_ext, - std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); + std::make_unique<QtConfig>(name_without_ext, Config::ConfigType::InputProfile)); } return true; @@ -85,7 +85,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p } map_profiles.insert_or_assign( - profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); + profile_name, std::make_unique<QtConfig>(profile_name, Config::ConfigType::InputProfile)); return SaveProfile(profile_name, player_index); } @@ -113,7 +113,7 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla return false; } - map_profiles[profile_name]->ReadControlPlayerValue(player_index); + map_profiles[profile_name]->ReadQtControlPlayerValues(player_index); return true; } @@ -122,7 +122,7 @@ bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t pla return false; } - map_profiles[profile_name]->SaveControlPlayerValue(player_index); + map_profiles[profile_name]->SaveQtControlPlayerValues(player_index); return true; } diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h index 2bf3e4250..023ec74a6 100644 --- a/src/yuzu/configuration/input_profiles.h +++ b/src/yuzu/configuration/input_profiles.h @@ -6,6 +6,8 @@ #include <string> #include <unordered_map> +#include "configuration/qt_config.h" + namespace Core { class System; } @@ -30,5 +32,5 @@ public: private: bool ProfileExistsInMap(const std::string& profile_name) const; - std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; + std::unordered_map<std::string, std::unique_ptr<QtConfig>> map_profiles; }; diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp new file mode 100644 index 000000000..5a8e69aa9 --- /dev/null +++ b/src/yuzu/configuration/qt_config.cpp @@ -0,0 +1,549 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "input_common/main.h" +#include "qt_config.h" +#include "uisettings.h" + +const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = { + Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, + Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, + Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, + Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, + Qt::Key_Q, Qt::Key_E, +}; + +const std::array<int, Settings::NativeMotion::NumMotions> QtConfig::default_motions = { + Qt::Key_7, + Qt::Key_8, +}; + +const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ + { + Qt::Key_W, + Qt::Key_S, + Qt::Key_A, + Qt::Key_D, + }, + { + Qt::Key_I, + Qt::Key_K, + Qt::Key_J, + Qt::Key_L, + }, +}}; + +const std::array<int, 2> QtConfig::default_stick_mod = { + Qt::Key_Shift, + 0, +}; + +const std::array<int, 2> QtConfig::default_ringcon_analogs{{ + Qt::Key_A, + Qt::Key_D, +}}; + +QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type) + : Config(config_type) { + Initialize(config_name); + if (config_type != ConfigType::InputProfile) { + ReadQtValues(); + SaveQtValues(); + } +} + +QtConfig::~QtConfig() { + if (global) { + QtConfig::SaveAllValues(); + } +} + +void QtConfig::ReloadAllValues() { + Reload(); + ReadQtValues(); + SaveQtValues(); +} + +void QtConfig::SaveAllValues() { + Save(); + SaveQtValues(); +} + +void QtConfig::ReadQtValues() { + if (global) { + ReadUIValues(); + } + ReadQtControlValues(); +} + +void QtConfig::ReadQtPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); + if (player_buttons.empty()) { + player_buttons = default_param; + } + } + + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; + + player_analogs = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); + if (player_analogs.empty()) { + player_analogs = default_param; + } + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); + if (player_motions.empty()) { + player_motions = default_param; + } + } +} + +void QtConfig::ReadHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + auto& ringcon_analogs = Settings::values.ringcon_analogs; + + ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); + if (ringcon_analogs.empty()) { + ringcon_analogs = default_param; + } +} + +void QtConfig::ReadDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; + + debug_pad_buttons = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); + if (debug_pad_buttons.empty()) { + debug_pad_buttons = default_param; + } + } + + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; + + debug_pad_analogs = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); + if (debug_pad_analogs.empty()) { + debug_pad_analogs = default_param; + } + } +} + +void QtConfig::ReadQtControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadQtPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + ReadDebugControlValues(); + ReadHidbusValues(); + + EndGroup(); +} + +void QtConfig::ReadPathValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); + + UISettings::values.roms_path = ReadStringSetting(std::string("romsPath")); + UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath")); + UISettings::values.game_dir_deprecated = + ReadStringSetting(std::string("gameListRootDir"), std::string(".")); + UISettings::values.game_dir_deprecated_deepscan = + ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false)); + + const int gamedirs_size = BeginArray(std::string("gamedirs")); + for (int i = 0; i < gamedirs_size; ++i) { + SetArrayIndex(i); + UISettings::GameDir game_dir; + game_dir.path = ReadStringSetting(std::string("path")); + game_dir.deep_scan = + ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false)); + game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true)); + UISettings::values.game_dirs.append(game_dir); + } + EndArray(); + + // Create NAND and SD card directories if empty, these are not removable through the UI, + // also carries over old game list settings if present + if (UISettings::values.game_dirs.empty()) { + UISettings::GameDir game_dir; + game_dir.path = std::string("SDMC"); + game_dir.expanded = true; + UISettings::values.game_dirs.append(game_dir); + game_dir.path = std::string("UserNAND"); + UISettings::values.game_dirs.append(game_dir); + game_dir.path = std::string("SysNAND"); + UISettings::values.game_dirs.append(game_dir); + if (UISettings::values.game_dir_deprecated != std::string(".")) { + game_dir.path = UISettings::values.game_dir_deprecated; + game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; + UISettings::values.game_dirs.append(game_dir); + } + } + UISettings::values.recent_files = + QString::fromStdString(ReadStringSetting(std::string("recentFiles"))) + .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive); + UISettings::values.language = ReadStringSetting(std::string("language"), std::string("")); + + EndGroup(); +} + +void QtConfig::ReadShortcutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); + + for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) { + BeginGroup(group); + BeginGroup(name); + + // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 + // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open + // a file dialog in windowed mode + UISettings::values.shortcuts.push_back( + {name, + group, + {ReadStringSetting(std::string("KeySeq"), shortcut.keyseq), + ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq), + shortcut.context, + ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}}); + + EndGroup(); // name + EndGroup(); // group + } + + EndGroup(); +} + +void QtConfig::ReadUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); + + UISettings::values.theme = ReadStringSetting( + std::string("theme"), + std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)); + + ReadUIGamelistValues(); + ReadUILayoutValues(); + ReadPathValues(); + ReadScreenshotValues(); + ReadShortcutValues(); + ReadMultiplayerValues(); + + ReadCategory(Settings::Category::Ui); + ReadCategory(Settings::Category::UiGeneral); + + EndGroup(); +} + +void QtConfig::ReadUIGamelistValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); + + ReadCategory(Settings::Category::UiGameList); + + const int favorites_size = BeginArray("favorites"); + for (int i = 0; i < favorites_size; i++) { + SetArrayIndex(i); + UISettings::values.favorited_ids.append( + ReadUnsignedIntegerSetting(std::string("program_id"))); + } + EndArray(); + + EndGroup(); +} + +void QtConfig::ReadUILayoutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); + + ReadCategory(Settings::Category::UiLayout); + + EndGroup(); +} + +void QtConfig::ReadMultiplayerValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer)); + + ReadCategory(Settings::Category::Multiplayer); + + // Read ban list back + int size = BeginArray(std::string("username_ban_list")); + UISettings::values.multiplayer_ban_list.first.resize(size); + for (int i = 0; i < size; ++i) { + SetArrayIndex(i); + UISettings::values.multiplayer_ban_list.first[i] = + ReadStringSetting(std::string("username"), std::string("")); + } + EndArray(); + + size = BeginArray(std::string("ip_ban_list")); + UISettings::values.multiplayer_ban_list.second.resize(size); + for (int i = 0; i < size; ++i) { + UISettings::values.multiplayer_ban_list.second[i] = + ReadStringSetting("username", std::string("")); + } + EndArray(); + + EndGroup(); +} + +void QtConfig::SaveQtValues() { + if (global) { + SaveUIValues(); + } + SaveQtControlValues(); + + WriteToIni(); +} + +void QtConfig::SaveQtPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig() && player.profile_name.empty()) { + // No custom profile selected + return; + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), + player.buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), + player.analogs[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), + player.motions[i], std::make_optional(default_param)); + } +} + +void QtConfig::SaveDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), + Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), + Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); + } +} + +void QtConfig::SaveHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, + std::make_optional(default_param)); +} + +void QtConfig::SaveQtControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SaveQtPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + SaveDebugControlValues(); + SaveHidbusValues(); + + EndGroup(); +} + +void QtConfig::SavePathValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); + + WriteSetting(std::string("romsPath"), UISettings::values.roms_path); + WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path); + BeginArray(std::string("gamedirs")); + for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { + SetArrayIndex(i); + const auto& game_dir = UISettings::values.game_dirs[i]; + WriteSetting(std::string("path"), game_dir.path); + WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); + WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true)); + } + EndArray(); + + WriteSetting(std::string("recentFiles"), + UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); + WriteSetting(std::string("language"), UISettings::values.language); + + EndGroup(); +} + +void QtConfig::SaveShortcutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); + + // Lengths of UISettings::values.shortcuts & default_hotkeys are same. + // However, their ordering must also be the same. + for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) { + const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; + const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut; + + BeginGroup(group); + BeginGroup(name); + + WriteSetting(std::string("KeySeq"), shortcut.keyseq, + std::make_optional(default_hotkey.keyseq)); + WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq, + std::make_optional(default_hotkey.controller_keyseq)); + WriteSetting(std::string("Context"), shortcut.context, + std::make_optional(default_hotkey.context)); + WriteSetting(std::string("Repeat"), shortcut.repeat, + std::make_optional(default_hotkey.repeat)); + + EndGroup(); // name + EndGroup(); // group + } + + EndGroup(); +} + +void QtConfig::SaveUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); + + WriteCategory(Settings::Category::Ui); + WriteCategory(Settings::Category::UiGeneral); + + WriteSetting(std::string("theme"), UISettings::values.theme, + std::make_optional(std::string( + UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second))); + + SaveUIGamelistValues(); + SaveUILayoutValues(); + SavePathValues(); + SaveScreenshotValues(); + SaveShortcutValues(); + SaveMultiplayerValues(); + + EndGroup(); +} + +void QtConfig::SaveUIGamelistValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); + + WriteCategory(Settings::Category::UiGameList); + + BeginArray(std::string("favorites")); + for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { + SetArrayIndex(i); + WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]); + } + EndArray(); // favorites + + EndGroup(); +} + +void QtConfig::SaveUILayoutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout)); + + WriteCategory(Settings::Category::UiLayout); + + EndGroup(); +} + +void QtConfig::SaveMultiplayerValues() { + BeginGroup(std::string("Multiplayer")); + + WriteCategory(Settings::Category::Multiplayer); + + // Write ban list + BeginArray(std::string("username_ban_list")); + for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { + SetArrayIndex(static_cast<int>(i)); + WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]); + } + EndArray(); // username_ban_list + + BeginArray(std::string("ip_ban_list")); + for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { + SetArrayIndex(static_cast<int>(i)); + WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]); + } + EndArray(); // ip_ban_list + + EndGroup(); +} + +std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) { + auto& map = Settings::values.linkage.by_category; + if (map.contains(category)) { + return Settings::values.linkage.by_category[category]; + } + return UISettings::values.linkage.by_category[category]; +} + +void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + ReadPlayerValues(player_index); + ReadQtPlayerValues(player_index); + + EndGroup(); +} + +void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + SavePlayerValues(player_index); + SaveQtPlayerValues(player_index); + + EndGroup(); + + WriteToIni(); +} diff --git a/src/yuzu/configuration/qt_config.h b/src/yuzu/configuration/qt_config.h new file mode 100644 index 000000000..dc2dceb4d --- /dev/null +++ b/src/yuzu/configuration/qt_config.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <QMetaType> + +#include "frontend_common/config.h" + +class QtConfig final : public Config { +public: + explicit QtConfig(const std::string& config_name = "qt-config", + ConfigType config_type = ConfigType::GlobalConfig); + ~QtConfig() override; + + void ReloadAllValues() override; + void SaveAllValues() override; + + void ReadQtControlPlayerValues(std::size_t player_index); + void SaveQtControlPlayerValues(std::size_t player_index); + +protected: + void ReadQtValues(); + void ReadQtPlayerValues(std::size_t player_index); + void ReadQtControlValues(); + void ReadHidbusValues() override; + void ReadDebugControlValues() override; + void ReadPathValues() override; + void ReadShortcutValues() override; + void ReadUIValues() override; + void ReadUIGamelistValues() override; + void ReadUILayoutValues() override; + void ReadMultiplayerValues() override; + + void SaveQtValues(); + void SaveQtPlayerValues(std::size_t player_index); + void SaveQtControlValues(); + void SaveHidbusValues() override; + void SaveDebugControlValues() override; + void SavePathValues() override; + void SaveShortcutValues() override; + void SaveUIValues() override; + void SaveUIGamelistValues() override; + void SaveUILayoutValues() override; + void SaveMultiplayerValues() override; + + std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override; + +public: + static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; + static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; + static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const std::array<int, 2> default_stick_mod; + static const std::array<int, 2> default_ringcon_analogs; +}; diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 1434b1a56..a7b5def32 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -1,17 +1,18 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/time_zone.h" #include "yuzu/configuration/shared_translation.h" #include <map> #include <memory> #include <tuple> #include <utility> +#include <QCoreApplication> #include <QWidget> #include "common/settings.h" #include "common/settings_enums.h" #include "common/settings_setting.h" +#include "common/time_zone.h" #include "yuzu/uisettings.h" namespace ConfigurationShared { @@ -21,123 +22,135 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; #define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ - translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}}) + translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}}) // A setting can be ignored by giving it a blank name // Audio - INSERT(Settings, sink_id, "Output Engine:", ""); - INSERT(Settings, audio_output_device_id, "Output Device:", ""); - INSERT(Settings, audio_input_device_id, "Input Device:", ""); - INSERT(Settings, audio_muted, "Mute audio", ""); - INSERT(Settings, volume, "Volume:", ""); - INSERT(Settings, dump_audio_commands, "", ""); - INSERT(UISettings, mute_when_in_background, "Mute audio when in background", ""); + INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); + INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); + INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral()); + INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral()); + INSERT(Settings, volume, tr("Volume:"), QStringLiteral()); + INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral()); + INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"), + QStringLiteral()); // Core - INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); - INSERT(Settings, memory_layout_mode, "Memory Layout", ""); - INSERT(Settings, use_speed_limit, "", ""); - INSERT(Settings, speed_limit, "Limit Speed Percent", ""); + INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral()); + INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral()); + INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral()); + INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral()); // Cpu - INSERT(Settings, cpu_accuracy, "Accuracy:", ""); + INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral()); // Cpu Debug // Cpu Unsafe - INSERT(Settings, cpuopt_unsafe_unfuse_fma, - "Unfuse FMA (improve performance on CPUs without FMA)", - "This option improves speed by reducing accuracy of fused-multiply-add instructions on " - "CPUs without native FMA support."); - INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE", - "This option improves the speed of some approximate floating-point functions by using " - "less accurate native approximations."); - INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)", - "This option improves the speed of 32 bits ASIMD floating-point functions by running " - "with incorrect rounding modes."); - INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling", - "This option improves speed by removing NaN checking. Please note this also reduces " - "accuracy of certain floating-point instructions."); INSERT( - Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", - "This option improves speed by eliminating a safety check before every memory read/write " - "in guest. Disabling it may allow a game to read/write the emulator's memory."); - INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor", - "This option improves speed by relying only on the semantics of cmpxchg to ensure " + Settings, cpuopt_unsafe_unfuse_fma, + tr("Unfuse FMA (improve performance on CPUs without FMA)"), + tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on " + "CPUs without native FMA support.")); + INSERT( + Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"), + tr("This option improves the speed of some approximate floating-point functions by using " + "less accurate native approximations.")); + INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, + tr("Faster ASIMD instructions (32 bits only)"), + tr("This option improves the speed of 32 bits ASIMD floating-point functions by running " + "with incorrect rounding modes.")); + INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), + tr("This option improves speed by removing NaN checking. Please note this also reduces " + "accuracy of certain floating-point instructions.")); + INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), + tr("This option improves speed by eliminating a safety check before every memory " + "read/write " + "in guest. Disabling it may allow a game to read/write the emulator's memory.")); + INSERT( + Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"), + tr("This option improves speed by relying only on the semantics of cmpxchg to ensure " "safety of exclusive access instructions. Please note this may result in deadlocks and " - "other race conditions."); + "other race conditions.")); // Renderer - INSERT(Settings, renderer_backend, "API:", ""); - INSERT(Settings, vulkan_device, "Device:", ""); - INSERT(Settings, shader_backend, "Shader Backend:", ""); - INSERT(Settings, resolution_setup, "Resolution:", ""); - INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); - INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); - INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); - INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); - INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); - INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); - INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); - INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); - INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); - INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); - INSERT(Settings, vsync_mode, "VSync Mode:", - "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " + INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral()); + INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral()); + INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral()); + INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral()); + INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral()); + INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral()); + INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral()); + INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral()); + INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral()); + INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral()); + INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), + QStringLiteral()); + INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral()); + INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral()); + INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral()); + INSERT( + Settings, vsync_mode, tr("VSync Mode:"), + tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " "frames.\nImmediate (no synchronization) just presents whatever is available and can " - "exhibit tearing."); - INSERT(Settings, bg_red, "", ""); - INSERT(Settings, bg_green, "", ""); - INSERT(Settings, bg_blue, "", ""); + "exhibit tearing.")); + INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral()); + INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral()); + INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral()); // Renderer (Advanced Graphics) - INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); - INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", - "Runs work in the background while waiting for graphics commands to keep the GPU from " - "lowering its clock speed."); - INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); - INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); - INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", - "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " - "is experimental."); - INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", - "Enables Fast GPU Time. This option will force most games to run at their highest " - "native resolution."); - INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", - "Enables GPU vendor-specific pipeline cache. This option can improve shader loading " - "time significantly in cases where the Vulkan driver does not store pipeline cache " - "files internally."); - INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", - "Enable compute pipelines, required by some games.\nThis setting only exists for Intel " + INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), + QStringLiteral()); + INSERT( + Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"), + tr("Runs work in the background while waiting for graphics commands to keep the GPU from " + "lowering its clock speed.")); + INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); + INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); + INSERT( + Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), + tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " + "is experimental.")); + INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"), + tr("Enables Fast GPU Time. This option will force most games to run at their highest " + "native resolution.")); + INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), + tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading " + "time significantly in cases where the Vulkan driver does not store pipeline cache " + "files internally.")); + INSERT( + Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"), + tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel " "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " - "on all other drivers."); - INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", - "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " - "syncing."); - INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", - "Run the game at normal speed during video playback, even when the framerate is " - "unlocked."); - INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", - "Improves rendering of transparency effects in specific games."); + "on all other drivers.")); + INSERT( + Settings, use_reactive_flushing, tr("Enable Reactive Flushing"), + tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory " + "syncing.")); + INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"), + tr("Run the game at normal speed during video playback, even when the framerate is " + "unlocked.")); + INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"), + tr("Improves rendering of transparency effects in specific games.")); // Renderer (Debug) // System - INSERT(Settings, rng_seed, "RNG Seed", ""); - INSERT(Settings, rng_seed_enabled, "", ""); - INSERT(Settings, device_name, "Device Name", ""); - INSERT(Settings, custom_rtc, "Custom RTC", ""); - INSERT(Settings, custom_rtc_enabled, "", ""); - INSERT(Settings, language_index, - "Language:", "Note: this can be overridden when region setting is auto-select"); - INSERT(Settings, region_index, "Region:", ""); - INSERT(Settings, time_zone_index, "Time Zone:", ""); - INSERT(Settings, sound_index, "Sound Output Mode:", ""); - INSERT(Settings, use_docked_mode, "Console Mode:", ""); - INSERT(Settings, current_user, "", ""); + INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); + INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); + INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); + INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral()); + INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); + INSERT(Settings, language_index, tr("Language:"), + tr("Note: this can be overridden when region setting is auto-select")); + INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); + INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); + INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); + INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); + INSERT(Settings, current_user, QStringLiteral(), QStringLiteral()); // Controls @@ -154,11 +167,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { // Ui // Ui General - INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); - INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); - INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); - INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); - INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); + INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral()); + INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), + QStringLiteral()); + INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), + QStringLiteral()); + INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral()); + INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), + QStringLiteral()); // Ui Debugging @@ -178,140 +194,141 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { return parent->tr(text, context); }; -#define PAIR(ENUM, VALUE, TRANSLATION) \ - { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) } -#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \ - { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) } +#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} // Intentionally skipping VSyncMode to let the UI fill that one out translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), { - PAIR(AstcDecodeMode, Cpu, "CPU"), - PAIR(AstcDecodeMode, Gpu, "GPU"), - PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), - }}); - translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(), - { - PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"), - PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"), - PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"), + PAIR(AstcDecodeMode, Cpu, tr("CPU")), + PAIR(AstcDecodeMode, Gpu, tr("GPU")), + PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")), }}); + translations->insert( + {Settings::EnumMetadata<Settings::AstcRecompression>::Index(), + { + PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")), + PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), + PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), + }}); translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), { #ifdef HAS_OPENGL - PAIR(RendererBackend, OpenGL, "OpenGL"), + PAIR(RendererBackend, OpenGL, tr("OpenGL")), #endif - PAIR(RendererBackend, Vulkan, "Vulkan"), - PAIR(RendererBackend, Null, "Null"), - }}); - translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(), - { - PAIR(ShaderBackend, Glsl, "GLSL"), - PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"), - PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"), + PAIR(RendererBackend, Vulkan, tr("Vulkan")), + PAIR(RendererBackend, Null, tr("Null")), }}); + translations->insert( + {Settings::EnumMetadata<Settings::ShaderBackend>::Index(), + { + PAIR(ShaderBackend, Glsl, tr("GLSL")), + PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")), + PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")), + }}); translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), { - PAIR(GpuAccuracy, Normal, "Normal"), - PAIR(GpuAccuracy, High, "High"), - PAIR(GpuAccuracy, Extreme, "Extreme"), - }}); - translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), - { - PAIR(CpuAccuracy, Auto, "Auto"), - PAIR(CpuAccuracy, Accurate, "Accurate"), - PAIR(CpuAccuracy, Unsafe, "Unsafe"), - PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"), + PAIR(GpuAccuracy, Normal, tr("Normal")), + PAIR(GpuAccuracy, High, tr("High")), + PAIR(GpuAccuracy, Extreme, tr("Extreme")), }}); + translations->insert( + {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), + { + PAIR(CpuAccuracy, Auto, tr("Auto")), + PAIR(CpuAccuracy, Accurate, tr("Accurate")), + PAIR(CpuAccuracy, Unsafe, tr("Unsafe")), + PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")), + }}); translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), { - PAIR(FullscreenMode, Borderless, "Borderless Windowed"), - PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), + PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")), + PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")), }}); translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), { - PAIR(NvdecEmulation, Off, "No Video Output"), - PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), - PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), - }}); - translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), - { - PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"), - PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"), - PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"), - PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"), - PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"), - PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"), - PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"), - PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"), - PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"), - PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"), - PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"), + PAIR(NvdecEmulation, Off, tr("No Video Output")), + PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")), + PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")), }}); + translations->insert( + {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), + { + PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")), + PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")), + PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")), + PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")), + PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")), + PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")), + PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")), + PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")), + PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")), + PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")), + PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")), + }}); translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), { - PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), - PAIR(ScalingFilter, Bilinear, "Bilinear"), - PAIR(ScalingFilter, Bicubic, "Bicubic"), - PAIR(ScalingFilter, Gaussian, "Gaussian"), - PAIR(ScalingFilter, ScaleForce, "ScaleForce"), - PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"), + PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), + PAIR(ScalingFilter, Bilinear, tr("Bilinear")), + PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, Gaussian, tr("Gaussian")), + PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), + PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), }}); translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), { - PAIR(AntiAliasing, None, "None"), - PAIR(AntiAliasing, Fxaa, "FXAA"), - PAIR(AntiAliasing, Smaa, "SMAA"), + PAIR(AntiAliasing, None, tr("None")), + PAIR(AntiAliasing, Fxaa, tr("FXAA")), + PAIR(AntiAliasing, Smaa, tr("SMAA")), }}); translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), { - PAIR(AspectRatio, R16_9, "Default (16:9)"), - PAIR(AspectRatio, R4_3, "Force 4:3"), - PAIR(AspectRatio, R21_9, "Force 21:9"), - PAIR(AspectRatio, R16_10, "Force 16:10"), - PAIR(AspectRatio, Stretch, "Stretch to Window"), + PAIR(AspectRatio, R16_9, tr("Default (16:9)")), + PAIR(AspectRatio, R4_3, tr("Force 4:3")), + PAIR(AspectRatio, R21_9, tr("Force 21:9")), + PAIR(AspectRatio, R16_10, tr("Force 16:10")), + PAIR(AspectRatio, Stretch, tr("Stretch to Window")), }}); translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), { - PAIR(AnisotropyMode, Automatic, "Automatic"), - PAIR(AnisotropyMode, Default, "Default"), - PAIR(AnisotropyMode, X2, "2x"), - PAIR(AnisotropyMode, X4, "4x"), - PAIR(AnisotropyMode, X8, "8x"), - PAIR(AnisotropyMode, X16, "16x"), + PAIR(AnisotropyMode, Automatic, tr("Automatic")), + PAIR(AnisotropyMode, Default, tr("Default")), + PAIR(AnisotropyMode, X2, tr("2x")), + PAIR(AnisotropyMode, X4, tr("4x")), + PAIR(AnisotropyMode, X8, tr("8x")), + PAIR(AnisotropyMode, X16, tr("16x")), }}); translations->insert( {Settings::EnumMetadata<Settings::Language>::Index(), { - PAIR(Language, Japanese, "Japanese (日本語)"), - PAIR(Language, EnglishAmerican, "American English"), - PAIR(Language, French, "French (français)"), - PAIR(Language, German, "German (Deutsch)"), - PAIR(Language, Italian, "Italian (italiano)"), - PAIR(Language, Spanish, "Spanish (español)"), - PAIR(Language, Chinese, "Chinese"), - PAIR(Language, Korean, "Korean (한국어)"), - PAIR(Language, Dutch, "Dutch (Nederlands)"), - PAIR(Language, Portuguese, "Portuguese (português)"), - PAIR(Language, Russian, "Russian (Русский)"), - PAIR(Language, Taiwanese, "Taiwanese"), - PAIR(Language, EnglishBritish, "British English"), - PAIR(Language, FrenchCanadian, "Canadian French"), - PAIR(Language, SpanishLatin, "Latin American Spanish"), - PAIR(Language, ChineseSimplified, "Simplified Chinese"), - PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), - PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), + PAIR(Language, Japanese, tr("Japanese (日本語)")), + PAIR(Language, EnglishAmerican, tr("American English")), + PAIR(Language, French, tr("French (français)")), + PAIR(Language, German, tr("German (Deutsch)")), + PAIR(Language, Italian, tr("Italian (italiano)")), + PAIR(Language, Spanish, tr("Spanish (español)")), + PAIR(Language, Chinese, tr("Chinese")), + PAIR(Language, Korean, tr("Korean (한국어)")), + PAIR(Language, Dutch, tr("Dutch (Nederlands)")), + PAIR(Language, Portuguese, tr("Portuguese (português)")), + PAIR(Language, Russian, tr("Russian (Русский)")), + PAIR(Language, Taiwanese, tr("Taiwanese")), + PAIR(Language, EnglishBritish, tr("British English")), + PAIR(Language, FrenchCanadian, tr("Canadian French")), + PAIR(Language, SpanishLatin, tr("Latin American Spanish")), + PAIR(Language, ChineseSimplified, tr("Simplified Chinese")), + PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")), + PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")), }}); translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), { - PAIR(Region, Japan, "Japan"), - PAIR(Region, Usa, "USA"), - PAIR(Region, Europe, "Europe"), - PAIR(Region, Australia, "Australia"), - PAIR(Region, China, "China"), - PAIR(Region, Korea, "Korea"), - PAIR(Region, Taiwan, "Taiwan"), + PAIR(Region, Japan, tr("Japan")), + PAIR(Region, Usa, tr("USA")), + PAIR(Region, Europe, tr("Europe")), + PAIR(Region, Australia, tr("Australia")), + PAIR(Region, China, tr("China")), + PAIR(Region, Korea, tr("Korea")), + PAIR(Region, Taiwan, tr("Taiwan")), }}); translations->insert( {Settings::EnumMetadata<Settings::TimeZone>::Index(), @@ -323,72 +340,74 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { {static_cast<u32>(Settings::TimeZone::Default), tr("Default (%1)", "Default time zone") .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, - PAIR(TimeZone, Cet, "CET"), - PAIR(TimeZone, Cst6Cdt, "CST6CDT"), - PAIR(TimeZone, Cuba, "Cuba"), - PAIR(TimeZone, Eet, "EET"), - PAIR(TimeZone, Egypt, "Egypt"), - PAIR(TimeZone, Eire, "Eire"), - PAIR(TimeZone, Est, "EST"), - PAIR(TimeZone, Est5Edt, "EST5EDT"), - PAIR(TimeZone, Gb, "GB"), - PAIR(TimeZone, GbEire, "GB-Eire"), - PAIR(TimeZone, Gmt, "GMT"), - PAIR(TimeZone, GmtPlusZero, "GMT+0"), - PAIR(TimeZone, GmtMinusZero, "GMT-0"), - PAIR(TimeZone, GmtZero, "GMT0"), - PAIR(TimeZone, Greenwich, "Greenwich"), - PAIR(TimeZone, Hongkong, "Hongkong"), - PAIR(TimeZone, Hst, "HST"), - PAIR(TimeZone, Iceland, "Iceland"), - PAIR(TimeZone, Iran, "Iran"), - PAIR(TimeZone, Israel, "Israel"), - PAIR(TimeZone, Jamaica, "Jamaica"), - PAIR(TimeZone, Japan, "Japan"), - PAIR(TimeZone, Kwajalein, "Kwajalein"), - PAIR(TimeZone, Libya, "Libya"), - PAIR(TimeZone, Met, "MET"), - PAIR(TimeZone, Mst, "MST"), - PAIR(TimeZone, Mst7Mdt, "MST7MDT"), - PAIR(TimeZone, Navajo, "Navajo"), - PAIR(TimeZone, Nz, "NZ"), - PAIR(TimeZone, NzChat, "NZ-CHAT"), - PAIR(TimeZone, Poland, "Poland"), - PAIR(TimeZone, Portugal, "Portugal"), - PAIR(TimeZone, Prc, "PRC"), - PAIR(TimeZone, Pst8Pdt, "PST8PDT"), - PAIR(TimeZone, Roc, "ROC"), - PAIR(TimeZone, Rok, "ROK"), - PAIR(TimeZone, Singapore, "Singapore"), - PAIR(TimeZone, Turkey, "Turkey"), - PAIR(TimeZone, Uct, "UCT"), - PAIR(TimeZone, Universal, "Universal"), - PAIR(TimeZone, Utc, "UTC"), - PAIR(TimeZone, WSu, "W-SU"), - PAIR(TimeZone, Wet, "WET"), - PAIR(TimeZone, Zulu, "Zulu"), + PAIR(TimeZone, Cet, tr("CET")), + PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), + PAIR(TimeZone, Cuba, tr("Cuba")), + PAIR(TimeZone, Eet, tr("EET")), + PAIR(TimeZone, Egypt, tr("Egypt")), + PAIR(TimeZone, Eire, tr("Eire")), + PAIR(TimeZone, Est, tr("EST")), + PAIR(TimeZone, Est5Edt, tr("EST5EDT")), + PAIR(TimeZone, Gb, tr("GB")), + PAIR(TimeZone, GbEire, tr("GB-Eire")), + PAIR(TimeZone, Gmt, tr("GMT")), + PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), + PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), + PAIR(TimeZone, GmtZero, tr("GMT0")), + PAIR(TimeZone, Greenwich, tr("Greenwich")), + PAIR(TimeZone, Hongkong, tr("Hongkong")), + PAIR(TimeZone, Hst, tr("HST")), + PAIR(TimeZone, Iceland, tr("Iceland")), + PAIR(TimeZone, Iran, tr("Iran")), + PAIR(TimeZone, Israel, tr("Israel")), + PAIR(TimeZone, Jamaica, tr("Jamaica")), + PAIR(TimeZone, Japan, tr("Japan")), + PAIR(TimeZone, Kwajalein, tr("Kwajalein")), + PAIR(TimeZone, Libya, tr("Libya")), + PAIR(TimeZone, Met, tr("MET")), + PAIR(TimeZone, Mst, tr("MST")), + PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), + PAIR(TimeZone, Navajo, tr("Navajo")), + PAIR(TimeZone, Nz, tr("NZ")), + PAIR(TimeZone, NzChat, tr("NZ-CHAT")), + PAIR(TimeZone, Poland, tr("Poland")), + PAIR(TimeZone, Portugal, tr("Portugal")), + PAIR(TimeZone, Prc, tr("PRC")), + PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), + PAIR(TimeZone, Roc, tr("ROC")), + PAIR(TimeZone, Rok, tr("ROK")), + PAIR(TimeZone, Singapore, tr("Singapore")), + PAIR(TimeZone, Turkey, tr("Turkey")), + PAIR(TimeZone, Uct, tr("UCT")), + PAIR(TimeZone, Universal, tr("Universal")), + PAIR(TimeZone, Utc, tr("UTC")), + PAIR(TimeZone, WSu, tr("W-SU")), + PAIR(TimeZone, Wet, tr("WET")), + PAIR(TimeZone, Zulu, tr("Zulu")), }}); translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), { - PAIR(AudioMode, Mono, "Mono"), - PAIR(AudioMode, Stereo, "Stereo"), - PAIR(AudioMode, Surround, "Surround"), + PAIR(AudioMode, Mono, tr("Mono")), + PAIR(AudioMode, Stereo, tr("Stereo")), + PAIR(AudioMode, Surround, tr("Surround")), }}); translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), { - PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), - PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), - PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), + PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")), + PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")), + PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")), + }}); + translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(), + { + PAIR(ConsoleMode, Docked, tr("Docked")), + PAIR(ConsoleMode, Handheld, tr("Handheld")), }}); - translations->insert( - {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), - {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); translations->insert( {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), { - PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), - PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), - PAIR(ConfirmStop, Ask_Never, "Never ask"), + PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")), + PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")), + PAIR(ConfirmStop, Ask_Never, tr("Never ask")), }}); #undef PAIR diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h index 99a0e808c..d5fc3b8de 100644 --- a/src/yuzu/configuration/shared_translation.h +++ b/src/yuzu/configuration/shared_translation.h @@ -10,6 +10,7 @@ #include <vector> #include <QString> #include "common/common_types.h" +#include "common/settings.h" class QWidget; @@ -22,4 +23,46 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent); std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent); +static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = { + {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, + {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, + {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, +}; + +static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = { + {Settings::ScalingFilter::NearestNeighbor, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, + {Settings::ScalingFilter::Bilinear, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, + {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, + {Settings::ScalingFilter::Gaussian, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, + {Settings::ScalingFilter::ScaleForce, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, + {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, +}; + +static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = { + {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, + {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, +}; + +static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = { + {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, + {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, + {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, +}; + +static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = { + {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, + {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, + {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, +}; + +static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = { + {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, + {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, + {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, +}; + } // namespace ConfigurationShared diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index ea8d7add4..941683a43 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer, return group; } - const auto get_selected = [=]() -> int { + const auto get_selected = [this]() -> int { for (const auto& [id, button] : radio_buttons) { if (button->isChecked()) { return id; @@ -203,7 +203,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer, return -1; }; - const auto set_index = [=](u32 value) { + const auto set_index = [this](u32 value) { for (const auto& [id, button] : radio_buttons) { button->setChecked(id == value); } diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 7049c57b6..6d227ef8d 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -36,10 +36,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{ bool IsDarkTheme() { const auto& theme = UISettings::values.theme; - return theme == QStringLiteral("qdarkstyle") || - theme == QStringLiteral("qdarkstyle_midnight_blue") || - theme == QStringLiteral("colorful_dark") || - theme == QStringLiteral("colorful_midnight_blue"); + return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") || + theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue"); } } // namespace diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 7e7d8e252..59b317135 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -278,7 +278,7 @@ void GameList::OnUpdateThemedIcons() { case GameListItemType::CustomDir: { const UISettings::GameDir& game_dir = UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; - const QString icon_name = QFileInfo::exists(game_dir.path) + const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path)) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); child->setData( @@ -567,9 +567,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); -#ifndef WIN32 QAction* create_applications_menu_shortcut = shortcut_menu->addAction(tr("Add to Applications Menu")); #endif @@ -647,10 +648,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); }); -#ifndef WIN32 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); }); @@ -725,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { }); connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { - emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); + emit OpenDirectory( + QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path)); }); } @@ -867,7 +870,7 @@ const QStringList GameList::supported_file_extensions = { QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; void GameList::RefreshGameDirectory() { - if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { + if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); PopulateAsync(UISettings::values.game_dirs); } diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 86a0c41d9..c330b574f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -286,13 +286,13 @@ public: setData(QObject::tr("System Titles"), Qt::DisplayRole); break; case GameListItemType::CustomDir: { - const QString icon_name = QFileInfo::exists(game_dir->path) - ? QStringLiteral("folder") - : QStringLiteral("bad_folder"); + const QString path = QString::fromStdString(game_dir->path); + const QString icon_name = + QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); - setData(game_dir->path, Qt::DisplayRole); + setData(path, Qt::DisplayRole); break; } default: diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 69be21027..dc006832e 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -456,29 +456,29 @@ void GameListWorker::run() { break; } - if (game_dir.path == QStringLiteral("SDMC")) { + if (game_dir.path == std::string("SDMC")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); - } else if (game_dir.path == QStringLiteral("UserNAND")) { + } else if (game_dir.path == std::string("UserNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); - } else if (game_dir.path == QStringLiteral("SysNAND")) { + } else if (game_dir.path == std::string("SysNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else { - watch_list.append(game_dir.path); + watch_list.append(QString::fromStdString(game_dir.path)); auto* const game_list_dir = new GameListDir(game_dir); DirEntryReady(game_list_dir); - ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), - game_dir.deep_scan, game_list_dir); - ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), - game_dir.deep_scan, game_list_dir); + ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan, + game_list_dir); + ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan, + game_list_dir); } } - RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); + RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); }); processing_completed.Set(); } diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index 6530186c1..eebfbf155 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp @@ -19,7 +19,7 @@ void HotkeyRegistry::SaveHotkeys() { for (const auto& hotkey : group.second) { UISettings::values.shortcuts.push_back( {hotkey.first, group.first, - UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), + UISettings::ContextualShortcut({hotkey.second.keyseq.toString().toStdString(), hotkey.second.controller_keyseq, hotkey.second.context, hotkey.second.repeat})}); } @@ -31,12 +31,12 @@ void HotkeyRegistry::LoadHotkeys() { // beginGroup() for (auto shortcut : UISettings::values.shortcuts) { Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; - if (!shortcut.shortcut.keyseq.isEmpty()) { - hk.keyseq = - QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText); + if (!shortcut.shortcut.keyseq.empty()) { + hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq), + QKeySequence::NativeText); hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context); } - if (!shortcut.shortcut.controller_keyseq.isEmpty()) { + if (!shortcut.shortcut.controller_keyseq.empty()) { hk.controller_keyseq = shortcut.shortcut.controller_keyseq; } if (hk.shortcut) { @@ -51,7 +51,8 @@ void HotkeyRegistry::LoadHotkeys() { } } -QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { +QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string& action, + QWidget* widget) { Hotkey& hk = hotkey_groups[group][action]; if (!hk.shortcut) { @@ -62,7 +63,8 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action return hk.shortcut; } -ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action, +ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group, + const std::string& action, Core::HID::EmulatedController* controller) { Hotkey& hk = hotkey_groups[group][action]; @@ -74,12 +76,12 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, co return hk.controller_shortcut; } -QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { +QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) { return hotkey_groups[group][action].keyseq; } -Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, - const QString& action) { +Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const std::string& group, + const std::string& action) { return hotkey_groups[group][action].context; } @@ -101,10 +103,10 @@ void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) { button_sequence = buttons; } -void ControllerShortcut::SetKey(const QString& buttons_shortcut) { +void ControllerShortcut::SetKey(const std::string& buttons_shortcut) { ControllerButtonSequence sequence{}; - name = buttons_shortcut.toStdString(); - std::istringstream command_line(buttons_shortcut.toStdString()); + name = buttons_shortcut; + std::istringstream command_line(buttons_shortcut); std::string line; while (std::getline(command_line, line, '+')) { if (line.empty()) { diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 56eee8d82..e11332d2e 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h @@ -33,7 +33,7 @@ public: ~ControllerShortcut(); void SetKey(const ControllerButtonSequence& buttons); - void SetKey(const QString& buttons_shortcut); + void SetKey(const std::string& buttons_shortcut); ControllerButtonSequence ButtonSequence() const; @@ -88,8 +88,8 @@ public: * will be the same. Thus, you shouldn't rely on the caller really being the * QShortcut's parent. */ - QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); - ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action, + QShortcut* GetHotkey(const std::string& group, const std::string& action, QWidget* widget); + ControllerShortcut* GetControllerHotkey(const std::string& group, const std::string& action, Core::HID::EmulatedController* controller); /** @@ -98,7 +98,7 @@ public: * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). * @param action Name of the action (e.g. "Start Emulation", "Load Image"). */ - QKeySequence GetKeySequence(const QString& group, const QString& action); + QKeySequence GetKeySequence(const std::string& group, const std::string& action); /** * Returns a Qt::ShortcutContext object who can be connected to other @@ -108,20 +108,20 @@ public: * "Debugger"). * @param action Name of the action (e.g. "Start Emulation", "Load Image"). */ - Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); + Qt::ShortcutContext GetShortcutContext(const std::string& group, const std::string& action); private: struct Hotkey { QKeySequence keyseq; - QString controller_keyseq; + std::string controller_keyseq; QShortcut* shortcut = nullptr; ControllerShortcut* controller_shortcut = nullptr; Qt::ShortcutContext context = Qt::WindowShortcut; bool repeat; }; - using HotkeyMap = std::map<QString, Hotkey>; - using HotkeyGroupMap = std::map<QString, HotkeyMap>; + using HotkeyMap = std::map<std::string, Hotkey>; + using HotkeyGroupMap = std::map<std::string, HotkeyMap>; HotkeyGroupMap hotkey_groups; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d2a054eaa..dcf68460a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -10,6 +10,7 @@ #include <thread> #include "core/loader/nca.h" #include "core/tools/renderdoc.h" + #ifdef __APPLE__ #include <unistd.h> // for chdir #endif @@ -46,6 +47,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/set/set_sys.h" #include "yuzu/multiplayer/state.h" #include "yuzu/util/controller_navigation.h" @@ -127,6 +129,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/loader/loader.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" +#include "frontend_common/config.h" #include "input_common/drivers/tas_input.h" #include "input_common/drivers/virtual_amiibo.h" #include "input_common/main.h" @@ -139,9 +142,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/bootmanager.h" #include "yuzu/compatdb.h" #include "yuzu/compatibility_list.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/configuration/configure_input_per_game.h" +#include "yuzu/configuration/qt_config.h" #include "yuzu/debugger/console.h" #include "yuzu/debugger/controller.h" #include "yuzu/debugger/profiler.h" @@ -310,7 +313,7 @@ bool GMainWindow::CheckDarkMode() { #endif // __unix__ } -GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) +GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan) : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, @@ -675,7 +678,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( // Don't forget to apply settings. system->HIDCore().DisableAllControllerConfiguration(); system->ApplySettings(); - config->Save(); + config->SaveAllValues(); UpdateStatusButtons(); @@ -1046,7 +1049,12 @@ void GMainWindow::InitializeWidgets() { statusBar()->addPermanentWidget(label); } - // TODO (flTobi): Add the widget when multiplayer is fully implemented + firmware_label = new QLabel(); + firmware_label->setObjectName(QStringLiteral("FirmwareLabel")); + firmware_label->setVisible(false); + firmware_label->setFocusPolicy(Qt::NoFocus); + statusBar()->addPermanentWidget(firmware_label); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); @@ -1064,12 +1072,6 @@ void GMainWindow::InitializeWidgets() { volume_slider->setObjectName(QStringLiteral("volume_slider")); volume_slider->setMaximum(200); volume_slider->setPageStep(5); - connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { - Settings::values.audio_muted = false; - const auto volume = static_cast<u8>(percentage); - Settings::values.volume.SetValue(volume); - UpdateVolumeUI(); - }); volume_popup->layout()->addWidget(volume_slider); volume_button = new VolumeButton(); @@ -1077,6 +1079,12 @@ void GMainWindow::InitializeWidgets() { volume_button->setFocusPolicy(Qt::NoFocus); volume_button->setCheckable(true); UpdateVolumeUI(); + connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { + Settings::values.audio_muted = false; + const auto volume = static_cast<u8>(percentage); + Settings::values.volume.SetValue(volume); + UpdateVolumeUI(); + }); connect(volume_button, &QPushButton::clicked, this, [&] { UpdateVolumeUI(); volume_popup->setVisible(!volume_popup->isVisible()); @@ -1128,7 +1136,7 @@ void GMainWindow::InitializeWidgets() { connect(aa_status_button, &QPushButton::customContextMenuRequested, [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { + for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) { context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { Settings::values.anti_aliasing.SetValue(aa_text_pair.first); UpdateAAText(); @@ -1152,7 +1160,7 @@ void GMainWindow::InitializeWidgets() { connect(filter_status_button, &QPushButton::customContextMenuRequested, [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { + for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) { context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { Settings::values.scaling_filter.SetValue(filter_text_pair.first); UpdateFilterText(); @@ -1175,7 +1183,7 @@ void GMainWindow::InitializeWidgets() { [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& pair : Config::use_docked_mode_texts_map) { + for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) { context_menu.addAction(pair.second, [this, &pair] { if (pair.first != Settings::values.use_docked_mode.GetValue()) { OnToggleDockedMode(); @@ -1199,7 +1207,7 @@ void GMainWindow::InitializeWidgets() { [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { + for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) { if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { continue; } @@ -1228,7 +1236,8 @@ void GMainWindow::InitializeWidgets() { [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { + for (auto const& renderer_backend_pair : + ConfigurationShared::renderer_backend_texts_map) { if (renderer_backend_pair.first == Settings::RendererBackend::Null) { continue; } @@ -1293,16 +1302,17 @@ void GMainWindow::InitializeRecentFileMenuActions() { void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, const bool tas_allowed) { - static const QString main_window = QStringLiteral("Main Window"); - action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); - action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); + static const auto main_window = std::string("Main Window"); + action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString())); + action->setShortcutContext( + hotkey_registry.GetShortcutContext(main_window, action_name.toStdString())); action->setAutoRepeat(false); this->addAction(action); auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); const auto* controller_hotkey = - hotkey_registry.GetControllerHotkey(main_window, action_name, controller); + hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller); connect( controller_hotkey, &ControllerShortcut::Activated, this, [action, tas_allowed, this] { @@ -1334,10 +1344,11 @@ void GMainWindow::InitializeHotkeys() { static const QString main_window = QStringLiteral("Main Window"); const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { - const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); + const auto* hotkey = + hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this); auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - const auto* controller_hotkey = - hotkey_registry.GetControllerHotkey(main_window, action_name, controller); + const auto* controller_hotkey = hotkey_registry.GetControllerHotkey( + main_window.toStdString(), action_name.toStdString(), controller); connect(hotkey, &QShortcut::activated, this, function); connect(controller_hotkey, &ControllerShortcut::Activated, this, function, Qt::QueuedConnection); @@ -1574,6 +1585,7 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Load_Cabinet_Formatter, [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); + connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu); connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); // TAS @@ -1601,14 +1613,13 @@ void GMainWindow::UpdateMenuState() { ui->action_Pause, }; - const std::array applet_actions{ - ui->action_Load_Album, - ui->action_Load_Cabinet_Nickname_Owner, - ui->action_Load_Cabinet_Eraser, - ui->action_Load_Cabinet_Restorer, - ui->action_Load_Cabinet_Formatter, - ui->action_Load_Mii_Edit, - }; + const std::array applet_actions{ui->action_Load_Album, + ui->action_Load_Cabinet_Nickname_Owner, + ui->action_Load_Cabinet_Eraser, + ui->action_Load_Cabinet_Restorer, + ui->action_Load_Cabinet_Formatter, + ui->action_Load_Mii_Edit, + ui->action_Open_Controller_Menu}; for (QAction* action : running_actions) { action->setEnabled(emulation_running); @@ -1917,7 +1928,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t // Save configurations UpdateUISettings(); game_list->SaveInterfaceLayout(); - config->Save(); + config->SaveAllValues(); u64 title_id{0}; @@ -1935,7 +1946,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); - Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); + QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig); system->HIDCore().ReloadInputDevices(); system->ApplySettings(); } @@ -2160,6 +2171,10 @@ void GMainWindow::OnEmulationStopped() { emu_frametime_label->setVisible(false); renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); + if (!firmware_label->text().isEmpty()) { + firmware_label->setVisible(true); + } + current_game_path.clear(); // When closing the game, destroy the GLWindow to clear the context after the game is closed @@ -2847,170 +2862,259 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } -void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, - GameListShortcutTarget target) { - // Get path to yuzu executable - const QStringList args = QApplication::arguments(); - std::filesystem::path yuzu_command = args[0].toStdString(); - - // If relative path, make it an absolute path - if (yuzu_command.c_str()[0] == '.') { - yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; +bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, + const std::string& comment, + const std::filesystem::path& icon_path, + const std::filesystem::path& command, + const std::string& arguments, const std::string& categories, + const std::string& keywords, const std::string& name) try { +#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD + std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop"); + std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); + if (!shortcut_stream.is_open()) { + LOG_ERROR(Frontend, "Failed to create shortcut"); + return false; } - -#if defined(__linux__) - // Warn once if we are making a shortcut to a volatile AppImage - const std::string appimage_ending = - std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); - if (yuzu_command.string().ends_with(appimage_ending) && - !UISettings::values.shortcut_already_warned) { - if (QMessageBox::warning(this, tr("Create Shortcut"), - tr("This will create a shortcut to the current AppImage. This may " - "not work well if you update. Continue?"), - QMessageBox::StandardButton::Ok | - QMessageBox::StandardButton::Cancel) == - QMessageBox::StandardButton::Cancel) { - return; + // TODO: Migrate fmt::print to std::print in futures STD C++ 23. + fmt::print(shortcut_stream, "[Desktop Entry]\n"); + fmt::print(shortcut_stream, "Type=Application\n"); + fmt::print(shortcut_stream, "Version=1.0\n"); + fmt::print(shortcut_stream, "Name={}\n", name); + if (!comment.empty()) { + fmt::print(shortcut_stream, "Comment={}\n", comment); + } + if (std::filesystem::is_regular_file(icon_path)) { + fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); + } + fmt::print(shortcut_stream, "TryExec={}\n", command.string()); + fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments); + if (!categories.empty()) { + fmt::print(shortcut_stream, "Categories={}\n", categories); + } + if (!keywords.empty()) { + fmt::print(shortcut_stream, "Keywords={}\n", keywords); + } + return true; +#elif defined(_WIN32) // Windows + HRESULT hr = CoInitialize(nullptr); + if (FAILED(hr)) { + LOG_ERROR(Frontend, "CoInitialize failed"); + return false; + } + SCOPE_EXIT({ CoUninitialize(); }); + IShellLinkW* ps1 = nullptr; + IPersistFile* persist_file = nullptr; + SCOPE_EXIT({ + if (persist_file != nullptr) { + persist_file->Release(); } - UISettings::values.shortcut_already_warned = true; + if (ps1 != nullptr) { + ps1->Release(); + } + }); + HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, + reinterpret_cast<void**>(&ps1)); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); + return false; } -#endif // __linux__ - - std::filesystem::path target_directory{}; - - switch (target) { - case GameListShortcutTarget::Desktop: { - const QString desktop_path = - QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); - target_directory = desktop_path.toUtf8().toStdString(); - break; + hres = ps1->SetPath(command.c_str()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set path"); + return false; } - case GameListShortcutTarget::Applications: { - const QString applications_path = - QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); - if (applications_path.isEmpty()) { - const char* home = std::getenv("HOME"); - if (home != nullptr) { - target_directory = std::filesystem::path(home) / ".local/share/applications"; - } - } else { - target_directory = applications_path.toUtf8().toStdString(); + if (!arguments.empty()) { + hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set arguments"); + return false; } - break; } - default: - return; + if (!comment.empty()) { + hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set description"); + return false; + } } - - const QDir dir(QString::fromStdString(target_directory.generic_string())); - if (!dir.exists()) { - QMessageBox::critical(this, tr("Create Shortcut"), - tr("Cannot create shortcut. Path \"%1\" does not exist.") - .arg(QString::fromStdString(target_directory.generic_string())), - QMessageBox::StandardButton::Ok); - return; + if (std::filesystem::is_regular_file(icon_path)) { + hres = ps1->SetIconLocation(icon_path.c_str(), 0); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set icon location"); + return false; + } } + hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file)); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to get IPersistFile interface"); + return false; + } + hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to save shortcut"); + return false; + } + return true; +#else // Unsupported platform + return false; +#endif +} catch (const std::exception& e) { + LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); + return false; +} +// Messages in pre-defined message boxes for less code spaghetti +bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { + int result = 0; + QMessageBox::StandardButtons buttons; + switch (imsg) { + case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: + buttons = QMessageBox::Yes | QMessageBox::No; + result = + QMessageBox::information(parent, tr("Create Shortcut"), + tr("Do you want to launch the game in fullscreen?"), buttons); + return result == QMessageBox::Yes; + case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: + QMessageBox::information(parent, tr("Create Shortcut"), + tr("Successfully created a shortcut to %1").arg(game_title)); + return false; + case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: + buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; + result = + QMessageBox::warning(this, tr("Create Shortcut"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + buttons); + return result == QMessageBox::Ok; + default: + buttons = QMessageBox::Ok; + QMessageBox::critical(parent, tr("Create Shortcut"), + tr("Failed to create a shortcut to %1").arg(game_title), buttons); + return false; + } +} - const std::string game_file_name = std::filesystem::path(game_path).filename().string(); - // Determine full paths for icon and shortcut -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - const char* home = std::getenv("HOME"); - const std::filesystem::path home_path = (home == nullptr ? "~" : home); - const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); - - std::filesystem::path system_icons_path = - (xdg_data_home == nullptr ? home_path / ".local/share/" - : std::filesystem::path(xdg_data_home)) / - "icons/hicolor/256x256"; - if (!Common::FS::CreateDirs(system_icons_path)) { +bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, + std::filesystem::path& out_icon_path) { + // Get path to Yuzu icons directory & icon extension + std::string ico_extension = "png"; +#if defined(_WIN32) + out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir); + ico_extension = "ico"; +#elif defined(__linux__) || defined(__FreeBSD__) + out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; +#endif + // Create icons directory if it doesn't exist + if (!Common::FS::CreateDirs(out_icon_path)) { QMessageBox::critical( this, tr("Create Icon"), tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") - .arg(QString::fromStdString(system_icons_path)), + .arg(QString::fromStdString(out_icon_path.string())), QMessageBox::StandardButton::Ok); - return; + out_icon_path.clear(); + return false; } - std::filesystem::path icon_path = - system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) - : fmt::format("yuzu-{:016X}.png", program_id)); - const std::filesystem::path shortcut_path = - target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) - : fmt::format("yuzu-{:016X}.desktop", program_id)); -#elif defined(WIN32) - std::filesystem::path icons_path = - Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir); - std::filesystem::path icon_path = - icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name) - : fmt::format("yuzu-{:016X}.ico", program_id))); -#else - std::string icon_extension; -#endif - - // Get title from game file - const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), - system->GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); - - std::string title{fmt::format("{:016X}", program_id)}; - if (control.first != nullptr) { - title = control.first->GetApplicationName(); - } else { - loader->ReadTitle(title); - } + // Create icon file path + out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) + : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); + return true; +} - // Get icon from game file - std::vector<u8> icon_image_file{}; - if (control.second != nullptr) { - icon_image_file = control.second->ReadAllBytes(); - } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { - LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); +void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target) { + std::string game_title; + QString qt_game_title; + std::filesystem::path out_icon_path; + // Get path to yuzu executable + const QStringList args = QApplication::arguments(); + std::filesystem::path yuzu_command = args[0].toStdString(); + // If relative path, make it an absolute path + if (yuzu_command.c_str()[0] == '.') { + yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; } - - QImage icon_data = - QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - // Convert and write the icon as a PNG - if (!icon_data.save(QString::fromStdString(icon_path.string()))) { - LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + // Shortcut path + std::filesystem::path shortcut_path{}; + if (target == GameListShortcutTarget::Desktop) { + shortcut_path = + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); + } else if (target == GameListShortcutTarget::Applications) { + shortcut_path = + QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); + } + // Icon path and title + if (std::filesystem::exists(shortcut_path)) { + // Get title from game file + const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), + system->GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = + Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + game_title = fmt::format("{:016X}", program_id); + if (control.first != nullptr) { + game_title = control.first->GetApplicationName(); + } else { + loader->ReadTitle(game_title); + } + // Delete illegal characters from title + const std::string illegal_chars = "<>:\"/\\|?*."; + for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) { + if (illegal_chars.find(*it) != std::string::npos) { + game_title.erase(it.base() - 1); + } + } + qt_game_title = QString::fromStdString(game_title); + // Get icon from game file + std::vector<u8> icon_image_file{}; + if (control.second != nullptr) { + icon_image_file = control.second->ReadAllBytes(); + } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { + LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + } + QImage icon_data = + QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); + if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { + if (!SaveIconToFile(out_icon_path, icon_data)) { + LOG_ERROR(Frontend, "Could not write icon to file"); + } + } } else { - LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); - } -#elif defined(WIN32) - if (!SaveIconToFile(icon_path.string(), icon_data)) { - LOG_ERROR(Frontend, "Could not write icon to file"); + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, + qt_game_title); + LOG_ERROR(Frontend, "Invalid shortcut target"); return; } +#if defined(__linux__) + // Special case for AppImages + // Warn once if we are making a shortcut to a volatile AppImage + const std::string appimage_ending = + std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); + if (yuzu_command.string().ends_with(appimage_ending) && + !UISettings::values.shortcut_already_warned) { + if (GMainWindow::CreateShortcutMessagesGUI( + this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) { + return; + } + UISettings::values.shortcut_already_warned = true; + } #endif // __linux__ - -#ifdef _WIN32 - // Replace characters that are illegal in Windows filenames by a dash - const std::string illegal_chars = "<>:\"/\\|?*"; - for (char c : illegal_chars) { - std::replace(title.begin(), title.end(), c, '_'); + // Create shortcut + std::string arguments = fmt::format("-g \"{:s}\"", game_path); + if (GMainWindow::CreateShortcutMessagesGUI( + this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) { + arguments = "-f " + arguments; } - const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); -#endif - - const std::string comment = - tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); - const std::string arguments = fmt::format("-g \"{:s}\"", game_path); + const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; - if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), - yuzu_command.string(), arguments, categories, keywords)) { - QMessageBox::critical(this, tr("Create Shortcut"), - tr("Failed to create a shortcut at %1") - .arg(QString::fromStdString(shortcut_path.string()))); + if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command, + arguments, categories, keywords, game_title)) { + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, + qt_game_title); return; } - - LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); - QMessageBox::information( - this, tr("Create Shortcut"), - tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, + qt_game_title); } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { @@ -3045,7 +3149,7 @@ void GMainWindow::OnGameListAddDirectory() { return; } - UISettings::GameDir game_dir{dir_path, false, true}; + UISettings::GameDir game_dir{dir_path.toStdString(), false, true}; if (!UISettings::values.game_dirs.contains(game_dir)) { UISettings::values.game_dirs.append(game_dir); game_list->PopulateAsync(UISettings::values.game_dirs); @@ -3091,14 +3195,14 @@ void GMainWindow::OnMenuLoadFile() { "%1 is an identifier for the Switch executable file extensions.") .arg(extensions); const QString filename = QFileDialog::getOpenFileName( - this, tr("Load File"), UISettings::values.roms_path, file_filter); + this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter); is_load_file_select_active = false; if (filename.isEmpty()) { return; } - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename); } @@ -3131,7 +3235,8 @@ void GMainWindow::OnMenuInstallToNAND() { "Image (*.xci)"); QStringList filenames = QFileDialog::getOpenFileNames( - this, tr("Install Files"), UISettings::values.roms_path, file_filter); + this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path), + file_filter); if (filenames.isEmpty()) { return; @@ -3149,7 +3254,7 @@ void GMainWindow::OnMenuInstallToNAND() { } // Save folder location of the first selected file - UISettings::values.roms_path = QFileInfo(filenames[0]).path(); + UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString(); int remaining = filenames.size(); @@ -3494,7 +3599,7 @@ void GMainWindow::OnExit() { void GMainWindow::OnSaveConfig() { system->ApplySettings(); - config->Save(); + config->SaveAllValues(); } void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { @@ -3750,7 +3855,7 @@ void GMainWindow::OnConfigure() { Settings::values.disabled_addons.clear(); - config = std::make_unique<Config>(); + config = std::make_unique<QtConfig>(); UISettings::values.reset_to_defaults = false; UISettings::values.game_dirs = std::move(old_game_dirs); @@ -3785,7 +3890,7 @@ void GMainWindow::OnConfigure() { UISettings::values.configuration_applied = false; - config->Save(); + config->SaveAllValues(); if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { render_window->installEventFilter(render_window); @@ -4001,68 +4106,8 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file UISettings::values.configuration_applied = false; if (!is_powered_on) { - config->Save(); - } -} - -bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, - const std::string& comment, const std::string& icon_path, - const std::string& command, const std::string& arguments, - const std::string& categories, const std::string& keywords) { -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - // This desktop file template was writing referencing - // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html - std::string shortcut_contents{}; - shortcut_contents.append("[Desktop Entry]\n"); - shortcut_contents.append("Type=Application\n"); - shortcut_contents.append("Version=1.0\n"); - shortcut_contents.append(fmt::format("Name={:s}\n", title)); - shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); - shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); - shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); - shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); - shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); - shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); - - std::ofstream shortcut_stream(shortcut_path); - if (!shortcut_stream.is_open()) { - LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); - return false; - } - shortcut_stream << shortcut_contents; - shortcut_stream.close(); - - return true; -#elif defined(WIN32) - IShellLinkW* shell_link; - auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, - (void**)&shell_link); - if (FAILED(hres)) { - return false; - } - shell_link->SetPath( - Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to - shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data()); - shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data()); - shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0); - - IPersistFile* persist_file; - hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); - if (FAILED(hres)) { - return false; + config->SaveAllValues(); } - - hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE); - if (FAILED(hres)) { - return false; - } - - persist_file->Release(); - shell_link->Release(); - - return true; -#endif - return false; } void GMainWindow::OnLoadAmiibo() { @@ -4103,7 +4148,6 @@ void GMainWindow::OnLoadAmiibo() { bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - QMessageBox* box_dialog = new QMessageBox(parent); box_dialog->setWindowTitle(title); box_dialog->setText(text); @@ -4295,7 +4339,7 @@ void GMainWindow::OnAlbum() { system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); const auto filename = QString::fromStdString(album_nca->GetFullPath()); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, AlbumId); } @@ -4319,7 +4363,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { system->GetAppletManager().SetCabinetMode(mode); const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, CabinetId); } @@ -4342,10 +4386,35 @@ void GMainWindow::OnMiiEdit() { system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, MiiEditId); } +void GMainWindow::OnOpenControllerMenu() { + constexpr u64 ControllerAppletId = + static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller); + auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + QMessageBox::warning(this, tr("No firmware available"), + tr("Please install the firmware to use the Controller Menu.")); + return; + } + + auto controller_applet_nca = + bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program); + if (!controller_applet_nca) { + QMessageBox::warning(this, tr("Controller Applet"), + tr("Controller Menu is not available. Please reinstall firmware.")); + return; + } + + system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); + + const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); + BootGame(filename, ControllerAppletId); +} + void GMainWindow::OnCaptureScreenshot() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; @@ -4532,11 +4601,13 @@ void GMainWindow::UpdateStatusBar() { emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); game_fps_label->setVisible(true); emu_frametime_label->setVisible(true); + firmware_label->setVisible(false); } void GMainWindow::UpdateGPUAccuracyButton() { const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); - const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; + const auto gpu_accuracy_text = + ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second; gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); } @@ -4545,31 +4616,32 @@ void GMainWindow::UpdateDockedButton() { const auto console_mode = Settings::values.use_docked_mode.GetValue(); dock_status_button->setChecked(Settings::IsDockedMode()); dock_status_button->setText( - Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); + ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); } void GMainWindow::UpdateAPIText() { const auto api = Settings::values.renderer_backend.GetValue(); - const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; + const auto renderer_status_text = + ConfigurationShared::renderer_backend_texts_map.find(api)->second; renderer_status_button->setText( api == Settings::RendererBackend::OpenGL - ? tr("%1 %2").arg( - renderer_status_text.toUpper(), - Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) - ->second) + ? tr("%1 %2").arg(renderer_status_text.toUpper(), + ConfigurationShared::shader_backend_texts_map + .find(Settings::values.shader_backend.GetValue()) + ->second) : renderer_status_text.toUpper()); } void GMainWindow::UpdateFilterText() { const auto filter = Settings::values.scaling_filter.GetValue(); - const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; + const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second; filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") : filter_text.toUpper()); } void GMainWindow::UpdateAAText() { const auto aa_mode = Settings::values.anti_aliasing.GetValue(); - const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; + const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second; aa_status_button->setText(aa_mode == Settings::AntiAliasing::None ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) : aa_text.toUpper()); @@ -4749,6 +4821,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { "games.")); } + SetFirmwareVersion(); + if (behavior == ReinitializeKeyBehavior::Warning) { game_list->PopulateAsync(UISettings::values.game_dirs); } @@ -4776,7 +4850,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() { } bool GMainWindow::CheckFirmwarePresence() { - constexpr u64 MiiEditId = 0x0100000000001009ull; + constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { @@ -4791,6 +4865,28 @@ bool GMainWindow::CheckFirmwarePresence() { return true; } +void GMainWindow::SetFirmwareVersion() { + Service::Set::FirmwareVersionFormat firmware_data{}; + const auto result = Service::Set::GetFirmwareVersionImpl( + firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); + + if (result.IsError() || !CheckFirmwarePresence()) { + LOG_INFO(Frontend, "Installed firmware: No firmware available"); + firmware_label->setVisible(false); + return; + } + + firmware_label->setVisible(true); + + const std::string display_version(firmware_data.display_version.data()); + const std::string display_title(firmware_data.display_title.data()); + + LOG_INFO(Frontend, "Installed firmware: {}", display_title); + + firmware_label->setText(QString::fromStdString(display_version)); + firmware_label->setToolTip(QString::fromStdString(display_title)); +} + bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, u64* selected_title_id, u8* selected_content_record_type) { using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; @@ -4872,6 +4968,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { UpdateUISettings(); game_list->SaveInterfaceLayout(); + UISettings::SaveWindowState(); hotkey_registry.SaveHotkeys(); // Unload controllers early @@ -5026,9 +5123,9 @@ static void AdjustLinkColor() { } void GMainWindow::UpdateUITheme() { - const QString default_theme = - QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); - QString current_theme = UISettings::values.theme; + const QString default_theme = QString::fromUtf8( + UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second); + QString current_theme = QString::fromStdString(UISettings::values.theme); if (current_theme.isEmpty()) { current_theme = default_theme; @@ -5056,7 +5153,7 @@ void GMainWindow::UpdateUITheme() { QFile f(theme_uri); if (!f.open(QFile::ReadOnly | QFile::Text)) { LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", - UISettings::values.theme.toStdString()); + UISettings::values.theme); current_theme = default_theme; } } @@ -5069,7 +5166,7 @@ void GMainWindow::UpdateUITheme() { setStyleSheet(ts.readAll()); } else { LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", - UISettings::values.theme.toStdString()); + UISettings::values.theme); qApp->setStyleSheet({}); setStyleSheet({}); } @@ -5078,27 +5175,28 @@ void GMainWindow::UpdateUITheme() { void GMainWindow::LoadTranslation() { bool loaded; - if (UISettings::values.language.isEmpty()) { + if (UISettings::values.language.empty()) { // If the selected language is empty, use system locale loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); } else { // Otherwise load from the specified file - loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); + loaded = translator.load(QString::fromStdString(UISettings::values.language), + QStringLiteral(":/languages/")); } if (loaded) { qApp->installTranslator(&translator); } else { - UISettings::values.language = QStringLiteral("en"); + UISettings::values.language = std::string("en"); } } void GMainWindow::OnLanguageChanged(const QString& locale) { - if (UISettings::values.language != QStringLiteral("en")) { + if (UISettings::values.language != std::string("en")) { qApp->removeTranslator(&translator); } - UISettings::values.language = locale; + UISettings::values.language = locale.toStdString(); LoadTranslation(); ui->retranslateUi(this); multiplayer_state->retranslateUi(); @@ -5124,7 +5222,7 @@ void GMainWindow::changeEvent(QEvent* event) { // UpdateUITheme is a decent work around if (event->type() == QEvent::PaletteChange) { const QPalette test_palette(qApp->palette()); - const QString current_theme = UISettings::values.theme; + const QString current_theme = QString::fromStdString(UISettings::values.theme); // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too static QColor last_window_color; const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); @@ -5218,7 +5316,8 @@ static void SetHighDPIAttributes() { } int main(int argc, char* argv[]) { - std::unique_ptr<Config> config = std::make_unique<Config>(); + std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>(); + UISettings::RestoreWindowState(config); bool has_broken_vulkan = false; bool is_child = false; if (CheckEnvVars(&is_child)) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f9c6efe4f..e99d58995 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -6,6 +6,7 @@ #include <memory> #include <optional> +#include <filesystem> #include <QMainWindow> #include <QMessageBox> #include <QPushButton> @@ -14,6 +15,7 @@ #include "common/announce_multiplayer_room.h" #include "common/common_types.h" +#include "configuration/qt_config.h" #include "input_common/drivers/tas_input.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" @@ -25,7 +27,7 @@ #include <QtDBus/QtDBus> #endif -class Config; +class QtConfig; class ClickableLabel; class EmuThread; class GameList; @@ -174,10 +176,17 @@ class GMainWindow : public QMainWindow { UI_EMU_STOPPING, }; + enum { + CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, + CREATE_SHORTCUT_MSGBOX_SUCCESS, + CREATE_SHORTCUT_MSGBOX_ERROR, + CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, + }; + public: void filterBarSetChecked(bool state); void UpdateUITheme(); - explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); + explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan); ~GMainWindow() override; bool DropAction(QDropEvent* event); @@ -402,6 +411,7 @@ private slots: void OnAlbum(); void OnCabinet(Service::NFP::CabinetMode mode); void OnMiiEdit(); + void OnOpenControllerMenu(); void OnCaptureScreenshot(); void OnReinitializeKeys(ReinitializeKeyBehavior behavior); void OnLanguageChanged(const QString& locale); @@ -448,6 +458,7 @@ private: bool CheckDarkMode(); bool CheckSystemArchiveDecryption(); bool CheckFirmwarePresence(); + void SetFirmwareVersion(); void ConfigureFilesystemProvider(const std::string& filepath); /** * Open (or not) the right confirm dialog based on current setting and game exit lock @@ -456,11 +467,14 @@ private: bool ConfirmShutdownGame(); QString GetTasStateDescription() const; - bool CreateShortcut(const std::string& shortcut_path, const std::string& title, - const std::string& comment, const std::string& icon_path, - const std::string& command, const std::string& arguments, - const std::string& categories, const std::string& keywords); - + bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); + bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, + std::filesystem::path& out_icon_path); + bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, + const std::filesystem::path& icon_path, + const std::filesystem::path& command, const std::string& arguments, + const std::string& categories, const std::string& keywords, + const std::string& name); /** * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog * The only difference is that it returns a boolean. @@ -499,6 +513,7 @@ private: QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; QLabel* tas_label = nullptr; + QLabel* firmware_label = nullptr; QPushButton* gpu_accuracy_button = nullptr; QPushButton* renderer_status_button = nullptr; QPushButton* dock_status_button = nullptr; @@ -509,7 +524,7 @@ private: QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; - std::unique_ptr<Config> config; + std::unique_ptr<QtConfig> config; // Whether emulation is currently running in yuzu. bool emulation_running = false; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 88684ffb5..e53f9951e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -25,7 +25,7 @@ </property> <widget class="QWidget" name="centralwidget"> <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="margin"> + <property name="margin" stdset="0"> <number>0</number> </property> </layout> @@ -36,7 +36,7 @@ <x>0</x> <y>0</y> <width>1280</width> - <height>26</height> + <height>21</height> </rect> </property> <widget class="QMenu" name="menu_File"> @@ -162,6 +162,7 @@ <addaction name="menu_cabinet_applet"/> <addaction name="action_Load_Album"/> <addaction name="action_Load_Mii_Edit"/> + <addaction name="action_Open_Controller_Menu"/> <addaction name="separator"/> <addaction name="action_Capture_Screenshot"/> <addaction name="menuTAS"/> @@ -382,9 +383,9 @@ </property> </action> <action name="action_Load_Album"> - <property name="text"> - <string>Open &Album</string> - </property> + <property name="text"> + <string>Open &Album</string> + </property> </action> <action name="action_Load_Cabinet_Nickname_Owner"> <property name="text"> @@ -407,9 +408,9 @@ </property> </action> <action name="action_Load_Mii_Edit"> - <property name="text"> - <string>Open &Mii Editor</string> - </property> + <property name="text"> + <string>Open &Mii Editor</string> + </property> </action> <action name="action_Configure_Tas"> <property name="text"> @@ -454,6 +455,11 @@ <string>R&ecord</string> </property> </action> + <action name="action_Open_Controller_Menu"> + <property name="text"> + <string>Open &Controller Menu</string> + </property> + </action> </widget> <resources> <include location="yuzu.qrc"/> diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index 1c833767b..7bb7e95af 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <QSettings> +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "yuzu/uisettings.h" #ifndef CANNOT_EXPLICITLY_INSTANTIATE @@ -15,6 +18,8 @@ template class Setting<unsigned long long>; } // namespace Settings #endif +namespace FS = Common::FS; + namespace UISettings { const Themes themes{{ @@ -28,10 +33,8 @@ const Themes themes{{ bool IsDarkTheme() { const auto& theme = UISettings::values.theme; - return theme == QStringLiteral("qdarkstyle") || - theme == QStringLiteral("qdarkstyle_midnight_blue") || - theme == QStringLiteral("colorful_dark") || - theme == QStringLiteral("colorful_midnight_blue"); + return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") || + theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue"); } Values values = {}; @@ -52,4 +55,58 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) { return height * 16 / 9; } +void SaveWindowState() { + const auto window_state_config_loc = + FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini"); + + void(FS::CreateParentDir(window_state_config_loc)); + QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat); + + config.setValue(QStringLiteral("geometry"), values.geometry); + config.setValue(QStringLiteral("state"), values.state); + config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry); + config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state); + config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry); + + config.sync(); +} + +void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) { + const auto window_state_config_loc = + FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini"); + + // Migrate window state from old location + if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) { + const auto config_loc = + FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini"); + QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat); + + config.beginGroup(QStringLiteral("UI")); + config.beginGroup(QStringLiteral("UILayout")); + values.geometry = config.value(QStringLiteral("geometry")).toByteArray(); + values.state = config.value(QStringLiteral("state")).toByteArray(); + values.renderwindow_geometry = + config.value(QStringLiteral("geometryRenderWindow")).toByteArray(); + values.gamelist_header_state = + config.value(QStringLiteral("gameListHeaderState")).toByteArray(); + values.microprofile_geometry = + config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray(); + config.endGroup(); + config.endGroup(); + return; + } + + void(FS::CreateParentDir(window_state_config_loc)); + const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat); + + values.geometry = config.value(QStringLiteral("geometry")).toByteArray(); + values.state = config.value(QStringLiteral("state")).toByteArray(); + values.renderwindow_geometry = + config.value(QStringLiteral("geometryRenderWindow")).toByteArray(); + values.gamelist_header_state = + config.value(QStringLiteral("gameListHeaderState")).toByteArray(); + values.microprofile_geometry = + config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray(); +} + } // namespace UISettings diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 77d992c54..549a39e1b 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -14,6 +14,7 @@ #include "common/common_types.h" #include "common/settings.h" #include "common/settings_enums.h" +#include "configuration/qt_config.h" using Settings::Category; using Settings::ConfirmStop; @@ -37,15 +38,15 @@ namespace UISettings { bool IsDarkTheme(); struct ContextualShortcut { - QString keyseq; - QString controller_keyseq; + std::string keyseq; + std::string controller_keyseq; int context; bool repeat; }; struct Shortcut { - QString name; - QString group; + std::string name; + std::string group; ContextualShortcut shortcut; }; @@ -58,11 +59,19 @@ enum class Theme { MidnightBlueColorful, }; +static constexpr Theme default_theme{ +#ifdef _WIN32 + Theme::DarkColorful +#else + Theme::DefaultColorful +#endif +}; + using Themes = std::array<std::pair<const char*, const char*>, 6>; extern const Themes themes; struct GameDir { - QString path; + std::string path; bool deep_scan = false; bool expanded = false; bool operator==(const GameDir& rhs) const { @@ -109,9 +118,13 @@ struct Values { Settings::Specialization::Default, true, true}; - Setting<bool> mute_when_in_background{ - linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, - true, true}; + Setting<bool> mute_when_in_background{linkage, + false, + "muteWhenInBackground", + Category::UiAudio, + Settings::Specialization::Default, + true, + true}; Setting<bool> hide_mouse{ linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, true, true}; @@ -140,15 +153,15 @@ struct Values { Category::Screenshots}; Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; - QString roms_path; - QString symbols_path; - QString game_dir_deprecated; + std::string roms_path; + std::string symbols_path; + std::string game_dir_deprecated; bool game_dir_deprecated_deepscan; - QVector<UISettings::GameDir> game_dirs; + QVector<GameDir> game_dirs; QStringList recent_files; - QString language; + std::string language; - QString theme; + std::string theme; // Shortcut name <Shortcut, context> std::vector<Shortcut> shortcuts; @@ -202,6 +215,54 @@ extern Values values; u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); +void SaveWindowState(); +void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig); + +// This shouldn't have anything except static initializers (no functions). So +// QKeySequence(...).toString() is NOT ALLOWED HERE. +// This must be in alphabetical order according to action name as it must have the same order as +// UISetting::values.shortcuts, which is alphabetically ordered. +// clang-format off +const std::array<Shortcut, 23> default_hotkeys{{ + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}}, +}}; +// clang-format on + } // namespace UISettings Q_DECLARE_METATYPE(UISettings::GameDir*); + +// These metatype declarations cannot be in common/settings.h because core is devoid of QT +Q_DECLARE_METATYPE(Settings::CpuAccuracy); +Q_DECLARE_METATYPE(Settings::GpuAccuracy); +Q_DECLARE_METATYPE(Settings::FullscreenMode); +Q_DECLARE_METATYPE(Settings::NvdecEmulation); +Q_DECLARE_METATYPE(Settings::ResolutionSetup); +Q_DECLARE_METATYPE(Settings::ScalingFilter); +Q_DECLARE_METATYPE(Settings::AntiAliasing); +Q_DECLARE_METATYPE(Settings::RendererBackend); +Q_DECLARE_METATYPE(Settings::ShaderBackend); +Q_DECLARE_METATYPE(Settings::AstcRecompression); +Q_DECLARE_METATYPE(Settings::AstcDecodeMode); diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index f2854c8ec..e22cf84bf 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -4,7 +4,10 @@ #include <array> #include <cmath> #include <QPainter> + +#include "common/logging/log.h" #include "yuzu/util/util.h" + #ifdef _WIN32 #include <windows.h> #include "common/fs/file.h" @@ -42,7 +45,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { return circle_pixmap; } -bool SaveIconToFile(const std::string_view path, const QImage& image) { +bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) { #if defined(WIN32) #pragma pack(push, 2) struct IconDir { @@ -73,7 +76,7 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { .id_count = static_cast<WORD>(scale_sizes.size()), }; - Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, + Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); if (!icon_file.IsOpen()) { return false; @@ -135,6 +138,14 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { icon_file.Close(); return true; +#elif defined(__linux__) || defined(__FreeBSD__) + // Convert and write the icon as a PNG + if (!image.save(QString::fromStdString(icon_path.string()))) { + LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } + return true; #else return false; #endif diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 09c14ce3f..4094cf6c2 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h @@ -3,6 +3,7 @@ #pragma once +#include <filesystem> #include <QFont> #include <QString> @@ -25,4 +26,4 @@ * @param image The image to save * @return bool If the operation succeeded */ -[[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); +[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 46eddf423..fbeba8813 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -13,9 +13,6 @@ function(create_resource file output filename) endfunction() add_executable(yuzu-cmd - config.cpp - config.h - default_ini.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h emu_window/emu_window_sdl2_gl.cpp @@ -25,14 +22,16 @@ add_executable(yuzu-cmd emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl2_vk.h precompiled_headers.h + sdl_config.cpp + sdl_config.h yuzu.cpp yuzu.rc ) create_target_directory_groups(yuzu-cmd) -target_link_libraries(yuzu-cmd PRIVATE common core input_common) -target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) +target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common) +target_link_libraries(yuzu-cmd PRIVATE glad) if (MSVC) target_link_libraries(yuzu-cmd PRIVATE getopt) endif() diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp deleted file mode 100644 index 0d25ff400..000000000 --- a/src/yuzu_cmd/config.cpp +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <memory> -#include <optional> -#include <sstream> -#include <INIReader.h> -#include <SDL.h> -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/hle/service/acc/profile_manager.h" -#include "input_common/main.h" -#include "yuzu_cmd/config.h" -#include "yuzu_cmd/default_ini.h" - -namespace FS = Common::FS; - -const std::filesystem::path default_config_path = - FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; - -Config::Config(std::optional<std::filesystem::path> config_path) - : sdl2_config_loc{config_path.value_or(default_config_path)}, - sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} { - Reload(); -} - -Config::~Config() = default; - -bool Config::LoadINI(const std::string& default_contents, bool retry) { - const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc); - if (sdl2_config->ParseError() < 0) { - if (retry) { - LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", - config_loc_str); - - void(FS::CreateParentDir(sdl2_config_loc)); - void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents)); - - sdl2_config = std::make_unique<INIReader>(config_loc_str); - - return LoadINI(default_contents, false); - } - LOG_ERROR(Config, "Failed."); - return false; - } - LOG_INFO(Config, "Successfully loaded {}", config_loc_str); - return true; -} - -static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = { - SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, - SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, - SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, -}; - -static const std::array<int, Settings::NativeMotion::NumMotions> default_motions = { - SDL_SCANCODE_7, - SDL_SCANCODE_8, -}; - -static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ - { - SDL_SCANCODE_UP, - SDL_SCANCODE_DOWN, - SDL_SCANCODE_LEFT, - SDL_SCANCODE_RIGHT, - SDL_SCANCODE_D, - }, - { - SDL_SCANCODE_I, - SDL_SCANCODE_K, - SDL_SCANCODE_J, - SDL_SCANCODE_L, - SDL_SCANCODE_D, - }, -}}; - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { - std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); - if (setting_value.empty()) { - setting_value = setting.GetDefault(); - } - setting = std::move(setting_value); -} - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { - setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); -} - -template <typename Type, bool ranged> -void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { - setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), - static_cast<long>(setting.GetDefault()))); -} - -void Config::ReadCategory(Settings::Category category) { - for (const auto setting : Settings::values.linkage.by_category[category]) { - const char* category_name = [&]() { - if (category == Settings::Category::Controls) { - // For compatibility with older configs - return "ControlsGeneral"; - } else { - return Settings::TranslateCategory(category); - } - }(); - std::string setting_value = - sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString()); - setting->LoadString(setting_value); - } -} - -void Config::ReadValues() { - // Controls - ReadCategory(Settings::Category::Controls); - - for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { - auto& player = Settings::values.players.GetValue()[p]; - - const auto group = fmt::format("ControlsP{}", p); - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - player.buttons[i] = - sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); - if (player.buttons[i].empty()) { - player.buttons[i] = default_param; - } - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); - player.analogs[i] = - sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); - if (player.analogs[i].empty()) { - player.analogs[i] = default_param; - } - } - - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = - sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param); - if (player_motions.empty()) { - player_motions = default_param; - } - } - - player.connected = sdl2_config->GetBoolean(group, "connected", false); - } - - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.debug_pad_buttons[i] = sdl2_config->Get( - "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i], - default_param); - if (Settings::values.debug_pad_buttons[i].empty()) - Settings::values.debug_pad_buttons[i] = default_param; - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.debug_pad_analogs[i] = sdl2_config->Get( - "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i], - default_param); - if (Settings::values.debug_pad_analogs[i].empty()) - Settings::values.debug_pad_analogs[i] = default_param; - } - - Settings::values.touchscreen.enabled = - sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); - Settings::values.touchscreen.rotation_angle = - sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); - Settings::values.touchscreen.diameter_x = - sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); - Settings::values.touchscreen.diameter_y = - sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); - - int num_touch_from_button_maps = - sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); - if (num_touch_from_button_maps > 0) { - for (int i = 0; i < num_touch_from_button_maps; ++i) { - Settings::TouchFromButtonMap map; - map.name = sdl2_config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_name"), - "default"); - const int num_touch_maps = sdl2_config->GetInteger( - "ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), - 0); - map.buttons.reserve(num_touch_maps); - - for (int j = 0; j < num_touch_maps; ++j) { - std::string touch_mapping = - sdl2_config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_bind_") + std::to_string(j), - ""); - map.buttons.emplace_back(std::move(touch_mapping)); - } - - Settings::values.touch_from_button_maps.emplace_back(std::move(map)); - } - } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); - num_touch_from_button_maps = 1; - } - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); - - ReadCategory(Settings::Category::Audio); - ReadCategory(Settings::Category::Core); - ReadCategory(Settings::Category::Cpu); - ReadCategory(Settings::Category::CpuDebug); - ReadCategory(Settings::Category::CpuUnsafe); - ReadCategory(Settings::Category::Renderer); - ReadCategory(Settings::Category::RendererAdvanced); - ReadCategory(Settings::Category::RendererDebug); - ReadCategory(Settings::Category::System); - ReadCategory(Settings::Category::SystemAudio); - ReadCategory(Settings::Category::DataStorage); - ReadCategory(Settings::Category::Debugging); - ReadCategory(Settings::Category::DebuggingGraphics); - ReadCategory(Settings::Category::Miscellaneous); - ReadCategory(Settings::Category::Network); - ReadCategory(Settings::Category::WebService); - - // Data Storage - FS::SetYuzuPath(FS::YuzuPath::NANDDir, - sdl2_config->Get("Data Storage", "nand_directory", - FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); - FS::SetYuzuPath(FS::YuzuPath::SDMCDir, - sdl2_config->Get("Data Storage", "sdmc_directory", - FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); - FS::SetYuzuPath(FS::YuzuPath::LoadDir, - sdl2_config->Get("Data Storage", "load_directory", - FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); - FS::SetYuzuPath(FS::YuzuPath::DumpDir, - sdl2_config->Get("Data Storage", "dump_directory", - FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - - // Debugging - Settings::values.record_frame_times = - sdl2_config->GetBoolean("Debugging", "record_frame_times", false); - - const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); - std::stringstream ss(title_list); - std::string line; - while (std::getline(ss, line, '|')) { - const auto title_id = std::strtoul(line.c_str(), nullptr, 16); - const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); - - std::stringstream inner_ss(disabled_list); - std::string inner_line; - std::vector<std::string> out; - while (std::getline(inner_ss, inner_line, '|')) { - out.push_back(inner_line); - } - - Settings::values.disabled_addons.insert_or_assign(title_id, out); - } -} - -void Config::Reload() { - LoadINI(DefaultINI::sdl2_config_file); - ReadValues(); -} diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h deleted file mode 100644 index 512591a39..000000000 --- a/src/yuzu_cmd/config.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <filesystem> -#include <memory> -#include <optional> -#include <string> - -#include "common/settings.h" - -class INIReader; - -class Config { - std::filesystem::path sdl2_config_loc; - std::unique_ptr<INIReader> sdl2_config; - - bool LoadINI(const std::string& default_contents = "", bool retry = true); - void ReadValues(); - -public: - explicit Config(std::optional<std::filesystem::path> config_path); - ~Config(); - - void Reload(); - -private: - /** - * Applies a value read from the sdl2_config to a Setting. - * - * @param group The name of the INI group - * @param setting The yuzu setting to modify - */ - template <typename Type, bool ranged> - void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); - void ReadCategory(Settings::Category category); -}; diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h deleted file mode 100644 index 119e22183..000000000 --- a/src/yuzu_cmd/default_ini.h +++ /dev/null @@ -1,553 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace DefaultINI { - -const char* sdl2_config_file = - R"( -[ControlsP0] -# The input devices and parameters for each Switch native input -# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... -# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." -# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values - -# Indicates if this player should be connected at boot -# 0 (default): Disabled, 1: Enabled -connected= - -# for button input, the following devices are available: -# - "keyboard" (default) for keyboard input. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "button"(optional): the index of the button to bind -# - "hat"(optional): the index of the hat to bind as direction buttons -# - "axis"(optional): the index of the axis to bind -# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is -# triggered if the axis value crosses -# - "direction"(only used for axis): "+" means the button is triggered when the axis value -# is greater than the threshold; "-" means the button is triggered when the axis value -# is smaller than the threshold -button_a= -button_b= -button_x= -button_y= -button_lstick= -button_rstick= -button_l= -button_r= -button_zl= -button_zr= -button_plus= -button_minus= -button_dleft= -button_dup= -button_dright= -button_ddown= -button_lstick_left= -button_lstick_up= -button_lstick_right= -button_lstick_down= -button_sl= -button_sr= -button_home= -button_screenshot= - -# for analog input, the following devices are available: -# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: -# - "up", "down", "left", "right": sub-devices for each direction. -# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" -# - "modifier": sub-devices as a modifier. -# - "modifier_scale": a float number representing the applied modifier scale to the analog input. -# Must be in range of 0.0-1.0. Defaults to 0.5 -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "axis_x": the index of the axis to bind as x-axis (default to 0) -# - "axis_y": the index of the axis to bind as y-axis (default to 1) -lstick= -rstick= - -# for motion input, the following devices are available: -# - "keyboard" (default) for emulating random motion input from buttons. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for motion input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "motion": the index of the motion sensor to bind -# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: -# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" -# - "port": the port of the cemu hook server -# - "pad": the index of the joystick -# - "motion": the index of the motion sensor of the joystick to bind -motionleft= -motionright= - -[ControlsGeneral] -# To use the debug_pad, prepend `debug_pad_` before each button setting above. -# i.e. debug_pad_button_a= - -# Enable debug pad inputs to the guest -# 0 (default): Disabled, 1: Enabled -debug_pad_enabled = - -# Enable sdl raw input. Allows to configure up to 8 xinput controllers. -# 0 (default): Disabled, 1: Enabled -enable_raw_input = - -# Enable yuzu joycon driver instead of SDL drive. -# 0: Disabled, 1 (default): Enabled -enable_joycon_driver = - -# Emulates an analog input from buttons. Allowing to dial any angle. -# 0 (default): Disabled, 1: Enabled -emulate_analog_keyboard = - -# Whether to enable or disable vibration -# 0: Disabled, 1 (default): Enabled -vibration_enabled= - -# Whether to enable or disable accurate vibrations -# 0 (default): Disabled, 1: Enabled -enable_accurate_vibrations= - -# Enables controller motion inputs -# 0: Disabled, 1 (default): Enabled -motion_enabled = - -# Defines the udp device's touch screen coordinate system for cemuhookudp devices -# - "min_x", "min_y", "max_x", "max_y" -touch_device= - -# for mapping buttons to touch inputs. -#touch_from_button_map=1 -#touch_from_button_maps_0_name=default -#touch_from_button_maps_0_count=2 -#touch_from_button_maps_0_bind_0=foo -#touch_from_button_maps_0_bind_1=bar -# etc. - -# List of Cemuhook UDP servers, delimited by ','. -# Default: 127.0.0.1:26760 -# Example: 127.0.0.1:26760,123.4.5.67:26761 -udp_input_servers = - -# Enable controlling an axis via a mouse input. -# 0 (default): Off, 1: On -mouse_panning = - -# Set mouse panning horizontal sensitivity. -# Default: 50.0 -mouse_panning_x_sensitivity = - -# Set mouse panning vertical sensitivity. -# Default: 50.0 -mouse_panning_y_sensitivity = - -# Set mouse panning deadzone horizontal counterweight. -# Default: 0.0 -mouse_panning_deadzone_x_counterweight = - -# Set mouse panning deadzone vertical counterweight. -# Default: 0.0 -mouse_panning_deadzone_y_counterweight = - -# Set mouse panning stick decay strength. -# Default: 22.0 -mouse_panning_decay_strength = - -# Set mouse panning stick minimum decay. -# Default: 5.0 -mouse_panning_minimum_decay = - -# Emulate an analog control stick from keyboard inputs. -# 0 (default): Disabled, 1: Enabled -emulate_analog_keyboard = - -# Enable mouse inputs to the guest -# 0 (default): Disabled, 1: Enabled -mouse_enabled = - -# Enable keyboard inputs to the guest -# 0 (default): Disabled, 1: Enabled -keyboard_enabled = - -)" - R"( -[Core] -# Whether to use multi-core for CPU emulation -# 0: Disabled, 1 (default): Enabled -use_multi_core = - -# Enable unsafe extended guest system memory layout (8GB DRAM) -# 0 (default): Disabled, 1: Enabled -use_unsafe_extended_memory_layout = - -[Cpu] -# Adjusts various optimizations. -# Auto-select mode enables choice unsafe optimizations. -# Accurate enables only safe optimizations. -# Unsafe allows any unsafe optimizations. -# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations -cpu_accuracy = - -# Allow disabling safe optimizations. -# 0 (default): Disabled, 1: Enabled -cpu_debug_mode = - -# Enable inline page tables optimization (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_page_tables = - -# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) -# 0: Disabled, 1 (default): Enabled -cpuopt_block_linking = - -# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) -# 0: Disabled, 1 (default): Enabled -cpuopt_return_stack_buffer = - -# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) -# 0: Disabled, 1 (default): Enabled -cpuopt_fast_dispatcher = - -# Enable context elimination CPU Optimization (reduce host memory use for guest context) -# 0: Disabled, 1 (default): Enabled -cpuopt_context_elimination = - -# Enable constant propagation CPU optimization (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_const_prop = - -# Enable miscellaneous CPU optimizations (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_misc_ir = - -# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) -# 0: Disabled, 1 (default): Enabled -cpuopt_reduce_misalign_checks = - -# Enable Host MMU Emulation (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem = - -# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem_exclusives = - -# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_recompile_exclusives = - -# Enable optimization to ignore invalid memory accesses (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_ignore_memory_aborts = - -# Enable unfuse FMA (improve performance on CPUs without FMA) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_unfuse_fma = - -# Enable faster FRSQRTE and FRECPE -# Only enabled if cpu_accuracy is set to Unsafe. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_reduce_fp_error = - -# Enable faster ASIMD instructions (32 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_standard_fpcr = - -# Enable inaccurate NaN handling -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_inaccurate_nan = - -# Disable address space checks (64 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_fastmem_check = - -# Enable faster exclusive instructions -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_global_monitor = - -)" - R"( -[Renderer] -# Which backend API to use. -# 0: OpenGL, 1 (default): Vulkan -backend = - -# Whether to enable asynchronous presentation (Vulkan only) -# 0 (default): Off, 1: On -async_presentation = - -# Enable graphics API debugging mode. -# 0 (default): Disabled, 1: Enabled -debug = - -# Enable shader feedback. -# 0 (default): Disabled, 1: Enabled -renderer_shader_feedback = - -# Enable Nsight Aftermath crash dumps -# 0 (default): Disabled, 1: Enabled -nsight_aftermath = - -# Disable shader loop safety checks, executing the shader without loop logic changes -# 0 (default): Disabled, 1: Enabled -disable_shader_loop_safety_checks = - -# Which Vulkan physical device to use (defaults to 0) -vulkan_device = - -# 0: 0.5x (360p/540p) [EXPERIMENTAL] -# 1: 0.75x (540p/810p) [EXPERIMENTAL] -# 2 (default): 1x (720p/1080p) -# 3: 1.5x (1080p/1620p) [EXPERIMENTAL] -# 4: 2x (1440p/2160p) -# 5: 3x (2160p/3240p) -# 6: 4x (2880p/4320p) -# 7: 5x (3600p/5400p) -# 8: 6x (4320p/6480p) -# 9: 7x (5040p/7560p) -# 10: 8x (5760/8640p) -resolution_setup = - -# Pixel filter to use when up- or down-sampling rendered frames. -# 0: Nearest Neighbor -# 1 (default): Bilinear -# 2: Bicubic -# 3: Gaussian -# 4: ScaleForce -# 5: AMD FidelityFX™️ Super Resolution -scaling_filter = - -# Anti-Aliasing (AA) -# 0 (default): None, 1: FXAA, 2: SMAA -anti_aliasing = - -# Whether to use fullscreen or borderless window mode -# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen -fullscreen_mode = - -# Aspect ratio -# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window -aspect_ratio = - -# Anisotropic filtering -# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x -max_anisotropy = - -# Whether to enable VSync or not. -# OpenGL: Values other than 0 enable VSync -# Vulkan: FIFO is selected if the requested mode is not supported by the driver. -# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. -# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. -# Mailbox can have lower latency than FIFO and does not tear but may drop frames. -# Immediate (no synchronization) just presents whatever is available and can exhibit tearing. -# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed -use_vsync = - -# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is -# not available and GLASM is selected, GLSL will be used. -# 0: GLSL, 1 (default): GLASM, 2: SPIR-V -shader_backend = - -# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. -# 0: Off, 1 (default): On -use_reactive_flushing = - -# Whether to allow asynchronous shader building. -# 0 (default): Off, 1: On -use_asynchronous_shaders = - -# NVDEC emulation. -# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding -nvdec_emulation = - -# Accelerate ASTC texture decoding. -# 0: Off, 1 (default): On -accelerate_astc = - -# Decode ASTC textures asynchronously. -# 0 (default): Off, 1: On -async_astc = - -# Recompress ASTC textures to a different format. -# 0 (default): Uncompressed, 1: BC1 (Low quality), 2: BC3: (Medium quality) -async_astc = - -# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value -# 0: Off, 1: On (default) -use_speed_limit = - -# Limits the speed of the game to run no faster than this value as a percentage of target speed -# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) -speed_limit = - -# Whether to use disk based shader cache -# 0: Off, 1 (default): On -use_disk_shader_cache = - -# Which gpu accuracy level to use -# 0: Normal, 1 (default): High, 2: Extreme (Very slow) -gpu_accuracy = - -# Whether to use asynchronous GPU emulation -# 0 : Off (slow), 1 (default): On (fast) -use_asynchronous_gpu_emulation = - -# Inform the guest that GPU operations completed more quickly than they did. -# 0: Off, 1 (default): On -use_fast_gpu_time = - -# Whether to use garbage collection or not for GPU caches. -# 0 (default): Off, 1: On -use_caches_gc = - -# The clear color for the renderer. What shows up on the sides of the bottom screen. -# Must be in range of 0-255. Defaults to 0 for all. -bg_red = -bg_blue = -bg_green = - -)" - R"( -[Audio] -# Which audio output engine to use. -# auto (default): Auto-select -# cubeb: Cubeb audio engine (if available) -# sdl2: SDL2 audio engine (if available) -# null: No audio output -output_engine = - -# Which audio device to use. -# auto (default): Auto-select -output_device = - -# Output volume. -# 100 (default): 100%, 0; mute -volume = - -[Data Storage] -# Whether to create a virtual SD card. -# 1 (default): Yes, 0: No -use_virtual_sd = - -# Whether or not to enable gamecard emulation -# 1: Yes, 0 (default): No -gamecard_inserted = - -# Whether or not the gamecard should be emulated as the current game -# If 'gamecard_inserted' is 0 this setting is irrelevant -# 1: Yes, 0 (default): No -gamecard_current_game = - -# Path to an XCI file to use as the gamecard -# If 'gamecard_inserted' is 0 this setting is irrelevant -# If 'gamecard_current_game' is 1 this setting is irrelevant -gamecard_path = - -[System] -# Whether the system is docked -# 1 (default): Yes, 0: No -use_docked_mode = - -# Sets the seed for the RNG generator built into the switch -# rng_seed will be ignored and randomly generated if rng_seed_enabled is false -rng_seed_enabled = -rng_seed = - -# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service -# This will auto-increment, with the time set being the time the game is started -# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used -custom_rtc_enabled = -custom_rtc = - -# Sets the systems language index -# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, -# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, -# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese -language_index = - -# The system region that yuzu will use during emulation -# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan -region_index = - -# The system time zone that yuzu will use during emulation -# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone -time_zone_index = - -# Sets the sound output mode. -# 0: Mono, 1 (default): Stereo, 2: Surround -sound_index = - -[Miscellaneous] -# A filter which removes logs below a certain logging level. -# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical -log_filter = *:Trace - -# Use developer keys -# 0 (default): Disabled, 1: Enabled -use_dev_keys = - -[Debugging] -# Record frame time data, can be found in the log directory. Boolean value -record_frame_times = -# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them -dump_exefs=false -# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them -dump_nso=false -# Determines whether or not yuzu will save the filesystem access log. -enable_fs_access_log=false -# Enables verbose reporting services -reporting_services = -# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode -# false: Retail/Normal Mode (default), true: Kiosk Mode -quest_flag = -# Determines whether debug asserts should be enabled, which will throw an exception on asserts. -# false: Disabled (default), true: Enabled -use_debug_asserts = -# Determines whether unimplemented HLE service calls should be automatically stubbed. -# false: Disabled (default), true: Enabled -use_auto_stub = -# Enables/Disables the macro JIT compiler -disable_macro_jit=false -# Determines whether to enable the GDB stub and wait for the debugger to attach before running. -# false: Disabled (default), true: Enabled -use_gdbstub=false -# The port to use for the GDB server, if it is enabled. -gdbstub_port=6543 - -[WebService] -# Whether or not to enable telemetry -# 0: No, 1 (default): Yes -enable_telemetry = -# URL for Web API -web_api_url = https://api.yuzu-emu.org -# Username and token for yuzu Web Service -# See https://profile.yuzu-emu.org/ for more info -yuzu_username = -yuzu_token = - -[Network] -# Name of the network interface device to use with yuzu LAN play. -# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' -# e.g. On Windows: 'Ethernet', 'Wi-Fi' -network_interface = - -[AddOns] -# Used to disable add-ons -# List of title IDs of games that will have add-ons disabled (separated by '|'): -title_ids = -# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') -# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey -)"; -} // namespace DefaultINI diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp new file mode 100644 index 000000000..39fd8050c --- /dev/null +++ b/src/yuzu_cmd/sdl_config.cpp @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h +#define SDL_MAIN_HANDLED +#include <SDL.h> + +#include "input_common/main.h" +#include "sdl_config.h" + +const std::array<int, Settings::NativeButton::NumButtons> SdlConfig::default_buttons = { + SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, + SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, + SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, +}; + +const std::array<int, Settings::NativeMotion::NumMotions> SdlConfig::default_motions = { + SDL_SCANCODE_7, + SDL_SCANCODE_8, +}; + +const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{ + { + { + SDL_SCANCODE_UP, + SDL_SCANCODE_DOWN, + SDL_SCANCODE_LEFT, + SDL_SCANCODE_RIGHT, + }, + { + SDL_SCANCODE_I, + SDL_SCANCODE_K, + SDL_SCANCODE_J, + SDL_SCANCODE_L, + }, + }}; + +const std::array<int, 2> SdlConfig::default_stick_mod = { + SDL_SCANCODE_D, + 0, +}; + +const std::array<int, 2> SdlConfig::default_ringcon_analogs{{ + 0, + 0, +}}; + +SdlConfig::SdlConfig(const std::optional<std::string> config_path) { + Initialize(config_path); + ReadSdlValues(); + SaveSdlValues(); +} + +SdlConfig::~SdlConfig() { + if (global) { + SdlConfig::SaveAllValues(); + } +} + +void SdlConfig::ReloadAllValues() { + Reload(); + ReadSdlValues(); + SaveSdlValues(); +} + +void SdlConfig::SaveAllValues() { + Save(); + SaveSdlValues(); +} + +void SdlConfig::ReadSdlValues() { + ReadSdlControlValues(); +} + +void SdlConfig::ReadSdlControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadSdlPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + ReadDebugControlValues(); + ReadHidbusValues(); + + EndGroup(); +} + +void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); + if (player_buttons.empty()) { + player_buttons = default_param; + } + } + + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; + + player_analogs = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); + if (player_analogs.empty()) { + player_analogs = default_param; + } + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); + if (player_motions.empty()) { + player_motions = default_param; + } + } +} + +void SdlConfig::ReadDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; + debug_pad_buttons = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); + if (debug_pad_buttons.empty()) { + debug_pad_buttons = default_param; + } + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; + debug_pad_analogs = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); + if (debug_pad_analogs.empty()) { + debug_pad_analogs = default_param; + } + } +} + +void SdlConfig::ReadHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + auto& ringcon_analogs = Settings::values.ringcon_analogs; + + ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); + if (ringcon_analogs.empty()) { + ringcon_analogs = default_param; + } +} + +void SdlConfig::SaveSdlValues() { + SaveSdlControlValues(); + + WriteToIni(); +} + +void SdlConfig::SaveSdlControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SaveSdlPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + SaveDebugControlValues(); + SaveHidbusValues(); + + EndGroup(); +} + +void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig() && player.profile_name.empty()) { + // No custom profile selected + return; + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), + player.buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), + player.analogs[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), + player.motions[i], std::make_optional(default_param)); + } +} + +void SdlConfig::SaveDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), + Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), + Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); + } +} + +void SdlConfig::SaveHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, + std::make_optional(default_param)); +} + +std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) { + return Settings::values.linkage.by_category[category]; +} diff --git a/src/yuzu_cmd/sdl_config.h b/src/yuzu_cmd/sdl_config.h new file mode 100644 index 000000000..1fd1c692d --- /dev/null +++ b/src/yuzu_cmd/sdl_config.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "frontend_common/config.h" + +class SdlConfig final : public Config { +public: + explicit SdlConfig(std::optional<std::string> config_path); + ~SdlConfig() override; + + void ReloadAllValues() override; + void SaveAllValues() override; + +protected: + void ReadSdlValues(); + void ReadSdlPlayerValues(std::size_t player_index); + void ReadSdlControlValues(); + void ReadHidbusValues() override; + void ReadDebugControlValues() override; + void ReadPathValues() override {} + void ReadShortcutValues() override {} + void ReadUIValues() override {} + void ReadUIGamelistValues() override {} + void ReadUILayoutValues() override {} + void ReadMultiplayerValues() override {} + + void SaveSdlValues(); + void SaveSdlPlayerValues(std::size_t player_index); + void SaveSdlControlValues(); + void SaveHidbusValues() override; + void SaveDebugControlValues() override; + void SavePathValues() override {} + void SaveShortcutValues() override {} + void SaveUIValues() override {} + void SaveUIGamelistValues() override {} + void SaveUILayoutValues() override {} + void SaveMultiplayerValues() override {} + + std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override; + +public: + static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; + static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; + static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const std::array<int, 2> default_stick_mod; + static const std::array<int, 2> default_ringcon_analogs; +}; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 087cfaa26..0416d5951 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -29,10 +29,11 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "core/telemetry_session.h" +#include "frontend_common/config.h" #include "input_common/main.h" #include "network/network.h" +#include "sdl_config.h" #include "video_core/renderer_base.h" -#include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" @@ -300,7 +301,7 @@ int main(int argc, char** argv) { } } - Config config{config_path}; + SdlConfig config{config_path}; // apply the log_filter setting // the logger was initialized before and doesn't pick up the filter on its own |