diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 9c32e044c0..5a7cf4ed73 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -219,10 +219,6 @@ object NativeLibrary {
 
     external fun reloadSettings()
 
-    external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String?
-
-    external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?)
-
     external fun initGameIni(gameID: String?)
 
     /**
@@ -413,14 +409,17 @@ object NativeLibrary {
                     details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
                 )
             }
+
             CoreError.ErrorSavestate -> {
                 title = emulationActivity.getString(R.string.save_load_error)
                 message = details
             }
+
             CoreError.ErrorUnknown -> {
                 title = emulationActivity.getString(R.string.fatal_error)
                 message = emulationActivity.getString(R.string.fatal_error_message)
             }
+
             else -> {
                 return true
             }
@@ -454,6 +453,7 @@ object NativeLibrary {
                 captionId = R.string.loader_error_video_core
                 descriptionId = R.string.loader_error_video_core_description
             }
+
             else -> {
                 captionId = R.string.loader_error_encrypted
                 descriptionId = R.string.loader_error_encrypted_roms_description
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 7461fb093f..6f52a7a8da 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
@@ -28,7 +28,6 @@ import android.view.Surface
 import android.view.View
 import android.view.inputmethod.InputMethodManager
 import android.widget.Toast
-import androidx.activity.viewModels
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
@@ -42,7 +41,6 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
 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.model.SettingsViewModel
 import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
 import org.yuzu.yuzu_emu.utils.ForegroundService
@@ -72,8 +70,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
     private val actionMute = "ACTION_EMULATOR_MUTE"
     private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
 
-    private val settingsViewModel: SettingsViewModel by viewModels()
-
     override fun onDestroy() {
         stopForegroundService(this)
         super.onDestroy()
@@ -82,8 +78,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
     override fun onCreate(savedInstanceState: Bundle?) {
         ThemeHelper.setTheme(this)
 
-        settingsViewModel.settings.loadSettings()
-
         super.onCreate(savedInstanceState)
 
         binding = ActivityEmulationBinding.inflate(layoutInflater)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
index a6e9833ee7..aeda8d2220 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
@@ -4,5 +4,7 @@
 package org.yuzu.yuzu_emu.features.settings.model
 
 interface AbstractBooleanSetting : AbstractSetting {
-    var boolean: Boolean
+    val boolean: Boolean
+
+    fun setBoolean(value: Boolean)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
similarity index 59%
rename from src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt
rename to src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
index bd9233d625..606519ad84 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
@@ -3,8 +3,8 @@
 
 package org.yuzu.yuzu_emu.features.settings.model
 
-import androidx.lifecycle.ViewModel
+interface AbstractByteSetting : AbstractSetting {
+    val byte: Byte
 
-class SettingsViewModel : ViewModel() {
-    val settings = Settings()
+    fun setByte(value: Byte)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
index 6fe4bc2635..974925eeda 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
@@ -4,5 +4,7 @@
 package org.yuzu.yuzu_emu.features.settings.model
 
 interface AbstractFloatSetting : AbstractSetting {
-    var float: Float
+    val float: Float
+
+    fun setFloat(value: Float)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
index 892b7dcfe1..89b285b108 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
@@ -4,5 +4,7 @@
 package org.yuzu.yuzu_emu.features.settings.model
 
 interface AbstractIntSetting : AbstractSetting {
-    var int: Int
+    val int: Int
+
+    fun setInt(value: Int)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
new file mode 100644
index 0000000000..4873942db7
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model
+
+interface AbstractLongSetting : AbstractSetting {
+    val long: Long
+
+    fun setLong(value: Long)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
index 2585802092..7afed95adf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
@@ -3,10 +3,17 @@
 
 package org.yuzu.yuzu_emu.features.settings.model
 
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
 interface AbstractSetting {
     val key: String?
-    val section: String?
-    val isRuntimeEditable: Boolean
-    val valueAsString: String
+    val category: Settings.Category
     val defaultValue: Any
+    val valueAsString: String
+        get() = ""
+
+    val isRuntimeModifiable: Boolean
+        get() = NativeConfig.getIsRuntimeModifiable(key!!)
+
+    fun reset() = run { }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
new file mode 100644
index 0000000000..91407ccbb4
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model
+
+interface AbstractShortSetting : AbstractSetting {
+    val short: Short
+
+    fun setShort(value: Short)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
index 0d02c59973..c8935cc48c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
@@ -4,5 +4,7 @@
 package org.yuzu.yuzu_emu.features.settings.model
 
 interface AbstractStringSetting : AbstractSetting {
-    var string: String
+    val string: String
+
+    fun setString(value: String)
 }
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 d41933766d..f7528642e9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -3,41 +3,34 @@
 
 package org.yuzu.yuzu_emu.features.settings.model
 
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
 enum class BooleanSetting(
     override val key: String,
-    override val section: String,
-    override val defaultValue: Boolean
+    override val category: Settings.Category
 ) : AbstractBooleanSetting {
-    CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false),
-    FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true),
-    FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true),
-    PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true),
-    USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
+    CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu),
+    FASTMEM("cpuopt_fastmem", Settings.Category.Cpu),
+    FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu),
+    RENDERER_USE_SPEED_LIMIT("use_speed_limit", Settings.Category.Core),
+    USE_DOCKED_MODE("use_docked_mode", Settings.Category.System),
+    RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache", Settings.Category.Renderer),
+    RENDERER_FORCE_MAX_CLOCK("force_max_clock", Settings.Category.Renderer),
+    RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders", Settings.Category.Renderer),
+    RENDERER_REACTIVE_FLUSHING("use_reactive_flushing", Settings.Category.Renderer),
+    RENDERER_DEBUG("debug", Settings.Category.Renderer),
+    PICTURE_IN_PICTURE("picture_in_picture", Settings.Category.Android),
+    USE_CUSTOM_RTC("custom_rtc_enabled", Settings.Category.System);
 
-    override var boolean: Boolean = defaultValue
+    override val boolean: Boolean
+        get() = NativeConfig.getBoolean(key, false)
+
+    override fun setBoolean(value: Boolean) = NativeConfig.setBoolean(key, value)
+
+    override val defaultValue: Boolean by lazy { NativeConfig.getBoolean(key, true) }
 
     override val valueAsString: String
-        get() = boolean.toString()
+        get() = if (boolean) "1" else "0"
 
-    override val isRuntimeEditable: Boolean
-        get() {
-            for (setting in NOT_RUNTIME_EDITABLE) {
-                if (setting == this) {
-                    return false
-                }
-            }
-            return true
-        }
-
-    companion object {
-        private val NOT_RUNTIME_EDITABLE = listOf(
-            PICTURE_IN_PICTURE,
-            USE_CUSTOM_RTC
-        )
-
-        fun from(key: String): BooleanSetting? =
-            BooleanSetting.values().firstOrNull { it.key == key }
-
-        fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue }
-    }
+    override fun reset() = NativeConfig.setBoolean(key, defaultValue)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
new file mode 100644
index 0000000000..6ec0a765ef
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model
+
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
+enum class ByteSetting(
+    override val key: String,
+    override val category: Settings.Category
+) : AbstractByteSetting {
+    AUDIO_VOLUME("volume", Settings.Category.Audio);
+
+    override val byte: Byte
+        get() = NativeConfig.getByte(key, false)
+
+    override fun setByte(value: Byte) = NativeConfig.setByte(key, value)
+
+    override val defaultValue: Byte by lazy { NativeConfig.getByte(key, true) }
+
+    override val valueAsString: String
+        get() = byte.toString()
+
+    override fun reset() = NativeConfig.setByte(key, defaultValue)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
index e5545a916a..0181d06f21 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
@@ -3,34 +3,24 @@
 
 package org.yuzu.yuzu_emu.features.settings.model
 
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
 enum class FloatSetting(
     override val key: String,
-    override val section: String,
-    override val defaultValue: Float
+    override val category: Settings.Category
 ) : AbstractFloatSetting {
     // No float settings currently exist
-    EMPTY_SETTING("", "", 0f);
+    EMPTY_SETTING("", Settings.Category.UiGeneral);
 
-    override var float: Float = defaultValue
+    override val float: Float
+        get() = NativeConfig.getFloat(key, false)
+
+    override fun setFloat(value: Float) = NativeConfig.setFloat(key, value)
+
+    override val defaultValue: Float by lazy { NativeConfig.getFloat(key, true) }
 
     override val valueAsString: String
         get() = float.toString()
 
-    override val isRuntimeEditable: Boolean
-        get() {
-            for (setting in NOT_RUNTIME_EDITABLE) {
-                if (setting == this) {
-                    return false
-                }
-            }
-            return true
-        }
-
-    companion object {
-        private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
-
-        fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key }
-
-        fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue }
-    }
+    override fun reset() = NativeConfig.setFloat(key, defaultValue)
 }
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 4427a7d9dc..a647572071 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -3,139 +3,34 @@
 
 package org.yuzu.yuzu_emu.features.settings.model
 
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
 enum class IntSetting(
     override val key: String,
-    override val section: String,
-    override val defaultValue: Int
+    override val category: Settings.Category
 ) : AbstractIntSetting {
-    RENDERER_USE_SPEED_LIMIT(
-        "use_speed_limit",
-        Settings.SECTION_RENDERER,
-        1
-    ),
-    USE_DOCKED_MODE(
-        "use_docked_mode",
-        Settings.SECTION_SYSTEM,
-        0
-    ),
-    RENDERER_USE_DISK_SHADER_CACHE(
-        "use_disk_shader_cache",
-        Settings.SECTION_RENDERER,
-        1
-    ),
-    RENDERER_FORCE_MAX_CLOCK(
-        "force_max_clock",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_ASYNCHRONOUS_SHADERS(
-        "use_asynchronous_shaders",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_REACTIVE_FLUSHING(
-        "use_reactive_flushing",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_DEBUG(
-        "debug",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_SPEED_LIMIT(
-        "speed_limit",
-        Settings.SECTION_RENDERER,
-        100
-    ),
-    CPU_ACCURACY(
-        "cpu_accuracy",
-        Settings.SECTION_CPU,
-        0
-    ),
-    REGION_INDEX(
-        "region_index",
-        Settings.SECTION_SYSTEM,
-        -1
-    ),
-    LANGUAGE_INDEX(
-        "language_index",
-        Settings.SECTION_SYSTEM,
-        1
-    ),
-    RENDERER_BACKEND(
-        "backend",
-        Settings.SECTION_RENDERER,
-        1
-    ),
-    RENDERER_ACCURACY(
-        "gpu_accuracy",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_RESOLUTION(
-        "resolution_setup",
-        Settings.SECTION_RENDERER,
-        2
-    ),
-    RENDERER_VSYNC(
-        "use_vsync",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_SCALING_FILTER(
-        "scaling_filter",
-        Settings.SECTION_RENDERER,
-        1
-    ),
-    RENDERER_ANTI_ALIASING(
-        "anti_aliasing",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    RENDERER_SCREEN_LAYOUT(
-        "screen_layout",
-        Settings.SECTION_RENDERER,
-        Settings.LayoutOption_MobileLandscape
-    ),
-    RENDERER_ASPECT_RATIO(
-        "aspect_ratio",
-        Settings.SECTION_RENDERER,
-        0
-    ),
-    AUDIO_VOLUME(
-        "volume",
-        Settings.SECTION_AUDIO,
-        100
-    );
+    CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
+    REGION_INDEX("region_index", Settings.Category.System),
+    LANGUAGE_INDEX("language_index", Settings.Category.System),
+    RENDERER_BACKEND("backend", Settings.Category.Renderer),
+    RENDERER_ACCURACY("gpu_accuracy", Settings.Category.Renderer),
+    RENDERER_RESOLUTION("resolution_setup", Settings.Category.Renderer),
+    RENDERER_VSYNC("use_vsync", Settings.Category.Renderer),
+    RENDERER_SCALING_FILTER("scaling_filter", Settings.Category.Renderer),
+    RENDERER_ANTI_ALIASING("anti_aliasing", Settings.Category.Renderer),
+    RENDERER_SCREEN_LAYOUT("screen_layout", Settings.Category.Android),
+    RENDERER_ASPECT_RATIO("aspect_ratio", Settings.Category.Renderer),
+    AUDIO_OUTPUT_ENGINE("output_engine", Settings.Category.Audio);
 
-    override var int: Int = defaultValue
+    override val int: Int
+        get() = NativeConfig.getInt(key, false)
+
+    override fun setInt(value: Int) = NativeConfig.setInt(key, value)
+
+    override val defaultValue: Int by lazy { NativeConfig.getInt(key, true) }
 
     override val valueAsString: String
         get() = int.toString()
 
-    override val isRuntimeEditable: Boolean
-        get() {
-            for (setting in NOT_RUNTIME_EDITABLE) {
-                if (setting == this) {
-                    return false
-                }
-            }
-            return true
-        }
-
-    companion object {
-        private val NOT_RUNTIME_EDITABLE = listOf(
-            RENDERER_USE_DISK_SHADER_CACHE,
-            RENDERER_ASYNCHRONOUS_SHADERS,
-            RENDERER_DEBUG,
-            RENDERER_BACKEND,
-            RENDERER_RESOLUTION,
-            RENDERER_VSYNC
-        )
-
-        fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
-
-        fun clear() = IntSetting.values().forEach { it.int = it.defaultValue }
-    }
+    override fun reset() = NativeConfig.setInt(key, defaultValue)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
new file mode 100644
index 0000000000..c526fc4cfa
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model
+
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
+enum class LongSetting(
+    override val key: String,
+    override val category: Settings.Category
+) : AbstractLongSetting {
+    CUSTOM_RTC("custom_rtc", Settings.Category.System);
+
+    override val long: Long
+        get() = NativeConfig.getLong(key, false)
+
+    override fun setLong(value: Long) = NativeConfig.setLong(key, value)
+
+    override val defaultValue: Long by lazy { NativeConfig.getLong(key, true) }
+
+    override val valueAsString: String
+        get() = long.toString()
+
+    override fun reset() = NativeConfig.setLong(key, defaultValue)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
deleted file mode 100644
index 474f598a91..0000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.features.settings.model
-
-/**
- * A semantically-related group of Settings objects. These Settings are
- * internally stored as a HashMap.
- */
-class SettingSection(val name: String) {
-    val settings = HashMap<String, AbstractSetting>()
-
-    /**
-     * Convenience method; inserts a value directly into the backing HashMap.
-     *
-     * @param setting The Setting to be inserted.
-     */
-    fun putSetting(setting: AbstractSetting) {
-        settings[setting.key!!] = setting
-    }
-
-    /**
-     * Convenience method; gets a value directly from the backing HashMap.
-     *
-     * @param key Used to retrieve the Setting.
-     * @return A Setting object (you should probably cast this before using)
-     */
-    fun getSetting(key: String): AbstractSetting? {
-        return settings[key]
-    }
-
-    fun mergeSection(settingSection: SettingSection) {
-        for (setting in settingSection.settings.values) {
-            putSetting(setting)
-        }
-    }
-}
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 a6251bafdd..0702236e81 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,195 +4,151 @@
 package org.yuzu.yuzu_emu.features.settings.model
 
 import android.text.TextUtils
