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 9b08f008d17fbc24dfc2fef011557a783a6d7139..93c8ce92252a857294e143f4634716f8c0e46fd3 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
@@ -49,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
 import org.yuzu.yuzu_emu.utils.InputHandler
 import org.yuzu.yuzu_emu.utils.Log
 import org.yuzu.yuzu_emu.utils.MemoryUtil
+import org.yuzu.yuzu_emu.utils.NativeConfig
 import org.yuzu.yuzu_emu.utils.NfcReader
 import org.yuzu.yuzu_emu.utils.ThemeHelper
 import java.text.NumberFormat
@@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
         stopMotionSensorListener()
     }
 
+    override fun onStop() {
+        super.onStop()
+        NativeConfig.saveGlobalConfig()
+    }
+
     override fun onUserLeaveHint() {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
             if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 110d15f1c51ea16de32c47d6443261251bf0ba93..86bd3367264bfed98d5fb0eb281d128a180bbe3f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -19,7 +19,13 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
     RENDERER_DEBUG("debug"),
     PICTURE_IN_PICTURE("picture_in_picture"),
     USE_CUSTOM_RTC("custom_rtc_enabled"),
-    BLACK_BACKGROUNDS("black_backgrounds");
+    BLACK_BACKGROUNDS("black_backgrounds"),
+    JOYSTICK_REL_CENTER("joystick_rel_center"),
+    DPAD_SLIDE("dpad_slide"),
+    HAPTIC_FEEDBACK("haptic_feedback"),
+    SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
+    SHOW_INPUT_OVERLAY("show_input_overlay"),
+    TOUCHSCREEN("touchscreen");
 
     override fun getBoolean(needsGlobal: Boolean): Boolean =
         NativeConfig.getBoolean(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index b0193d83e9a59a4a6fb524d8d3f9f7d04dea9dce..16fb87614fcffe0709303485ddb2f90ef010ebee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -21,7 +21,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
     AUDIO_OUTPUT_ENGINE("output_engine"),
     MAX_ANISOTROPY("max_anisotropy"),
     THEME("theme"),
-    THEME_MODE("theme_mode");
+    THEME_MODE("theme_mode"),
+    OVERLAY_SCALE("control_scale"),
+    OVERLAY_OPACITY("control_opacity");
 
     override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
 
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 360bdaf3ef9830934f980abf0ecc1290ac43df73..43caac989a01f318aaaf3d7c3b29b8692180d3d5 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
@@ -15,18 +15,10 @@ object Settings {
         SECTION_DEBUG(R.string.preferences_debug);
     }
 
+    const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
     const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
 
-    const val PREF_OVERLAY_VERSION = "OverlayVersion"
-    const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
-    const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
-    const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
-    val overlayLayoutPrefs = listOf(
-        PREF_LANDSCAPE_OVERLAY_VERSION,
-        PREF_PORTRAIT_OVERLAY_VERSION,
-        PREF_FOLDABLE_OVERLAY_VERSION
-    )
-
+    // Deprecated input overlay preference keys
     const val PREF_CONTROL_SCALE = "controlScale"
     const val PREF_CONTROL_OPACITY = "controlOpacity"
     const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@@ -47,24 +39,12 @@ object Settings {
     const val PREF_BUTTON_STICK_R = "buttonToggle14"
     const val PREF_BUTTON_HOME = "buttonToggle15"
     const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
-
     const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
     const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
     const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
     const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
     const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
-
-    // Deprecated theme preference keys
-    const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
-    const val PREF_THEME = "Theme"
-    const val PREF_THEME_MODE = "ThemeMode"
-    const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
-
     val overlayPreferences = listOf(
-        PREF_OVERLAY_VERSION,
-        PREF_CONTROL_SCALE,
-        PREF_CONTROL_OPACITY,
-        PREF_TOUCH_ENABLED,
         PREF_BUTTON_A,
         PREF_BUTTON_B,
         PREF_BUTTON_X,
@@ -84,6 +64,21 @@ object Settings {
         PREF_BUTTON_STICK_R
     )
 
+    // Deprecated layout preference keys
+    const val PREF_LANDSCAPE_SUFFIX = "_Landscape"
+    const val PREF_PORTRAIT_SUFFIX = "_Portrait"
+    const val PREF_FOLDABLE_SUFFIX = "_Foldable"
+    val overlayLayoutSuffixes = listOf(
+        PREF_LANDSCAPE_SUFFIX,
+        PREF_PORTRAIT_SUFFIX,
+        PREF_FOLDABLE_SUFFIX
+    )
+
+    // Deprecated theme preference keys
+    const val PREF_THEME = "Theme"
+    const val PREF_THEME_MODE = "ThemeMode"
+    const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
+
     const val LayoutOption_Unspecified = 0
     const val LayoutOption_MobilePortrait = 4
     const val LayoutOption_MobileLandscape = 5
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index d7b38f62d01bd5a911ae9095b3903d6d2b899c29..6e5dd1dbaee974e2733595465cde15b5e434bc5a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -7,7 +7,6 @@ import android.annotation.SuppressLint
 import android.app.AlertDialog
 import android.content.Context
 import android.content.DialogInterface
-import android.content.SharedPreferences
 import android.content.pm.ActivityInfo
 import android.content.res.Configuration
 import android.net.Uri
@@ -33,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
-import androidx.preference.PreferenceManager
 import androidx.window.layout.FoldingFeature
 import androidx.window.layout.WindowInfoTracker
 import androidx.window.layout.WindowLayoutInfo
@@ -46,22 +44,22 @@ import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.HomeNavigationDirections
 import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.activities.EmulationActivity
 import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
 import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
 import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 import org.yuzu.yuzu_emu.features.settings.model.Settings
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.model.DriverViewModel
 import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.EmulationViewModel
-import org.yuzu.yuzu_emu.overlay.InputOverlay
+import org.yuzu.yuzu_emu.overlay.model.OverlayControl
+import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
 import org.yuzu.yuzu_emu.utils.*
 import java.lang.NullPointerException
 
 class EmulationFragment : Fragment(), SurfaceHolder.Callback {
-    private lateinit var preferences: SharedPreferences
     private lateinit var emulationState: EmulationState
     private var emulationActivity: EmulationActivity? = null
     private var perfStatsUpdater: (() -> Unit)? = null
@@ -141,7 +139,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 
         // So this fragment doesn't restart on configuration changes; i.e. rotation.
         retainInstance = true
-        preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
         emulationState = EmulationState(game.path)
     }
 
@@ -382,24 +379,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
         }
 
         updateScreenLayout()
+        val showInputOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
         if (emulationActivity?.isInPictureInPictureMode == true) {
             if (binding.drawerLayout.isOpen) {
                 binding.drawerLayout.close()
             }
-            if (EmulationMenuSettings.showOverlay) {
+            if (showInputOverlay) {
                 binding.surfaceInputOverlay.visibility = View.INVISIBLE
             }
         } else {
-            if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
+            if (showInputOverlay && emulationViewModel.emulationStarted.value) {
                 binding.surfaceInputOverlay.visibility = View.VISIBLE
             } else {
                 binding.surfaceInputOverlay.visibility = View.INVISIBLE
             }
             if (!isInFoldableLayout) {
                 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
-                    binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
+                    binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
                 } else {
-                    binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
+                    binding.surfaceInputOverlay.layout = OverlayLayout.Landscape
                 }
             }
         }
@@ -423,17 +421,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
     }
 
     private fun resetInputOverlay() {
-        preferences.edit()
-            .remove(Settings.PREF_CONTROL_SCALE)
-            .remove(Settings.PREF_CONTROL_OPACITY)
-            .apply()
+        IntSetting.OVERLAY_SCALE.reset()
+        IntSetting.OVERLAY_OPACITY.reset()
         binding.surfaceInputOverlay.post {
             binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
         }
     }
 
     private fun updateShowFpsOverlay() {
-        if (EmulationMenuSettings.showFps) {
+        if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
             val SYSTEM_FPS = 0
             val FPS = 1
             val FRAMETIME = 2
@@ -496,7 +492,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                         binding.inGameMenu.layoutParams.height = it.bounds.bottom
 
                         isInFoldableLayout = true
-                        binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
+                        binding.surfaceInputOverlay.layout = OverlayLayout.Foldable
                     }
                 }
                 it.isSeparating
@@ -535,18 +531,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
         popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
 
         popup.menu.apply {
-            findItem(R.id.menu_toggle_fps).isChecked = EmulationMenuSettings.showFps
-            findItem(R.id.menu_rel_stick_center).isChecked = EmulationMenuSettings.joystickRelCenter
-            findItem(R.id.menu_dpad_slide).isChecked = EmulationMenuSettings.dpadSlide
-            findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
-            findItem(R.id.menu_haptics).isChecked = EmulationMenuSettings.hapticFeedback
+            findItem(R.id.menu_toggle_fps).isChecked =
+                BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
+            findItem(R.id.menu_rel_stick_center).isChecked =
+                BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
+            findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
+            findItem(R.id.menu_show_overlay).isChecked =
+                BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
+            findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
         }
 
         popup.setOnMenuItemClickListener {
             when (it.itemId) {
                 R.id.menu_toggle_fps -> {
                     it.isChecked = !it.isChecked
-                    EmulationMenuSettings.showFps = it.isChecked
+                    BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
                     updateShowFpsOverlay()
                     true
                 }
@@ -564,11 +563,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                 }
 
                 R.id.menu_toggle_controls -> {
-                    val preferences =
-                        PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-                    val optionsArray = BooleanArray(Settings.overlayPreferences.size)
-                    Settings.overlayPreferences.forEachIndexed { i, _ ->
-                        optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
+                    val overlayControlData = NativeConfig.getOverlayControlData()
+                    val optionsArray = BooleanArray(overlayControlData.size)
+                    overlayControlData.forEachIndexed { i, _ ->
+                        optionsArray[i] = overlayControlData.firstOrNull { data ->
+                            OverlayControl.entries[i].id == data.id
+                        }?.enabled == true
                     }
 
                     val dialog = MaterialAlertDialogBuilder(requireContext())
@@ -577,11 +577,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                             R.array.gamepadButtons,
                             optionsArray
                         ) { _, indexSelected, isChecked ->
-                            preferences.edit()
-                                .putBoolean("buttonToggle$indexSelected", isChecked)
-                                .apply()
+                            overlayControlData.firstOrNull { data ->
+                                OverlayControl.entries[indexSelected].id == data.id
+                            }?.enabled = isChecked
                         }
                         .setPositiveButton(android.R.string.ok) { _, _ ->
+                            NativeConfig.setOverlayControlData(overlayControlData)
+                            NativeConfig.saveGlobalConfig()
                             binding.surfaceInputOverlay.refreshControls()
                         }
                         .setNegativeButton(android.R.string.cancel, null)
@@ -592,12 +594,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                     dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
                         .setOnClickListener {
                             val isChecked = !optionsArray[0]
-                            Settings.overlayPreferences.forEachIndexed { i, _ ->
+                            overlayControlData.forEachIndexed { i, _ ->
                                 optionsArray[i] = isChecked
                                 dialog.listView.setItemChecked(i, isChecked)
-                                preferences.edit()
-                                    .putBoolean("buttonToggle$i", isChecked)
-                                    .apply()
+                                overlayControlData[i].enabled = isChecked
                             }
                         }
                     true
@@ -605,26 +605,26 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 
                 R.id.menu_show_overlay -> {
                     it.isChecked = !it.isChecked
-                    EmulationMenuSettings.showOverlay = it.isChecked
+                    BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
                     binding.surfaceInputOverlay.refreshControls()
                     true
                 }
 
                 R.id.menu_rel_stick_center -> {
                     it.isChecked = !it.isChecked
-                    EmulationMenuSettings.joystickRelCenter = it.isChecked
+                    BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(it.isChecked)
                     true
                 }
 
                 R.id.menu_dpad_slide -> {
                     it.isChecked = !it.isChecked
-                    EmulationMenuSettings.dpadSlide = it.isChecked
+                    BooleanSetting.DPAD_SLIDE.setBoolean(it.isChecked)
                     true
                 }
 
                 R.id.menu_haptics -> {
                     it.isChecked = !it.isChecked
-                    EmulationMenuSettings.hapticFeedback = it.isChecked
+                    BooleanSetting.HAPTIC_FEEDBACK.setBoolean(it.isChecked)
                     true
                 }
 
@@ -667,6 +667,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
             }
         }
+        NativeConfig.saveGlobalConfig()
     }
 
     @SuppressLint("SetTextI18n")
@@ -675,7 +676,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
         adjustBinding.apply {
             inputScaleSlider.apply {
                 valueTo = 150F
-                value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
+                value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
                 addOnChangeListener(
                     Slider.OnChangeListener { _, value, _ ->
                         inputScaleValue.text = "${value.toInt()}%"
@@ -685,7 +686,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
             }
             inputOpacitySlider.apply {
                 valueTo = 100F
-                value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
+                value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
                 addOnChangeListener(
                     Slider.OnChangeListener { _, value, _ ->
                         inputOpacityValue.text = "${value.toInt()}%"
@@ -709,16 +710,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
     }
 
     private fun setControlScale(scale: Int) {
-        preferences.edit()
-            .putInt(Settings.PREF_CONTROL_SCALE, scale)
-            .apply()
+        IntSetting.OVERLAY_SCALE.setInt(scale)
         binding.surfaceInputOverlay.refreshControls()
     }
 
     private fun setControlOpacity(opacity: Int) {
-        preferences.edit()
-            .putInt(Settings.PREF_CONTROL_OPACITY, opacity)
-            .apply()
+        IntSetting.OVERLAY_OPACITY.setInt(opacity)
         binding.surfaceInputOverlay.refreshControls()
     }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index a13faf3c78dc7bd04d90e4e3de71459aa4e77034..bb69b8bd54951db420e2b5234831ef0c4ec2cad5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -21,7 +21,6 @@ import android.view.View
 import android.view.View.OnTouchListener
 import android.view.WindowInsets
 import androidx.core.content.ContextCompat
-import androidx.preference.PreferenceManager
 import androidx.window.layout.WindowMetricsCalculator
 import kotlin.math.max
 import kotlin.math.min
@@ -29,9 +28,13 @@ import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
 import org.yuzu.yuzu_emu.NativeLibrary.StickType
 import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
+import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
+import org.yuzu.yuzu_emu.overlay.model.OverlayControl
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
+import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
+import org.yuzu.yuzu_emu.utils.NativeConfig
 
 /**
  * Draws the interactive input overlay on top of the
@@ -51,23 +54,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
 
     private lateinit var windowInsets: WindowInsets
 
-    var layout = LANDSCAPE
+    var layout = OverlayLayout.Landscape
 
     override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
         super.onLayout(changed, left, top, right, bottom)
 
         windowInsets = rootWindowInsets
 
-        val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0)
-        if (overlayVersion != OVERLAY_VERSION) {
-            resetAllLayouts()
+        val overlayControlData = NativeConfig.getOverlayControlData()
+        if (overlayControlData.isEmpty()) {
+            populateDefaultConfig()
         } else {
-            val layoutIndex = overlayLayouts.indexOf(layout)
-            val currentLayoutVersion =
-                preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
-            if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
-                resetCurrentLayout()
-            }
+            checkForNewControls(overlayControlData)
         }
 
         // Load the controls.
@@ -123,7 +121,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
         }
 
         for (dpad in overlayDpads) {
-            if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide)) {
+            if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) {
                 continue
             }
             NativeLibrary.onGamePadButtonEvent(
@@ -174,7 +172,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
             invalidate()
         }
 
-        if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
+        if (!BooleanSetting.TOUCHSCREEN.getBoolean()) {
             return true
         }
 
@@ -211,7 +209,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
     }
 
     private fun playHaptics(event: MotionEvent) {
-        if (EmulationMenuSettings.hapticFeedback) {
+        if (BooleanSetting.HAPTIC_FEEDBACK.getBoolean()) {
             when (event.actionMasked) {
                 MotionEvent.ACTION_DOWN,
                 MotionEvent.ACTION_POINTER_DOWN ->
@@ -255,10 +253,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
                 MotionEvent.ACTION_POINTER_DOWN ->
                     // If no button is being moved now, remember the currently touched button to move.
                     if (buttonBeingConfigured == null &&
-                        button.bounds.contains(
-                                fingerPositionX,
-                                fingerPositionY
-                            )
+                        button.bounds.contains(fingerPositionX, fingerPositionY)
                     ) {
                         buttonBeingConfigured = button
                         buttonBeingConfigured!!.onConfigureTouch(event)
@@ -274,7 +269,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
                 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
                     // Persist button position by saving new place.
                     saveControlPosition(
-                        buttonBeingConfigured!!.prefId,
+                        buttonBeingConfigured!!.overlayControlData.id,
                         buttonBeingConfigured!!.bounds.centerX(),
                         buttonBeingConfigured!!.bounds.centerY(),
                         layout
@@ -321,10 +316,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
             when (event.action) {
                 MotionEvent.ACTION_DOWN,
                 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
-                    joystick.bounds.contains(
-                            fingerPositionX,
-                            fingerPositionY
-                        )
+                    joystick.bounds.contains(fingerPositionX, fingerPositionY)
                 ) {
                     joystickBeingConfigured = joystick
                     joystickBeingConfigured!!.onConfigureTouch(event)
@@ -351,231 +343,257 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
         return true
     }
 
-    private fun addOverlayControls(layout: String) {
+    private fun addOverlayControls(layout: OverlayLayout) {
         val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
-        if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_a,
-                    R.drawable.facebutton_a_depressed,
-                    ButtonType.BUTTON_A,
-                    Settings.PREF_BUTTON_A,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_b,
-                    R.drawable.facebutton_b_depressed,
-                    ButtonType.BUTTON_B,
-                    Settings.PREF_BUTTON_B,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_x,
-                    R.drawable.facebutton_x_depressed,
-                    ButtonType.BUTTON_X,
-                    Settings.PREF_BUTTON_X,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_y,
-                    R.drawable.facebutton_y_depressed,
-                    ButtonType.BUTTON_Y,
-                    Settings.PREF_BUTTON_Y,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.l_shoulder,
-                    R.drawable.l_shoulder_depressed,
-                    ButtonType.TRIGGER_L,
-                    Settings.PREF_BUTTON_L,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.r_shoulder,
-                    R.drawable.r_shoulder_depressed,
-                    ButtonType.TRIGGER_R,
-                    Settings.PREF_BUTTON_R,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.zl_trigger,
-                    R.drawable.zl_trigger_depressed,
-                    ButtonType.TRIGGER_ZL,
-                    Settings.PREF_BUTTON_ZL,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.zr_trigger,
-                    R.drawable.zr_trigger_depressed,
-                    ButtonType.TRIGGER_ZR,
-                    Settings.PREF_BUTTON_ZR,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_plus,
-                    R.drawable.facebutton_plus_depressed,
-                    ButtonType.BUTTON_PLUS,
-                    Settings.PREF_BUTTON_PLUS,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_minus,
-                    R.drawable.facebutton_minus_depressed,
-                    ButtonType.BUTTON_MINUS,
-                    Settings.PREF_BUTTON_MINUS,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) {
-            overlayDpads.add(
-                initializeOverlayDpad(
-                    context,
-                    windowSize,
-                    R.drawable.dpad_standard,
-                    R.drawable.dpad_standard_cardinal_depressed,
-                    R.drawable.dpad_standard_diagonal_depressed,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_STICK_L, true)) {
-            overlayJoysticks.add(
-                initializeOverlayJoystick(
-                    context,
-                    windowSize,
-                    R.drawable.joystick_range,
-                    R.drawable.joystick,
-                    R.drawable.joystick_depressed,
-                    StickType.STICK_L,
-                    ButtonType.STICK_L,
-                    Settings.PREF_STICK_L,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_STICK_R, true)) {
-            overlayJoysticks.add(
-                initializeOverlayJoystick(
-                    context,
-                    windowSize,
-                    R.drawable.joystick_range,
-                    R.drawable.joystick,
-                    R.drawable.joystick_depressed,
-                    StickType.STICK_R,
-                    ButtonType.STICK_R,
-                    Settings.PREF_STICK_R,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_home,
-                    R.drawable.facebutton_home_depressed,
-                    ButtonType.BUTTON_HOME,
-                    Settings.PREF_BUTTON_HOME,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.facebutton_screenshot,
-                    R.drawable.facebutton_screenshot_depressed,
-                    ButtonType.BUTTON_CAPTURE,
-                    Settings.PREF_BUTTON_SCREENSHOT,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.button_l3,
-                    R.drawable.button_l3_depressed,
-                    ButtonType.STICK_L,
-                    Settings.PREF_BUTTON_STICK_L,
-                    layout
-                )
-            )
-        }
-        if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) {
-            overlayButtons.add(
-                initializeOverlayButton(
-                    context,
-                    windowSize,
-                    R.drawable.button_r3,
-                    R.drawable.button_r3_depressed,
-                    ButtonType.STICK_R,
-                    Settings.PREF_BUTTON_STICK_R,
-                    layout
-                )
-            )
+        val overlayControlData = NativeConfig.getOverlayControlData()
+        for (data in overlayControlData) {
+            if (!data.enabled) {
+                continue
+            }
+
+            val position = data.positionFromLayout(layout)
+            when (data.id) {
+                OverlayControl.BUTTON_A.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_a,
+                            R.drawable.facebutton_a_depressed,
+                            ButtonType.BUTTON_A,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_B.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_b,
+                            R.drawable.facebutton_b_depressed,
+                            ButtonType.BUTTON_B,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_X.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_x,
+                            R.drawable.facebutton_x_depressed,
+                            ButtonType.BUTTON_X,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_Y.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_y,
+                            R.drawable.facebutton_y_depressed,
+                            ButtonType.BUTTON_Y,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_PLUS.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_plus,
+                            R.drawable.facebutton_plus_depressed,
+                            ButtonType.BUTTON_PLUS,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_MINUS.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_minus,
+                            R.drawable.facebutton_minus_depressed,
+                            ButtonType.BUTTON_MINUS,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_HOME.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_home,
+                            R.drawable.facebutton_home_depressed,
+                            ButtonType.BUTTON_HOME,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_CAPTURE.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.facebutton_screenshot,
+                            R.drawable.facebutton_screenshot_depressed,
+                            ButtonType.BUTTON_CAPTURE,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_L.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.l_shoulder,
+                            R.drawable.l_shoulder_depressed,
+                            ButtonType.TRIGGER_L,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_R.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.r_shoulder,
+                            R.drawable.r_shoulder_depressed,
+                            ButtonType.TRIGGER_R,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_ZL.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.zl_trigger,
+                            R.drawable.zl_trigger_depressed,
+                            ButtonType.TRIGGER_ZL,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_ZR.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.zr_trigger,
+                            R.drawable.zr_trigger_depressed,
+                            ButtonType.TRIGGER_ZR,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_STICK_L.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.button_l3,
+                            R.drawable.button_l3_depressed,
+                            ButtonType.STICK_L,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.BUTTON_STICK_R.id -> {
+                    overlayButtons.add(
+                        initializeOverlayButton(
+                            context,
+                            windowSize,
+                            R.drawable.button_r3,
+                            R.drawable.button_r3_depressed,
+                            ButtonType.STICK_R,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.STICK_L.id -> {
+                    overlayJoysticks.add(
+                        initializeOverlayJoystick(
+                            context,
+                            windowSize,
+                            R.drawable.joystick_range,
+                            R.drawable.joystick,
+                            R.drawable.joystick_depressed,
+                            StickType.STICK_L,
+                            ButtonType.STICK_L,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.STICK_R.id -> {
+                    overlayJoysticks.add(
+                        initializeOverlayJoystick(
+                            context,
+                            windowSize,
+                            R.drawable.joystick_range,
+                            R.drawable.joystick,
+                            R.drawable.joystick_depressed,
+                            StickType.STICK_R,
+                            ButtonType.STICK_R,
+                            data,
+                            position
+                        )
+                    )
+                }
+
+                OverlayControl.COMBINED_DPAD.id -> {
+                    overlayDpads.add(
+                        initializeOverlayDpad(
+                            context,
+                            windowSize,
+                            R.drawable.dpad_standard,
+                            R.drawable.dpad_standard_cardinal_depressed,
+                            R.drawable.dpad_standard_diagonal_depressed,
+                            position
+                        )
+                    )
+                }
+            }
         }
     }
 
@@ -586,313 +604,87 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
         overlayJoysticks.clear()
 
         // Add all the enabled overlay items back to the HashSet.
-        if (EmulationMenuSettings.showOverlay) {
+        if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
             addOverlayControls(layout)
         }
         invalidate()
     }
 
-    private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) {
+    private fun saveControlPosition(id: String, x: Int, y: Int, layout: OverlayLayout) {
         val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
         val min = windowSize.first
         val max = windowSize.second
-        PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
-            .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x)
-            .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y)
-            .apply()
+        val overlayControlData = NativeConfig.getOverlayControlData()
+        val data = overlayControlData.firstOrNull { it.id == id }
+        val newPosition = Pair((x - min.x).toDouble() / max.x, (y - min.y).toDouble() / max.y)
+        when (layout) {
+            OverlayLayout.Landscape -> data?.landscapePosition = newPosition
+            OverlayLayout.Portrait -> data?.portraitPosition = newPosition
+            OverlayLayout.Foldable -> data?.foldablePosition = newPosition
+        }
+        NativeConfig.setOverlayControlData(overlayControlData)
     }
 
     fun setIsInEditMode(editMode: Boolean) {
         inEditMode = editMode
     }
 
-    private fun resetCurrentLayout() {
-        defaultOverlayByLayout(layout)
-        val layoutIndex = overlayLayouts.indexOf(layout)
-        preferences.edit()
-            .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex])
-            .apply()
+    /**
+     * Applies and saves all default values for the overlay
+     */
+    private fun populateDefaultConfig() {
+        val newConfig = OverlayControl.entries.map { it.toOverlayControlData() }
+        NativeConfig.setOverlayControlData(newConfig.toTypedArray())
+        NativeConfig.saveGlobalConfig()
     }
 
-    private fun resetAllLayouts() {
-        val editor = preferences.edit()
-        overlayLayouts.forEachIndexed { i, layout ->
-            defaultOverlayByLayout(layout)
-            editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i])
+    /**
+     * Checks if any new controls were added to OverlayControl that do not exist within deserialized
+     * config and adds / saves them if necessary
+     *
+     * @param overlayControlData Overlay control data from [NativeConfig.getOverlayControlData]
+     */
+    private fun checkForNewControls(overlayControlData: Array<OverlayControlData>) {
+        val missingControls = mutableListOf<OverlayControlData>()
+        OverlayControl.entries.forEach { defaultControl ->
+            val controlData = overlayControlData.firstOrNull { it.id == defaultControl.id }
+            if (controlData == null) {
+                missingControls.add(defaultControl.toOverlayControlData())
+            }
+        }
+
+        if (missingControls.isNotEmpty()) {
+            NativeConfig.setOverlayControlData(
+                arrayOf(*overlayControlData, *(missingControls.toTypedArray()))
+            )
+            NativeConfig.saveGlobalConfig()
         }
-        editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
-        editor.apply()
     }
 
     fun resetLayoutVisibilityAndPlacement() {
-        defaultOverlayByLayout(layout)
-        val editor = preferences.edit()
-        Settings.overlayPreferences.forEachIndexed { _, pref ->
-            editor.remove(pref)
+        defaultOverlayPositionByLayout(layout)
+
+        val overlayControlData = NativeConfig.getOverlayControlData()
+        overlayControlData.forEach {
+            it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
         }
-        editor.apply()
+        NativeConfig.setOverlayControlData(overlayControlData)
+
         refreshControls()
     }
 
-    private val landscapeResources = arrayOf(
-        R.integer.SWITCH_BUTTON_A_X,
-        R.integer.SWITCH_BUTTON_A_Y,
-        R.integer.SWITCH_BUTTON_B_X,
-        R.integer.SWITCH_BUTTON_B_Y,
-        R.integer.SWITCH_BUTTON_X_X,
-        R.integer.SWITCH_BUTTON_X_Y,
-        R.integer.SWITCH_BUTTON_Y_X,
-        R.integer.SWITCH_BUTTON_Y_Y,
-        R.integer.SWITCH_TRIGGER_ZL_X,
-        R.integer.SWITCH_TRIGGER_ZL_Y,
-        R.integer.SWITCH_TRIGGER_ZR_X,
-        R.integer.SWITCH_TRIGGER_ZR_Y,
-        R.integer.SWITCH_BUTTON_DPAD_X,
-        R.integer.SWITCH_BUTTON_DPAD_Y,
-        R.integer.SWITCH_TRIGGER_L_X,
-        R.integer.SWITCH_TRIGGER_L_Y,
-        R.integer.SWITCH_TRIGGER_R_X,
-        R.integer.SWITCH_TRIGGER_R_Y,
-        R.integer.SWITCH_BUTTON_PLUS_X,
-        R.integer.SWITCH_BUTTON_PLUS_Y,
-        R.integer.SWITCH_BUTTON_MINUS_X,
-        R.integer.SWITCH_BUTTON_MINUS_Y,
-        R.integer.SWITCH_BUTTON_HOME_X,
-        R.integer.SWITCH_BUTTON_HOME_Y,
-        R.integer.SWITCH_BUTTON_CAPTURE_X,
-        R.integer.SWITCH_BUTTON_CAPTURE_Y,
-        R.integer.SWITCH_STICK_R_X,
-        R.integer.SWITCH_STICK_R_Y,
-        R.integer.SWITCH_STICK_L_X,
-        R.integer.SWITCH_STICK_L_Y,
-        R.integer.SWITCH_BUTTON_STICK_L_X,
-        R.integer.SWITCH_BUTTON_STICK_L_Y,
-        R.integer.SWITCH_BUTTON_STICK_R_X,
-        R.integer.SWITCH_BUTTON_STICK_R_Y
-    )
-
-    private val portraitResources = arrayOf(
-        R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
-        R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
-        R.integer.SWITCH_STICK_R_X_PORTRAIT,
-        R.integer.SWITCH_STICK_R_Y_PORTRAIT,
-        R.integer.SWITCH_STICK_L_X_PORTRAIT,
-        R.integer.SWITCH_STICK_L_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT,
-        R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT,
-        R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT
-    )
-
-    private val foldableResources = arrayOf(
-        R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
-        R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
-        R.integer.SWITCH_STICK_R_X_FOLDABLE,
-        R.integer.SWITCH_STICK_R_Y_FOLDABLE,
-        R.integer.SWITCH_STICK_L_X_FOLDABLE,
-        R.integer.SWITCH_STICK_L_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE,
-        R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE,
-        R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE
-    )
-
-    private fun getResourceValue(layout: String, position: Int): Float {
-        return when (layout) {
-            PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
-            FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
-            else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
+    private fun defaultOverlayPositionByLayout(layout: OverlayLayout) {
+        val overlayControlData = NativeConfig.getOverlayControlData()
+        for (data in overlayControlData) {
+            val defaultControlData = OverlayControl.from(data.id) ?: continue
+            val position = defaultControlData.getDefaultPositionForLayout(layout)
+            when (layout) {
+                OverlayLayout.Landscape -> data.landscapePosition = position
+                OverlayLayout.Portrait -> data.portraitPosition = position
+                OverlayLayout.Foldable -> data.foldablePosition = position
+            }
         }
-    }
-
-    private fun defaultOverlayByLayout(layout: String) {
-        // Each value represents the position of the button in relation to the screen size without insets.
-        preferences.edit()
-            .putFloat(
-                "${Settings.PREF_BUTTON_A}-X$layout",
-                getResourceValue(layout, 0)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_A}-Y$layout",
-                getResourceValue(layout, 1)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_B}-X$layout",
-                getResourceValue(layout, 2)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_B}-Y$layout",
-                getResourceValue(layout, 3)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_X}-X$layout",
-                getResourceValue(layout, 4)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_X}-Y$layout",
-                getResourceValue(layout, 5)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_Y}-X$layout",
-                getResourceValue(layout, 6)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_Y}-Y$layout",
-                getResourceValue(layout, 7)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_ZL}-X$layout",
-                getResourceValue(layout, 8)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_ZL}-Y$layout",
-                getResourceValue(layout, 9)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_ZR}-X$layout",
-                getResourceValue(layout, 10)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_ZR}-Y$layout",
-                getResourceValue(layout, 11)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_DPAD}-X$layout",
-                getResourceValue(layout, 12)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_DPAD}-Y$layout",
-                getResourceValue(layout, 13)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_L}-X$layout",
-                getResourceValue(layout, 14)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_L}-Y$layout",
-                getResourceValue(layout, 15)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_R}-X$layout",
-                getResourceValue(layout, 16)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_R}-Y$layout",
-                getResourceValue(layout, 17)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_PLUS}-X$layout",
-                getResourceValue(layout, 18)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_PLUS}-Y$layout",
-                getResourceValue(layout, 19)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_MINUS}-X$layout",
-                getResourceValue(layout, 20)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_MINUS}-Y$layout",
-                getResourceValue(layout, 21)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_HOME}-X$layout",
-                getResourceValue(layout, 22)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_HOME}-Y$layout",
-                getResourceValue(layout, 23)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
-                getResourceValue(layout, 24)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
-                getResourceValue(layout, 25)
-            )
-            .putFloat(
-                "${Settings.PREF_STICK_R}-X$layout",
-                getResourceValue(layout, 26)
-            )
-            .putFloat(
-                "${Settings.PREF_STICK_R}-Y$layout",
-                getResourceValue(layout, 27)
-            )
-            .putFloat(
-                "${Settings.PREF_STICK_L}-X$layout",
-                getResourceValue(layout, 28)
-            )
-            .putFloat(
-                "${Settings.PREF_STICK_L}-Y$layout",
-                getResourceValue(layout, 29)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_STICK_L}-X$layout",
-                getResourceValue(layout, 30)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_STICK_L}-Y$layout",
-                getResourceValue(layout, 31)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_STICK_R}-X$layout",
-                getResourceValue(layout, 32)
-            )
-            .putFloat(
-                "${Settings.PREF_BUTTON_STICK_R}-Y$layout",
-                getResourceValue(layout, 33)
-            )
-            .apply()
+        NativeConfig.setOverlayControlData(overlayControlData)
     }
 
     override fun isInEditMode(): Boolean {
@@ -913,18 +705,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
             FOLDABLE_OVERLAY_VERSION
         )
 
