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