-import java.util.*
+import android.widget.Toast
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 
-class Settings {
-    private var gameId: String? = null
+object Settings {
+    private val context get() = YuzuApplication.appContext
 
-    var isLoaded = false
-
-    /**
-     * A HashMap<String></String>, SettingSection> that constructs a new SettingSection instead of returning null
-     * when getting a key not already in the map
-     */
-    class SettingsSectionMap : HashMap<String, SettingSection?>() {
-        override operator fun get(key: String): SettingSection? {
-            if (!super.containsKey(key)) {
-                val section = SettingSection(key)
-                super.put(key, section)
-                return section
-            }
-            return super.get(key)
-        }
-    }
-
-    var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
-
-    fun getSection(sectionName: String): SettingSection? {
-        return sections[sectionName]
-    }
-
-    val isEmpty: Boolean
-        get() = sections.isEmpty()
-
-    fun loadSettings(view: SettingsActivityView? = null) {
-        sections = SettingsSectionMap()
-        loadYuzuSettings(view)
-        if (!TextUtils.isEmpty(gameId)) {
-            loadCustomGameSettings(gameId!!, view)
-        }
-        isLoaded = true
-    }
-
-    private fun loadYuzuSettings(view: SettingsActivityView?) {
-        for ((fileName) in configFileSectionsMap) {
-            sections.putAll(SettingsFile.readFile(fileName, view))
-        }
-    }
-
-    private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) {
-        // Custom game settings
-        mergeSections(SettingsFile.readCustomGameSettings(gameId, view))
-    }
-
-    private fun mergeSections(updatedSections: HashMap<String, SettingSection?>) {
-        for ((key, updatedSection) in updatedSections) {
-            if (sections.containsKey(key)) {
-                val originalSection = sections[key]
-                originalSection!!.mergeSection(updatedSection!!)
-            } else {
-                sections[key] = updatedSection
-            }
-        }
-    }
-
-    fun loadSettings(gameId: String, view: SettingsActivityView) {
-        this.gameId = gameId
-        loadSettings(view)
-    }
-
-    fun saveSettings(view: SettingsActivityView) {
+    fun saveSettings(gameId: String = "") {
         if (TextUtils.isEmpty(gameId)) {
-            view.showToastMessage(
-                YuzuApplication.appContext.getString(R.string.ini_saved),
-                false
-            )
-
-            for ((fileName, sectionNames) in configFileSectionsMap) {
-                val iniSections = TreeMap<String, SettingSection>()
-                for (section in sectionNames) {
-                    iniSections[section] = sections[section]!!
-                }
-
-                SettingsFile.saveFile(fileName, iniSections, view)
-            }
+            Toast.makeText(
+                context,
+                context.getString(R.string.ini_saved),
+                Toast.LENGTH_SHORT
+            ).show()
+            SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
         } else {
-            // Custom game settings
-            view.showToastMessage(
-                YuzuApplication.appContext.getString(R.string.gameid_saved, gameId),
-                false
-            )
-
-            SettingsFile.saveCustomGameSettings(gameId, sections)
+            // TODO: Save custom game settings
+            Toast.makeText(
+                context,
+                context.getString(R.string.gameid_saved, gameId),
+                Toast.LENGTH_SHORT
+            ).show()
         }
     }
 
-    companion object {
-        const val SECTION_GENERAL = "General"
-        const val SECTION_SYSTEM = "System"
-        const val SECTION_RENDERER = "Renderer"
-        const val SECTION_AUDIO = "Audio"
-        const val SECTION_CPU = "Cpu"
-        const val SECTION_THEME = "Theme"
-        const val SECTION_DEBUG = "Debug"
-
-        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
-        )
-
-        const val PREF_CONTROL_SCALE = "controlScale"
-        const val PREF_CONTROL_OPACITY = "controlOpacity"
-        const val PREF_TOUCH_ENABLED = "isTouchEnabled"
-        const val PREF_BUTTON_A = "buttonToggle0"
-        const val PREF_BUTTON_B = "buttonToggle1"
-        const val PREF_BUTTON_X = "buttonToggle2"
-        const val PREF_BUTTON_Y = "buttonToggle3"
-        const val PREF_BUTTON_L = "buttonToggle4"
-        const val PREF_BUTTON_R = "buttonToggle5"
-        const val PREF_BUTTON_ZL = "buttonToggle6"
-        const val PREF_BUTTON_ZR = "buttonToggle7"
-        const val PREF_BUTTON_PLUS = "buttonToggle8"
-        const val PREF_BUTTON_MINUS = "buttonToggle9"
-        const val PREF_BUTTON_DPAD = "buttonToggle10"
-        const val PREF_STICK_L = "buttonToggle11"
-        const val PREF_STICK_R = "buttonToggle12"
-        const val PREF_BUTTON_STICK_L = "buttonToggle13"
-        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"
-
-        const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
-        const val PREF_THEME = "Theme"
-        const val PREF_THEME_MODE = "ThemeMode"
-        const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
-
-        private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
-
-        val overlayPreferences = listOf(
-            PREF_OVERLAY_VERSION,
-            PREF_CONTROL_SCALE,
-            PREF_CONTROL_OPACITY,
-            PREF_TOUCH_ENABLED,
-            PREF_BUTTON_A,
-            PREF_BUTTON_B,
-            PREF_BUTTON_X,
-            PREF_BUTTON_Y,
-            PREF_BUTTON_L,
-            PREF_BUTTON_R,
-            PREF_BUTTON_ZL,
-            PREF_BUTTON_ZR,
-            PREF_BUTTON_PLUS,
-            PREF_BUTTON_MINUS,
-            PREF_BUTTON_DPAD,
-            PREF_STICK_L,
-            PREF_STICK_R,
-            PREF_BUTTON_HOME,
-            PREF_BUTTON_SCREENSHOT,
-            PREF_BUTTON_STICK_L,
-            PREF_BUTTON_STICK_R
-        )
-
-        const val LayoutOption_Unspecified = 0
-        const val LayoutOption_MobilePortrait = 4
-        const val LayoutOption_MobileLandscape = 5
-
-        init {
-            configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
-                listOf(
-                    SECTION_GENERAL,
-                    SECTION_SYSTEM,
-                    SECTION_RENDERER,
-                    SECTION_AUDIO,
-                    SECTION_CPU
-                )
-        }
+    enum class Category {
+        Android,
+        Audio,
+        Core,
+        Cpu,
+        CpuDebug,
+        CpuUnsafe,
+        Renderer,
+        RendererAdvanced,
+        RendererDebug,
+        System,
+        SystemAudio,
+        DataStorage,
+        Debugging,
+        DebuggingGraphics,
+        Miscellaneous,
+        Network,
+        WebService,
+        AddOns,
+        Controls,
+        Ui,
+        UiGeneral,
+        UiLayout,
+        UiGameList,
+        Screenshots,
+        Shortcuts,
+        Multiplayer,
+        Services,
+        Paths,
+        MaxEnum
     }
+
+    val settingsList = listOf<AbstractSetting>(
+        *BooleanSetting.values(),
+        *ByteSetting.values(),
+        *ShortSetting.values(),
+        *IntSetting.values(),
+        *FloatSetting.values(),
+        *LongSetting.values(),
+        *StringSetting.values()
+    )
+
+    const val SECTION_GENERAL = "General"
+    const val SECTION_SYSTEM = "System"
+    const val SECTION_RENDERER = "Renderer"
+    const val SECTION_AUDIO = "Audio"
+    const val SECTION_CPU = "Cpu"
+    const val SECTION_THEME = "Theme"
+    const val SECTION_DEBUG = "Debug"
+
+    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
+    )
+
+    const val PREF_CONTROL_SCALE = "controlScale"
+    const val PREF_CONTROL_OPACITY = "controlOpacity"
+    const val PREF_TOUCH_ENABLED = "isTouchEnabled"
+    const val PREF_BUTTON_A = "buttonToggle0"
+    const val PREF_BUTTON_B = "buttonToggle1"
+    const val PREF_BUTTON_X = "buttonToggle2"
+    const val PREF_BUTTON_Y = "buttonToggle3"
+    const val PREF_BUTTON_L = "buttonToggle4"
+    const val PREF_BUTTON_R = "buttonToggle5"
+    const val PREF_BUTTON_ZL = "buttonToggle6"
+    const val PREF_BUTTON_ZR = "buttonToggle7"
+    const val PREF_BUTTON_PLUS = "buttonToggle8"
+    const val PREF_BUTTON_MINUS = "buttonToggle9"
+    const val PREF_BUTTON_DPAD = "buttonToggle10"
+    const val PREF_STICK_L = "buttonToggle11"
+    const val PREF_STICK_R = "buttonToggle12"
+    const val PREF_BUTTON_STICK_L = "buttonToggle13"
+    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"
+
+    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,
+        PREF_BUTTON_Y,
+        PREF_BUTTON_L,
+        PREF_BUTTON_R,
+        PREF_BUTTON_ZL,
+        PREF_BUTTON_ZR,
+        PREF_BUTTON_PLUS,
+        PREF_BUTTON_MINUS,
+        PREF_BUTTON_DPAD,
+        PREF_STICK_L,
+        PREF_STICK_R,
+        PREF_BUTTON_HOME,
+        PREF_BUTTON_SCREENSHOT,
+        PREF_BUTTON_STICK_L,
+        PREF_BUTTON_STICK_R
+    )
+
+    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/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
new file mode 100644
index 0000000000..c9a0c664cb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model
+
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
+enum class ShortSetting(
+    override val key: String,
+    override val category: Settings.Category
+) : AbstractShortSetting {
+    RENDERER_SPEED_LIMIT("speed_limit", Settings.Category.Core);
+
+    override val short: Short
+        get() = NativeConfig.getShort(key, false)
+
+    override fun setShort(value: Short) = NativeConfig.setShort(key, value)
+
+    override val defaultValue: Short by lazy { NativeConfig.getShort(key, true) }
+
+    override val valueAsString: String
+        get() = short.toString()
+
+    override fun reset() = NativeConfig.setShort(key, defaultValue)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 6621289fd5..9bb3e66d4f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -3,36 +3,24 @@
 
 package org.yuzu.yuzu_emu.features.settings.model
 
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
 enum class StringSetting(
     override val key: String,
-    override val section: String,
-    override val defaultValue: String
+    override val category: Settings.Category
 ) : AbstractStringSetting {
-    AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"),
-    CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
+    // No string settings currently exist
+    EMPTY_SETTING("", Settings.Category.UiGeneral);
 
-    override var string: String = defaultValue
+    override val string: String
+        get() = NativeConfig.getString(key, false)
+
+    override fun setString(value: String) = NativeConfig.setString(key, value)
+
+    override val defaultValue: String by lazy { NativeConfig.getString(key, true) }
 
     override val valueAsString: String
         get() = string
 
-    override val isRuntimeEditable: Boolean
-        get() {
-            for (setting in NOT_RUNTIME_EDITABLE) {
-                if (setting == this) {
-                    return false
-                }
-            }
-            return true
-        }
-
-    companion object {
-        private val NOT_RUNTIME_EDITABLE = listOf(
-            CUSTOM_RTC
-        )
-
-        fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
-
-        fun clear() = StringSetting.values().forEach { it.string = it.defaultValue }
-    }
+    override fun reset() = NativeConfig.setString(key, defaultValue)
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index bc0bf77889..7c858916e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
@@ -3,29 +3,29 @@
 
 package org.yuzu.yuzu_emu.features.settings.model.view
 
+import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
 
 class DateTimeSetting(
     setting: AbstractSetting?,
     titleId: Int,
     descriptionId: Int,
     val key: String? = null,
-    private val defaultValue: String? = null
+    private val defaultValue: Long? = null
 ) : SettingsItem(setting, titleId, descriptionId) {
     override val type = TYPE_DATETIME_SETTING
 
-    val value: String
+    val value: Long
         get() = if (setting != null) {
-            val setting = setting as AbstractStringSetting
-            setting.string
+            val setting = setting as AbstractLongSetting
+            setting.long
         } else {
             defaultValue!!
         }
 
-    fun setSelectedValue(datetime: String): AbstractStringSetting {
-        val stringSetting = setting as AbstractStringSetting
-        stringSetting.string = datetime
-        return stringSetting
+    fun setSelectedValue(datetime: Long): AbstractLongSetting {
+        val longSetting = setting as AbstractLongSetting
+        longSetting.setLong(datetime)
+        return longSetting
     }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 07520849ec..a6cba977c6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -23,7 +23,7 @@ abstract class SettingsItem(
     val isEditable: Boolean
         get() {
             if (!NativeLibrary.isRunning()) return true
-            return setting?.isRuntimeEditable ?: false
+            return setting?.isRuntimeModifiable ?: false
         }
 
     companion object {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 7306ec458e..b6a8c46128 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -33,7 +33,7 @@ class SingleChoiceSetting(
      */
     fun setSelectedValue(selection: Int): AbstractIntSetting {
         val intSetting = setting as AbstractIntSetting
-        intSetting.int = selection
+        intSetting.setInt(selection)
         return intSetting
     }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 92d0167ae0..e71a29e354 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,10 +3,12 @@
 
 package org.yuzu.yuzu_emu.features.settings.model.view
 
+import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
 import kotlin.math.roundToInt
 import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
+import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
 import org.yuzu.yuzu_emu.utils.Log
 
 class SliderSetting(
@@ -17,14 +19,16 @@ class SliderSetting(
     val max: Int,
     val units: String,
     val key: String? = null,
-    val defaultValue: Int? = null
+    val defaultValue: Any? = null
 ) : SettingsItem(setting, titleId, descriptionId) {
     override val type = TYPE_SLIDER
 
-    val selectedValue: Int
+    val selectedValue: Any
         get() {
             val setting = setting ?: return defaultValue!!
             return when (setting) {
+                is AbstractByteSetting -> setting.byte.toInt()
+                is AbstractShortSetting -> setting.short.toInt()
                 is AbstractIntSetting -> setting.int
                 is AbstractFloatSetting -> setting.float.roundToInt()
                 else -> {
@@ -43,7 +47,7 @@ class SliderSetting(
      */
     fun setSelectedValue(selection: Int): AbstractIntSetting {
         val intSetting = setting as AbstractIntSetting
-        intSetting.int = selection
+        intSetting.setInt(selection)
         return intSetting
     }
 
@@ -56,7 +60,19 @@ class SliderSetting(
      */
     fun setSelectedValue(selection: Float): AbstractFloatSetting {
         val floatSetting = setting as AbstractFloatSetting
-        floatSetting.float = selection
+        floatSetting.setFloat(selection)
         return floatSetting
     }
+
+    fun setSelectedValue(selection: Short): AbstractShortSetting {
+        val shortSetting = setting as AbstractShortSetting
+        shortSetting.setShort(selection)
+        return shortSetting
+    }
+
+    fun setSelectedValue(selection: Byte): AbstractByteSetting {
+        val byteSetting = setting as AbstractByteSetting
+        byteSetting.setByte(selection)
+        return byteSetting
+    }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 3b6731dcd5..2195641e3f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -53,7 +53,7 @@ class StringSingleChoiceSetting(
      */
     fun setSelectedValue(selection: String): AbstractStringSetting {
         val stringSetting = setting as AbstractStringSetting
-        stringSetting.string = selection
+        stringSetting.setString(selection)
         return stringSetting
     }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 90b198718a..4ed8070e5b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
@@ -49,14 +49,14 @@ class SwitchSetting(
         // Try integer setting
         try {
             val setting = setting as AbstractIntSetting
-            setting.int = if (checked) 1 else 0
+            setting.setInt(if (checked) 1 else 0)
             return setting
         } catch (_: ClassCastException) {
         }
 
         // Try boolean setting
         val setting = setting as AbstractBooleanSetting
-        setting.boolean = checked
+        setting.setBoolean(checked)
         return setting
     }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index e6fffc8328..733a53c8cf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -21,12 +21,7 @@ import com.google.android.material.color.MaterialColors
 import java.io.IOException
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
-import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
-import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
-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.model.SettingsViewModel
-import org.yuzu.yuzu_emu.features.settings.model.StringSetting
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.utils.*
 
@@ -35,10 +30,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
 
     private lateinit var binding: ActivitySettingsBinding
 
-    private val settingsViewModel: SettingsViewModel by viewModels()
-
-    override val settings: Settings get() = settingsViewModel.settings
-
     override fun onCreate(savedInstanceState: Bundle?) {
         ThemeHelper.setTheme(this)
 
@@ -171,14 +162,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
         fragment?.loadSettingsList()
     }
 
-    override fun showToastMessage(message: String, is_long: Boolean) {
-        Toast.makeText(
-            this,
-            message,
-            if (is_long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
-        ).show()
-    }
-
     override fun onSettingChanged() {
         presenter.onSettingChanged()
     }
@@ -187,19 +170,18 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
         // Prevents saving to a non-existent settings file
         presenter.onSettingsReset()
 
-        // Reset the static memory representation of each setting
-        BooleanSetting.clear()
-        FloatSetting.clear()
-        IntSetting.clear()
-        StringSetting.clear()
-
         // Delete settings file because the user may have changed values that do not exist in the UI
         val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
         if (!settingsFile.delete()) {
             throw IOException("Failed to delete $settingsFile")
         }
+        Settings.settingsList.forEach { it.reset() }
 
-        showToastMessage(getString(R.string.settings_reset), true)
+        Toast.makeText(
+            applicationContext,
+            getString(R.string.settings_reset),
+            Toast.LENGTH_LONG
+        ).show()
         finish()
     }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
index 93e677b21b..fdbad32bf1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.ui
 
 import android.content.Context
 import android.os.Bundle
-import android.text.TextUtils
 import java.io.File
 import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.features.settings.model.Settings
@@ -14,8 +13,6 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.Log
 
 class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
-    val settings: Settings get() = activityView.settings
-
     private var shouldSave = false
     private lateinit var menuTag: String
     private lateinit var gameId: String
@@ -33,13 +30,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
     }
 
     private fun loadSettingsUI() {
-        if (!settings.isLoaded) {
-            if (!TextUtils.isEmpty(gameId)) {
-                settings.loadSettings(gameId, activityView)
-            } else {
-                settings.loadSettings(activityView)
-            }
-        }
+        // TODO: Load custom settings contextually
         activityView.showSettingsFragment(menuTag, false, gameId)
         activityView.onSettingsFileLoaded()
     }
@@ -67,9 +58,9 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
     fun onStop(finishing: Boolean) {
         if (finishing && shouldSave) {
             Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
-            settings.saveSettings(activityView)
+            Settings.saveSettings()
+            NativeLibrary.reloadSettings()
         }
-        NativeLibrary.reloadSettings()
     }
 
     fun onSettingChanged() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
index c186fc388c..07a58b4eaa 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
@@ -3,8 +3,6 @@
 
 package org.yuzu.yuzu_emu.features.settings.ui
 
-import org.yuzu.yuzu_emu.features.settings.model.Settings
-
 /**
  * Abstraction for the Activity that manages SettingsFragments.
  */
@@ -17,15 +15,6 @@ interface SettingsActivityView {
      */
     fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
 
-    /**
-     * Called by a contained Fragment to get access to the Setting HashMap
-     * loaded from disk, so that each Fragment doesn't need to perform its own
-     * read operation.
-     *
-     * @return A HashMap of Settings.
-     */
-    val settings: Settings
-
     /**
      * Called when a load operation completes.
      */
@@ -36,14 +25,6 @@ interface SettingsActivityView {
      */
     fun onSettingsFileNotFound()
 
-    /**
-     * Display a popup text message on screen.
-     *
-     * @param message The contents of the onscreen message.
-     * @param is_long Whether this should be a long Toast or short one.
-     */
-    fun showToastMessage(message: String, is_long: Boolean)
-
     /**
      * End the activity.
      */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 9711e2c51e..e2e8d8bece 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -24,12 +24,10 @@ import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
-import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
-import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
-import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
+import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
 import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
+import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
 
@@ -115,8 +113,7 @@ class SettingsAdapter(
     }
 
     fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
-        val setting = item.setChecked(checked)
-        fragmentView.putSetting(setting)
+        item.setChecked(checked)
         fragmentView.onSettingChanged()
     }
 
@@ -150,7 +147,7 @@ class SettingsAdapter(
     fun onDateTimeClick(item: DateTimeSetting, position: Int) {
         clickedItem = item
         clickedPosition = position
-        val storedTime = java.lang.Long.decode(item.value) * 1000
+        val storedTime = item.value * 1000
 
         // Helper to extract hour and minute from epoch time
         val calendar: Calendar = Calendar.getInstance()
@@ -183,13 +180,11 @@ class SettingsAdapter(
             var epochTime: Long = datePicker.selection!! / 1000
             epochTime += timePicker.hour.toLong() * 60 * 60
             epochTime += timePicker.minute.toLong() * 60
-            val rtcString = epochTime.toString()
-            if (item.value != rtcString) {
+            if (item.value != epochTime) {
                 fragmentView.onSettingChanged()
+                notifyItemChanged(clickedPosition)
+                item.setSelectedValue(epochTime)
             }
-            notifyItemChanged(clickedPosition)
-            val setting = item.setSelectedValue(rtcString)
-            fragmentView.putSetting(setting)
             clickedItem = null
         }
         datePicker.show(
@@ -201,7 +196,7 @@ class SettingsAdapter(
     fun onSliderClick(item: SliderSetting, position: Int) {
         clickedItem = item
         clickedPosition = position
-        sliderProgress = item.selectedValue
+        sliderProgress = item.selectedValue as Int
 
         val inflater = LayoutInflater.from(context)
         val sliderBinding = DialogSliderBinding.inflate(inflater)
@@ -249,8 +244,7 @@ class SettingsAdapter(
                 }
 
                 // Get the backing Setting, which may be null (if for example it was missing from the file)
-                val setting = scSetting.setSelectedValue(value)
-                fragmentView.putSetting(setting)
+                scSetting.setSelectedValue(value)
                 closeDialog()
             }
 
@@ -258,8 +252,7 @@ class SettingsAdapter(
                 val scSetting = clickedItem as StringSingleChoiceSetting
                 val value = scSetting.getValueAt(which)
                 if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
-                val setting = scSetting.setSelectedValue(value!!)
-                fragmentView.putSetting(setting)
+                scSetting.setSelectedValue(value!!)
                 closeDialog()
             }
 
@@ -268,13 +261,25 @@ class SettingsAdapter(
                 if (sliderSetting.selectedValue != sliderProgress) {
                     fragmentView.onSettingChanged()
                 }
-                if (sliderSetting.setting is FloatSetting) {
-                    val value = sliderProgress.toFloat()
-                    val setting = sliderSetting.setSelectedValue(value)
-                    fragmentView.putSetting(setting)
-                } else {
-                    val setting = sliderSetting.setSelectedValue(sliderProgress)
-                    fragmentView.putSetting(setting)
+                when (sliderSetting.setting) {
+                    is ByteSetting -> {
+                        val value = sliderProgress.toByte()
+                        sliderSetting.setSelectedValue(value)
+                    }
+
+                    is ShortSetting -> {
+                        val value = sliderProgress.toShort()
+                        sliderSetting.setSelectedValue(value)
+                    }
+
+                    is FloatSetting -> {
+                        val value = sliderProgress.toFloat()
+                        sliderSetting.setSelectedValue(value)
+                    }
+
+                    else -> {
+                        sliderSetting.setSelectedValue(sliderProgress)
+                    }
                 }
                 closeDialog()
             }
@@ -286,13 +291,8 @@ class SettingsAdapter(
     fun onLongClick(setting: AbstractSetting, position: Int): Boolean {
         MaterialAlertDialogBuilder(context)
             .setMessage(R.string.reset_setting_confirmation)
-            .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int ->
-                when (setting) {
-                    is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean
-                    is AbstractFloatSetting -> setting.float = setting.defaultValue as Float
-                    is AbstractIntSetting -> setting.int = setting.defaultValue as Int
-                    is AbstractStringSetting -> setting.string = setting.defaultValue as String
-                }
+            .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
+                setting.reset()
                 notifyItemChanged(position)
                 fragmentView.onSettingChanged()
             }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 70a74c4dda..dc1bf6eb1d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -15,7 +15,6 @@ import androidx.fragment.app.Fragment
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.google.android.material.divider.MaterialDividerItemDecoration
 import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
-import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 
 class SettingsFragment : Fragment(), SettingsFragmentView {
@@ -89,14 +88,6 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
         )
     }
 
-    override fun showToastMessage(message: String?, is_long: Boolean) {
-        activityView!!.showToastMessage(message!!, is_long)
-    }
-
-    override fun putSetting(setting: AbstractSetting) {
-        fragmentPresenter.putSetting(setting)
-    }
-
     override fun onSettingChanged() {
         activityView!!.onSettingChanged()
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 59c1d9d545..2bab9e5427 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -6,16 +6,18 @@ package org.yuzu.yuzu_emu.features.settings.ui
 import android.content.SharedPreferences
 import android.os.Build
 import android.text.TextUtils
+import android.widget.Toast
 import androidx.preference.PreferenceManager
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
-import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
+import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
 import org.yuzu.yuzu_emu.features.settings.model.IntSetting
+import org.yuzu.yuzu_emu.features.settings.model.LongSetting
 import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.features.settings.model.StringSetting
+import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
@@ -27,7 +29,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
     private var settingsList: ArrayList<SettingsItem>? = null
 
     private val settingsActivity get() = fragmentView.activityView as SettingsActivity
-    private val settings get() = fragmentView.activityView!!.settings
 
     private lateinit var preferences: SharedPreferences
 
@@ -41,17 +42,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
         loadSettingsList()
     }
 
-    fun putSetting(setting: AbstractSetting) {
-        if (setting.section == null || setting.key == null) {
-            return
-        }
-
-        val section = settings.getSection(setting.section!!)!!
-        if (section.getSetting(setting.key!!) == null) {
-            section.putSetting(setting)
-        }
-    }
-
     fun loadSettingsList() {
         if (!TextUtils.isEmpty(gameId)) {
             settingsActivity.setToolbarTitle("Game Settings: $gameId")
@@ -69,7 +59,12 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             Settings.SECTION_THEME -> addThemeSettings(sl)
             Settings.SECTION_DEBUG -> addDebugSettings(sl)
             else -> {
-                fragmentView.showToastMessage("Unimplemented menu", false)
+                val context = YuzuApplication.appContext
+                Toast.makeText(
+                    context,
+                    context.getString(R.string.unimplemented_menu),
+                    Toast.LENGTH_SHORT
+                ).show()
                 return
             }
         }
@@ -135,23 +130,23 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
         sl.apply {
             add(
                 SwitchSetting(
-                    IntSetting.RENDERER_USE_SPEED_LIMIT,
+                    BooleanSetting.RENDERER_USE_SPEED_LIMIT,
                     R.string.frame_limit_enable,
                     R.string.frame_limit_enable_description,
-                    IntSetting.RENDERER_USE_SPEED_LIMIT.key,
-                    IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
+                    BooleanSetting.RENDERER_USE_SPEED_LIMIT.key,
+                    BooleanSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
                 )
             )
             add(
                 SliderSetting(
-                    IntSetting.RENDERER_SPEED_LIMIT,
+                    ShortSetting.RENDERER_SPEED_LIMIT,
                     R.string.frame_limit_slider,
                     R.string.frame_limit_slider_description,
                     1,
                     200,
                     "%",
-                    IntSetting.RENDERER_SPEED_LIMIT.key,
-                    IntSetting.RENDERER_SPEED_LIMIT.defaultValue
+                    ShortSetting.RENDERER_SPEED_LIMIT.key,
+                    ShortSetting.RENDERER_SPEED_LIMIT.defaultValue
                 )
             )
             add(
@@ -182,11 +177,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
         sl.apply {
             add(
                 SwitchSetting(
-                    IntSetting.USE_DOCKED_MODE,
+                    BooleanSetting.USE_DOCKED_MODE,
                     R.string.use_docked_mode,
                     R.string.use_docked_mode_description,
-                    IntSetting.USE_DOCKED_MODE.key,
-                    IntSetting.USE_DOCKED_MODE.defaultValue
+                    BooleanSetting.USE_DOCKED_MODE.key,
+                    BooleanSetting.USE_DOCKED_MODE.defaultValue
                 )
             )
             add(
@@ -222,11 +217,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             )
             add(
                 DateTimeSetting(
-                    StringSetting.CUSTOM_RTC,
+                    LongSetting.CUSTOM_RTC,
                     R.string.set_custom_rtc,
                     0,
-                    StringSetting.CUSTOM_RTC.key,
-                    StringSetting.CUSTOM_RTC.defaultValue
+                    LongSetting.CUSTOM_RTC.key,
+                    LongSetting.CUSTOM_RTC.defaultValue
                 )
             )
         }
@@ -314,38 +309,38 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             )
             add(
                 SwitchSetting(
-                    IntSetting.RENDERER_USE_DISK_SHADER_CACHE,
+                    BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
                     R.string.use_disk_shader_cache,
                     R.string.use_disk_shader_cache_description,
-                    IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
-                    IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
+                    BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
+                    BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
                 )
             )
             add(
                 SwitchSetting(
-                    IntSetting.RENDERER_FORCE_MAX_CLOCK,
+                    BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
                     R.string.renderer_force_max_clock,
                     R.string.renderer_force_max_clock_description,
-                    IntSetting.RENDERER_FORCE_MAX_CLOCK.key,
-                    IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
+                    BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key,
+                    BooleanSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
                 )
             )
             add(
                 SwitchSetting(
-                    IntSetting.RENDERER_ASYNCHRONOUS_SHADERS,
+                    BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
                     R.string.renderer_asynchronous_shaders,
                     R.string.renderer_asynchronous_shaders_description,
-                    IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
-                    IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
+                    BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
+                    BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
                 )
             )
             add(
                 SwitchSetting(
-                    IntSetting.RENDERER_REACTIVE_FLUSHING,
+                    BooleanSetting.RENDERER_REACTIVE_FLUSHING,
                     R.string.renderer_reactive_flushing,
                     R.string.renderer_reactive_flushing_description,
-                    IntSetting.RENDERER_REACTIVE_FLUSHING.key,
-                    IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
+                    BooleanSetting.RENDERER_REACTIVE_FLUSHING.key,
+                    BooleanSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
                 )
             )
         }
@@ -355,26 +350,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
         settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
         sl.apply {
             add(
-                StringSingleChoiceSetting(
-                    StringSetting.AUDIO_OUTPUT_ENGINE,
+                SingleChoiceSetting(
+                    IntSetting.AUDIO_OUTPUT_ENGINE,
                     R.string.audio_output_engine,
                     0,
-                    settingsActivity.resources.getStringArray(R.array.outputEngineEntries),
-                    settingsActivity.resources.getStringArray(R.array.outputEngineValues),
-                    StringSetting.AUDIO_OUTPUT_ENGINE.key,
-                    StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue
+                    R.array.outputEngineEntries,
+                    R.array.outputEngineValues,
+                    IntSetting.AUDIO_OUTPUT_ENGINE.key,
+                    IntSetting.AUDIO_OUTPUT_ENGINE.defaultValue
                 )
             )
             add(
                 SliderSetting(
-                    IntSetting.AUDIO_VOLUME,
+                    ByteSetting.AUDIO_VOLUME,
                     R.string.audio_volume,
                     R.string.audio_volume_description,
                     0,
                     100,
                     "%",
-                    IntSetting.AUDIO_VOLUME.key,
-                    IntSetting.AUDIO_VOLUME.defaultValue
+                    ByteSetting.AUDIO_VOLUME.key,
+                    ByteSetting.AUDIO_VOLUME.defaultValue
                 )
             )
         }
@@ -384,19 +379,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
         settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme))
         sl.apply {
             val theme: AbstractIntSetting = object : AbstractIntSetting {
-                override var int: Int
+                override val int: Int
                     get() = preferences.getInt(Settings.PREF_THEME, 0)
-                    set(value) {
-                        preferences.edit()
-                            .putInt(Settings.PREF_THEME, value)
-                            .apply()
-                        settingsActivity.recreate()
-                    }
+
+                override fun setInt(value: Int) {
+                    preferences.edit()
+                        .putInt(Settings.PREF_THEME, value)
+                        .apply()
+                    settingsActivity.recreate()
+                }
+
                 override val key: String? = null
-                override val section: String? = null
-                override val isRuntimeEditable: Boolean = false
-                override val valueAsString: String
-                    get() = preferences.getInt(Settings.PREF_THEME, 0).toString()
+                override val category = Settings.Category.UiGeneral
+                override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Any = 0
             }
 
@@ -423,19 +418,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             }
 
             val themeMode: AbstractIntSetting = object : AbstractIntSetting {
-                override var int: Int
+                override val int: Int
                     get() = preferences.getInt(Settings.PREF_THEME_MODE, -1)
-                    set(value) {
-                        preferences.edit()
-                            .putInt(Settings.PREF_THEME_MODE, value)
-                            .apply()
-                        ThemeHelper.setThemeMode(settingsActivity)
-                    }
+
+                override fun setInt(value: Int) {
+                    preferences.edit()
+                        .putInt(Settings.PREF_THEME_MODE, value)
+                        .apply()
+                    ThemeHelper.setThemeMode(settingsActivity)
+                }
+
                 override val key: String? = null
-                override val section: String? = null
-                override val isRuntimeEditable: Boolean = false
-                override val valueAsString: String
-                    get() = preferences.getInt(Settings.PREF_THEME_MODE, -1).toString()
+                override val category = Settings.Category.UiGeneral
+                override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Any = -1
             }
 
@@ -450,20 +445,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             )
 
             val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
-                override var boolean: Boolean
+                override val boolean: Boolean
                     get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
-                    set(value) {
-                        preferences.edit()
-                            .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
-                            .apply()
-                        settingsActivity.recreate()
-                    }
+
+                override fun setBoolean(value: Boolean) {
+                    preferences.edit()
+                        .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
+                        .apply()
+                    settingsActivity.recreate()
+                }
+
                 override val key: String? = null
-                override val section: String? = null
-                override val isRuntimeEditable: Boolean = false
-                override val valueAsString: String
-                    get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
-                        .toString()
+                override val category = Settings.Category.UiGeneral
+                override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Any = false
             }
 
@@ -494,11 +488,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             )
             add(
                 SwitchSetting(
-                    IntSetting.RENDERER_DEBUG,
+                    BooleanSetting.RENDERER_DEBUG,
                     R.string.renderer_debug,
                     R.string.renderer_debug_description,
-                    IntSetting.RENDERER_DEBUG.key,
-                    IntSetting.RENDERER_DEBUG.defaultValue
+                    BooleanSetting.RENDERER_DEBUG.key,
+                    BooleanSetting.RENDERER_DEBUG.defaultValue
                 )
             )
 
@@ -514,17 +508,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             )
 
             val fastmem = object : AbstractBooleanSetting {
-                override var boolean: Boolean
+                override val boolean: Boolean
                     get() =
                         BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
-                    set(value) {
-                        BooleanSetting.FASTMEM.boolean = value
-                        BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value
-                    }
+
+                override fun setBoolean(value: Boolean) {
+                    BooleanSetting.FASTMEM.setBoolean(value)
+                    BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value)
+                }
+
                 override val key: String? = null
-                override val section: String = Settings.SECTION_CPU
-                override val isRuntimeEditable: Boolean = false
-                override val valueAsString: String = ""
+                override val category = Settings.Category.Cpu
+                override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Any = true
             }
             add(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
index 1ebe35eaad..a4d7a80aac 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
@@ -3,7 +3,6 @@
 
 package org.yuzu.yuzu_emu.features.settings.ui
 
-import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 
 /**
@@ -36,21 +35,6 @@ interface SettingsFragmentView {
      */
     fun loadSubMenu(menuKey: String)
 
-    /**
-     * Tell the Fragment to tell the containing activity to display a toast message.
-     *
-     * @param message Text to be shown in the Toast
-     * @param is_long Whether this should be a long Toast or short one.
-     */
-    fun showToastMessage(message: String?, is_long: Boolean)
-
-    /**
-     * Have the fragment add a setting to the HashMap.
-     *
-     * @param setting The (possibly previously missing) new setting.
-     */
-    fun putSetting(setting: AbstractSetting)
-
     /**
      * Have the fragment tell the containing Activity that a setting was modified.
      */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 79572fc06b..eb25ea4fb7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -29,7 +29,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
         }
 
         binding.textSettingValue.visibility = View.VISIBLE
-        val epochTime = setting.value.toLong()
+        val epochTime = setting.value
         val instant = Instant.ofEpochMilli(epochTime * 1000)
         val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
         val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 70a52df5d4..2b04d666a9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,18 +3,15 @@
 
 package org.yuzu.yuzu_emu.features.settings.utils
 
+import android.widget.Toast
 import java.io.*
-import java.util.*
 import org.ini4j.Wini
-import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.features.settings.model.*
-import org.yuzu.yuzu_emu.features.settings.model.Settings.SettingsSectionMap
-import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
-import org.yuzu.yuzu_emu.utils.BiMap
 import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.Log
+import org.yuzu.yuzu_emu.utils.NativeConfig
 
 /**
  * Contains static methods for interacting with .ini files in which settings are stored.
@@ -22,243 +19,41 @@ import org.yuzu.yuzu_emu.utils.Log
 object SettingsFile {
     const val FILE_NAME_CONFIG = "config"
 
-    private var sectionsMap = BiMap<String?, String?>()
-
-    /**
-     * Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves
-     * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
-     * failed.
-     *
-     * @param ini          The ini file to load the settings from
-     * @param isCustomGame
-     * @param view         The current view.
-     * @return An Observable that emits a HashMap of the file's contents, then completes.
-     */
-    private fun readFile(
-        ini: File?,
-        isCustomGame: Boolean,
-        view: SettingsActivityView? = null
-    ): HashMap<String, SettingSection?> {
-        val sections: HashMap<String, SettingSection?> = SettingsSectionMap()
-        var reader: BufferedReader? = null
-        try {
-            reader = BufferedReader(FileReader(ini))
-            var current: SettingSection? = null
-            var line: String?
-            while (reader.readLine().also { line = it } != null) {
-                if (line!!.startsWith("[") && line!!.endsWith("]")) {
-                    current = sectionFromLine(line!!, isCustomGame)
-                    sections[current.name] = current
-                } else if (current != null) {
-                    val setting = settingFromLine(line!!)
-                    if (setting != null) {
-                        current.putSetting(setting)
-                    }
-                }
-            }
-        } catch (e: FileNotFoundException) {
-            Log.error("[SettingsFile] File not found: " + e.message)
-            view?.onSettingsFileNotFound()
-        } catch (e: IOException) {
-            Log.error("[SettingsFile] Error reading from: " + e.message)
-            view?.onSettingsFileNotFound()
-        } finally {
-            if (reader != null) {
-                try {
-                    reader.close()
-                } catch (e: IOException) {
-                    Log.error("[SettingsFile] Error closing: " + e.message)
-                }
-            }
-        }
-        return sections
-    }
-
-    fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> {
-        return readFile(getSettingsFile(fileName), false, view)
-    }
-
-    fun readFile(fileName: String): HashMap<String, SettingSection?> =
-        readFile(getSettingsFile(fileName), false)
-
-    /**
-     * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves
-     * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
-     * failed.
-     *
-     * @param gameId the id of the game to load it's settings.
-     * @param view   The current view.
-     */
-    fun readCustomGameSettings(
-        gameId: String,
-        view: SettingsActivityView?
-    ): HashMap<String, SettingSection?> {
-        return readFile(getCustomGameSettingsFile(gameId), true, view)
-    }
-
     /**
      * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
      * telling why it failed.
      *
      * @param fileName The target filename without a path or extension.
-     * @param sections The HashMap containing the Settings we want to serialize.
-     * @param view     The current view.
      */
-    fun saveFile(
-        fileName: String,
-        sections: TreeMap<String, SettingSection>,
-        view: SettingsActivityView
-    ) {
+    fun saveFile(fileName: String) {
         val ini = getSettingsFile(fileName)
         try {
-            val writer = Wini(ini)
-            val keySet: Set<String> = sections.keys
-            for (key in keySet) {
-                val section = sections[key]
-                writeSection(writer, section!!)
+            val wini = Wini(ini)
+            for (specificCategory in Settings.Category.values()) {
+                val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
+                for (setting in Settings.settingsList) {
+                    if (setting.key!!.isEmpty()) continue
+
+                    val settingCategoryHeader =
+                        NativeConfig.getConfigHeader(setting.category.ordinal)
+                    val iniSetting: String? = wini.get(categoryHeader, setting.key)
+                    if (iniSetting != null || settingCategoryHeader == categoryHeader) {
+                        wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
+                    }
+                }
             }
-            writer.store()
+            wini.store()
         } catch (e: IOException) {
             Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
-            view.showToastMessage(
-                YuzuApplication.appContext
-                    .getString(R.string.error_saving, fileName, e.message),
-                false
-            )
+            val context = YuzuApplication.appContext
+            Toast.makeText(
+                context,
+                context.getString(R.string.error_saving, fileName, e.message),
+                Toast.LENGTH_SHORT
+            ).show()
         }
     }
 
-    fun saveCustomGameSettings(gameId: String?, sections: HashMap<String, SettingSection?>) {
-        val sortedSections: Set<String> = TreeSet(sections.keys)
-        for (sectionKey in sortedSections) {
-            val section = sections[sectionKey]
-            val settings = section!!.settings
-            val sortedKeySet: Set<String> = TreeSet(settings.keys)
-            for (settingKey in sortedKeySet) {
-                val setting = settings[settingKey]
-                NativeLibrary.setUserSetting(
-                    gameId,
-                    mapSectionNameFromIni(
-                        section.name
-                    ),
-                    setting!!.key,
-                    setting.valueAsString
-                )
-            }
-        }
-    }
-
-    private fun mapSectionNameFromIni(generalSectionName: String): String? {
-        return if (sectionsMap.getForward(generalSectionName) != null) {
-            sectionsMap.getForward(generalSectionName)
-        } else {
-            generalSectionName
-        }
-    }
-
-    private fun mapSectionNameToIni(generalSectionName: String): String {
-        return if (sectionsMap.getBackward(generalSectionName) != null) {
-            sectionsMap.getBackward(generalSectionName).toString()
-        } else {
-            generalSectionName
-        }
-    }
-
-    fun getSettingsFile(fileName: String): File {
-        return File(
-            DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini"
-        )
-    }
-
-    private fun getCustomGameSettingsFile(gameId: String): File {
-        return File(DirectoryInitialization.userDirectory + "/GameSettings/" + gameId + ".ini")
-    }
-
-    private fun sectionFromLine(line: String, isCustomGame: Boolean): SettingSection {
-        var sectionName: String = line.substring(1, line.length - 1)
-        if (isCustomGame) {
-            sectionName = mapSectionNameToIni(sectionName)
-        }
-        return SettingSection(sectionName)
-    }
-
-    /**
-     * For a line of text, determines what type of data is being represented, and returns
-     * a Setting object containing this data.
-     *
-     * @param line    The line of text being parsed.
-     * @return A typed Setting containing the key/value contained in the line.
-     */
-    private fun settingFromLine(line: String): AbstractSetting? {
-        val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
-        if (splitLine.size != 2) {
-            return null
-        }
-        val key = splitLine[0].trim { it <= ' ' }
-        val value = splitLine[1].trim { it <= ' ' }
-        if (value.isEmpty()) {
-            return null
-        }
-
-        val booleanSetting = BooleanSetting.from(key)
-        if (booleanSetting != null) {
-            booleanSetting.boolean = value.toBoolean()
-            return booleanSetting
-        }
-
-        val intSetting = IntSetting.from(key)
-        if (intSetting != null) {
-            intSetting.int = value.toInt()
-            return intSetting
-        }
-
-        val floatSetting = FloatSetting.from(key)
-        if (floatSetting != null) {
-            floatSetting.float = value.toFloat()
-            return floatSetting
-        }
-
-        val stringSetting = StringSetting.from(key)
-        if (stringSetting != null) {
-            stringSetting.string = value
-            return stringSetting
-        }
-
-        return null
-    }
-
-    /**
-     * Writes the contents of a Section HashMap to disk.
-     *
-     * @param parser  A Wini pointed at a file on disk.
-     * @param section A section containing settings to be written to the file.
-     */
-    private fun writeSection(parser: Wini, section: SettingSection) {
-        // Write the section header.
-        val header = section.name
-
-        // Write this section's values.
-        val settings = section.settings
-        val keySet: Set<String> = settings.keys
-        for (key in keySet) {
-            val setting = settings[key]
-            parser.put(header, setting!!.key, setting.valueAsString)
-        }
-
-        BooleanSetting.values().forEach {
-            if (!keySet.contains(it.key)) {
-                parser.put(header, it.key, it.valueAsString)
-            }
-        }
-        IntSetting.values().forEach {
-            if (!keySet.contains(it.key)) {
-                parser.put(header, it.key, it.valueAsString)
-            }
-        }
-        StringSetting.values().forEach {
-            if (!keySet.contains(it.key)) {
-                parser.put(header, it.key, it.valueAsString)
-            }
-        }
-    }
+    fun getSettingsFile(fileName: String): File =
+        File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index aaf3a0ec1e..d8dbf1f45f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -39,7 +39,6 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
 import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
 import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
 import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@@ -54,7 +53,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 
     private val homeViewModel: HomeViewModel by viewModels()
     private val gamesViewModel: GamesViewModel by viewModels()
-    private val settingsViewModel: SettingsViewModel by viewModels()
 
     override var themeId: Int = 0
 
@@ -62,8 +60,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
         val splashScreen = installSplashScreen()
         splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
 
-        settingsViewModel.settings.loadSettings()
-
         ThemeHelper.setTheme(this)
 
         super.onCreate(savedInstanceState)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
deleted file mode 100644
index 9cfda74ee0..0000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.utils
-
-class BiMap<K, V> {
-    private val forward: MutableMap<K, V> = HashMap()
-    private val backward: MutableMap<V, K> = HashMap()
-
-    @Synchronized
-    fun add(key: K, value: V) {
-        forward[key] = value
-        backward[value] = key
-    }
-
-    @Synchronized
-    fun getForward(key: K): V? {
-        return forward[key]
-    }
-
-    @Synchronized
-    fun getBackward(key: V): K? {
-        return backward[key]
-    }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
new file mode 100644
index 0000000000..d4d981f9ef
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+object NativeConfig {
+    external fun getBoolean(key: String, getDefault: Boolean): Boolean
+    external fun setBoolean(key: String, value: Boolean)
+
+    external fun getByte(key: String, getDefault: Boolean): Byte
+    external fun setByte(key: String, value: Byte)
+
+    external fun getShort(key: String, getDefault: Boolean): Short
+    external fun setShort(key: String, value: Short)
+
+    external fun getInt(key: String, getDefault: Boolean): Int
+    external fun setInt(key: String, value: Int)
+
+    external fun getFloat(key: String, getDefault: Boolean): Float
+    external fun setFloat(key: String, value: Float)
+
+    external fun getLong(key: String, getDefault: Boolean): Long
+    external fun setLong(key: String, value: Long)
+
+    external fun getString(key: String, getDefault: Boolean): String
+    external fun setString(key: String, value: String)
+
+    external fun getIsRuntimeModifiable(key: String): Boolean
+
+    external fun getConfigHeader(category: Int): String
+}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index e2ed08e9fc..e15d1480b3 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -14,6 +14,8 @@ add_library(yuzu-android SHARED
     id_cache.cpp
     id_cache.h
     native.cpp
+    native_config.cpp
+    uisettings.cpp
 )
 
 set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 9de9bd93e2..34b425cb45 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -16,18 +16,20 @@
 #include "input_common/main.h"
 #include "jni/config.h"
 #include "jni/default_ini.h"
+#include "uisettings.h"
 
 namespace FS = Common::FS;
 
-Config::Config(std::optional<std::filesystem::path> config_path)
-    : config_loc{config_path.value_or(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini")},
-      config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} {
-    Reload();
+Config::Config(const std::string& config_name, ConfigType config_type)
+    : type(config_type), global{config_type == ConfigType::GlobalConfig} {
+    Initialize(config_name);
 }
 
 Config::~Config() = default;
 
 bool Config::LoadINI(const std::string& default_contents, bool retry) {
+    void(FS::CreateParentDir(config_loc));
+    config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
     const auto config_loc_str = FS::PathToUTF8String(config_loc);
     if (config->ParseError() < 0) {
         if (retry) {
@@ -301,9 +303,28 @@ void Config::ReadValues() {
 
     // Network
     ReadSetting("Network", Settings::values.network_interface);
+
+    // Android
+    ReadSetting("Android", AndroidSettings::values.picture_in_picture);
+    ReadSetting("Android", AndroidSettings::values.screen_layout);
 }
 
-void Config::Reload() {
+void Config::Initialize(const std::string& config_name) {
+    const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
+    const auto config_file = fmt::format("{}.ini", config_name);
+
+    switch (type) {
+    case ConfigType::GlobalConfig:
+        config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
+        break;
+    case ConfigType::PerGameConfig:
+        config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
+        break;
+    case ConfigType::InputProfile:
+        config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
+        LoadINI(DefaultINI::android_config_file);
+        return;
+    }
     LoadINI(DefaultINI::android_config_file);
     ReadValues();
 }
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
index 0d7d6e94df..e1e8f47ed2 100644
--- a/src/android/app/src/main/jni/config.h
+++ b/src/android/app/src/main/jni/config.h
@@ -13,25 +13,35 @@
 class INIReader;
 
 class Config {
-    std::filesystem::path config_loc;
-    std::unique_ptr<INIReader> config;
-
     bool LoadINI(const std::string& default_contents = "", bool retry = true);
-    void ReadValues();
 
 public:
-    explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt);
+    enum class ConfigType {
+        GlobalConfig,
+        PerGameConfig,
+        InputProfile,
+    };
+
+    explicit Config(const std::string& config_name = "config",
+                    ConfigType config_type = ConfigType::GlobalConfig);
     ~Config();
 
-    void Reload();
+    void Initialize(const std::string& config_name);
 
 private:
     /**
-     * Applies a value read from the sdl2_config to a Setting.
+     * Applies a value read from the config to a Setting.
      *
      * @param group The name of the INI group
      * @param setting The yuzu setting to modify
      */
     template <typename Type, bool ranged>
     void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
+
+    void ReadValues();
+
+    const ConfigType type;
+    std::unique_ptr<INIReader> config;
+    std::string config_loc;
+    const bool global;
 };
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 7e17833a0c..b2adfdedaa 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -824,34 +824,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass cl
     Config{};
 }
 
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
-                                                             jstring j_game_id, jstring j_section,
-                                                             jstring j_key) {
-    std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
-    std::string_view section = env->GetStringUTFChars(j_section, 0);
-    std::string_view key = env->GetStringUTFChars(j_key, 0);
-
-    env->ReleaseStringUTFChars(j_game_id, game_id.data());
-    env->ReleaseStringUTFChars(j_section, section.data());
-    env->ReleaseStringUTFChars(j_key, key.data());
-
-    return env->NewStringUTF("");
-}
-
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
-                                                          jstring j_game_id, jstring j_section,
-                                                          jstring j_key, jstring j_value) {
-    std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
-    std::string_view section = env->GetStringUTFChars(j_section, 0);
-    std::string_view key = env->GetStringUTFChars(j_key, 0);
-    std::string_view value = env->GetStringUTFChars(j_value, 0);
-
-    env->ReleaseStringUTFChars(j_game_id, game_id.data());
-    env->ReleaseStringUTFChars(j_section, section.data());
-    env->ReleaseStringUTFChars(j_key, key.data());
-    env->ReleaseStringUTFChars(j_value, value.data());
-}
-
 void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
                                                        jstring j_game_id) {
     std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
new file mode 100644
index 0000000000..6123b3d08d
--- /dev/null
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -0,0 +1,224 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <string>
+
+#include <jni.h>
+
+#include "common/logging/log.h"
+#include "common/settings.h"
+#include "jni/android_common/android_common.h"
+#include "jni/config.h"
+#include "uisettings.h"
+
+template <typename T>
+Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
+    auto key = GetJString(env, jkey);
+    auto basicSetting = Settings::values.linkage.by_key[key];
+    auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key];
+    if (basicSetting != 0) {
+        return static_cast<Settings::Setting<T>*>(basicSetting);
+    }
+    if (basicAndroidSetting != 0) {
+        return static_cast<Settings::Setting<T>*>(basicAndroidSetting);
+    }
+    LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
+    return nullptr;
+}
+
+extern "C" {
+
+jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
+                                                               jstring jkey, jboolean getDefault) {
+    auto setting = getSetting<bool>(env, jkey);
+    if (setting == nullptr) {
+        return false;
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return setting->GetDefault();
+    }
+
+    return setting->GetValue();
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey,
+                                                           jboolean value) {
+    auto setting = getSetting<bool>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+    setting->SetGlobal(true);
+    setting->SetValue(static_cast<bool>(value));
+}
+
+jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey,
+                                                         jboolean getDefault) {
+    auto setting = getSetting<u8>(env, jkey);
+    if (setting == nullptr) {
+        return -1;
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return setting->GetDefault();
+    }
+
+    return setting->GetValue();
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey,
+                                                        jbyte value) {
+    auto setting = getSetting<u8>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+    setting->SetGlobal(true);
+    setting->SetValue(value);
+}
+
+jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey,
+                                                           jboolean getDefault) {
+    auto setting = getSetting<u16>(env, jkey);
+    if (setting == nullptr) {
+        return -1;
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return setting->GetDefault();
+    }
+
+    return setting->GetValue();
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey,
+                                                         jshort value) {
+    auto setting = getSetting<u16>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+    setting->SetGlobal(true);
+    setting->SetValue(value);
+}
+
+jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
+                                                       jboolean getDefault) {
+    auto setting = getSetting<int>(env, jkey);
+    if (setting == nullptr) {
+        return -1;
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return setting->GetDefault();
+    }
+
+    return setting->GetValue();
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey,
+                                                       jint value) {
+    auto setting = getSetting<int>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+    setting->SetGlobal(true);
+    setting->SetValue(value);
+}
+
+jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey,
+                                                           jboolean getDefault) {
+    auto setting = getSetting<float>(env, jkey);
+    if (setting == nullptr) {
+        return -1;
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return setting->GetDefault();
+    }
+
+    return setting->GetValue();
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey,
+                                                         jfloat value) {
+    auto setting = getSetting<float>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+    setting->SetGlobal(true);
+    setting->SetValue(value);
+}
+
+jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey,
+                                                         jboolean getDefault) {
+    auto setting = getSetting<long>(env, jkey);
+    if (setting == nullptr) {
+        return -1;
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return setting->GetDefault();
+    }
+
+    return setting->GetValue();
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey,
+                                                        jlong value) {
+    auto setting = getSetting<long>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+    setting->SetGlobal(true);
+    setting->SetValue(value);
+}
+
+jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey,
+                                                             jboolean getDefault) {
+    auto setting = getSetting<std::string>(env, jkey);
+    if (setting == nullptr) {
+        return ToJString(env, "");
+    }
+    setting->SetGlobal(true);
+
+    if (static_cast<bool>(getDefault)) {
+        return ToJString(env, setting->GetDefault());
+    }
+
+    return ToJString(env, setting->GetValue());
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
+                                                          jstring value) {
+    auto setting = getSetting<std::string>(env, jkey);
+    if (setting == nullptr) {
+        return;
+    }
+
+    setting->SetGlobal(true);
+    setting->SetValue(GetJString(env, value));
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
+                                                                           jstring jkey) {
+    auto key = GetJString(env, jkey);
+    auto setting = Settings::values.linkage.by_key[key];
+    if (setting != 0) {
+        return setting->RuntimeModfiable();
+    }
+    LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
+    return true;
+}
+
+jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj,
+                                                                         jint jcategory) {
+    auto category = static_cast<Settings::Category>(jcategory);
+    return ToJString(env, Settings::TranslateCategory(category));
+}
+
+} // extern "C"
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/uisettings.cpp
new file mode 100644
index 0000000000..f2f0bad50b
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.cpp
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "uisettings.h"
+
+namespace AndroidSettings {
+
+Values values;
+
+} // namespace AndroidSettings
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/uisettings.h
new file mode 100644
index 0000000000..494654af75
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.h
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <common/settings_common.h>
+#include "common/common_types.h"
+#include "common/settings_setting.h"
+
+namespace AndroidSettings {
+
+struct Values {
+    Settings::Linkage linkage;
+
+    // Android
+    Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture",
+                                               Settings::Category::Android};
+    Settings::Setting<s32> screen_layout{linkage,
+                                         5,
+                                         "screen_layout",
+                                         Settings::Category::Android,
+                                         Settings::Specialization::Default,
+                                         true,
+                                         true};
+};
+
+extern Values values;
+
+} // namespace AndroidSettings
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 200b99185f..dc10159c9b 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -243,10 +243,10 @@
         <item>@string/cubeb</item>
         <item>@string/string_null</item>
     </string-array>
-    <string-array name="outputEngineValues">
-        <item>auto</item>
-        <item>cubeb</item>
-        <item>null</item>
-    </string-array>
+    <integer-array name="outputEngineValues">
+        <item>0</item>
+        <item>1</item>
+        <item>3</item>
+    </integer-array>
 
 </resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index de1b2909b5..df76563fc2 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -200,6 +200,7 @@
     <string name="ini_saved">Saved settings</string>
     <string name="gameid_saved">Saved settings for %1$s</string>
     <string name="error_saving">Error saving %1$s.ini: %2$s</string>
+    <string name="unimplemented_menu">Unimplemented Menu</string>
     <string name="loading">Loading…</string>
     <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
     <string name="reset_to_default">Reset to default</string>
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 5240568417..4ecaf550b1 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -159,6 +159,8 @@ float Volume() {
 
 const char* TranslateCategory(Category category) {
     switch (category) {
+    case Category::Android:
+        return "Android";
     case Category::Audio:
         return "Audio";
     case Category::Core:
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index 137b65d5f7..5960b78aa5 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -14,6 +14,7 @@ BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Categ
     : label{name}, category{category_}, id{linkage.count}, save{save_},
       runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
       other_setting{other_setting_} {
+    linkage.by_key.insert({name, this});
     linkage.by_category[category].push_back(this);
     linkage.count++;
 }
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 3082e0ce1a..5b170dfd5d 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -12,6 +12,7 @@
 namespace Settings {
 
 enum class Category : u32 {
+    Android,
     Audio,
     Core,
     Cpu,
@@ -68,6 +69,7 @@ public:
     explicit Linkage(u32 initial_count = 0);
     ~Linkage();
     std::map<Category, std::vector<BasicSetting*>> by_category{};
+    std::map<std::string, Settings::BasicSetting*> by_key{};
     std::vector<std::function<void()>> restore_functions{};
     u32 count;
 };