-        private val preferences: SharedPreferences =
-            PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
-        const val LANDSCAPE = "_Landscape"
-        const val PORTRAIT = "_Portrait"
-        const val FOLDABLE = "_Foldable"
-        val overlayLayouts = listOf(
-            LANDSCAPE,
-            PORTRAIT,
-            FOLDABLE
-        )
-
         /**
          * Resizes a [Bitmap] by a given scale factor
          *
@@ -1036,29 +816,19 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
          * In the input overlay configuration menu,
          * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay).
          * the X and Y coordinates of the button at the END of its touch event
-         * (when you remove your finger/stylus from the touchscreen) are then stored
-         * within a SharedPreferences instance so that those values can be retrieved here.
-         *
-         *
-         * This has a few benefits over the conventional way of storing the values
-         * (ie. within the yuzu ini file).
-         *
-         *  * No native calls
-         *  * Keeps Android-only values inside the Android environment
-         *
-         *
+         * (when you remove your finger/stylus from the touchscreen) are then stored in a native .
          *
          * Technically no modifications should need to be performed on the returned
          * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait
          * for Android to call the onDraw method.
          *
-         * @param context      The current [Context].
-         * @param windowSize   The size of the window to draw the overlay on.
-         * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
-         * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
-         * @param buttonId     Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
-         * @param prefId       Identifier for determining where a button appears on screen.
-         * @param layout       The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
+         * @param context            The current [Context].
+         * @param windowSize         The size of the window to draw the overlay on.
+         * @param defaultResId       The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
+         * @param pressedResId       The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
+         * @param buttonId           Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
+         * @param overlayControlData Identifier for determining where a button appears on screen.
+         * @param position           The position on screen as represented by an x and y value between 0 and 1.
          * @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
          */
         private fun initializeOverlayButton(
@@ -1067,33 +837,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
             defaultResId: Int,
             pressedResId: Int,
             buttonId: Int,
-            prefId: String,
-            layout: String
+            overlayControlData: OverlayControlData,
+            position: Pair<Double, Double>
         ): InputOverlayDrawableButton {
             // Resources handle for fetching the initial Drawable resource.
             val res = context.resources
 
-            // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
-            val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
             // Decide scale based on button preference ID and user preference
-            var scale: Float = when (prefId) {
-                Settings.PREF_BUTTON_HOME,
-                Settings.PREF_BUTTON_SCREENSHOT,
-                Settings.PREF_BUTTON_PLUS,
-                Settings.PREF_BUTTON_MINUS -> 0.07f
+            var scale: Float = when (overlayControlData.id) {
+                OverlayControl.BUTTON_HOME.id,
+                OverlayControl.BUTTON_CAPTURE.id,
+                OverlayControl.BUTTON_PLUS.id,
+                OverlayControl.BUTTON_MINUS.id -> 0.07f
 
-                Settings.PREF_BUTTON_L,
-                Settings.PREF_BUTTON_R,
-                Settings.PREF_BUTTON_ZL,
-                Settings.PREF_BUTTON_ZR -> 0.26f
+                OverlayControl.BUTTON_L.id,
+                OverlayControl.BUTTON_R.id,
+                OverlayControl.BUTTON_ZL.id,
+                OverlayControl.BUTTON_ZR.id -> 0.26f
 
-                Settings.PREF_BUTTON_STICK_L,
-                Settings.PREF_BUTTON_STICK_R -> 0.155f
+                OverlayControl.BUTTON_STICK_L.id,
+                OverlayControl.BUTTON_STICK_R.id -> 0.155f
 
                 else -> 0.11f
             }
-            scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat()
+            scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
             scale /= 100f
 
             // Initialize the InputOverlayDrawableButton.
@@ -1104,7 +871,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
                 defaultStateBitmap,
                 pressedStateBitmap,
                 buttonId,
-                prefId
+                overlayControlData
             )
 
             // Get the minimum and maximum coordinates of the screen where the button can be placed.
@@ -1113,12 +880,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
 
             // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
             // These were set in the input overlay configuration menu.
-            val xKey = "$prefId-X$layout"
-            val yKey = "$prefId-Y$layout"
-            val drawableXPercent = sPrefs.getFloat(xKey, 0f)
-            val drawableYPercent = sPrefs.getFloat(yKey, 0f)
-            val drawableX = (drawableXPercent * max.x + min.x).toInt()
-            val drawableY = (drawableYPercent * max.y + min.y).toInt()
+            val drawableX = (position.first * max.x + min.x).toInt()
+            val drawableY = (position.second * max.y + min.y).toInt()
             val width = overlayDrawable.width
             val height = overlayDrawable.height
 
@@ -1136,8 +899,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
                 drawableX - (width / 2),
                 drawableY - (height / 2)
             )
