summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt)6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt151
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt316
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt62
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt181
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt255
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt31
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt2
-rw-r--r--src/android/app/src/main/jni/config.cpp31
-rw-r--r--src/android/app/src/main/jni/config.h24
-rw-r--r--src/android/app/src/main/jni/native.cpp28
-rw-r--r--src/android/app/src/main/jni/native_config.cpp224
-rw-r--r--src/android/app/src/main/jni/uisettings.cpp10
-rw-r--r--src/android/app/src/main/jni/uisettings.h29
-rw-r--r--src/android/app/src/main/res/values/arrays.xml10
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings_common.cpp1
-rw-r--r--src/common/settings_common.h2
49 files changed, 869 insertions, 969 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 9c32e044c..5a7cf4ed7 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 {
219 219
220 external fun reloadSettings() 220 external fun reloadSettings()
221 221
222 external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String?
223
224 external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?)
225
226 external fun initGameIni(gameID: String?) 222 external fun initGameIni(gameID: String?)
227 223
228 /** 224 /**
@@ -413,14 +409,17 @@ object NativeLibrary {
413 details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) } 409 details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
414 ) 410 )
415 } 411 }
412
416 CoreError.ErrorSavestate -> { 413 CoreError.ErrorSavestate -> {
417 title = emulationActivity.getString(R.string.save_load_error) 414 title = emulationActivity.getString(R.string.save_load_error)
418 message = details 415 message = details
419 } 416 }
417
420 CoreError.ErrorUnknown -> { 418 CoreError.ErrorUnknown -> {
421 title = emulationActivity.getString(R.string.fatal_error) 419 title = emulationActivity.getString(R.string.fatal_error)
422 message = emulationActivity.getString(R.string.fatal_error_message) 420 message = emulationActivity.getString(R.string.fatal_error_message)
423 } 421 }
422
424 else -> { 423 else -> {
425 return true 424 return true
426 } 425 }
@@ -454,6 +453,7 @@ object NativeLibrary {
454 captionId = R.string.loader_error_video_core 453 captionId = R.string.loader_error_video_core
455 descriptionId = R.string.loader_error_video_core_description 454 descriptionId = R.string.loader_error_video_core_description
456 } 455 }
456
457 else -> { 457 else -> {
458 captionId = R.string.loader_error_encrypted 458 captionId = R.string.loader_error_encrypted
459 descriptionId = R.string.loader_error_encrypted_roms_description 459 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 7461fb093..6f52a7a8d 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
28import android.view.View 28import android.view.View
29import android.view.inputmethod.InputMethodManager 29import android.view.inputmethod.InputMethodManager
30import android.widget.Toast 30import android.widget.Toast
31import androidx.activity.viewModels
32import androidx.appcompat.app.AppCompatActivity 31import androidx.appcompat.app.AppCompatActivity
33import androidx.core.view.WindowCompat 32import androidx.core.view.WindowCompat
34import androidx.core.view.WindowInsetsCompat 33import androidx.core.view.WindowInsetsCompat
@@ -42,7 +41,6 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 41import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
43import org.yuzu.yuzu_emu.features.settings.model.IntSetting 42import org.yuzu.yuzu_emu.features.settings.model.IntSetting
44import org.yuzu.yuzu_emu.features.settings.model.Settings 43import org.yuzu.yuzu_emu.features.settings.model.Settings
45import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
46import org.yuzu.yuzu_emu.model.Game 44import org.yuzu.yuzu_emu.model.Game
47import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 45import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
48import org.yuzu.yuzu_emu.utils.ForegroundService 46import org.yuzu.yuzu_emu.utils.ForegroundService
@@ -72,8 +70,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
72 private val actionMute = "ACTION_EMULATOR_MUTE" 70 private val actionMute = "ACTION_EMULATOR_MUTE"
73 private val actionUnmute = "ACTION_EMULATOR_UNMUTE" 71 private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
74 72
75 private val settingsViewModel: SettingsViewModel by viewModels()
76
77 override fun onDestroy() { 73 override fun onDestroy() {
78 stopForegroundService(this) 74 stopForegroundService(this)
79 super.onDestroy() 75 super.onDestroy()
@@ -82,8 +78,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
82 override fun onCreate(savedInstanceState: Bundle?) { 78 override fun onCreate(savedInstanceState: Bundle?) {
83 ThemeHelper.setTheme(this) 79 ThemeHelper.setTheme(this)
84 80
85 settingsViewModel.settings.loadSettings()
86
87 super.onCreate(savedInstanceState) 81 super.onCreate(savedInstanceState)
88 82
89 binding = ActivityEmulationBinding.inflate(layoutInflater) 83 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 a6e9833ee..aeda8d222 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractBooleanSetting : AbstractSetting { 6interface AbstractBooleanSetting : AbstractSetting {
7 var boolean: Boolean 7 val boolean: Boolean
8
9 fun setBoolean(value: Boolean)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
index bd9233d62..606519ad8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
@@ -3,8 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import androidx.lifecycle.ViewModel 6interface AbstractByteSetting : AbstractSetting {
7 val byte: Byte
7 8
8class SettingsViewModel : ViewModel() { 9 fun setByte(value: Byte)
9 val settings = Settings()
10} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
index 6fe4bc263..974925eed 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractFloatSetting : AbstractSetting { 6interface AbstractFloatSetting : AbstractSetting {
7 var float: Float 7 val float: Float
8
9 fun setFloat(value: Float)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
index 892b7dcfe..89b285b10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractIntSetting : AbstractSetting { 6interface AbstractIntSetting : AbstractSetting {
7 var int: Int 7 val int: Int
8
9 fun setInt(value: Int)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
new file mode 100644
index 000000000..4873942db
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6interface AbstractLongSetting : AbstractSetting {
7 val long: Long
8
9 fun setLong(value: Long)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
index 258580209..7afed95ad 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6interface AbstractSetting { 8interface AbstractSetting {
7 val key: String? 9 val key: String?
8 val section: String? 10 val category: Settings.Category
9 val isRuntimeEditable: Boolean
10 val valueAsString: String
11 val defaultValue: Any 11 val defaultValue: Any
12 val valueAsString: String
13 get() = ""
14
15 val isRuntimeModifiable: Boolean
16 get() = NativeConfig.getIsRuntimeModifiable(key!!)
17
18 fun reset() = run { }
12} 19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
new file mode 100644
index 000000000..91407ccbb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6interface AbstractShortSetting : AbstractSetting {
7 val short: Short
8
9 fun setShort(value: Short)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
index 0d02c5997..c8935cc48 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractStringSetting : AbstractSetting { 6interface AbstractStringSetting : AbstractSetting {
7 var string: String 7 val string: String
8
9 fun setString(value: String)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index d41933766..f7528642e 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class BooleanSetting( 8enum class BooleanSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Boolean
10) : AbstractBooleanSetting { 11) : AbstractBooleanSetting {
11 CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false), 12 CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu),
12 FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true), 13 FASTMEM("cpuopt_fastmem", Settings.Category.Cpu),
13 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true), 14 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu),
14 PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), 15 RENDERER_USE_SPEED_LIMIT("use_speed_limit", Settings.Category.Core),
15 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); 16 USE_DOCKED_MODE("use_docked_mode", Settings.Category.System),
16 17 RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache", Settings.Category.Renderer),
17 override var boolean: Boolean = defaultValue 18 RENDERER_FORCE_MAX_CLOCK("force_max_clock", Settings.Category.Renderer),
19 RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders", Settings.Category.Renderer),
20 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing", Settings.Category.Renderer),
21 RENDERER_DEBUG("debug", Settings.Category.Renderer),
22 PICTURE_IN_PICTURE("picture_in_picture", Settings.Category.Android),
23 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.Category.System);
24
25 override val boolean: Boolean
26 get() = NativeConfig.getBoolean(key, false)
27
28 override fun setBoolean(value: Boolean) = NativeConfig.setBoolean(key, value)
29
30 override val defaultValue: Boolean by lazy { NativeConfig.getBoolean(key, true) }
18 31
19 override val valueAsString: String 32 override val valueAsString: String
20 get() = boolean.toString() 33 get() = if (boolean) "1" else "0"
21 34
22 override val isRuntimeEditable: Boolean 35 override fun reset() = NativeConfig.setBoolean(key, defaultValue)
23 get() {
24 for (setting in NOT_RUNTIME_EDITABLE) {
25 if (setting == this) {
26 return false
27 }
28 }
29 return true
30 }
31
32 companion object {
33 private val NOT_RUNTIME_EDITABLE = listOf(
34 PICTURE_IN_PICTURE,
35 USE_CUSTOM_RTC
36 )
37
38 fun from(key: String): BooleanSetting? =
39 BooleanSetting.values().firstOrNull { it.key == key }
40
41 fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue }
42 }
43} 36}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
new file mode 100644
index 000000000..6ec0a765e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class ByteSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractByteSetting {
12 AUDIO_VOLUME("volume", Settings.Category.Audio);
13
14 override val byte: Byte
15 get() = NativeConfig.getByte(key, false)
16
17 override fun setByte(value: Byte) = NativeConfig.setByte(key, value)
18
19 override val defaultValue: Byte by lazy { NativeConfig.getByte(key, true) }
20
21 override val valueAsString: String
22 get() = byte.toString()
23
24 override fun reset() = NativeConfig.setByte(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
index e5545a916..0181d06f2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
@@ -3,34 +3,24 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class FloatSetting( 8enum class FloatSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Float
10) : AbstractFloatSetting { 11) : AbstractFloatSetting {
11 // No float settings currently exist 12 // No float settings currently exist
12 EMPTY_SETTING("", "", 0f); 13 EMPTY_SETTING("", Settings.Category.UiGeneral);
13
14 override var float: Float = defaultValue
15 14
16 override val valueAsString: String 15 override val float: Float
17 get() = float.toString() 16 get() = NativeConfig.getFloat(key, false)
18 17
19 override val isRuntimeEditable: Boolean 18 override fun setFloat(value: Float) = NativeConfig.setFloat(key, value)
20 get() {
21 for (setting in NOT_RUNTIME_EDITABLE) {
22 if (setting == this) {
23 return false
24 }
25 }
26 return true
27 }
28 19
29 companion object { 20 override val defaultValue: Float by lazy { NativeConfig.getFloat(key, true) }
30 private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
31 21
32 fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key } 22 override val valueAsString: String
23 get() = float.toString()
33 24
34 fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue } 25 override fun reset() = NativeConfig.setFloat(key, defaultValue)
35 }
36} 26}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 4427a7d9d..a64757207 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class IntSetting( 8enum class IntSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Int
10) : AbstractIntSetting { 11) : AbstractIntSetting {
11 RENDERER_USE_SPEED_LIMIT( 12 CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
12 "use_speed_limit", 13 REGION_INDEX("region_index", Settings.Category.System),
13 Settings.SECTION_RENDERER, 14 LANGUAGE_INDEX("language_index", Settings.Category.System),
14 1 15 RENDERER_BACKEND("backend", Settings.Category.Renderer),
15 ), 16 RENDERER_ACCURACY("gpu_accuracy", Settings.Category.Renderer),
16 USE_DOCKED_MODE( 17 RENDERER_RESOLUTION("resolution_setup", Settings.Category.Renderer),
17 "use_docked_mode", 18 RENDERER_VSYNC("use_vsync", Settings.Category.Renderer),
18 Settings.SECTION_SYSTEM, 19 RENDERER_SCALING_FILTER("scaling_filter", Settings.Category.Renderer),
19 0 20 RENDERER_ANTI_ALIASING("anti_aliasing", Settings.Category.Renderer),
20 ), 21 RENDERER_SCREEN_LAYOUT("screen_layout", Settings.Category.Android),
21 RENDERER_USE_DISK_SHADER_CACHE( 22 RENDERER_ASPECT_RATIO("aspect_ratio", Settings.Category.Renderer),
22 "use_disk_shader_cache", 23 AUDIO_OUTPUT_ENGINE("output_engine", Settings.Category.Audio);
23 Settings.SECTION_RENDERER, 24
24 1 25 override val int: Int
25 ), 26 get() = NativeConfig.getInt(key, false)
26 RENDERER_FORCE_MAX_CLOCK( 27
27 "force_max_clock", 28 override fun setInt(value: Int) = NativeConfig.setInt(key, value)
28 Settings.SECTION_RENDERER, 29
29 0 30 override val defaultValue: Int by lazy { NativeConfig.getInt(key, true) }
30 ),
31 RENDERER_ASYNCHRONOUS_SHADERS(
32 "use_asynchronous_shaders",
33 Settings.SECTION_RENDERER,
34 0
35 ),
36 RENDERER_REACTIVE_FLUSHING(
37 "use_reactive_flushing",
38 Settings.SECTION_RENDERER,
39 0
40 ),
41 RENDERER_DEBUG(
42 "debug",
43 Settings.SECTION_RENDERER,
44 0
45 ),
46 RENDERER_SPEED_LIMIT(
47 "speed_limit",
48 Settings.SECTION_RENDERER,
49 100
50 ),
51 CPU_ACCURACY(
52 "cpu_accuracy",
53 Settings.SECTION_CPU,
54 0
55 ),
56 REGION_INDEX(
57 "region_index",
58 Settings.SECTION_SYSTEM,
59 -1
60 ),
61 LANGUAGE_INDEX(
62 "language_index",
63 Settings.SECTION_SYSTEM,
64 1
65 ),
66 RENDERER_BACKEND(
67 "backend",
68 Settings.SECTION_RENDERER,
69 1
70 ),
71 RENDERER_ACCURACY(
72 "gpu_accuracy",
73 Settings.SECTION_RENDERER,
74 0
75 ),
76 RENDERER_RESOLUTION(
77 "resolution_setup",
78 Settings.SECTION_RENDERER,
79 2
80 ),
81 RENDERER_VSYNC(
82 "use_vsync",
83 Settings.SECTION_RENDERER,
84 0
85 ),
86 RENDERER_SCALING_FILTER(
87 "scaling_filter",
88 Settings.SECTION_RENDERER,
89 1
90 ),
91 RENDERER_ANTI_ALIASING(
92 "anti_aliasing",
93 Settings.SECTION_RENDERER,
94 0
95 ),
96 RENDERER_SCREEN_LAYOUT(
97 "screen_layout",
98 Settings.SECTION_RENDERER,
99 Settings.LayoutOption_MobileLandscape
100 ),
101 RENDERER_ASPECT_RATIO(
102 "aspect_ratio",
103 Settings.SECTION_RENDERER,
104 0
105 ),
106 AUDIO_VOLUME(
107 "volume",
108 Settings.SECTION_AUDIO,
109 100
110 );
111
112 override var int: Int = defaultValue
113 31
114 override val valueAsString: String 32 override val valueAsString: String
115 get() = int.toString() 33 get() = int.toString()
116 34
117 override val isRuntimeEditable: Boolean 35 override fun reset() = NativeConfig.setInt(key, defaultValue)
118 get() {
119 for (setting in NOT_RUNTIME_EDITABLE) {
120 if (setting == this) {
121 return false
122 }
123 }
124 return true
125 }
126
127 companion object {
128 private val NOT_RUNTIME_EDITABLE = listOf(
129 RENDERER_USE_DISK_SHADER_CACHE,
130 RENDERER_ASYNCHRONOUS_SHADERS,
131 RENDERER_DEBUG,
132 RENDERER_BACKEND,
133 RENDERER_RESOLUTION,
134 RENDERER_VSYNC
135 )
136
137 fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
138
139 fun clear() = IntSetting.values().forEach { it.int = it.defaultValue }
140 }
141} 36}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
new file mode 100644
index 000000000..c526fc4cf
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class LongSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractLongSetting {
12 CUSTOM_RTC("custom_rtc", Settings.Category.System);
13
14 override val long: Long
15 get() = NativeConfig.getLong(key, false)
16
17 override fun setLong(value: Long) = NativeConfig.setLong(key, value)
18
19 override val defaultValue: Long by lazy { NativeConfig.getLong(key, true) }
20
21 override val valueAsString: String
22 get() = long.toString()
23
24 override fun reset() = NativeConfig.setLong(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
deleted file mode 100644
index 474f598a9..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
+++ /dev/null
@@ -1,37 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6/**
7 * A semantically-related group of Settings objects. These Settings are
8 * internally stored as a HashMap.
9 */
10class SettingSection(val name: String) {
11 val settings = HashMap<String, AbstractSetting>()
12
13 /**
14 * Convenience method; inserts a value directly into the backing HashMap.
15 *
16 * @param setting The Setting to be inserted.
17 */
18 fun putSetting(setting: AbstractSetting) {
19 settings[setting.key!!] = setting
20 }
21
22 /**
23 * Convenience method; gets a value directly from the backing HashMap.
24 *
25 * @param key Used to retrieve the Setting.
26 * @return A Setting object (you should probably cast this before using)
27 */
28 fun getSetting(key: String): AbstractSetting? {
29 return settings[key]
30 }
31
32 fun mergeSection(settingSection: SettingSection) {
33 for (setting in settingSection.settings.values) {
34 putSetting(setting)
35 }
36 }
37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index a6251bafd..0702236e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,195 +4,151 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import android.text.TextUtils 6import android.text.TextUtils
7import java.util.* 7import android.widget.Toast
8import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
11import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
12 11
13class Settings { 12object Settings {
14 private var gameId: String? = null 13 private val context get() = YuzuApplication.appContext
15 14
16 var isLoaded = false 15 fun saveSettings(gameId: String = "") {
17
18 /**
19 * A HashMap<String></String>, SettingSection> that constructs a new SettingSection instead of returning null
20 * when getting a key not already in the map
21 */
22 class SettingsSectionMap : HashMap<String, SettingSection?>() {
23 override operator fun get(key: String): SettingSection? {
24 if (!super.containsKey(key)) {
25 val section = SettingSection(key)
26 super.put(key, section)
27 return section
28 }
29 return super.get(key)
30 }
31 }
32
33 var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
34
35 fun getSection(sectionName: String): SettingSection? {
36 return sections[sectionName]
37 }
38
39 val isEmpty: Boolean
40 get() = sections.isEmpty()
41
42 fun loadSettings(view: SettingsActivityView? = null) {
43 sections = SettingsSectionMap()
44 loadYuzuSettings(view)
45 if (!TextUtils.isEmpty(gameId)) {
46 loadCustomGameSettings(gameId!!, view)
47 }
48 isLoaded = true
49 }
50
51 private fun loadYuzuSettings(view: SettingsActivityView?) {
52 for ((fileName) in configFileSectionsMap) {
53 sections.putAll(SettingsFile.readFile(fileName, view))
54 }
55 }
56
57 private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) {
58 // Custom game settings
59 mergeSections(SettingsFile.readCustomGameSettings(gameId, view))
60 }
61
62 private fun mergeSections(updatedSections: HashMap<String, SettingSection?>) {
63 for ((key, updatedSection) in updatedSections) {
64 if (sections.containsKey(key)) {
65 val originalSection = sections[key]
66 originalSection!!.mergeSection(updatedSection!!)
67 } else {
68 sections[key] = updatedSection
69 }
70 }
71 }
72
73 fun loadSettings(gameId: String, view: SettingsActivityView) {
74 this.gameId = gameId
75 loadSettings(view)
76 }
77
78 fun saveSettings(view: SettingsActivityView) {
79 if (TextUtils.isEmpty(gameId)) { 16 if (TextUtils.isEmpty(gameId)) {
80 view.showToastMessage( 17 Toast.makeText(
81 YuzuApplication.appContext.getString(R.string.ini_saved), 18 context,
82 false 19 context.getString(R.string.ini_saved),
83 ) 20 Toast.LENGTH_SHORT
84 21 ).show()
85 for ((fileName, sectionNames) in configFileSectionsMap) { 22 SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
86 val iniSections = TreeMap<String, SettingSection>()
87 for (section in sectionNames) {
88 iniSections[section] = sections[section]!!
89 }
90
91 SettingsFile.saveFile(fileName, iniSections, view)
92 }
93 } else { 23 } else {
94 // Custom game settings 24 // TODO: Save custom game settings
95 view.showToastMessage( 25 Toast.makeText(
96 YuzuApplication.appContext.getString(R.string.gameid_saved, gameId), 26 context,
97 false 27 context.getString(R.string.gameid_saved, gameId),
98 ) 28 Toast.LENGTH_SHORT
99 29 ).show()
100 SettingsFile.saveCustomGameSettings(gameId, sections)
101 } 30 }
102 } 31 }
103 32
104 companion object { 33 enum class Category {
105 const val SECTION_GENERAL = "General" 34 Android,
106 const val SECTION_SYSTEM = "System" 35 Audio,
107 const val SECTION_RENDERER = "Renderer" 36 Core,
108 const val SECTION_AUDIO = "Audio" 37 Cpu,
109 const val SECTION_CPU = "Cpu" 38 CpuDebug,
110 const val SECTION_THEME = "Theme" 39 CpuUnsafe,
111 const val SECTION_DEBUG = "Debug" 40 Renderer,
112 41 RendererAdvanced,
113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 42 RendererDebug,
114 43 System,
115 const val PREF_OVERLAY_VERSION = "OverlayVersion" 44 SystemAudio,
116 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" 45 DataStorage,
117 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" 46 Debugging,
118 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" 47 DebuggingGraphics,
119 val overlayLayoutPrefs = listOf( 48 Miscellaneous,
120 PREF_LANDSCAPE_OVERLAY_VERSION, 49 Network,
121 PREF_PORTRAIT_OVERLAY_VERSION, 50 WebService,
122 PREF_FOLDABLE_OVERLAY_VERSION 51 AddOns,
123 ) 52 Controls,
124 53 Ui,
125 const val PREF_CONTROL_SCALE = "controlScale" 54 UiGeneral,
126 const val PREF_CONTROL_OPACITY = "controlOpacity" 55 UiLayout,
127 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 56 UiGameList,
128 const val PREF_BUTTON_A = "buttonToggle0" 57 Screenshots,
129 const val PREF_BUTTON_B = "buttonToggle1" 58 Shortcuts,
130 const val PREF_BUTTON_X = "buttonToggle2" 59 Multiplayer,
131 const val PREF_BUTTON_Y = "buttonToggle3" 60 Services,
132 const val PREF_BUTTON_L = "buttonToggle4" 61 Paths,
133 const val PREF_BUTTON_R = "buttonToggle5" 62 MaxEnum
134 const val PREF_BUTTON_ZL = "buttonToggle6"
135 const val PREF_BUTTON_ZR = "buttonToggle7"
136 const val PREF_BUTTON_PLUS = "buttonToggle8"
137 const val PREF_BUTTON_MINUS = "buttonToggle9"
138 const val PREF_BUTTON_DPAD = "buttonToggle10"
139 const val PREF_STICK_L = "buttonToggle11"
140 const val PREF_STICK_R = "buttonToggle12"
141 const val PREF_BUTTON_STICK_L = "buttonToggle13"
142 const val PREF_BUTTON_STICK_R = "buttonToggle14"
143 const val PREF_BUTTON_HOME = "buttonToggle15"
144 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
145
146 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
147 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
148 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
149 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
150 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
151
152 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
153 const val PREF_THEME = "Theme"
154 const val PREF_THEME_MODE = "ThemeMode"
155 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
156
157 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
158
159 val overlayPreferences = listOf(
160 PREF_OVERLAY_VERSION,
161 PREF_CONTROL_SCALE,
162 PREF_CONTROL_OPACITY,
163 PREF_TOUCH_ENABLED,
164 PREF_BUTTON_A,
165 PREF_BUTTON_B,
166 PREF_BUTTON_X,
167 PREF_BUTTON_Y,
168 PREF_BUTTON_L,
169 PREF_BUTTON_R,
170 PREF_BUTTON_ZL,
171 PREF_BUTTON_ZR,
172 PREF_BUTTON_PLUS,
173 PREF_BUTTON_MINUS,
174 PREF_BUTTON_DPAD,
175 PREF_STICK_L,
176 PREF_STICK_R,
177 PREF_BUTTON_HOME,
178 PREF_BUTTON_SCREENSHOT,
179 PREF_BUTTON_STICK_L,
180 PREF_BUTTON_STICK_R
181 )
182
183 const val LayoutOption_Unspecified = 0
184 const val LayoutOption_MobilePortrait = 4
185 const val LayoutOption_MobileLandscape = 5
186
187 init {
188 configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
189 listOf(
190 SECTION_GENERAL,
191 SECTION_SYSTEM,
192 SECTION_RENDERER,
193 SECTION_AUDIO,
194 SECTION_CPU
195 )
196 }
197 } 63 }
64
65 val settingsList = listOf<AbstractSetting>(
66 *BooleanSetting.values(),
67 *ByteSetting.values(),
68 *ShortSetting.values(),
69 *IntSetting.values(),
70 *FloatSetting.values(),
71 *LongSetting.values(),
72 *StringSetting.values()
73 )
74
75 const val SECTION_GENERAL = "General"
76 const val SECTION_SYSTEM = "System"
77 const val SECTION_RENDERER = "Renderer"
78 const val SECTION_AUDIO = "Audio"
79 const val SECTION_CPU = "Cpu"
80 const val SECTION_THEME = "Theme"
81 const val SECTION_DEBUG = "Debug"
82
83 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
84
85 const val PREF_OVERLAY_VERSION = "OverlayVersion"
86 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
87 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
88 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
89 val overlayLayoutPrefs = listOf(
90 PREF_LANDSCAPE_OVERLAY_VERSION,
91 PREF_PORTRAIT_OVERLAY_VERSION,
92 PREF_FOLDABLE_OVERLAY_VERSION
93 )
94
95 const val PREF_CONTROL_SCALE = "controlScale"
96 const val PREF_CONTROL_OPACITY = "controlOpacity"
97 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
98 const val PREF_BUTTON_A = "buttonToggle0"
99 const val PREF_BUTTON_B = "buttonToggle1"
100 const val PREF_BUTTON_X = "buttonToggle2"
101 const val PREF_BUTTON_Y = "buttonToggle3"
102 const val PREF_BUTTON_L = "buttonToggle4"
103 const val PREF_BUTTON_R = "buttonToggle5"
104 const val PREF_BUTTON_ZL = "buttonToggle6"
105 const val PREF_BUTTON_ZR = "buttonToggle7"
106 const val PREF_BUTTON_PLUS = "buttonToggle8"
107 const val PREF_BUTTON_MINUS = "buttonToggle9"
108 const val PREF_BUTTON_DPAD = "buttonToggle10"
109 const val PREF_STICK_L = "buttonToggle11"
110 const val PREF_STICK_R = "buttonToggle12"
111 const val PREF_BUTTON_STICK_L = "buttonToggle13"
112 const val PREF_BUTTON_STICK_R = "buttonToggle14"
113 const val PREF_BUTTON_HOME = "buttonToggle15"
114 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
115
116 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
117 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
118 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
119 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
120 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
121
122 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
123 const val PREF_THEME = "Theme"
124 const val PREF_THEME_MODE = "ThemeMode"
125 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
126
127 val overlayPreferences = listOf(
128 PREF_OVERLAY_VERSION,
129 PREF_CONTROL_SCALE,
130 PREF_CONTROL_OPACITY,
131 PREF_TOUCH_ENABLED,
132 PREF_BUTTON_A,
133 PREF_BUTTON_B,
134 PREF_BUTTON_X,
135 PREF_BUTTON_Y,
136 PREF_BUTTON_L,
137 PREF_BUTTON_R,
138 PREF_BUTTON_ZL,
139 PREF_BUTTON_ZR,
140 PREF_BUTTON_PLUS,
141 PREF_BUTTON_MINUS,
142 PREF_BUTTON_DPAD,
143 PREF_STICK_L,
144 PREF_STICK_R,
145 PREF_BUTTON_HOME,
146 PREF_BUTTON_SCREENSHOT,
147 PREF_BUTTON_STICK_L,
148 PREF_BUTTON_STICK_R
149 )
150
151 const val LayoutOption_Unspecified = 0
152 const val LayoutOption_MobilePortrait = 4
153 const val LayoutOption_MobileLandscape = 5
198} 154}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
new file mode 100644
index 000000000..c9a0c664c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class ShortSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractShortSetting {
12 RENDERER_SPEED_LIMIT("speed_limit", Settings.Category.Core);
13
14 override val short: Short
15 get() = NativeConfig.getShort(key, false)
16
17 override fun setShort(value: Short) = NativeConfig.setShort(key, value)
18
19 override val defaultValue: Short by lazy { NativeConfig.getShort(key, true) }
20
21 override val valueAsString: String
22 get() = short.toString()
23
24 override fun reset() = NativeConfig.setShort(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 6621289fd..9bb3e66d4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -3,36 +3,24 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class StringSetting( 8enum class StringSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: String
10) : AbstractStringSetting { 11) : AbstractStringSetting {
11 AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"), 12 // No string settings currently exist
12 CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); 13 EMPTY_SETTING("", Settings.Category.UiGeneral);
14
15 override val string: String
16 get() = NativeConfig.getString(key, false)
17
18 override fun setString(value: String) = NativeConfig.setString(key, value)
13 19
14 override var string: String = defaultValue 20 override val defaultValue: String by lazy { NativeConfig.getString(key, true) }
15 21
16 override val valueAsString: String 22 override val valueAsString: String
17 get() = string 23 get() = string
18 24
19 override val isRuntimeEditable: Boolean 25 override fun reset() = NativeConfig.setString(key, defaultValue)
20 get() {
21 for (setting in NOT_RUNTIME_EDITABLE) {
22 if (setting == this) {
23 return false
24 }
25 }
26 return true
27 }
28
29 companion object {
30 private val NOT_RUNTIME_EDITABLE = listOf(
31 CUSTOM_RTC
32 )
33
34 fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
35
36 fun clear() = StringSetting.values().forEach { it.string = it.defaultValue }
37 }
38} 26}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index bc0bf7788..7c858916e 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
8 8
9class DateTimeSetting( 9class DateTimeSetting(
10 setting: AbstractSetting?, 10 setting: AbstractSetting?,
11 titleId: Int, 11 titleId: Int,
12 descriptionId: Int, 12 descriptionId: Int,
13 val key: String? = null, 13 val key: String? = null,
14 private val defaultValue: String? = null 14 private val defaultValue: Long? = null
15) : SettingsItem(setting, titleId, descriptionId) { 15) : SettingsItem(setting, titleId, descriptionId) {
16 override val type = TYPE_DATETIME_SETTING 16 override val type = TYPE_DATETIME_SETTING
17 17
18 val value: String 18 val value: Long
19 get() = if (setting != null) { 19 get() = if (setting != null) {
20 val setting = setting as AbstractStringSetting 20 val setting = setting as AbstractLongSetting
21 setting.string 21 setting.long
22 } else { 22 } else {
23 defaultValue!! 23 defaultValue!!
24 } 24 }
25 25
26 fun setSelectedValue(datetime: String): AbstractStringSetting { 26 fun setSelectedValue(datetime: Long): AbstractLongSetting {
27 val stringSetting = setting as AbstractStringSetting 27 val longSetting = setting as AbstractLongSetting
28 stringSetting.string = datetime 28 longSetting.setLong(datetime)
29 return stringSetting 29 return longSetting
30 } 30 }
31} 31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 07520849e..a6cba977c 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(
23 val isEditable: Boolean 23 val isEditable: Boolean
24 get() { 24 get() {
25 if (!NativeLibrary.isRunning()) return true 25 if (!NativeLibrary.isRunning()) return true
26 return setting?.isRuntimeEditable ?: false 26 return setting?.isRuntimeModifiable ?: false
27 } 27 }
28 28
29 companion object { 29 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 7306ec458..b6a8c4612 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(
33 */ 33 */
34 fun setSelectedValue(selection: Int): AbstractIntSetting { 34 fun setSelectedValue(selection: Int): AbstractIntSetting {
35 val intSetting = setting as AbstractIntSetting 35 val intSetting = setting as AbstractIntSetting
36 intSetting.int = selection 36 intSetting.setInt(selection)
37 return intSetting 37 return intSetting
38 } 38 }
39} 39}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 92d0167ae..e71a29e35 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
6import kotlin.math.roundToInt 7import kotlin.math.roundToInt
7import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 10import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
11import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
10import org.yuzu.yuzu_emu.utils.Log 12import org.yuzu.yuzu_emu.utils.Log
11 13
12class SliderSetting( 14class SliderSetting(
@@ -17,14 +19,16 @@ class SliderSetting(
17 val max: Int, 19 val max: Int,
18 val units: String, 20 val units: String,
19 val key: String? = null, 21 val key: String? = null,
20 val defaultValue: Int? = null 22 val defaultValue: Any? = null
21) : SettingsItem(setting, titleId, descriptionId) { 23) : SettingsItem(setting, titleId, descriptionId) {
22 override val type = TYPE_SLIDER 24 override val type = TYPE_SLIDER
23 25
24 val selectedValue: Int 26 val selectedValue: Any
25 get() { 27 get() {
26 val setting = setting ?: return defaultValue!! 28 val setting = setting ?: return defaultValue!!
27 return when (setting) { 29 return when (setting) {
30 is AbstractByteSetting -> setting.byte.toInt()
31 is AbstractShortSetting -> setting.short.toInt()
28 is AbstractIntSetting -> setting.int 32 is AbstractIntSetting -> setting.int
29 is AbstractFloatSetting -> setting.float.roundToInt() 33 is AbstractFloatSetting -> setting.float.roundToInt()
30 else -> { 34 else -> {
@@ -43,7 +47,7 @@ class SliderSetting(
43 */ 47 */
44 fun setSelectedValue(selection: Int): AbstractIntSetting { 48 fun setSelectedValue(selection: Int): AbstractIntSetting {
45 val intSetting = setting as AbstractIntSetting 49 val intSetting = setting as AbstractIntSetting
46 intSetting.int = selection 50 intSetting.setInt(selection)
47 return intSetting 51 return intSetting
48 } 52 }
49 53
@@ -56,7 +60,19 @@ class SliderSetting(
56 */ 60 */
57 fun setSelectedValue(selection: Float): AbstractFloatSetting { 61 fun setSelectedValue(selection: Float): AbstractFloatSetting {
58 val floatSetting = setting as AbstractFloatSetting 62 val floatSetting = setting as AbstractFloatSetting
59 floatSetting.float = selection 63 floatSetting.setFloat(selection)
60 return floatSetting 64 return floatSetting
61 } 65 }
66
67 fun setSelectedValue(selection: Short): AbstractShortSetting {
68 val shortSetting = setting as AbstractShortSetting
69 shortSetting.setShort(selection)
70 return shortSetting
71 }
72
73 fun setSelectedValue(selection: Byte): AbstractByteSetting {
74 val byteSetting = setting as AbstractByteSetting
75 byteSetting.setByte(selection)
76 return byteSetting
77 }
62} 78}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 3b6731dcd..2195641e3 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(
53 */ 53 */
54 fun setSelectedValue(selection: String): AbstractStringSetting { 54 fun setSelectedValue(selection: String): AbstractStringSetting {
55 val stringSetting = setting as AbstractStringSetting 55 val stringSetting = setting as AbstractStringSetting
56 stringSetting.string = selection 56 stringSetting.setString(selection)
57 return stringSetting 57 return stringSetting
58 } 58 }
59} 59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 90b198718..4ed8070e5 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(
49 // Try integer setting 49 // Try integer setting
50 try { 50 try {
51 val setting = setting as AbstractIntSetting 51 val setting = setting as AbstractIntSetting
52 setting.int = if (checked) 1 else 0 52 setting.setInt(if (checked) 1 else 0)
53 return setting 53 return setting
54 } catch (_: ClassCastException) { 54 } catch (_: ClassCastException) {
55 } 55 }
56 56
57 // Try boolean setting 57 // Try boolean setting
58 val setting = setting as AbstractBooleanSetting 58 val setting = setting as AbstractBooleanSetting
59 setting.boolean = checked 59 setting.setBoolean(checked)
60 return setting 60 return setting
61 } 61 }
62} 62}
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 e6fffc832..733a53c8c 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
21import java.io.IOException 21import java.io.IOException
22import org.yuzu.yuzu_emu.R 22import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 23import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
24import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
25import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
26import org.yuzu.yuzu_emu.features.settings.model.IntSetting
27import org.yuzu.yuzu_emu.features.settings.model.Settings 24import org.yuzu.yuzu_emu.features.settings.model.Settings
28import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
29import org.yuzu.yuzu_emu.features.settings.model.StringSetting
30import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 25import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
31import org.yuzu.yuzu_emu.utils.* 26import org.yuzu.yuzu_emu.utils.*
32 27
@@ -35,10 +30,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
35 30
36 private lateinit var binding: ActivitySettingsBinding 31 private lateinit var binding: ActivitySettingsBinding
37 32
38 private val settingsViewModel: SettingsViewModel by viewModels()
39
40 override val settings: Settings get() = settingsViewModel.settings
41
42 override fun onCreate(savedInstanceState: Bundle?) { 33 override fun onCreate(savedInstanceState: Bundle?) {
43 ThemeHelper.setTheme(this) 34 ThemeHelper.setTheme(this)
44 35
@@ -171,14 +162,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
171 fragment?.loadSettingsList() 162 fragment?.loadSettingsList()
172 } 163 }
173 164
174 override fun showToastMessage(message: String, is_long: Boolean) {
175 Toast.makeText(
176 this,
177 message,
178 if (is_long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
179 ).show()
180 }
181
182 override fun onSettingChanged() { 165 override fun onSettingChanged() {
183 presenter.onSettingChanged() 166 presenter.onSettingChanged()
184 } 167 }
@@ -187,19 +170,18 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
187 // Prevents saving to a non-existent settings file 170 // Prevents saving to a non-existent settings file
188 presenter.onSettingsReset() 171 presenter.onSettingsReset()
189 172
190 // Reset the static memory representation of each setting
191 BooleanSetting.clear()
192 FloatSetting.clear()
193 IntSetting.clear()
194 StringSetting.clear()
195
196 // Delete settings file because the user may have changed values that do not exist in the UI 173 // Delete settings file because the user may have changed values that do not exist in the UI
197 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 174 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
198 if (!settingsFile.delete()) { 175 if (!settingsFile.delete()) {
199 throw IOException("Failed to delete $settingsFile") 176 throw IOException("Failed to delete $settingsFile")
200 } 177 }
178 Settings.settingsList.forEach { it.reset() }
201 179
202 showToastMessage(getString(R.string.settings_reset), true) 180 Toast.makeText(
181 applicationContext,
182 getString(R.string.settings_reset),
183 Toast.LENGTH_LONG
184 ).show()
203 finish() 185 finish()
204 } 186 }
205 187
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 93e677b21..fdbad32bf 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
5 5
6import android.content.Context 6import android.content.Context
7import android.os.Bundle 7import android.os.Bundle
8import android.text.TextUtils
9import java.io.File 8import java.io.File
10import org.yuzu.yuzu_emu.NativeLibrary 9import org.yuzu.yuzu_emu.NativeLibrary
11import org.yuzu.yuzu_emu.features.settings.model.Settings 10import org.yuzu.yuzu_emu.features.settings.model.Settings
@@ -14,8 +13,6 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
14import org.yuzu.yuzu_emu.utils.Log 13import org.yuzu.yuzu_emu.utils.Log
15 14
16class SettingsActivityPresenter(private val activityView: SettingsActivityView) { 15class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
17 val settings: Settings get() = activityView.settings
18
19 private var shouldSave = false 16 private var shouldSave = false
20 private lateinit var menuTag: String 17 private lateinit var menuTag: String
21 private lateinit var gameId: String 18 private lateinit var gameId: String
@@ -33,13 +30,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
33 } 30 }
34 31
35 private fun loadSettingsUI() { 32 private fun loadSettingsUI() {
36 if (!settings.isLoaded) { 33 // TODO: Load custom settings contextually
37 if (!TextUtils.isEmpty(gameId)) {
38 settings.loadSettings(gameId, activityView)
39 } else {
40 settings.loadSettings(activityView)
41 }
42 }
43 activityView.showSettingsFragment(menuTag, false, gameId) 34 activityView.showSettingsFragment(menuTag, false, gameId)
44 activityView.onSettingsFileLoaded() 35 activityView.onSettingsFileLoaded()
45 } 36 }
@@ -67,9 +58,9 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
67 fun onStop(finishing: Boolean) { 58 fun onStop(finishing: Boolean) {
68 if (finishing && shouldSave) { 59 if (finishing && shouldSave) {
69 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") 60 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
70 settings.saveSettings(activityView) 61 Settings.saveSettings()
62 NativeLibrary.reloadSettings()
71 } 63 }
72 NativeLibrary.reloadSettings()
73 } 64 }
74 65
75 fun onSettingChanged() { 66 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 c186fc388..07a58b4ea 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import org.yuzu.yuzu_emu.features.settings.model.Settings
7
8/** 6/**
9 * Abstraction for the Activity that manages SettingsFragments. 7 * Abstraction for the Activity that manages SettingsFragments.
10 */ 8 */
@@ -18,15 +16,6 @@ interface SettingsActivityView {
18 fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) 16 fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
19 17
20 /** 18 /**
21 * Called by a contained Fragment to get access to the Setting HashMap
22 * loaded from disk, so that each Fragment doesn't need to perform its own
23 * read operation.
24 *
25 * @return A HashMap of Settings.
26 */
27 val settings: Settings
28
29 /**
30 * Called when a load operation completes. 19 * Called when a load operation completes.
31 */ 20 */
32 fun onSettingsFileLoaded() 21 fun onSettingsFileLoaded()
@@ -37,14 +26,6 @@ interface SettingsActivityView {
37 fun onSettingsFileNotFound() 26 fun onSettingsFileNotFound()
38 27
39 /** 28 /**
40 * Display a popup text message on screen.
41 *
42 * @param message The contents of the onscreen message.
43 * @param is_long Whether this should be a long Toast or short one.
44 */
45 fun showToastMessage(message: String, is_long: Boolean)
46
47 /**
48 * End the activity. 29 * End the activity.
49 */ 30 */
50 fun finish() 31 fun finish()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 9711e2c51..e2e8d8bec 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
24import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 24import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
25import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding 25import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
26import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding 26import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
27import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
28import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
29import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
30import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 27import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
31import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting 28import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
32import org.yuzu.yuzu_emu.features.settings.model.FloatSetting 29import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
30import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
33import org.yuzu.yuzu_emu.features.settings.model.view.* 31import org.yuzu.yuzu_emu.features.settings.model.view.*
34import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* 32import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
35 33
@@ -115,8 +113,7 @@ class SettingsAdapter(
115 } 113 }
116 114
117 fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) { 115 fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
118 val setting = item.setChecked(checked) 116 item.setChecked(checked)
119 fragmentView.putSetting(setting)
120 fragmentView.onSettingChanged() 117 fragmentView.onSettingChanged()
121 } 118 }
122 119
@@ -150,7 +147,7 @@ class SettingsAdapter(
150 fun onDateTimeClick(item: DateTimeSetting, position: Int) { 147 fun onDateTimeClick(item: DateTimeSetting, position: Int) {
151 clickedItem = item 148 clickedItem = item
152 clickedPosition = position 149 clickedPosition = position
153 val storedTime = java.lang.Long.decode(item.value) * 1000 150 val storedTime = item.value * 1000
154 151
155 // Helper to extract hour and minute from epoch time 152 // Helper to extract hour and minute from epoch time
156 val calendar: Calendar = Calendar.getInstance() 153 val calendar: Calendar = Calendar.getInstance()
@@ -183,13 +180,11 @@ class SettingsAdapter(
183 var epochTime: Long = datePicker.selection!! / 1000 180 var epochTime: Long = datePicker.selection!! / 1000
184 epochTime += timePicker.hour.toLong() * 60 * 60 181 epochTime += timePicker.hour.toLong() * 60 * 60
185 epochTime += timePicker.minute.toLong() * 60 182 epochTime += timePicker.minute.toLong() * 60
186 val rtcString = epochTime.toString() 183 if (item.value != epochTime) {
187 if (item.value != rtcString) {
188 fragmentView.onSettingChanged() 184 fragmentView.onSettingChanged()
185 notifyItemChanged(clickedPosition)
186 item.setSelectedValue(epochTime)
189 } 187 }
190 notifyItemChanged(clickedPosition)
191 val setting = item.setSelectedValue(rtcString)
192 fragmentView.putSetting(setting)
193 clickedItem = null 188 clickedItem = null
194 } 189 }
195 datePicker.show( 190 datePicker.show(
@@ -201,7 +196,7 @@ class SettingsAdapter(
201 fun onSliderClick(item: SliderSetting, position: Int) { 196 fun onSliderClick(item: SliderSetting, position: Int) {
202 clickedItem = item 197 clickedItem = item
203 clickedPosition = position 198 clickedPosition = position
204 sliderProgress = item.selectedValue 199 sliderProgress = item.selectedValue as Int
205 200
206 val inflater = LayoutInflater.from(context) 201 val inflater = LayoutInflater.from(context)
207 val sliderBinding = DialogSliderBinding.inflate(inflater) 202 val sliderBinding = DialogSliderBinding.inflate(inflater)
@@ -249,8 +244,7 @@ class SettingsAdapter(
249 } 244 }
250 245
251 // Get the backing Setting, which may be null (if for example it was missing from the file) 246 // Get the backing Setting, which may be null (if for example it was missing from the file)
252 val setting = scSetting.setSelectedValue(value) 247 scSetting.setSelectedValue(value)
253 fragmentView.putSetting(setting)
254 closeDialog() 248 closeDialog()
255 } 249 }
256 250
@@ -258,8 +252,7 @@ class SettingsAdapter(
258 val scSetting = clickedItem as StringSingleChoiceSetting 252 val scSetting = clickedItem as StringSingleChoiceSetting
259 val value = scSetting.getValueAt(which) 253 val value = scSetting.getValueAt(which)
260 if (scSetting.selectedValue != value) fragmentView.onSettingChanged() 254 if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
261 val setting = scSetting.setSelectedValue(value!!) 255 scSetting.setSelectedValue(value!!)
262 fragmentView.putSetting(setting)
263 closeDialog() 256 closeDialog()
264 } 257 }
265 258
@@ -268,13 +261,25 @@ class SettingsAdapter(
268 if (sliderSetting.selectedValue != sliderProgress) { 261 if (sliderSetting.selectedValue != sliderProgress) {
269 fragmentView.onSettingChanged() 262 fragmentView.onSettingChanged()
270 } 263 }
271 if (sliderSetting.setting is FloatSetting) { 264 when (sliderSetting.setting) {
272 val value = sliderProgress.toFloat() 265 is ByteSetting -> {
273 val setting = sliderSetting.setSelectedValue(value) 266 val value = sliderProgress.toByte()
274 fragmentView.putSetting(setting) 267 sliderSetting.setSelectedValue(value)
275 } else { 268 }
276 val setting = sliderSetting.setSelectedValue(sliderProgress) 269
277 fragmentView.putSetting(setting) 270 is ShortSetting -> {
271 val value = sliderProgress.toShort()
272 sliderSetting.setSelectedValue(value)
273 }
274
275 is FloatSetting -> {
276 val value = sliderProgress.toFloat()
277 sliderSetting.setSelectedValue(value)
278 }
279
280 else -> {
281 sliderSetting.setSelectedValue(sliderProgress)
282 }
278 } 283 }
279 closeDialog() 284 closeDialog()
280 } 285 }
@@ -286,13 +291,8 @@ class SettingsAdapter(
286 fun onLongClick(setting: AbstractSetting, position: Int): Boolean { 291 fun onLongClick(setting: AbstractSetting, position: Int): Boolean {
287 MaterialAlertDialogBuilder(context) 292 MaterialAlertDialogBuilder(context)
288 .setMessage(R.string.reset_setting_confirmation) 293 .setMessage(R.string.reset_setting_confirmation)
289 .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> 294 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
290 when (setting) { 295 setting.reset()
291 is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean
292 is AbstractFloatSetting -> setting.float = setting.defaultValue as Float
293 is AbstractIntSetting -> setting.int = setting.defaultValue as Int
294 is AbstractStringSetting -> setting.string = setting.defaultValue as String
295 }
296 notifyItemChanged(position) 296 notifyItemChanged(position)
297 fragmentView.onSettingChanged() 297 fragmentView.onSettingChanged()
298 } 298 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 70a74c4dd..dc1bf6eb1 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
15import androidx.recyclerview.widget.LinearLayoutManager 15import androidx.recyclerview.widget.LinearLayoutManager
16import com.google.android.material.divider.MaterialDividerItemDecoration 16import com.google.android.material.divider.MaterialDividerItemDecoration
17import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 17import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
18import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
19import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 18import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
20 19
21class SettingsFragment : Fragment(), SettingsFragmentView { 20class SettingsFragment : Fragment(), SettingsFragmentView {
@@ -89,14 +88,6 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
89 ) 88 )
90 } 89 }
91 90
92 override fun showToastMessage(message: String?, is_long: Boolean) {
93 activityView!!.showToastMessage(message!!, is_long)
94 }
95
96 override fun putSetting(setting: AbstractSetting) {
97 fragmentPresenter.putSetting(setting)
98 }
99
100 override fun onSettingChanged() { 91 override fun onSettingChanged() {
101 activityView!!.onSettingChanged() 92 activityView!!.onSettingChanged()
102 } 93 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 59c1d9d54..2bab9e542 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
6import android.content.SharedPreferences 6import android.content.SharedPreferences
7import android.os.Build 7import android.os.Build
8import android.text.TextUtils 8import android.text.TextUtils
9import android.widget.Toast
9import androidx.preference.PreferenceManager 10import androidx.preference.PreferenceManager
10import org.yuzu.yuzu_emu.R 11import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 12import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 13import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
13import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 14import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
14import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
15import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 15import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
16import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
16import org.yuzu.yuzu_emu.features.settings.model.IntSetting 17import org.yuzu.yuzu_emu.features.settings.model.IntSetting
18import org.yuzu.yuzu_emu.features.settings.model.LongSetting
17import org.yuzu.yuzu_emu.features.settings.model.Settings 19import org.yuzu.yuzu_emu.features.settings.model.Settings
18import org.yuzu.yuzu_emu.features.settings.model.StringSetting 20import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
19import org.yuzu.yuzu_emu.features.settings.model.view.* 21import org.yuzu.yuzu_emu.features.settings.model.view.*
20import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 22import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
21import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment 23import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
@@ -27,7 +29,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
27 private var settingsList: ArrayList<SettingsItem>? = null 29 private var settingsList: ArrayList<SettingsItem>? = null
28 30
29 private val settingsActivity get() = fragmentView.activityView as SettingsActivity 31 private val settingsActivity get() = fragmentView.activityView as SettingsActivity
30 private val settings get() = fragmentView.activityView!!.settings
31 32
32 private lateinit var preferences: SharedPreferences 33 private lateinit var preferences: SharedPreferences
33 34
@@ -41,17 +42,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
41 loadSettingsList() 42 loadSettingsList()
42 } 43 }
43 44
44 fun putSetting(setting: AbstractSetting) {
45 if (setting.section == null || setting.key == null) {
46 return
47 }
48
49 val section = settings.getSection(setting.section!!)!!
50 if (section.getSetting(setting.key!!) == null) {
51 section.putSetting(setting)
52 }
53 }
54
55 fun loadSettingsList() { 45 fun loadSettingsList() {
56 if (!TextUtils.isEmpty(gameId)) { 46 if (!TextUtils.isEmpty(gameId)) {
57 settingsActivity.setToolbarTitle("Game Settings: $gameId") 47 settingsActivity.setToolbarTitle("Game Settings: $gameId")
@@ -69,7 +59,12 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
69 Settings.SECTION_THEME -> addThemeSettings(sl) 59 Settings.SECTION_THEME -> addThemeSettings(sl)
70 Settings.SECTION_DEBUG -> addDebugSettings(sl) 60 Settings.SECTION_DEBUG -> addDebugSettings(sl)
71 else -> { 61 else -> {
72 fragmentView.showToastMessage("Unimplemented menu", false) 62 val context = YuzuApplication.appContext
63 Toast.makeText(
64 context,
65 context.getString(R.string.unimplemented_menu),
66 Toast.LENGTH_SHORT
67 ).show()
73 return 68 return
74 } 69 }
75 } 70 }
@@ -135,23 +130,23 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
135 sl.apply { 130 sl.apply {
136 add( 131 add(
137 SwitchSetting( 132 SwitchSetting(
138 IntSetting.RENDERER_USE_SPEED_LIMIT, 133 BooleanSetting.RENDERER_USE_SPEED_LIMIT,
139 R.string.frame_limit_enable, 134 R.string.frame_limit_enable,
140 R.string.frame_limit_enable_description, 135 R.string.frame_limit_enable_description,
141 IntSetting.RENDERER_USE_SPEED_LIMIT.key, 136 BooleanSetting.RENDERER_USE_SPEED_LIMIT.key,
142 IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue 137 BooleanSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
143 ) 138 )
144 ) 139 )
145 add( 140 add(
146 SliderSetting( 141 SliderSetting(
147 IntSetting.RENDERER_SPEED_LIMIT, 142 ShortSetting.RENDERER_SPEED_LIMIT,
148 R.string.frame_limit_slider, 143 R.string.frame_limit_slider,
149 R.string.frame_limit_slider_description, 144 R.string.frame_limit_slider_description,
150 1, 145 1,
151 200, 146 200,
152 "%", 147 "%",
153 IntSetting.RENDERER_SPEED_LIMIT.key, 148 ShortSetting.RENDERER_SPEED_LIMIT.key,
154 IntSetting.RENDERER_SPEED_LIMIT.defaultValue 149 ShortSetting.RENDERER_SPEED_LIMIT.defaultValue
155 ) 150 )
156 ) 151 )
157 add( 152 add(
@@ -182,11 +177,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
182 sl.apply { 177 sl.apply {
183 add( 178 add(
184 SwitchSetting( 179 SwitchSetting(
185 IntSetting.USE_DOCKED_MODE, 180 BooleanSetting.USE_DOCKED_MODE,
186 R.string.use_docked_mode, 181 R.string.use_docked_mode,
187 R.string.use_docked_mode_description, 182 R.string.use_docked_mode_description,
188 IntSetting.USE_DOCKED_MODE.key, 183 BooleanSetting.USE_DOCKED_MODE.key,
189 IntSetting.USE_DOCKED_MODE.defaultValue 184 BooleanSetting.USE_DOCKED_MODE.defaultValue
190 ) 185 )
191 ) 186 )
192 add( 187 add(
@@ -222,11 +217,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
222 ) 217 )
223 add( 218 add(
224 DateTimeSetting( 219 DateTimeSetting(
225 StringSetting.CUSTOM_RTC, 220 LongSetting.CUSTOM_RTC,
226 R.string.set_custom_rtc, 221 R.string.set_custom_rtc,
227 0, 222 0,
228 StringSetting.CUSTOM_RTC.key, 223 LongSetting.CUSTOM_RTC.key,
229 StringSetting.CUSTOM_RTC.defaultValue 224 LongSetting.CUSTOM_RTC.defaultValue
230 ) 225 )
231 ) 226 )
232 } 227 }
@@ -314,38 +309,38 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
314 ) 309 )
315 add( 310 add(
316 SwitchSetting( 311 SwitchSetting(
317 IntSetting.RENDERER_USE_DISK_SHADER_CACHE, 312 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
318 R.string.use_disk_shader_cache, 313 R.string.use_disk_shader_cache,
319 R.string.use_disk_shader_cache_description, 314 R.string.use_disk_shader_cache_description,
320 IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key, 315 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
321 IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue 316 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
322 ) 317 )
323 ) 318 )
324 add( 319 add(
325 SwitchSetting( 320 SwitchSetting(
326 IntSetting.RENDERER_FORCE_MAX_CLOCK, 321 BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
327 R.string.renderer_force_max_clock, 322 R.string.renderer_force_max_clock,
328 R.string.renderer_force_max_clock_description, 323 R.string.renderer_force_max_clock_description,
329 IntSetting.RENDERER_FORCE_MAX_CLOCK.key, 324 BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key,
330 IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue 325 BooleanSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
331 ) 326 )
332 ) 327 )
333 add( 328 add(
334 SwitchSetting( 329 SwitchSetting(
335 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS, 330 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
336 R.string.renderer_asynchronous_shaders, 331 R.string.renderer_asynchronous_shaders,
337 R.string.renderer_asynchronous_shaders_description, 332 R.string.renderer_asynchronous_shaders_description,
338 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key, 333 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
339 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue 334 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
340 ) 335 )
341 ) 336 )
342 add( 337 add(
343 SwitchSetting( 338 SwitchSetting(
344 IntSetting.RENDERER_REACTIVE_FLUSHING, 339 BooleanSetting.RENDERER_REACTIVE_FLUSHING,
345 R.string.renderer_reactive_flushing, 340 R.string.renderer_reactive_flushing,
346 R.string.renderer_reactive_flushing_description, 341 R.string.renderer_reactive_flushing_description,
347 IntSetting.RENDERER_REACTIVE_FLUSHING.key, 342 BooleanSetting.RENDERER_REACTIVE_FLUSHING.key,
348 IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue 343 BooleanSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
349 ) 344 )
350 ) 345 )
351 } 346 }
@@ -355,26 +350,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
355 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) 350 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
356 sl.apply { 351 sl.apply {
357 add( 352 add(
358 StringSingleChoiceSetting( 353 SingleChoiceSetting(
359 StringSetting.AUDIO_OUTPUT_ENGINE, 354 IntSetting.AUDIO_OUTPUT_ENGINE,
360 R.string.audio_output_engine, 355 R.string.audio_output_engine,
361 0, 356 0,
362 settingsActivity.resources.getStringArray(R.array.outputEngineEntries), 357 R.array.outputEngineEntries,
363 settingsActivity.resources.getStringArray(R.array.outputEngineValues), 358 R.array.outputEngineValues,
364 StringSetting.AUDIO_OUTPUT_ENGINE.key, 359 IntSetting.AUDIO_OUTPUT_ENGINE.key,
365 StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue 360 IntSetting.AUDIO_OUTPUT_ENGINE.defaultValue
366 ) 361 )
367 ) 362 )
368 add( 363 add(
369 SliderSetting( 364 SliderSetting(
370 IntSetting.AUDIO_VOLUME, 365 ByteSetting.AUDIO_VOLUME,
371 R.string.audio_volume, 366 R.string.audio_volume,
372 R.string.audio_volume_description, 367 R.string.audio_volume_description,
373 0, 368 0,
374 100, 369 100,
375 "%", 370 "%",
376 IntSetting.AUDIO_VOLUME.key, 371 ByteSetting.AUDIO_VOLUME.key,
377 IntSetting.AUDIO_VOLUME.defaultValue 372 ByteSetting.AUDIO_VOLUME.defaultValue
378 ) 373 )
379 ) 374 )
380 } 375 }
@@ -384,19 +379,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
384 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme)) 379 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme))
385 sl.apply { 380 sl.apply {
386 val theme: AbstractIntSetting = object : AbstractIntSetting { 381 val theme: AbstractIntSetting = object : AbstractIntSetting {
387 override var int: Int 382 override val int: Int
388 get() = preferences.getInt(Settings.PREF_THEME, 0) 383 get() = preferences.getInt(Settings.PREF_THEME, 0)
389 set(value) { 384
390 preferences.edit() 385 override fun setInt(value: Int) {
391 .putInt(Settings.PREF_THEME, value) 386 preferences.edit()
392 .apply() 387 .putInt(Settings.PREF_THEME, value)
393 settingsActivity.recreate() 388 .apply()
394 } 389 settingsActivity.recreate()
390 }
391
395 override val key: String? = null 392 override val key: String? = null
396 override val section: String? = null 393 override val category = Settings.Category.UiGeneral
397 override val isRuntimeEditable: Boolean = false 394 override val isRuntimeModifiable: Boolean = false
398 override val valueAsString: String
399 get() = preferences.getInt(Settings.PREF_THEME, 0).toString()
400 override val defaultValue: Any = 0 395 override val defaultValue: Any = 0
401 } 396 }
402 397
@@ -423,19 +418,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
423 } 418 }
424 419
425 val themeMode: AbstractIntSetting = object : AbstractIntSetting { 420 val themeMode: AbstractIntSetting = object : AbstractIntSetting {
426 override var int: Int 421 override val int: Int
427 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) 422 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1)
428 set(value) { 423
429 preferences.edit() 424 override fun setInt(value: Int) {
430 .putInt(Settings.PREF_THEME_MODE, value) 425 preferences.edit()
431 .apply() 426 .putInt(Settings.PREF_THEME_MODE, value)
432 ThemeHelper.setThemeMode(settingsActivity) 427 .apply()
433 } 428 ThemeHelper.setThemeMode(settingsActivity)
429 }
430
434 override val key: String? = null 431 override val key: String? = null
435 override val section: String? = null 432 override val category = Settings.Category.UiGeneral
436 override val isRuntimeEditable: Boolean = false 433 override val isRuntimeModifiable: Boolean = false
437 override val valueAsString: String
438 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1).toString()
439 override val defaultValue: Any = -1 434 override val defaultValue: Any = -1
440 } 435 }
441 436
@@ -450,20 +445,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
450 ) 445 )
451 446
452 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { 447 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
453 override var boolean: Boolean 448 override val boolean: Boolean
454 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 449 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
455 set(value) { 450
456 preferences.edit() 451 override fun setBoolean(value: Boolean) {
457 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value) 452 preferences.edit()
458 .apply() 453 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
459 settingsActivity.recreate() 454 .apply()
460 } 455 settingsActivity.recreate()
456 }
457
461 override val key: String? = null 458 override val key: String? = null
462 override val section: String? = null 459 override val category = Settings.Category.UiGeneral
463 override val isRuntimeEditable: Boolean = false 460 override val isRuntimeModifiable: Boolean = false
464 override val valueAsString: String
465 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
466 .toString()
467 override val defaultValue: Any = false 461 override val defaultValue: Any = false
468 } 462 }
469 463
@@ -494,11 +488,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
494 ) 488 )
495 add( 489 add(
496 SwitchSetting( 490 SwitchSetting(
497 IntSetting.RENDERER_DEBUG, 491 BooleanSetting.RENDERER_DEBUG,
498 R.string.renderer_debug, 492 R.string.renderer_debug,
499 R.string.renderer_debug_description, 493 R.string.renderer_debug_description,
500 IntSetting.RENDERER_DEBUG.key, 494 BooleanSetting.RENDERER_DEBUG.key,
501 IntSetting.RENDERER_DEBUG.defaultValue 495 BooleanSetting.RENDERER_DEBUG.defaultValue
502 ) 496 )
503 ) 497 )
504 498
@@ -514,17 +508,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
514 ) 508 )
515 509
516 val fastmem = object : AbstractBooleanSetting { 510 val fastmem = object : AbstractBooleanSetting {
517 override var boolean: Boolean 511 override val boolean: Boolean
518 get() = 512 get() =
519 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean 513 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
520 set(value) { 514
521 BooleanSetting.FASTMEM.boolean = value 515 override fun setBoolean(value: Boolean) {
522 BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value 516 BooleanSetting.FASTMEM.setBoolean(value)
523 } 517 BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value)
518 }
519
524 override val key: String? = null 520 override val key: String? = null
525 override val section: String = Settings.SECTION_CPU 521 override val category = Settings.Category.Cpu
526 override val isRuntimeEditable: Boolean = false 522 override val isRuntimeModifiable: Boolean = false
527 override val valueAsString: String = ""
528 override val defaultValue: Any = true 523 override val defaultValue: Any = true
529 } 524 }
530 add( 525 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 1ebe35eaa..a4d7a80aa 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 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 6import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
8 7
9/** 8/**
@@ -37,21 +36,6 @@ interface SettingsFragmentView {
37 fun loadSubMenu(menuKey: String) 36 fun loadSubMenu(menuKey: String)
38 37
39 /** 38 /**
40 * Tell the Fragment to tell the containing activity to display a toast message.
41 *
42 * @param message Text to be shown in the Toast
43 * @param is_long Whether this should be a long Toast or short one.
44 */
45 fun showToastMessage(message: String?, is_long: Boolean)
46
47 /**
48 * Have the fragment add a setting to the HashMap.
49 *
50 * @param setting The (possibly previously missing) new setting.
51 */
52 fun putSetting(setting: AbstractSetting)
53
54 /**
55 * Have the fragment tell the containing Activity that a setting was modified. 39 * Have the fragment tell the containing Activity that a setting was modified.
56 */ 40 */
57 fun onSettingChanged() 41 fun onSettingChanged()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 79572fc06..eb25ea4fb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -29,7 +29,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
29 } 29 }
30 30
31 binding.textSettingValue.visibility = View.VISIBLE 31 binding.textSettingValue.visibility = View.VISIBLE
32 val epochTime = setting.value.toLong() 32 val epochTime = setting.value
33 val instant = Instant.ofEpochMilli(epochTime * 1000) 33 val instant = Instant.ofEpochMilli(epochTime * 1000)
34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) 34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) 35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 70a52df5d..2b04d666a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,18 +3,15 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import android.widget.Toast
6import java.io.* 7import java.io.*
7import java.util.*
8import org.ini4j.Wini 8import org.ini4j.Wini
9import org.yuzu.yuzu_emu.NativeLibrary
10import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 10import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.* 11import org.yuzu.yuzu_emu.features.settings.model.*
13import org.yuzu.yuzu_emu.features.settings.model.Settings.SettingsSectionMap
14import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
15import org.yuzu.yuzu_emu.utils.BiMap
16import org.yuzu.yuzu_emu.utils.DirectoryInitialization 12import org.yuzu.yuzu_emu.utils.DirectoryInitialization
17import org.yuzu.yuzu_emu.utils.Log 13import org.yuzu.yuzu_emu.utils.Log
14import org.yuzu.yuzu_emu.utils.NativeConfig
18 15
19/** 16/**
20 * Contains static methods for interacting with .ini files in which settings are stored. 17 * Contains static methods for interacting with .ini files in which settings are stored.
@@ -22,243 +19,41 @@ import org.yuzu.yuzu_emu.utils.Log
22object SettingsFile { 19object SettingsFile {
23 const val FILE_NAME_CONFIG = "config" 20 const val FILE_NAME_CONFIG = "config"
24 21
25 private var sectionsMap = BiMap<String?, String?>()
26
27 /**
28 * Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves
29 * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
30 * failed.
31 *
32 * @param ini The ini file to load the settings from
33 * @param isCustomGame
34 * @param view The current view.
35 * @return An Observable that emits a HashMap of the file's contents, then completes.
36 */
37 private fun readFile(
38 ini: File?,
39 isCustomGame: Boolean,
40 view: SettingsActivityView? = null
41 ): HashMap<String, SettingSection?> {
42 val sections: HashMap<String, SettingSection?> = SettingsSectionMap()
43 var reader: BufferedReader? = null
44 try {
45 reader = BufferedReader(FileReader(ini))
46 var current: SettingSection? = null
47 var line: String?
48 while (reader.readLine().also { line = it } != null) {
49 if (line!!.startsWith("[") && line!!.endsWith("]")) {
50 current = sectionFromLine(line!!, isCustomGame)
51 sections[current.name] = current
52 } else if (current != null) {
53 val setting = settingFromLine(line!!)
54 if (setting != null) {
55 current.putSetting(setting)
56 }
57 }
58 }
59 } catch (e: FileNotFoundException) {
60 Log.error("[SettingsFile] File not found: " + e.message)
61 view?.onSettingsFileNotFound()
62 } catch (e: IOException) {
63 Log.error("[SettingsFile] Error reading from: " + e.message)
64 view?.onSettingsFileNotFound()
65 } finally {
66 if (reader != null) {
67 try {
68 reader.close()
69 } catch (e: IOException) {
70 Log.error("[SettingsFile] Error closing: " + e.message)
71 }
72 }
73 }
74 return sections
75 }
76
77 fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> {
78 return readFile(getSettingsFile(fileName), false, view)
79 }
80
81 fun readFile(fileName: String): HashMap<String, SettingSection?> =
82 readFile(getSettingsFile(fileName), false)
83
84 /**
85 * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves
86 * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
87 * failed.
88 *
89 * @param gameId the id of the game to load it's settings.
90 * @param view The current view.
91 */
92 fun readCustomGameSettings(
93 gameId: String,
94 view: SettingsActivityView?
95 ): HashMap<String, SettingSection?> {
96 return readFile(getCustomGameSettingsFile(gameId), true, view)
97 }
98
99 /** 22 /**
100 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error 23 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
101 * telling why it failed. 24 * telling why it failed.
102 * 25 *
103 * @param fileName The target filename without a path or extension. 26 * @param fileName The target filename without a path or extension.
104 * @param sections The HashMap containing the Settings we want to serialize.
105 * @param view The current view.
106 */ 27 */
107 fun saveFile( 28 fun saveFile(fileName: String) {
108 fileName: String,
109 sections: TreeMap<String, SettingSection>,
110 view: SettingsActivityView
111 ) {
112 val ini = getSettingsFile(fileName) 29 val ini = getSettingsFile(fileName)
113 try { 30 try {
114 val writer = Wini(ini) 31 val wini = Wini(ini)
115 val keySet: Set<String> = sections.keys 32 for (specificCategory in Settings.Category.values()) {
116 for (key in keySet) { 33 val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
117 val section = sections[key] 34 for (setting in Settings.settingsList) {
118 writeSection(writer, section!!) 35 if (setting.key!!.isEmpty()) continue
36
37 val settingCategoryHeader =
38 NativeConfig.getConfigHeader(setting.category.ordinal)
39 val iniSetting: String? = wini.get(categoryHeader, setting.key)
40 if (iniSetting != null || settingCategoryHeader == categoryHeader) {
41 wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
42 }
43 }
119 } 44 }
120 writer.store() 45 wini.store()
121 } catch (e: IOException) { 46 } catch (e: IOException) {
122 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) 47 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
123 view.showToastMessage( 48 val context = YuzuApplication.appContext
124 YuzuApplication.appContext 49 Toast.makeText(
125 .getString(R.string.error_saving, fileName, e.message), 50 context,
126 false 51 context.getString(R.string.error_saving, fileName, e.message),
127 ) 52 Toast.LENGTH_SHORT
128 } 53 ).show()
129 }
130
131 fun saveCustomGameSettings(gameId: String?, sections: HashMap<String, SettingSection?>) {
132 val sortedSections: Set<String> = TreeSet(sections.keys)
133 for (sectionKey in sortedSections) {
134 val section = sections[sectionKey]
135 val settings = section!!.settings
136 val sortedKeySet: Set<String> = TreeSet(settings.keys)
137 for (settingKey in sortedKeySet) {
138 val setting = settings[settingKey]
139 NativeLibrary.setUserSetting(
140 gameId,
141 mapSectionNameFromIni(
142 section.name
143 ),
144 setting!!.key,
145 setting.valueAsString
146 )
147 }
148 }
149 }
150
151 private fun mapSectionNameFromIni(generalSectionName: String): String? {
152 return if (sectionsMap.getForward(generalSectionName) != null) {
153 sectionsMap.getForward(generalSectionName)
154 } else {
155 generalSectionName
156 }
157 }
158
159 private fun mapSectionNameToIni(generalSectionName: String): String {
160 return if (sectionsMap.getBackward(generalSectionName) != null) {
161 sectionsMap.getBackward(generalSectionName).toString()
162 } else {
163 generalSectionName
164 }
165 }
166
167 fun getSettingsFile(fileName: String): File {
168 return File(
169 DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini"
170 )
171 }
172
173 private fun getCustomGameSettingsFile(gameId: String): File {
174 return File(DirectoryInitialization.userDirectory + "/GameSettings/" + gameId + ".ini")
175 }
176
177 private fun sectionFromLine(line: String, isCustomGame: Boolean): SettingSection {
178 var sectionName: String = line.substring(1, line.length - 1)
179 if (isCustomGame) {
180 sectionName = mapSectionNameToIni(sectionName)
181 } 54 }
182 return SettingSection(sectionName)
183 } 55 }
184 56
185 /** 57 fun getSettingsFile(fileName: String): File =
186 * For a line of text, determines what type of data is being represented, and returns 58 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
187 * a Setting object containing this data.
188 *
189 * @param line The line of text being parsed.
190 * @return A typed Setting containing the key/value contained in the line.
191 */
192 private fun settingFromLine(line: String): AbstractSetting? {
193 val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
194 if (splitLine.size != 2) {
195 return null
196 }
197 val key = splitLine[0].trim { it <= ' ' }
198 val value = splitLine[1].trim { it <= ' ' }
199 if (value.isEmpty()) {
200 return null
201 }
202
203 val booleanSetting = BooleanSetting.from(key)
204 if (booleanSetting != null) {
205 booleanSetting.boolean = value.toBoolean()
206 return booleanSetting
207 }
208
209 val intSetting = IntSetting.from(key)
210 if (intSetting != null) {
211 intSetting.int = value.toInt()
212 return intSetting
213 }
214
215 val floatSetting = FloatSetting.from(key)
216 if (floatSetting != null) {
217 floatSetting.float = value.toFloat()
218 return floatSetting
219 }
220
221 val stringSetting = StringSetting.from(key)
222 if (stringSetting != null) {
223 stringSetting.string = value
224 return stringSetting
225 }
226
227 return null
228 }
229
230 /**
231 * Writes the contents of a Section HashMap to disk.
232 *
233 * @param parser A Wini pointed at a file on disk.
234 * @param section A section containing settings to be written to the file.
235 */
236 private fun writeSection(parser: Wini, section: SettingSection) {
237 // Write the section header.
238 val header = section.name
239
240 // Write this section's values.
241 val settings = section.settings
242 val keySet: Set<String> = settings.keys
243 for (key in keySet) {
244 val setting = settings[key]
245 parser.put(header, setting!!.key, setting.valueAsString)
246 }
247
248 BooleanSetting.values().forEach {
249 if (!keySet.contains(it.key)) {
250 parser.put(header, it.key, it.valueAsString)
251 }
252 }
253 IntSetting.values().forEach {
254 if (!keySet.contains(it.key)) {
255 parser.put(header, it.key, it.valueAsString)
256 }
257 }
258 StringSetting.values().forEach {
259 if (!keySet.contains(it.key)) {
260 parser.put(header, it.key, it.valueAsString)
261 }
262 }
263 }
264} 59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index aaf3a0ec1..d8dbf1f45 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
39import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 39import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
40import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 40import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
41import org.yuzu.yuzu_emu.features.settings.model.Settings 41import org.yuzu.yuzu_emu.features.settings.model.Settings
42import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
43import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 42import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
44import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@@ -54,7 +53,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
54 53
55 private val homeViewModel: HomeViewModel by viewModels() 54 private val homeViewModel: HomeViewModel by viewModels()
56 private val gamesViewModel: GamesViewModel by viewModels() 55 private val gamesViewModel: GamesViewModel by viewModels()
57 private val settingsViewModel: SettingsViewModel by viewModels()
58 56
59 override var themeId: Int = 0 57 override var themeId: Int = 0
60 58
@@ -62,8 +60,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
62 val splashScreen = installSplashScreen() 60 val splashScreen = installSplashScreen()
63 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } 61 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
64 62
65 settingsViewModel.settings.loadSettings()
66
67 ThemeHelper.setTheme(this) 63 ThemeHelper.setTheme(this)
68 64
69 super.onCreate(savedInstanceState) 65 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 9cfda74ee..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
+++ /dev/null
@@ -1,25 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6class BiMap<K, V> {
7 private val forward: MutableMap<K, V> = HashMap()
8 private val backward: MutableMap<V, K> = HashMap()
9
10 @Synchronized
11 fun add(key: K, value: V) {
12 forward[key] = value
13 backward[value] = key
14 }
15
16 @Synchronized
17 fun getForward(key: K): V? {
18 return forward[key]
19 }
20
21 @Synchronized
22 fun getBackward(key: V): K? {
23 return backward[key]
24 }
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
new file mode 100644
index 000000000..d4d981f9e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6object NativeConfig {
7 external fun getBoolean(key: String, getDefault: Boolean): Boolean
8 external fun setBoolean(key: String, value: Boolean)
9
10 external fun getByte(key: String, getDefault: Boolean): Byte
11 external fun setByte(key: String, value: Byte)
12
13 external fun getShort(key: String, getDefault: Boolean): Short
14 external fun setShort(key: String, value: Short)
15
16 external fun getInt(key: String, getDefault: Boolean): Int
17 external fun setInt(key: String, value: Int)
18
19 external fun getFloat(key: String, getDefault: Boolean): Float
20 external fun setFloat(key: String, value: Float)
21
22 external fun getLong(key: String, getDefault: Boolean): Long
23 external fun setLong(key: String, value: Long)
24
25 external fun getString(key: String, getDefault: Boolean): String
26 external fun setString(key: String, value: String)
27
28 external fun getIsRuntimeModifiable(key: String): Boolean
29
30 external fun getConfigHeader(category: Int): String
31}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index e2ed08e9f..e15d1480b 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -14,6 +14,8 @@ add_library(yuzu-android SHARED
14 id_cache.cpp 14 id_cache.cpp
15 id_cache.h 15 id_cache.h
16 native.cpp 16 native.cpp
17 native_config.cpp
18 uisettings.cpp
17) 19)
18 20
19set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 21set_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 9de9bd93e..34b425cb4 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -16,18 +16,20 @@
16#include "input_common/main.h" 16#include "input_common/main.h"
17#include "jni/config.h" 17#include "jni/config.h"
18#include "jni/default_ini.h" 18#include "jni/default_ini.h"
19#include "uisettings.h"
19 20
20namespace FS = Common::FS; 21namespace FS = Common::FS;
21 22
22Config::Config(std::optional<std::filesystem::path> config_path) 23Config::Config(const std::string& config_name, ConfigType config_type)
23 : config_loc{config_path.value_or(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini")}, 24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
24 config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} { 25 Initialize(config_name);
25 Reload();
26} 26}
27 27
28Config::~Config() = default; 28Config::~Config() = default;
29 29
30bool Config::LoadINI(const std::string& default_contents, bool retry) { 30bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 void(FS::CreateParentDir(config_loc));
32 config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
31 const auto config_loc_str = FS::PathToUTF8String(config_loc); 33 const auto config_loc_str = FS::PathToUTF8String(config_loc);
32 if (config->ParseError() < 0) { 34 if (config->ParseError() < 0) {
33 if (retry) { 35 if (retry) {
@@ -301,9 +303,28 @@ void Config::ReadValues() {
301 303
302 // Network 304 // Network
303 ReadSetting("Network", Settings::values.network_interface); 305 ReadSetting("Network", Settings::values.network_interface);
306
307 // Android
308 ReadSetting("Android", AndroidSettings::values.picture_in_picture);
309 ReadSetting("Android", AndroidSettings::values.screen_layout);
304} 310}
305 311
306void Config::Reload() { 312void Config::Initialize(const std::string& config_name) {
313 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
314 const auto config_file = fmt::format("{}.ini", config_name);
315
316 switch (type) {
317 case ConfigType::GlobalConfig:
318 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
319 break;
320 case ConfigType::PerGameConfig:
321 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
322 break;
323 case ConfigType::InputProfile:
324 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
325 LoadINI(DefaultINI::android_config_file);
326 return;
327 }
307 LoadINI(DefaultINI::android_config_file); 328 LoadINI(DefaultINI::android_config_file);
308 ReadValues(); 329 ReadValues();
309} 330}
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
index 0d7d6e94d..e1e8f47ed 100644
--- a/src/android/app/src/main/jni/config.h
+++ b/src/android/app/src/main/jni/config.h
@@ -13,25 +13,35 @@
13class INIReader; 13class INIReader;
14 14
15class Config { 15class Config {
16 std::filesystem::path config_loc;
17 std::unique_ptr<INIReader> config;
18
19 bool LoadINI(const std::string& default_contents = "", bool retry = true); 16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues();
21 17
22public: 18public:
23 explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt); 19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_name = "config",
26 ConfigType config_type = ConfigType::GlobalConfig);
24 ~Config(); 27 ~Config();
25 28
26 void Reload(); 29 void Initialize(const std::string& config_name);
27 30
28private: 31private:
29 /** 32 /**
30 * Applies a value read from the sdl2_config to a Setting. 33 * Applies a value read from the config to a Setting.
31 * 34 *
32 * @param group The name of the INI group 35 * @param group The name of the INI group
33 * @param setting The yuzu setting to modify 36 * @param setting The yuzu setting to modify
34 */ 37 */
35 template <typename Type, bool ranged> 38 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); 39 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
40
41 void ReadValues();
42
43 const ConfigType type;
44 std::unique_ptr<INIReader> config;
45 std::string config_loc;
46 const bool global;
37}; 47};
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 7e17833a0..b2adfdeda 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
824 Config{}; 824 Config{};
825} 825}
826 826
827jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
828 jstring j_game_id, jstring j_section,
829 jstring j_key) {
830 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
831 std::string_view section = env->GetStringUTFChars(j_section, 0);
832 std::string_view key = env->GetStringUTFChars(j_key, 0);
833
834 env->ReleaseStringUTFChars(j_game_id, game_id.data());
835 env->ReleaseStringUTFChars(j_section, section.data());
836 env->ReleaseStringUTFChars(j_key, key.data());
837
838 return env->NewStringUTF("");
839}
840
841void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
842 jstring j_game_id, jstring j_section,
843 jstring j_key, jstring j_value) {
844 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
845 std::string_view section = env->GetStringUTFChars(j_section, 0);
846 std::string_view key = env->GetStringUTFChars(j_key, 0);
847 std::string_view value = env->GetStringUTFChars(j_value, 0);
848
849 env->ReleaseStringUTFChars(j_game_id, game_id.data());
850 env->ReleaseStringUTFChars(j_section, section.data());
851 env->ReleaseStringUTFChars(j_key, key.data());
852 env->ReleaseStringUTFChars(j_value, value.data());
853}
854
855void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, 827void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
856 jstring j_game_id) { 828 jstring j_game_id) {
857 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 829 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
new file mode 100644
index 000000000..6123b3d08
--- /dev/null
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -0,0 +1,224 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <string>
5
6#include <jni.h>
7
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "jni/android_common/android_common.h"
11#include "jni/config.h"
12#include "uisettings.h"
13
14template <typename T>
15Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
16 auto key = GetJString(env, jkey);
17 auto basicSetting = Settings::values.linkage.by_key[key];
18 auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key];
19 if (basicSetting != 0) {
20 return static_cast<Settings::Setting<T>*>(basicSetting);
21 }
22 if (basicAndroidSetting != 0) {
23 return static_cast<Settings::Setting<T>*>(basicAndroidSetting);
24 }
25 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
26 return nullptr;
27}
28
29extern "C" {
30
31jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
32 jstring jkey, jboolean getDefault) {
33 auto setting = getSetting<bool>(env, jkey);
34 if (setting == nullptr) {
35 return false;
36 }
37 setting->SetGlobal(true);
38
39 if (static_cast<bool>(getDefault)) {
40 return setting->GetDefault();
41 }
42
43 return setting->GetValue();
44}
45
46void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey,
47 jboolean value) {
48 auto setting = getSetting<bool>(env, jkey);
49 if (setting == nullptr) {
50 return;
51 }
52 setting->SetGlobal(true);
53 setting->SetValue(static_cast<bool>(value));
54}
55
56jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey,
57 jboolean getDefault) {
58 auto setting = getSetting<u8>(env, jkey);
59 if (setting == nullptr) {
60 return -1;
61 }
62 setting->SetGlobal(true);
63
64 if (static_cast<bool>(getDefault)) {
65 return setting->GetDefault();
66 }
67
68 return setting->GetValue();
69}
70
71void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey,
72 jbyte value) {
73 auto setting = getSetting<u8>(env, jkey);
74 if (setting == nullptr) {
75 return;
76 }
77 setting->SetGlobal(true);
78 setting->SetValue(value);
79}
80
81jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey,
82 jboolean getDefault) {
83 auto setting = getSetting<u16>(env, jkey);
84 if (setting == nullptr) {
85 return -1;
86 }
87 setting->SetGlobal(true);
88
89 if (static_cast<bool>(getDefault)) {
90 return setting->GetDefault();
91 }
92
93 return setting->GetValue();
94}
95
96void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey,
97 jshort value) {
98 auto setting = getSetting<u16>(env, jkey);
99 if (setting == nullptr) {
100 return;
101 }
102 setting->SetGlobal(true);
103 setting->SetValue(value);
104}
105
106jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
107 jboolean getDefault) {
108 auto setting = getSetting<int>(env, jkey);
109 if (setting == nullptr) {
110 return -1;
111 }
112 setting->SetGlobal(true);
113
114 if (static_cast<bool>(getDefault)) {
115 return setting->GetDefault();
116 }
117
118 return setting->GetValue();
119}
120
121void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey,
122 jint value) {
123 auto setting = getSetting<int>(env, jkey);
124 if (setting == nullptr) {
125 return;
126 }
127 setting->SetGlobal(true);
128 setting->SetValue(value);
129}
130
131jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey,
132 jboolean getDefault) {
133 auto setting = getSetting<float>(env, jkey);
134 if (setting == nullptr) {
135 return -1;
136 }
137 setting->SetGlobal(true);
138
139 if (static_cast<bool>(getDefault)) {
140 return setting->GetDefault();
141 }
142
143 return setting->GetValue();
144}
145
146void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey,
147 jfloat value) {
148 auto setting = getSetting<float>(env, jkey);
149 if (setting == nullptr) {
150 return;
151 }
152 setting->SetGlobal(true);
153 setting->SetValue(value);
154}
155
156jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey,
157 jboolean getDefault) {
158 auto setting = getSetting<long>(env, jkey);
159 if (setting == nullptr) {
160 return -1;
161 }
162 setting->SetGlobal(true);
163
164 if (static_cast<bool>(getDefault)) {
165 return setting->GetDefault();
166 }
167
168 return setting->GetValue();
169}
170
171void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey,
172 jlong value) {
173 auto setting = getSetting<long>(env, jkey);
174 if (setting == nullptr) {
175 return;
176 }
177 setting->SetGlobal(true);
178 setting->SetValue(value);
179}
180
181jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey,
182 jboolean getDefault) {
183 auto setting = getSetting<std::string>(env, jkey);
184 if (setting == nullptr) {
185 return ToJString(env, "");
186 }
187 setting->SetGlobal(true);
188
189 if (static_cast<bool>(getDefault)) {
190 return ToJString(env, setting->GetDefault());
191 }
192
193 return ToJString(env, setting->GetValue());
194}
195
196void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
197 jstring value) {
198 auto setting = getSetting<std::string>(env, jkey);
199 if (setting == nullptr) {
200 return;
201 }
202
203 setting->SetGlobal(true);
204 setting->SetValue(GetJString(env, value));
205}
206
207jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
208 jstring jkey) {
209 auto key = GetJString(env, jkey);
210 auto setting = Settings::values.linkage.by_key[key];
211 if (setting != 0) {
212 return setting->RuntimeModfiable();
213 }
214 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
215 return true;
216}
217
218jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj,
219 jint jcategory) {
220 auto category = static_cast<Settings::Category>(jcategory);
221 return ToJString(env, Settings::TranslateCategory(category));
222}
223
224} // extern "C"
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/uisettings.cpp
new file mode 100644
index 000000000..f2f0bad50
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.cpp
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "uisettings.h"
5
6namespace AndroidSettings {
7
8Values values;
9
10} // namespace AndroidSettings
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/uisettings.h
new file mode 100644
index 000000000..494654af7
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <common/settings_common.h>
7#include "common/common_types.h"
8#include "common/settings_setting.h"
9
10namespace AndroidSettings {
11
12struct Values {
13 Settings::Linkage linkage;
14
15 // Android
16 Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture",
17 Settings::Category::Android};
18 Settings::Setting<s32> screen_layout{linkage,
19 5,
20 "screen_layout",
21 Settings::Category::Android,
22 Settings::Specialization::Default,
23 true,
24 true};
25};
26
27extern Values values;
28
29} // 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 200b99185..dc10159c9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -243,10 +243,10 @@
243 <item>@string/cubeb</item> 243 <item>@string/cubeb</item>
244 <item>@string/string_null</item> 244 <item>@string/string_null</item>
245 </string-array> 245 </string-array>
246 <string-array name="outputEngineValues"> 246 <integer-array name="outputEngineValues">
247 <item>auto</item> 247 <item>0</item>
248 <item>cubeb</item> 248 <item>1</item>
249 <item>null</item> 249 <item>3</item>
250 </string-array> 250 </integer-array>
251 251
252</resources> 252</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index de1b2909b..df76563fc 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 @@
200 <string name="ini_saved">Saved settings</string> 200 <string name="ini_saved">Saved settings</string>
201 <string name="gameid_saved">Saved settings for %1$s</string> 201 <string name="gameid_saved">Saved settings for %1$s</string>
202 <string name="error_saving">Error saving %1$s.ini: %2$s</string> 202 <string name="error_saving">Error saving %1$s.ini: %2$s</string>
203 <string name="unimplemented_menu">Unimplemented Menu</string>
203 <string name="loading">Loading…</string> 204 <string name="loading">Loading…</string>
204 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> 205 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
205 <string name="reset_to_default">Reset to default</string> 206 <string name="reset_to_default">Reset to default</string>
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 524056841..4ecaf550b 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -159,6 +159,8 @@ float Volume() {
159 159
160const char* TranslateCategory(Category category) { 160const char* TranslateCategory(Category category) {
161 switch (category) { 161 switch (category) {
162 case Category::Android:
163 return "Android";
162 case Category::Audio: 164 case Category::Audio:
163 return "Audio"; 165 return "Audio";
164 case Category::Core: 166 case Category::Core:
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index 137b65d5f..5960b78aa 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -14,6 +14,7 @@ BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Categ
14 : label{name}, category{category_}, id{linkage.count}, save{save_}, 14 : label{name}, category{category_}, id{linkage.count}, save{save_},
15 runtime_modifiable{runtime_modifiable_}, specialization{specialization_}, 15 runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
16 other_setting{other_setting_} { 16 other_setting{other_setting_} {
17 linkage.by_key.insert({name, this});
17 linkage.by_category[category].push_back(this); 18 linkage.by_category[category].push_back(this);
18 linkage.count++; 19 linkage.count++;
19} 20}
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 3082e0ce1..5b170dfd5 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -12,6 +12,7 @@
12namespace Settings { 12namespace Settings {
13 13
14enum class Category : u32 { 14enum class Category : u32 {
15 Android,
15 Audio, 16 Audio,
16 Core, 17 Core,
17 Cpu, 18 Cpu,
@@ -68,6 +69,7 @@ public:
68 explicit Linkage(u32 initial_count = 0); 69 explicit Linkage(u32 initial_count = 0);
69 ~Linkage(); 70 ~Linkage();
70 std::map<Category, std::vector<BasicSetting*>> by_category{}; 71 std::map<Category, std::vector<BasicSetting*>> by_category{};
72 std::map<std::string, Settings::BasicSetting*> by_key{};
71 std::vector<std::function<void()>> restore_functions{}; 73 std::vector<std::function<void()>> restore_functions{};
72 u32 count; 74 u32 count;
73}; 75};