-            val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100)
-            overlayDrawable.setOpacity(savedOpacity * 255 / 100)
+            overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
             return overlayDrawable
         }
 
@@ -1149,7 +911,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
          * @param defaultResId              The [Bitmap] resource ID of the default state.
          * @param pressedOneDirectionResId  The [Bitmap] resource ID of the pressed state in one direction.
          * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
-         * @param layout                    The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
+         * @param position                  The position on screen as represented by an x and y value between 0 and 1.
          * @return The initialized [InputOverlayDrawableDpad]
          */
         private fun initializeOverlayDpad(
@@ -1158,17 +920,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
             defaultResId: Int,
             pressedOneDirectionResId: Int,
             pressedTwoDirectionsResId: Int,
-            layout: String
+            position: Pair<Double, Double>
         ): InputOverlayDrawableDpad {
             // Resources handle for fetching the initial Drawable resource.
             val res = context.resources
 
-            // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad.
-            val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
             // Decide scale based on button ID and user preference
             var scale = 0.25f
-            scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat()
+            scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
             scale /= 100f
 
             // Initialize the InputOverlayDrawableDpad.
@@ -1195,10 +954,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
 
             // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
             // These were set in the input overlay configuration menu.
-            val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f)
-            val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f)
-            val drawableX = (drawableXPercent * max.x + min.x).toInt()
-            val drawableY = (drawableYPercent * max.y + min.y).toInt()
+            val drawableX = (position.first * max.x + min.x).toInt()
+            val drawableY = (position.second * max.y + min.y).toInt()
             val width = overlayDrawable.width
             val height = overlayDrawable.height
 
@@ -1213,8 +970,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
 
             // Need to set the image's position
             overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2))
-            val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100)
-            overlayDrawable.setOpacity(savedOpacity * 255 / 100)
+            overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
             return overlayDrawable
         }
 
@@ -1227,9 +983,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
          * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around).
          * @param pressedResInner Resource ID for the pressed inner image of the joystick.
          * @param joystick        Identifier for which joystick this is.
-         * @param button          Identifier for which joystick button this is.
-         * @param prefId          Identifier for determining where a button appears on screen.
-         * @param layout          The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
+         * @param buttonId          Identifier for which joystick button this is.
+         * @param overlayControlData Identifier for determining where a button appears on screen.
+         * @param position           The position on screen as represented by an x and y value between 0 and 1.
          * @return The initialized [InputOverlayDrawableJoystick].
          */
         private fun initializeOverlayJoystick(
@@ -1239,19 +995,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
             defaultResInner: Int,
             pressedResInner: Int,
             joystick: Int,
-            button: Int,
-            prefId: String,
-            layout: String
+            buttonId: Int,
+            overlayControlData: OverlayControlData,
+            position: Pair<Double, Double>
         ): InputOverlayDrawableJoystick {
             // Resources handle for fetching the initial Drawable resource.
             val res = context.resources
 
-            // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick.
-            val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
             // Decide scale based on user preference
             var scale = 0.3f
-            scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat()
+            scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
             scale /= 100f
 
             // Initialize the InputOverlayDrawableJoystick.
@@ -1265,10 +1018,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
 
             // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
             // These were set in the input overlay configuration menu.
-            val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f)
-            val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f)
-            val drawableX = (drawableXPercent * max.x + min.x).toInt()
-            val drawableY = (drawableYPercent * max.y + min.y).toInt()
+            val drawableX = (position.first * max.x + min.x).toInt()
+            val drawableY = (position.second * max.y + min.y).toInt()
             val outerScale = 1.66f
 
             // Now set the bounds for the InputOverlayDrawableJoystick.
@@ -1292,14 +1043,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
                 outerRect,
                 innerRect,
                 joystick,
-                button,
-                prefId
+                buttonId,
+                overlayControlData.id
             )
 
             // Need to set the image's position
             overlayDrawable.setPosition(drawableX, drawableY)
-            val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100)
-            overlayDrawable.setOpacity(savedOpacity * 255 / 100)
+            overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
             return overlayDrawable
         }
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
index 2c28dda8865d992572e47ca826e9ebda5b751e54..b14a4f96e49573ffd3395dca77a2618b8b07ce98 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
@@ -10,6 +10,7 @@ import android.graphics.Rect
 import android.graphics.drawable.BitmapDrawable
 import android.view.MotionEvent
 import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
 
 /**
  * Custom [BitmapDrawable] that is capable
@@ -25,7 +26,7 @@ class InputOverlayDrawableButton(
     defaultStateBitmap: Bitmap,
     pressedStateBitmap: Bitmap,
     val buttonId: Int,
-    val prefId: String
+    val overlayControlData: OverlayControlData
 ) {
     // The ID value what motion event is tracking
     var trackId: Int
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index 518b1e783743798361b804e62d79d6ad5f709f2b..113bf7c2480e6ace2cc09242c563435099ab70b5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -14,7 +14,7 @@ import kotlin.math.cos
 import kotlin.math.sin
 import kotlin.math.sqrt
 import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
 
 /**
  * Custom [BitmapDrawable] that is capable
@@ -125,7 +125,7 @@ class InputOverlayDrawableJoystick(
             pressedState = true
             outerBitmap.alpha = 0
             boundsBoxBitmap.alpha = opacity
-            if (EmulationMenuSettings.joystickRelCenter) {
+            if (BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()) {
                 virtBounds.offset(
                     xPosition - virtBounds.centerX(),
                     yPosition - virtBounds.centerY()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a0eeadf4bcaa12a9009539610597548de6ed4797
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
@@ -0,0 +1,188 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+import androidx.annotation.IntegerRes
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+
+enum class OverlayControl(
+    val id: String,
+    val defaultVisibility: Boolean,
+    @IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
+    @IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
+    @IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
+) {
+    BUTTON_A(
+        "button_a",
+        true,
+        Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
+        Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
+    ),
+    BUTTON_B(
+        "button_b",
+        true,
+        Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
+        Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
+    ),
+    BUTTON_X(
+        "button_x",
+        true,
+        Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
+        Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
+    ),
+    BUTTON_Y(
+        "button_y",
+        true,
+        Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
+        Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
+    ),
+    BUTTON_PLUS(
+        "button_plus",
+        true,
+        Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
+        Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
+    ),
+    BUTTON_MINUS(
+        "button_minus",
+        true,
+        Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
+        Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
+    ),
+    BUTTON_HOME(
+        "button_home",
+        false,
+        Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
+        Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
+    ),
+    BUTTON_CAPTURE(
+        "button_capture",
+        false,
+        Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
+        Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
+    ),
+    BUTTON_L(
+        "button_l",
+        true,
+        Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
+        Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
+    ),
+    BUTTON_R(
+        "button_r",
+        true,
+        Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
+        Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
+    ),
+    BUTTON_ZL(
+        "button_zl",
+        true,
+        Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
+        Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
+    ),
+    BUTTON_ZR(
+        "button_zr",
+        true,
+        Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
+        Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
+    ),
+    BUTTON_STICK_L(
+        "button_stick_l",
+        true,
+        Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
+        Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
+    ),
+    BUTTON_STICK_R(
+        "button_stick_r",
+        true,
+        Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
+        Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
+        Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
+    ),
+    STICK_L(
+        "stick_l",
+        true,
+        Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
+        Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
+        Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
+    ),
+    STICK_R(
+        "stick_r",
+        true,
+        Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
+        Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
+        Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
+    ),
+    COMBINED_DPAD(
+        "combined_dpad",
+        true,
+        Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
+        Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
+        Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
+    );
+
+    fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
+        val rawResourcePair: Pair<Int, Int>
+        YuzuApplication.appContext.resources.apply {
+            rawResourcePair = when (layout) {
+                OverlayLayout.Landscape -> {
+                    Pair(
+                        getInteger(this@OverlayControl.defaultLandscapePositionResources.first),
+                        getInteger(this@OverlayControl.defaultLandscapePositionResources.second)
+                    )
+                }
+
+                OverlayLayout.Portrait -> {
+                    Pair(
+                        getInteger(this@OverlayControl.defaultPortraitPositionResources.first),
+                        getInteger(this@OverlayControl.defaultPortraitPositionResources.second)
+                    )
+                }
+
+                OverlayLayout.Foldable -> {
+                    Pair(
+                        getInteger(this@OverlayControl.defaultFoldablePositionResources.first),
+                        getInteger(this@OverlayControl.defaultFoldablePositionResources.second)
+                    )
+                }
+            }
+        }
+
+        return Pair(
+            rawResourcePair.first.toDouble() / 1000,
+            rawResourcePair.second.toDouble() / 1000
+        )
+    }
+
+    fun toOverlayControlData(): OverlayControlData =
+        OverlayControlData(
+            id,
+            defaultVisibility,
+            getDefaultPositionForLayout(OverlayLayout.Landscape),
+            getDefaultPositionForLayout(OverlayLayout.Portrait),
+            getDefaultPositionForLayout(OverlayLayout.Foldable)
+        )
+
+    companion object {
+        val map: HashMap<String, OverlayControl> by lazy {
+            val hashMap = hashMapOf<String, OverlayControl>()
+            entries.forEach { hashMap[it.id] = it }
+            hashMap
+        }
+
+        fun from(id: String): OverlayControl? = map[id]
+    }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..26cfeb1db52246f49b8c3d8ee0644fde80b67302
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+data class OverlayControlData(
+    val id: String,
+    var enabled: Boolean,
+    var landscapePosition: Pair<Double, Double>,
+    var portraitPosition: Pair<Double, Double>,
+    var foldablePosition: Pair<Double, Double>
+) {
+    fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
+        when (layout) {
+            OverlayLayout.Landscape -> landscapePosition
+            OverlayLayout.Portrait -> portraitPosition
+            OverlayLayout.Foldable -> foldablePosition
+        }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6bd74c82fe3c4e95d15cebd7cf638c066fdbef6b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+import androidx.annotation.IntegerRes
+
+data class OverlayControlDefault(
+    val buttonId: String,
+    @IntegerRes val landscapePositionResource: Pair<Int, Int>,
+    @IntegerRes val portraitPositionResource: Pair<Int, Int>,
+    @IntegerRes val foldablePositionResource: Pair<Int, Int>
+)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d728164e5916dff80e1012aff3a0851a85ea0aeb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+enum class OverlayLayout(val id: String) {
+    Landscape("Landscape"),
+    Portrait("Portrait"),
+    Foldable("Foldable")
+}
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 d4a9da06fdc09743bf634bbd82e9bb6cfe40d133..de0794a17fc73a4dc841849b5fc3cacee0385643 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
@@ -10,6 +10,9 @@ import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
 import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
+import org.yuzu.yuzu_emu.overlay.model.OverlayControl
+import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
 import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
 
 object DirectoryInitialization {
@@ -64,8 +67,147 @@ object DirectoryInitialization {
             saveConfig = true
         }
 
+        val joystickRelCenter =
+            preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER)
+        if (joystickRelCenter != null) {
+            BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(joystickRelCenter)
+            saveConfig = true
+        }
+
+        val dpadSlide =
+            preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE)
+        if (dpadSlide != null) {
+            BooleanSetting.DPAD_SLIDE.setBoolean(dpadSlide)
+            saveConfig = true
+        }
+
+        val hapticFeedback =
+            preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_HAPTICS)
+        if (hapticFeedback != null) {
+            BooleanSetting.HAPTIC_FEEDBACK.setBoolean(hapticFeedback)
+            saveConfig = true
+        }
+
+        val showPerformanceOverlay =
+            preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_FPS)
+        if (showPerformanceOverlay != null) {
+            BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(showPerformanceOverlay)
+            saveConfig = true
+        }
+
+        val showInputOverlay =
+            preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY)
+        if (showInputOverlay != null) {
+            BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(showInputOverlay)
+            saveConfig = true
+        }
+
+        val overlayOpacity = preferences.migratePreference<Int>(Settings.PREF_CONTROL_OPACITY)
+        if (overlayOpacity != null) {
+            IntSetting.OVERLAY_OPACITY.setInt(overlayOpacity)
+            saveConfig = true
+        }
+
+        val overlayScale = preferences.migratePreference<Int>(Settings.PREF_CONTROL_SCALE)
+        if (overlayScale != null) {
+            IntSetting.OVERLAY_SCALE.setInt(overlayScale)
+            saveConfig = true
+        }
+
+        var setOverlayData = false
+        val overlayControlData = NativeConfig.getOverlayControlData()
+        if (overlayControlData.isEmpty()) {
+            val overlayControlDataMap =
+                NativeConfig.getOverlayControlData().associateBy { it.id }.toMutableMap()
+            for (button in Settings.overlayPreferences) {
+                val buttonId = convertButtonId(button)
+                var buttonEnabled = preferences.migratePreference<Boolean>(button)
+                if (buttonEnabled == null) {
+                    buttonEnabled = OverlayControl.map[buttonId]?.defaultVisibility == true
+                }
+
+                var landscapeXPosition = preferences.migratePreference<Float>(
+                    "$button-X${Settings.PREF_LANDSCAPE_SUFFIX}"
+                )?.toDouble()
+                var landscapeYPosition = preferences.migratePreference<Float>(
+                    "$button-Y${Settings.PREF_LANDSCAPE_SUFFIX}"
+                )?.toDouble()
+                if (landscapeXPosition == null || landscapeYPosition == null) {
+                    val landscapePosition = OverlayControl.map[buttonId]
+                        ?.getDefaultPositionForLayout(OverlayLayout.Landscape) ?: Pair(0.0, 0.0)
+                    landscapeXPosition = landscapePosition.first
+                    landscapeYPosition = landscapePosition.second
+                }
+
+                var portraitXPosition = preferences.migratePreference<Float>(
+                    "$button-X${Settings.PREF_PORTRAIT_SUFFIX}"
+                )?.toDouble()
+                var portraitYPosition = preferences.migratePreference<Float>(
+                    "$button-Y${Settings.PREF_PORTRAIT_SUFFIX}"
+                )?.toDouble()
+                if (portraitXPosition == null || portraitYPosition == null) {
+                    val portraitPosition = OverlayControl.map[buttonId]
+                        ?.getDefaultPositionForLayout(OverlayLayout.Portrait) ?: Pair(0.0, 0.0)
+                    portraitXPosition = portraitPosition.first
+                    portraitYPosition = portraitPosition.second
+                }
+
+                var foldableXPosition = preferences.migratePreference<Float>(
+                    "$button-X${Settings.PREF_FOLDABLE_SUFFIX}"
+                )?.toDouble()
+                var foldableYPosition = preferences.migratePreference<Float>(
+                    "$button-Y${Settings.PREF_FOLDABLE_SUFFIX}"
+                )?.toDouble()
+                if (foldableXPosition == null || foldableYPosition == null) {
+                    val foldablePosition = OverlayControl.map[buttonId]
+                        ?.getDefaultPositionForLayout(OverlayLayout.Foldable) ?: Pair(0.0, 0.0)
+                    foldableXPosition = foldablePosition.first
+                    foldableYPosition = foldablePosition.second
+                }
+
+                val controlData = OverlayControlData(
+                    buttonId,
+                    buttonEnabled,
+                    Pair(landscapeXPosition, landscapeYPosition),
+                    Pair(portraitXPosition, portraitYPosition),
+                    Pair(foldableXPosition, foldableYPosition)
+                )
+                overlayControlDataMap[buttonId] = controlData
+                setOverlayData = true
+            }
+
+            if (setOverlayData) {
+                NativeConfig.setOverlayControlData(
+                    overlayControlDataMap.map { it.value }.toTypedArray()
+                )
+                saveConfig = true
+            }
+        }
+
         if (saveConfig) {
             NativeConfig.saveGlobalConfig()
         }
     }
+
+    private fun convertButtonId(buttonId: String): String =
+        when (buttonId) {
+            Settings.PREF_BUTTON_A -> OverlayControl.BUTTON_A.id
+            Settings.PREF_BUTTON_B -> OverlayControl.BUTTON_B.id
+            Settings.PREF_BUTTON_X -> OverlayControl.BUTTON_X.id
+            Settings.PREF_BUTTON_Y -> OverlayControl.BUTTON_Y.id
+            Settings.PREF_BUTTON_L -> OverlayControl.BUTTON_L.id
+            Settings.PREF_BUTTON_R -> OverlayControl.BUTTON_R.id
+            Settings.PREF_BUTTON_ZL -> OverlayControl.BUTTON_ZL.id
+            Settings.PREF_BUTTON_ZR -> OverlayControl.BUTTON_ZR.id
+            Settings.PREF_BUTTON_PLUS -> OverlayControl.BUTTON_PLUS.id
+            Settings.PREF_BUTTON_MINUS -> OverlayControl.BUTTON_MINUS.id
+            Settings.PREF_BUTTON_DPAD -> OverlayControl.COMBINED_DPAD.id
+            Settings.PREF_STICK_L -> OverlayControl.STICK_L.id
+            Settings.PREF_STICK_R -> OverlayControl.STICK_R.id
+            Settings.PREF_BUTTON_HOME -> OverlayControl.BUTTON_HOME.id
+            Settings.PREF_BUTTON_SCREENSHOT -> OverlayControl.BUTTON_CAPTURE.id
+            Settings.PREF_BUTTON_STICK_L -> OverlayControl.BUTTON_STICK_L.id
+            Settings.PREF_BUTTON_STICK_R -> OverlayControl.BUTTON_STICK_R.id
+            else -> ""
+        }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
deleted file mode 100644
index 7e8f058c14d9381c12ce0efee660629ae8df5f48..0000000000000000000000000000000000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.utils
-
-import androidx.preference.PreferenceManager
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.features.settings.model.Settings
-
-object EmulationMenuSettings {
-    private val preferences =
-        PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
-    var joystickRelCenter: Boolean
-        get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
-        set(value) {
-            preferences.edit()
-                .putBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, value)
-                .apply()
-        }
-    var dpadSlide: Boolean
-        get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, true)
-        set(value) {
-            preferences.edit()
-                .putBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, value)
-                .apply()
-        }
-    var hapticFeedback: Boolean
-        get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, false)
-        set(value) {
-            preferences.edit()
-                .putBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, value)
-                .apply()
-        }
-
-    var showFps: Boolean
-        get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
-        set(value) {
-            preferences.edit()
-                .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, value)
-                .apply()
-        }
-    var showOverlay: Boolean
-        get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, true)
-        set(value) {
-            preferences.edit()
-                .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, value)
-                .apply()
-        }
-}
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 7512d5eed9f3f840ef345071350c017ce7c00c53..a4c14b3a7725d72030547b12cb387609145c1f1b 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,7 @@
 package org.yuzu.yuzu_emu.utils
 
 import org.yuzu.yuzu_emu.model.GameDir
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
 
 object NativeConfig {
     /**
@@ -150,4 +151,21 @@ object NativeConfig {
      */
     @Synchronized
     external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
+
+    /**
+     * Gets an array of [OverlayControlData] from settings
+     *
+     * @return An array of [OverlayControlData]
+     */
+    @Synchronized
+    external fun getOverlayControlData(): Array<OverlayControlData>
+
+    /**
+     * Clears the AndroidSettings::values.overlay_control_data array and replaces its values
+     * with [overlayControlData]
+     *
+     * @param overlayControlData Replacement array of [OverlayControlData]
+     */
+    @Synchronized
+    external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
 }
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp
index 52d8ecfeb0257ec650adb6ad9fbbe073e952527b..1e884ffdd2ed603d6b32de9af56a1c1530486643 100644
--- a/src/android/app/src/main/jni/android_common/android_common.cpp
+++ b/src/android/app/src/main/jni/android_common/android_common.cpp
@@ -9,6 +9,7 @@
 #include <jni.h>
 
 #include "common/string_util.h"
+#include "jni/id_cache.h"
 
 std::string GetJString(JNIEnv* env, jstring jstr) {
     if (!jstr) {
@@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
 jstring ToJString(JNIEnv* env, std::u16string_view str) {
     return ToJString(env, Common::UTF16ToUTF8(str));
 }
+
+double GetJDouble(JNIEnv* env, jobject jdouble) {
+    return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
+}
+
+jobject ToJDouble(JNIEnv* env, double value) {
+    return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
+}
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h
index ccb0c06f74048fe1d2b94fbb2ba5fc32e22c37b2..8eb803e1b08c49333cec490d007266c2954eb382 100644
--- a/src/android/app/src/main/jni/android_common/android_common.h
+++ b/src/android/app/src/main/jni/android_common/android_common.h
@@ -10,3 +10,6 @@
 std::string GetJString(JNIEnv* env, jstring jstr);
 jstring ToJString(JNIEnv* env, std::string_view str);
 jstring ToJString(JNIEnv* env, std::u16string_view str);
+
+double GetJDouble(JNIEnv* env, jobject jdouble);
+jobject ToJDouble(JNIEnv* env, double value);
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index fb17ab6f60acc6b940349bf749aa1b33676dc1d2..c86aa1c3965a4abbd727e486f12a9c00c72b55a9 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -35,6 +35,7 @@ void AndroidConfig::ReadAndroidValues() {
     if (global) {
         ReadAndroidUIValues();
         ReadUIValues();
+        ReadOverlayValues();
     }
     ReadDriverValues();
 }
@@ -81,10 +82,42 @@ void AndroidConfig::ReadDriverValues() {
     EndGroup();
 }
 
+void AndroidConfig::ReadOverlayValues() {
+    BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
+
+    ReadCategory(Settings::Category::Overlay);
+
+    AndroidSettings::values.overlay_control_data.clear();
+    const int control_data_size = BeginArray("control_data");
+    for (int i = 0; i < control_data_size; ++i) {
+        SetArrayIndex(i);
+        AndroidSettings::OverlayControlData control_data;
+        control_data.id = ReadStringSetting(std::string("id"));
+        control_data.enabled = ReadBooleanSetting(std::string("enabled"));
+        control_data.landscape_position.first =
+            ReadDoubleSetting(std::string("landscape\\x_position"));
+        control_data.landscape_position.second =
+            ReadDoubleSetting(std::string("landscape\\y_position"));
+        control_data.portrait_position.first =
+            ReadDoubleSetting(std::string("portrait\\x_position"));
+        control_data.portrait_position.second =
+            ReadDoubleSetting(std::string("portrait\\y_position"));
+        control_data.foldable_position.first =
+            ReadDoubleSetting(std::string("foldable\\x_position"));
+        control_data.foldable_position.second =
+            ReadDoubleSetting(std::string("foldable\\y_position"));
+        AndroidSettings::values.overlay_control_data.push_back(control_data);
+    }
+    EndArray();
+
+    EndGroup();
+}
+
 void AndroidConfig::SaveAndroidValues() {
     if (global) {
         SaveAndroidUIValues();
         SaveUIValues();
+        SaveOverlayValues();
     }
     SaveDriverValues();
 
@@ -131,6 +164,35 @@ void AndroidConfig::SaveDriverValues() {
     EndGroup();
 }
 
+void AndroidConfig::SaveOverlayValues() {
+    BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
+
+    WriteCategory(Settings::Category::Overlay);
+
+    BeginArray("control_data");
+    for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
+        SetArrayIndex(i);
+        const auto& control_data = AndroidSettings::values.overlay_control_data[i];
+        WriteStringSetting(std::string("id"), control_data.id);
+        WriteBooleanSetting(std::string("enabled"), control_data.enabled);
+        WriteDoubleSetting(std::string("landscape\\x_position"),
+                           control_data.landscape_position.first);
+        WriteDoubleSetting(std::string("landscape\\y_position"),
+                           control_data.landscape_position.second);
+        WriteDoubleSetting(std::string("portrait\\x_position"),
+                           control_data.portrait_position.first);
+        WriteDoubleSetting(std::string("portrait\\y_position"),
+                           control_data.portrait_position.second);
+        WriteDoubleSetting(std::string("foldable\\x_position"),
+                           control_data.foldable_position.first);
+        WriteDoubleSetting(std::string("foldable\\y_position"),
+                           control_data.foldable_position.second);
+    }
+    EndArray();
+
+    EndGroup();
+}
+
 std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
     auto& map = Settings::values.linkage.by_category;
     if (map.contains(category)) {
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index 2c12874e15444b048335b777cab020ebed229775..d83852de9eb434f1fb316d35a692bb9707b34bb7 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -18,6 +18,7 @@ protected:
     void ReadAndroidValues();
     void ReadAndroidUIValues();
     void ReadDriverValues();
+    void ReadOverlayValues();
     void ReadHidbusValues() override {}
     void ReadDebugControlValues() override {}
     void ReadPathValues() override;
@@ -30,6 +31,7 @@ protected:
     void SaveAndroidValues();
     void SaveAndroidUIValues();
     void SaveDriverValues();
+    void SaveOverlayValues();
     void SaveHidbusValues() override {}
     void SaveDebugControlValues() override {}
     void SavePathValues() override;
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 1e4906b9a50abdbb15b472c16e804dd6b8b0097c..559ae83eb739b5179efb78f976444356f380498c 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -14,6 +14,14 @@ struct GameDir {
     bool deep_scan = false;
 };
 
+struct OverlayControlData {
+    std::string id;
+    bool enabled;
+    std::pair<double, double> landscape_position;
+    std::pair<double, double> portrait_position;
+    std::pair<double, double> foldable_position;
+};
+
 struct Values {
     Settings::Linkage linkage;
 
@@ -38,6 +46,23 @@ struct Values {
     Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
     Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
                                               Settings::Category::Android};
+
+    // Input/performance overlay settings
+    std::vector<OverlayControlData> overlay_control_data;
+    Settings::Setting<s32> overlay_scale{linkage, 50, "control_scale", Settings::Category::Overlay};
+    Settings::Setting<s32> overlay_opacity{linkage, 100, "control_opacity",
+                                           Settings::Category::Overlay};
+
+    Settings::Setting<bool> joystick_rel_center{linkage, true, "joystick_rel_center",
+                                                Settings::Category::Overlay};
+    Settings::Setting<bool> dpad_slide{linkage, true, "dpad_slide", Settings::Category::Overlay};
+    Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
+                                            Settings::Category::Overlay};
+    Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
+                                                     Settings::Category::Overlay};
+    Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
+                                               Settings::Category::Overlay};
+    Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
 };
 
 extern Values values;
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index e7a86d3fd0f926b6decaff925d33637d40a6a7cc..c79ad7d76b96dc36ae430e76d8194719c3603fe6 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
 static jfieldID s_pair_first_field;
 static jfieldID s_pair_second_field;
 
+static jclass s_overlay_control_data_class;
+static jmethodID s_overlay_control_data_constructor;
+static jfieldID s_overlay_control_data_id_field;
+static jfieldID s_overlay_control_data_enabled_field;
+static jfieldID s_overlay_control_data_landscape_position_field;
+static jfieldID s_overlay_control_data_portrait_position_field;
+static jfieldID s_overlay_control_data_foldable_position_field;
+
+static jclass s_double_class;
+static jmethodID s_double_constructor;
+static jfieldID s_double_value_field;
+
 static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
 
 namespace IDCache {
@@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
     return s_pair_second_field;
 }
 
+jclass GetOverlayControlDataClass() {
+    return s_overlay_control_data_class;
+}
+
+jmethodID GetOverlayControlDataConstructor() {
+    return s_overlay_control_data_constructor;
+}
+
+jfieldID GetOverlayControlDataIdField() {
+    return s_overlay_control_data_id_field;
+}
+
+jfieldID GetOverlayControlDataEnabledField() {
+    return s_overlay_control_data_enabled_field;
+}
+
+jfieldID GetOverlayControlDataLandscapePositionField() {
+    return s_overlay_control_data_landscape_position_field;
+}
+
+jfieldID GetOverlayControlDataPortraitPositionField() {
+    return s_overlay_control_data_portrait_position_field;
+}
+
+jfieldID GetOverlayControlDataFoldablePositionField() {
+    return s_overlay_control_data_foldable_position_field;
+}
+
+jclass GetDoubleClass() {
+    return s_double_class;
+}
+
+jmethodID GetDoubleConstructor() {
+    return s_double_constructor;
+}
+
+jfieldID GetDoubleValueField() {
+    return s_double_value_field;
+}
+
 } // namespace IDCache
 
 #ifdef __cplusplus
@@ -207,6 +259,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
     s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
     env->DeleteLocalRef(pair_class);
 
+    const jclass overlay_control_data_class =
+        env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
+    s_overlay_control_data_class =
+        reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
+    s_overlay_control_data_constructor =
+        env->GetMethodID(overlay_control_data_class, "<init>",
+                         "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
+    s_overlay_control_data_id_field =
+        env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
+    s_overlay_control_data_enabled_field =
+        env->GetFieldID(overlay_control_data_class, "enabled", "Z");
+    s_overlay_control_data_landscape_position_field =
+        env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
+    s_overlay_control_data_portrait_position_field =
+        env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
+    s_overlay_control_data_foldable_position_field =
+        env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
+    env->DeleteLocalRef(overlay_control_data_class);
+
+    const jclass double_class = env->FindClass("java/lang/Double");
+    s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
+    s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
+    s_double_value_field = env->GetFieldID(double_class, "value", "D");
+    env->DeleteLocalRef(double_class);
+
     // Initialize Android Storage
     Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
 
@@ -231,6 +308,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
     env->DeleteGlobalRef(s_game_class);
     env->DeleteGlobalRef(s_string_class);
     env->DeleteGlobalRef(s_pair_class);
+    env->DeleteGlobalRef(s_overlay_control_data_class);
+    env->DeleteGlobalRef(s_double_class);
 
     // UnInitialize applets
     SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 24030be42185c105c4dfea1a4628062eb445f3f9..784d1412f659a379c7599b847e5215c32f5a4ee8 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
 jfieldID GetPairFirstField();
 jfieldID GetPairSecondField();
 
+jclass GetOverlayControlDataClass();
+jmethodID GetOverlayControlDataConstructor();
+jfieldID GetOverlayControlDataIdField();
+jfieldID GetOverlayControlDataEnabledField();
+jfieldID GetOverlayControlDataLandscapePositionField();
+jfieldID GetOverlayControlDataPortraitPositionField();
+jfieldID GetOverlayControlDataFoldablePositionField();
+
+jclass GetDoubleClass();
+jmethodID GetDoubleConstructor();
+jfieldID GetDoubleValueField();
+
 } // namespace IDCache
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 324d9e9cdc72961e2b31a3dd7ed5ab3574752535..5359024833de8c13077367e415fd5d7712bf1e76 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -344,4 +344,74 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
     Settings::values.disabled_addons[program_id] = disabled_addons;
 }
 
+jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JNIEnv* env,
+                                                                              jobject obj) {
+    jobjectArray joverlayControlDataArray =
+        env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
+                            IDCache::GetOverlayControlDataClass(), nullptr);
+    for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
+        const auto& control_data = AndroidSettings::values.overlay_control_data[i];
+        jobject jlandscapePosition =
+            env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
+                           ToJDouble(env, control_data.landscape_position.first),
+                           ToJDouble(env, control_data.landscape_position.second));
+        jobject jportraitPosition =
+            env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
+                           ToJDouble(env, control_data.portrait_position.first),
+                           ToJDouble(env, control_data.portrait_position.second));
+        jobject jfoldablePosition =
+            env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
+                           ToJDouble(env, control_data.foldable_position.first),
+                           ToJDouble(env, control_data.foldable_position.second));
+
+        jobject jcontrolData = env->NewObject(
+            IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
+            ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
+            jportraitPosition, jfoldablePosition);
+        env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
+    }
+    return joverlayControlDataArray;
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
+    JNIEnv* env, jobject obj, jobjectArray joverlayControlDataArray) {
+    AndroidSettings::values.overlay_control_data.clear();
+    int size = env->GetArrayLength(joverlayControlDataArray);
+
+    if (size == 0) {
+        return;
+    }
+
+    for (int i = 0; i < size; ++i) {
+        jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
+        jstring jidString = static_cast<jstring>(
+            env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
+        bool enabled = static_cast<bool>(env->GetBooleanField(
+            joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
+
+        jobject jlandscapePosition = env->GetObjectField(
+            joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
+        std::pair<double, double> landscape_position = std::make_pair(
+            GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
+            GetJDouble(env,
+                       env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
+
+        jobject jportraitPosition = env->GetObjectField(
+            joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
+        std::pair<double, double> portrait_position = std::make_pair(
+            GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
+            GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
+
+        jobject jfoldablePosition = env->GetObjectField(
+            joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
+        std::pair<double, double> foldable_position = std::make_pair(
+            GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
+            GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
+
+        AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
+            GetJString(env, jidString), enabled, landscape_position, portrait_position,
+            foldable_position});
+    }
+}
+
 } // extern "C"
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index c882a8e62922eedc64bfb885566ccd8f53a8550d..45d57c3ea684ec441d07e5ce329dadd80e57a993 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -212,19 +212,19 @@
         <item>B</item>
         <item>X</item>
         <item>Y</item>
+        <item>+</item>
+        <item>-</item>
+        <item>@string/gamepad_home</item>
+        <item>@string/gamepad_screenshot</item>
         <item>L</item>
         <item>R</item>
         <item>ZL</item>
         <item>ZR</item>
-        <item>+</item>
-        <item>-</item>
-        <item>@string/gamepad_d_pad</item>
         <item>@string/gamepad_left_stick</item>
         <item>@string/gamepad_right_stick</item>
         <item>L3</item>
         <item>R3</item>
-        <item>@string/gamepad_home</item>
-        <item>@string/gamepad_screenshot</item>
+        <item>@string/gamepad_d_pad</item>
     </string-array>
 
     <string-array name="themeEntries">
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index dc527965c9eed7f2e121215c7abf8080aa4b2368..1c6f5db93a66f35dd4ad921ba54233fd790b4a25 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -3,111 +3,111 @@
     <integer name="grid_columns">1</integer>
 
     <!-- Default SWITCH landscape layout -->
-    <integer name="SWITCH_BUTTON_A_X">760</integer>
-    <integer name="SWITCH_BUTTON_A_Y">790</integer>
-    <integer name="SWITCH_BUTTON_B_X">710</integer>
-    <integer name="SWITCH_BUTTON_B_Y">900</integer>
-    <integer name="SWITCH_BUTTON_X_X">710</integer>
-    <integer name="SWITCH_BUTTON_X_Y">680</integer>
-    <integer name="SWITCH_BUTTON_Y_X">660</integer>
-    <integer name="SWITCH_BUTTON_Y_Y">790</integer>
-    <integer name="SWITCH_STICK_L_X">100</integer>
-    <integer name="SWITCH_STICK_L_Y">670</integer>
-    <integer name="SWITCH_STICK_R_X">900</integer>
-    <integer name="SWITCH_STICK_R_Y">670</integer>
-    <integer name="SWITCH_TRIGGER_L_X">70</integer>
-    <integer name="SWITCH_TRIGGER_L_Y">220</integer>
-    <integer name="SWITCH_TRIGGER_R_X">930</integer>
-    <integer name="SWITCH_TRIGGER_R_Y">220</integer>
-    <integer name="SWITCH_TRIGGER_ZL_X">70</integer>
-    <integer name="SWITCH_TRIGGER_ZL_Y">90</integer>
-    <integer name="SWITCH_TRIGGER_ZR_X">930</integer>
-    <integer name="SWITCH_TRIGGER_ZR_Y">90</integer>
-    <integer name="SWITCH_BUTTON_MINUS_X">460</integer>
-    <integer name="SWITCH_BUTTON_MINUS_Y">950</integer>
-    <integer name="SWITCH_BUTTON_PLUS_X">540</integer>
-    <integer name="SWITCH_BUTTON_PLUS_Y">950</integer>
-    <integer name="SWITCH_BUTTON_HOME_X">600</integer>
-    <integer name="SWITCH_BUTTON_HOME_Y">950</integer>
-    <integer name="SWITCH_BUTTON_CAPTURE_X">400</integer>
-    <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
-    <integer name="SWITCH_BUTTON_DPAD_X">260</integer>
-    <integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
-    <integer name="SWITCH_BUTTON_STICK_L_X">870</integer>
-    <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer>
-    <integer name="SWITCH_BUTTON_STICK_R_X">960</integer>
-    <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer>
+    <integer name="BUTTON_A_X">760</integer>
+    <integer name="BUTTON_A_Y">790</integer>
+    <integer name="BUTTON_B_X">710</integer>
+    <integer name="BUTTON_B_Y">900</integer>
+    <integer name="BUTTON_X_X">710</integer>
+    <integer name="BUTTON_X_Y">680</integer>
+    <integer name="BUTTON_Y_X">660</integer>
+    <integer name="BUTTON_Y_Y">790</integer>
+    <integer name="BUTTON_PLUS_X">540</integer>
+    <integer name="BUTTON_PLUS_Y">950</integer>
+    <integer name="BUTTON_MINUS_X">460</integer>
+    <integer name="BUTTON_MINUS_Y">950</integer>
+    <integer name="BUTTON_HOME_X">600</integer>
+    <integer name="BUTTON_HOME_Y">950</integer>
+    <integer name="BUTTON_CAPTURE_X">400</integer>
+    <integer name="BUTTON_CAPTURE_Y">950</integer>
+    <integer name="BUTTON_L_X">70</integer>
+    <integer name="BUTTON_L_Y">220</integer>
+    <integer name="BUTTON_R_X">930</integer>
+    <integer name="BUTTON_R_Y">220</integer>
+    <integer name="BUTTON_ZL_X">70</integer>
+    <integer name="BUTTON_ZL_Y">90</integer>
+    <integer name="BUTTON_ZR_X">930</integer>
+    <integer name="BUTTON_ZR_Y">90</integer>
+    <integer name="BUTTON_STICK_L_X">870</integer>
+    <integer name="BUTTON_STICK_L_Y">400</integer>
+    <integer name="BUTTON_STICK_R_X">960</integer>
+    <integer name="BUTTON_STICK_R_Y">430</integer>
+    <integer name="STICK_L_X">100</integer>
+    <integer name="STICK_L_Y">670</integer>
+    <integer name="STICK_R_X">900</integer>
+    <integer name="STICK_R_Y">670</integer>
+    <integer name="COMBINED_DPAD_X">260</integer>
+    <integer name="COMBINED_DPAD_Y">790</integer>
 
     <!-- Default SWITCH portrait layout -->
-    <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
-    <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer>
-    <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer>
-    <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer>
-    <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer>
-    <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer>
-    <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer>
-    <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer>
-    <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer>
-    <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer>
-    <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer>
-    <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer>
-    <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer>
-    <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer>
-    <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer>
-    <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer>
-    <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer>
-    <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer>
-    <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer>
-    <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer>
-    <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer>
-    <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer>
-    <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer>
-    <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer>
-    <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer>
-    <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer>
-    <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer>
-    <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
-    <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
-    <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
-    <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer>
-    <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer>
-    <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer>
-    <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer>
+    <integer name="BUTTON_A_X_PORTRAIT">840</integer>
+    <integer name="BUTTON_A_Y_PORTRAIT">840</integer>
+    <integer name="BUTTON_B_X_PORTRAIT">740</integer>
+    <integer name="BUTTON_B_Y_PORTRAIT">880</integer>
+    <integer name="BUTTON_X_X_PORTRAIT">740</integer>
+    <integer name="BUTTON_X_Y_PORTRAIT">800</integer>
+    <integer name="BUTTON_Y_X_PORTRAIT">640</integer>
+    <integer name="BUTTON_Y_Y_PORTRAIT">840</integer>
+    <integer name="BUTTON_PLUS_Y_PORTRAIT">950</integer>
+    <integer name="BUTTON_MINUS_X_PORTRAIT">440</integer>
+    <integer name="BUTTON_MINUS_Y_PORTRAIT">950</integer>
+    <integer name="BUTTON_HOME_X_PORTRAIT">680</integer>
+    <integer name="BUTTON_HOME_Y_PORTRAIT">950</integer>
+    <integer name="BUTTON_CAPTURE_X_PORTRAIT">320</integer>
+    <integer name="BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
+    <integer name="BUTTON_L_X_PORTRAIT">140</integer>
+    <integer name="BUTTON_L_Y_PORTRAIT">260</integer>
+    <integer name="BUTTON_R_X_PORTRAIT">860</integer>
+    <integer name="BUTTON_R_Y_PORTRAIT">260</integer>
+    <integer name="BUTTON_ZL_X_PORTRAIT">140</integer>
+    <integer name="BUTTON_ZL_Y_PORTRAIT">200</integer>
+    <integer name="BUTTON_ZR_X_PORTRAIT">860</integer>
+    <integer name="BUTTON_ZR_Y_PORTRAIT">200</integer>
+    <integer name="BUTTON_PLUS_X_PORTRAIT">560</integer>
+    <integer name="BUTTON_STICK_L_X_PORTRAIT">730</integer>
+    <integer name="BUTTON_STICK_L_Y_PORTRAIT">510</integer>
+    <integer name="BUTTON_STICK_R_X_PORTRAIT">900</integer>
+    <integer name="BUTTON_STICK_R_Y_PORTRAIT">540</integer>
+    <integer name="STICK_L_X_PORTRAIT">180</integer>
+    <integer name="STICK_L_Y_PORTRAIT">660</integer>
+    <integer name="STICK_R_X_PORTRAIT">820</integer>
+    <integer name="STICK_R_Y_PORTRAIT">660</integer>
+    <integer name="COMBINED_DPAD_X_PORTRAIT">240</integer>
+    <integer name="COMBINED_DPAD_Y_PORTRAIT">840</integer>
 
     <!-- Default SWITCH foldable layout -->
-    <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
-    <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer>
-    <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer>
-    <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer>
-    <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer>
-    <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer>
-    <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer>
-    <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer>
-    <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer>
-    <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer>
-    <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer>
-    <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer>
-    <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer>
-    <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer>
-    <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer>
-    <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer>
-    <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer>
-    <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer>
-    <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer>
-    <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer>
-    <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer>
-    <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer>
-    <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer>
-    <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer>
-    <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer>
-    <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer>
-    <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer>
-    <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
-    <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
-    <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
-    <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer>
-    <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer>
-    <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer>
-    <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer>
+    <integer name="BUTTON_A_X_FOLDABLE">840</integer>
+    <integer name="BUTTON_A_Y_FOLDABLE">390</integer>
+    <integer name="BUTTON_B_X_FOLDABLE">740</integer>
+    <integer name="BUTTON_B_Y_FOLDABLE">430</integer>
+    <integer name="BUTTON_X_X_FOLDABLE">740</integer>
+    <integer name="BUTTON_X_Y_FOLDABLE">350</integer>
+    <integer name="BUTTON_Y_X_FOLDABLE">640</integer>
+    <integer name="BUTTON_Y_Y_FOLDABLE">390</integer>
+    <integer name="BUTTON_PLUS_X_FOLDABLE">560</integer>
+    <integer name="BUTTON_PLUS_Y_FOLDABLE">470</integer>
+    <integer name="BUTTON_MINUS_X_FOLDABLE">440</integer>
+    <integer name="BUTTON_MINUS_Y_FOLDABLE">470</integer>
+    <integer name="BUTTON_HOME_X_FOLDABLE">680</integer>
+    <integer name="BUTTON_HOME_Y_FOLDABLE">470</integer>
+    <integer name="BUTTON_CAPTURE_X_FOLDABLE">320</integer>
+    <integer name="BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
+    <integer name="BUTTON_L_X_FOLDABLE">140</integer>
+    <integer name="BUTTON_L_Y_FOLDABLE">130</integer>
+    <integer name="BUTTON_R_X_FOLDABLE">860</integer>
+    <integer name="BUTTON_R_Y_FOLDABLE">130</integer>
+    <integer name="BUTTON_ZL_X_FOLDABLE">140</integer>
+    <integer name="BUTTON_ZL_Y_FOLDABLE">70</integer>
+    <integer name="BUTTON_ZR_X_FOLDABLE">860</integer>
+    <integer name="BUTTON_ZR_Y_FOLDABLE">70</integer>
+    <integer name="BUTTON_STICK_L_X_FOLDABLE">550</integer>
+    <integer name="BUTTON_STICK_L_Y_FOLDABLE">210</integer>
+    <integer name="BUTTON_STICK_R_X_FOLDABLE">550</integer>
+    <integer name="BUTTON_STICK_R_Y_FOLDABLE">280</integer>
+    <integer name="STICK_L_X_FOLDABLE">180</integer>
+    <integer name="STICK_L_Y_FOLDABLE">250</integer>
+    <integer name="STICK_R_X_FOLDABLE">820</integer>
+    <integer name="STICK_R_Y_FOLDABLE">250</integer>
+    <integer name="COMBINED_DPAD_X_FOLDABLE">240</integer>
+    <integer name="COMBINED_DPAD_Y_FOLDABLE">390</integer>
 
 </resources>
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index ea52bbfa677b616fc9282e69c04d1779f84f7e17..07709d4e5c193fda96640e238e254fe300a22c0e 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -199,6 +199,8 @@ const char* TranslateCategory(Category category) {
     case Category::CpuDebug:
     case Category::CpuUnsafe:
         return "Cpu";
+    case Category::Overlay:
+        return "Overlay";
     case Category::Renderer:
     case Category::RendererAdvanced:
     case Category::RendererDebug:
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index c82e174959729436f310ef75a3faad1c717f0238..1a290ad7708419fe7fdb1656987c2b4a881c2b0f 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -18,6 +18,7 @@ enum class Category : u32 {
     Cpu,
     CpuDebug,
     CpuUnsafe,
+    Overlay,
     Renderer,
     RendererAdvanced,
     RendererDebug,