mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
more settings options
This commit is contained in:
@@ -23,6 +23,7 @@ static bool xemu_snapshots_dirty = true;
|
||||
static GLuint g_snapshot_display_tex = 0;
|
||||
static bool g_snapshot_display_flip = false;
|
||||
static SDL_atomic_t g_snapshot_pending = { 0 };
|
||||
static SDL_atomic_t g_reboot_pending = { 0 };
|
||||
|
||||
#define SNAPSHOT_PREVIEW_WIDTH 320
|
||||
#define SNAPSHOT_PREVIEW_HEIGHT 240
|
||||
@@ -391,6 +392,11 @@ static struct {
|
||||
|
||||
void xemu_android_process_snapshot_request(void)
|
||||
{
|
||||
if (SDL_AtomicCAS(&g_reboot_pending, 1, 0)) {
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_AtomicGet(&g_snapshot_pending) == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -469,3 +475,12 @@ Java_com_izzy2lost_x1box_MainActivity_nativeLoadSnapshot(
|
||||
(void)obj;
|
||||
return dispatch_snapshot(env, name, SNAP_LOAD);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_izzy2lost_x1box_MainActivity_nativeRebootSystem(
|
||||
JNIEnv *env, jobject obj)
|
||||
{
|
||||
(void)env;
|
||||
(void)obj;
|
||||
SDL_AtomicSet(&g_reboot_pending, 1);
|
||||
}
|
||||
|
||||
@@ -434,6 +434,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
|
||||
private external fun nativeSaveSnapshot(name: String): Boolean
|
||||
private external fun nativeLoadSnapshot(name: String): Boolean
|
||||
private external fun nativeRebootSystem()
|
||||
|
||||
private fun slotName(slot: Int) = "android_slot_$slot"
|
||||
|
||||
@@ -699,6 +700,18 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
showSnapshotSlotDialog(save = false)
|
||||
}
|
||||
|
||||
private fun showRebootSystemConfirmation() {
|
||||
MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.in_game_menu_reboot_title)
|
||||
.setMessage(R.string.in_game_menu_reboot_message)
|
||||
.setPositiveButton(R.string.in_game_menu_reboot_action) { _, _ ->
|
||||
onScreenController?.resetAllInputs()
|
||||
nativeRebootSystem()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showInGameMenu() {
|
||||
swipeUpGestureRecognizer.reset()
|
||||
if (inGameMenuDialog?.isShowing == true) {
|
||||
@@ -714,6 +727,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
},
|
||||
getString(R.string.in_game_menu_save_state),
|
||||
getString(R.string.in_game_menu_load_state),
|
||||
getString(R.string.in_game_menu_reboot_system),
|
||||
getString(R.string.in_game_menu_exit_to_library),
|
||||
getString(R.string.in_game_menu_quit_app),
|
||||
)
|
||||
@@ -726,8 +740,9 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
1 -> toggleOnScreenController()
|
||||
2 -> showSaveStateDialog()
|
||||
3 -> showLoadStateDialog()
|
||||
4 -> exitToGameLibrary()
|
||||
5 -> quitApp()
|
||||
4 -> showRebootSystemConfirmation()
|
||||
5 -> exitToGameLibrary()
|
||||
6 -> quitApp()
|
||||
}
|
||||
}
|
||||
.setOnDismissListener {
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.materialswitch.MaterialSwitch
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import java.io.File
|
||||
@@ -31,6 +32,21 @@ class SettingsActivity : AppCompatActivity() {
|
||||
val labelRes: Int,
|
||||
)
|
||||
|
||||
private data class EepromAspectRatioOption(
|
||||
val value: XboxEepromEditor.AspectRatio,
|
||||
val labelRes: Int,
|
||||
)
|
||||
|
||||
private data class EepromRefreshRateOption(
|
||||
val value: XboxEepromEditor.RefreshRate,
|
||||
val labelRes: Int,
|
||||
)
|
||||
|
||||
private data class CacheClearResult(
|
||||
val deletedEntries: Int,
|
||||
val hadFailures: Boolean,
|
||||
)
|
||||
|
||||
private val eepromLanguageOptions = listOf(
|
||||
EepromLanguageOption(XboxEepromEditor.Language.ENGLISH, R.string.settings_eeprom_language_english),
|
||||
EepromLanguageOption(XboxEepromEditor.Language.JAPANESE, R.string.settings_eeprom_language_japanese),
|
||||
@@ -50,6 +66,18 @@ class SettingsActivity : AppCompatActivity() {
|
||||
EepromVideoOption(XboxEepromEditor.VideoStandard.PAL_M, R.string.settings_eeprom_video_standard_pal_m),
|
||||
)
|
||||
|
||||
private val eepromAspectRatioOptions = listOf(
|
||||
EepromAspectRatioOption(XboxEepromEditor.AspectRatio.NORMAL, R.string.settings_eeprom_aspect_ratio_normal),
|
||||
EepromAspectRatioOption(XboxEepromEditor.AspectRatio.WIDESCREEN, R.string.settings_eeprom_aspect_ratio_widescreen),
|
||||
EepromAspectRatioOption(XboxEepromEditor.AspectRatio.LETTERBOX, R.string.settings_eeprom_aspect_ratio_letterbox),
|
||||
)
|
||||
|
||||
private val eepromRefreshRateOptions = listOf(
|
||||
EepromRefreshRateOption(XboxEepromEditor.RefreshRate.DEFAULT, R.string.settings_eeprom_refresh_rate_default),
|
||||
EepromRefreshRateOption(XboxEepromEditor.RefreshRate.HZ_60, R.string.settings_eeprom_refresh_rate_60),
|
||||
EepromRefreshRateOption(XboxEepromEditor.RefreshRate.HZ_50, R.string.settings_eeprom_refresh_rate_50),
|
||||
)
|
||||
|
||||
private var pendingVulkanUri: String? = null
|
||||
private var pendingVulkanName: String? = null
|
||||
private var clearVulkan = false
|
||||
@@ -58,11 +86,20 @@ class SettingsActivity : AppCompatActivity() {
|
||||
private lateinit var tvEepromStatus: TextView
|
||||
private lateinit var inputEepromLanguage: TextInputLayout
|
||||
private lateinit var inputEepromVideoStandard: TextInputLayout
|
||||
private lateinit var inputEepromAspectRatio: TextInputLayout
|
||||
private lateinit var inputEepromRefreshRate: TextInputLayout
|
||||
private lateinit var dropdownEepromLanguage: AutoCompleteTextView
|
||||
private lateinit var dropdownEepromVideoStandard: AutoCompleteTextView
|
||||
private lateinit var dropdownEepromAspectRatio: AutoCompleteTextView
|
||||
private lateinit var dropdownEepromRefreshRate: AutoCompleteTextView
|
||||
private lateinit var switchEeprom480p: MaterialSwitch
|
||||
private lateinit var switchEeprom720p: MaterialSwitch
|
||||
private lateinit var switchEeprom1080i: MaterialSwitch
|
||||
|
||||
private var selectedEepromLanguage = XboxEepromEditor.Language.ENGLISH
|
||||
private var selectedEepromVideoStandard = XboxEepromEditor.VideoStandard.NTSC_M
|
||||
private var selectedEepromAspectRatio = XboxEepromEditor.AspectRatio.NORMAL
|
||||
private var selectedEepromRefreshRate = XboxEepromEditor.RefreshRate.DEFAULT
|
||||
private var eepromEditable = false
|
||||
private var eepromMissing = false
|
||||
private var eepromError = false
|
||||
@@ -103,14 +140,22 @@ class SettingsActivity : AppCompatActivity() {
|
||||
val toggleAudioDriver = findViewById<MaterialButtonToggleGroup>(R.id.toggle_audio_driver)
|
||||
val btnSave = findViewById<MaterialButton>(R.id.btn_settings_save)
|
||||
val btnRedoSetup = findViewById<MaterialButton>(R.id.btn_redo_setup_wizard)
|
||||
val btnClearCache = findViewById<MaterialButton>(R.id.btn_clear_system_cache)
|
||||
tvVulkanDriverName = findViewById(R.id.tv_vulkan_driver_name)
|
||||
val btnVulkanBrowse = findViewById<MaterialButton>(R.id.btn_vulkan_browse)
|
||||
val btnVulkanClear = findViewById<MaterialButton>(R.id.btn_vulkan_clear)
|
||||
tvEepromStatus = findViewById(R.id.tv_eeprom_status)
|
||||
inputEepromLanguage = findViewById(R.id.input_eeprom_language)
|
||||
inputEepromVideoStandard = findViewById(R.id.input_eeprom_video_standard)
|
||||
inputEepromAspectRatio = findViewById(R.id.input_eeprom_aspect_ratio)
|
||||
inputEepromRefreshRate = findViewById(R.id.input_eeprom_refresh_rate)
|
||||
dropdownEepromLanguage = findViewById(R.id.dropdown_eeprom_language)
|
||||
dropdownEepromVideoStandard = findViewById(R.id.dropdown_eeprom_video_standard)
|
||||
dropdownEepromAspectRatio = findViewById(R.id.dropdown_eeprom_aspect_ratio)
|
||||
dropdownEepromRefreshRate = findViewById(R.id.dropdown_eeprom_refresh_rate)
|
||||
switchEeprom480p = findViewById(R.id.switch_eeprom_480p)
|
||||
switchEeprom720p = findViewById(R.id.switch_eeprom_720p)
|
||||
switchEeprom1080i = findViewById(R.id.switch_eeprom_1080i)
|
||||
|
||||
// Load current values
|
||||
val renderer = prefs.getString("setting_renderer", "opengl") ?: "opengl"
|
||||
@@ -198,7 +243,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
setupEepromEditor()
|
||||
|
||||
btnSave.setOnClickListener {
|
||||
fun persistSettings(): Pair<Int, Int> {
|
||||
val selectedDisplayMode = when (toggleDisplayMode.checkedButtonId) {
|
||||
R.id.btn_display_4_3 -> 1
|
||||
R.id.btn_display_16_9 -> 2
|
||||
@@ -263,7 +308,15 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
edit.apply()
|
||||
|
||||
val toastResult = applyEepromEdits()
|
||||
return applyEepromEdits()
|
||||
}
|
||||
|
||||
btnClearCache.setOnClickListener {
|
||||
showClearCacheConfirmation()
|
||||
}
|
||||
|
||||
btnSave.setOnClickListener {
|
||||
val toastResult = persistSettings()
|
||||
Toast.makeText(this, toastResult.first, toastResult.second).show()
|
||||
finish()
|
||||
}
|
||||
@@ -272,6 +325,8 @@ class SettingsActivity : AppCompatActivity() {
|
||||
private fun setupEepromEditor() {
|
||||
val languageLabels = eepromLanguageOptions.map { getString(it.labelRes) }
|
||||
val videoLabels = eepromVideoOptions.map { getString(it.labelRes) }
|
||||
val aspectRatioLabels = eepromAspectRatioOptions.map { getString(it.labelRes) }
|
||||
val refreshRateLabels = eepromRefreshRateOptions.map { getString(it.labelRes) }
|
||||
|
||||
dropdownEepromLanguage.setAdapter(
|
||||
ArrayAdapter(this, android.R.layout.simple_list_item_1, languageLabels)
|
||||
@@ -279,6 +334,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||
dropdownEepromVideoStandard.setAdapter(
|
||||
ArrayAdapter(this, android.R.layout.simple_list_item_1, videoLabels)
|
||||
)
|
||||
dropdownEepromAspectRatio.setAdapter(
|
||||
ArrayAdapter(this, android.R.layout.simple_list_item_1, aspectRatioLabels)
|
||||
)
|
||||
dropdownEepromRefreshRate.setAdapter(
|
||||
ArrayAdapter(this, android.R.layout.simple_list_item_1, refreshRateLabels)
|
||||
)
|
||||
|
||||
dropdownEepromLanguage.setOnItemClickListener { _, _, position, _ ->
|
||||
selectedEepromLanguage = eepromLanguageOptions[position].value
|
||||
@@ -286,6 +347,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||
dropdownEepromVideoStandard.setOnItemClickListener { _, _, position, _ ->
|
||||
selectedEepromVideoStandard = eepromVideoOptions[position].value
|
||||
}
|
||||
dropdownEepromAspectRatio.setOnItemClickListener { _, _, position, _ ->
|
||||
selectedEepromAspectRatio = eepromAspectRatioOptions[position].value
|
||||
}
|
||||
dropdownEepromRefreshRate.setOnItemClickListener { _, _, position, _ ->
|
||||
selectedEepromRefreshRate = eepromRefreshRateOptions[position].value
|
||||
}
|
||||
|
||||
val eepromFile = resolveEepromFile()
|
||||
if (!eepromFile.isFile) {
|
||||
@@ -295,6 +362,15 @@ class SettingsActivity : AppCompatActivity() {
|
||||
setEepromEditorEnabled(false)
|
||||
setEepromLanguageSelection(selectedEepromLanguage)
|
||||
setEepromVideoSelection(selectedEepromVideoStandard)
|
||||
setEepromVideoSettingsSelection(
|
||||
XboxEepromEditor.VideoSettings(
|
||||
allow480p = false,
|
||||
allow720p = false,
|
||||
allow1080i = false,
|
||||
aspectRatio = selectedEepromAspectRatio,
|
||||
refreshRate = selectedEepromRefreshRate,
|
||||
)
|
||||
)
|
||||
tvEepromStatus.text = getString(
|
||||
R.string.settings_eeprom_status_missing,
|
||||
eepromFile.absolutePath,
|
||||
@@ -310,10 +386,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||
setEepromEditorEnabled(true)
|
||||
setEepromLanguageSelection(snapshot.language)
|
||||
setEepromVideoSelection(snapshot.videoStandard)
|
||||
setEepromVideoSettingsSelection(snapshot.videoSettings)
|
||||
|
||||
val hasUnknownValues =
|
||||
snapshot.rawLanguage != snapshot.language.id ||
|
||||
snapshot.rawVideoStandard != snapshot.videoStandard.id
|
||||
snapshot.rawVideoStandard != snapshot.videoStandard.id ||
|
||||
snapshot.hasManagedVideoSettingsMismatch
|
||||
tvEepromStatus.text = if (hasUnknownValues) {
|
||||
getString(R.string.settings_eeprom_status_unknown, eepromFile.absolutePath)
|
||||
} else {
|
||||
@@ -326,6 +404,15 @@ class SettingsActivity : AppCompatActivity() {
|
||||
setEepromEditorEnabled(false)
|
||||
setEepromLanguageSelection(selectedEepromLanguage)
|
||||
setEepromVideoSelection(selectedEepromVideoStandard)
|
||||
setEepromVideoSettingsSelection(
|
||||
XboxEepromEditor.VideoSettings(
|
||||
allow480p = false,
|
||||
allow720p = false,
|
||||
allow1080i = false,
|
||||
aspectRatio = selectedEepromAspectRatio,
|
||||
refreshRate = selectedEepromRefreshRate,
|
||||
)
|
||||
)
|
||||
tvEepromStatus.text = getString(
|
||||
R.string.settings_eeprom_status_invalid,
|
||||
eepromFile.absolutePath,
|
||||
@@ -337,6 +424,15 @@ class SettingsActivity : AppCompatActivity() {
|
||||
setEepromEditorEnabled(false)
|
||||
setEepromLanguageSelection(selectedEepromLanguage)
|
||||
setEepromVideoSelection(selectedEepromVideoStandard)
|
||||
setEepromVideoSettingsSelection(
|
||||
XboxEepromEditor.VideoSettings(
|
||||
allow480p = false,
|
||||
allow720p = false,
|
||||
allow1080i = false,
|
||||
aspectRatio = selectedEepromAspectRatio,
|
||||
refreshRate = selectedEepromRefreshRate,
|
||||
)
|
||||
)
|
||||
tvEepromStatus.text = getString(
|
||||
R.string.settings_eeprom_status_error,
|
||||
eepromFile.absolutePath,
|
||||
@@ -357,6 +453,13 @@ class SettingsActivity : AppCompatActivity() {
|
||||
resolveEepromFile(),
|
||||
selectedEepromLanguage,
|
||||
selectedEepromVideoStandard,
|
||||
XboxEepromEditor.VideoSettings(
|
||||
allow480p = switchEeprom480p.isChecked,
|
||||
allow720p = switchEeprom720p.isChecked,
|
||||
allow1080i = switchEeprom1080i.isChecked,
|
||||
aspectRatio = selectedEepromAspectRatio,
|
||||
refreshRate = selectedEepromRefreshRate,
|
||||
),
|
||||
)
|
||||
if (changed) {
|
||||
Pair(R.string.settings_saved_with_eeprom, Toast.LENGTH_SHORT)
|
||||
@@ -371,8 +474,15 @@ class SettingsActivity : AppCompatActivity() {
|
||||
private fun setEepromEditorEnabled(enabled: Boolean) {
|
||||
inputEepromLanguage.isEnabled = enabled
|
||||
inputEepromVideoStandard.isEnabled = enabled
|
||||
inputEepromAspectRatio.isEnabled = enabled
|
||||
inputEepromRefreshRate.isEnabled = enabled
|
||||
dropdownEepromLanguage.isEnabled = enabled
|
||||
dropdownEepromVideoStandard.isEnabled = enabled
|
||||
dropdownEepromAspectRatio.isEnabled = enabled
|
||||
dropdownEepromRefreshRate.isEnabled = enabled
|
||||
switchEeprom480p.isEnabled = enabled
|
||||
switchEeprom720p.isEnabled = enabled
|
||||
switchEeprom1080i.isEnabled = enabled
|
||||
}
|
||||
|
||||
private fun setEepromLanguageSelection(language: XboxEepromEditor.Language) {
|
||||
@@ -389,6 +499,134 @@ class SettingsActivity : AppCompatActivity() {
|
||||
dropdownEepromVideoStandard.setText(getString(option.labelRes), false)
|
||||
}
|
||||
|
||||
private fun setEepromVideoSettingsSelection(videoSettings: XboxEepromEditor.VideoSettings) {
|
||||
switchEeprom480p.isChecked = videoSettings.allow480p
|
||||
switchEeprom720p.isChecked = videoSettings.allow720p
|
||||
switchEeprom1080i.isChecked = videoSettings.allow1080i
|
||||
setEepromAspectRatioSelection(videoSettings.aspectRatio)
|
||||
setEepromRefreshRateSelection(videoSettings.refreshRate)
|
||||
}
|
||||
|
||||
private fun setEepromAspectRatioSelection(aspectRatio: XboxEepromEditor.AspectRatio) {
|
||||
selectedEepromAspectRatio = aspectRatio
|
||||
val option = eepromAspectRatioOptions.firstOrNull { it.value == aspectRatio }
|
||||
?: eepromAspectRatioOptions.first()
|
||||
dropdownEepromAspectRatio.setText(getString(option.labelRes), false)
|
||||
}
|
||||
|
||||
private fun setEepromRefreshRateSelection(refreshRate: XboxEepromEditor.RefreshRate) {
|
||||
selectedEepromRefreshRate = refreshRate
|
||||
val option = eepromRefreshRateOptions.firstOrNull { it.value == refreshRate }
|
||||
?: eepromRefreshRateOptions.first()
|
||||
dropdownEepromRefreshRate.setText(getString(option.labelRes), false)
|
||||
}
|
||||
|
||||
private fun showClearCacheConfirmation() {
|
||||
MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_clear_cache_title)
|
||||
.setMessage(R.string.settings_clear_cache_message)
|
||||
.setPositiveButton(R.string.settings_clear_cache_action) { _, _ ->
|
||||
val result = clearSystemCache()
|
||||
val messageRes = when {
|
||||
result.hadFailures -> R.string.settings_clear_cache_partial
|
||||
result.deletedEntries > 0 -> R.string.settings_clear_cache_success
|
||||
else -> R.string.settings_clear_cache_empty
|
||||
}
|
||||
Toast.makeText(this, getString(messageRes), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun clearSystemCache(): CacheClearResult {
|
||||
var result = CacheClearResult(0, false)
|
||||
|
||||
val cacheRoots = buildList {
|
||||
add(cacheDir)
|
||||
add(codeCacheDir)
|
||||
externalCacheDir?.let { add(it) }
|
||||
}
|
||||
for (root in cacheRoots.distinctBy { it.absolutePath }) {
|
||||
result = mergeCacheClearResults(result, clearDirectoryChildren(root))
|
||||
}
|
||||
|
||||
val persistentRoots = buildList {
|
||||
add(filesDir)
|
||||
getExternalFilesDir(null)?.let { add(it) }
|
||||
}
|
||||
for (root in persistentRoots.distinctBy { it.absolutePath }) {
|
||||
result = mergeCacheClearResults(result, clearPersistentCacheEntries(root))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun clearDirectoryChildren(dir: File?): CacheClearResult {
|
||||
if (dir == null || !dir.exists()) {
|
||||
return CacheClearResult(0, false)
|
||||
}
|
||||
|
||||
val children = dir.listFiles() ?: return CacheClearResult(0, false)
|
||||
var deletedEntries = 0
|
||||
var hadFailures = false
|
||||
for (child in children) {
|
||||
val deleted = runCatching { child.deleteRecursively() }.getOrDefault(false)
|
||||
if (deleted) {
|
||||
deletedEntries++
|
||||
} else {
|
||||
hadFailures = true
|
||||
}
|
||||
}
|
||||
return CacheClearResult(deletedEntries, hadFailures)
|
||||
}
|
||||
|
||||
private fun clearPersistentCacheEntries(root: File): CacheClearResult {
|
||||
if (!root.exists() || !root.isDirectory) {
|
||||
return CacheClearResult(0, false)
|
||||
}
|
||||
|
||||
val children = root.listFiles() ?: return CacheClearResult(0, false)
|
||||
var deletedEntries = 0
|
||||
var hadFailures = false
|
||||
|
||||
for (child in children) {
|
||||
if (isPersistentCacheEntry(child.name)) {
|
||||
val deleted = runCatching { child.deleteRecursively() }.getOrDefault(false)
|
||||
if (deleted) {
|
||||
deletedEntries++
|
||||
} else {
|
||||
hadFailures = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (child.isDirectory) {
|
||||
val nested = clearPersistentCacheEntries(child)
|
||||
deletedEntries += nested.deletedEntries
|
||||
hadFailures = hadFailures || nested.hadFailures
|
||||
}
|
||||
}
|
||||
|
||||
return CacheClearResult(deletedEntries, hadFailures)
|
||||
}
|
||||
|
||||
private fun isPersistentCacheEntry(name: String): Boolean {
|
||||
return name == "shaders" ||
|
||||
name == "shader_cache_list" ||
|
||||
name.startsWith("scache-") ||
|
||||
name.startsWith("vk_pipeline_cache_")
|
||||
}
|
||||
|
||||
private fun mergeCacheClearResults(
|
||||
first: CacheClearResult,
|
||||
second: CacheClearResult,
|
||||
): CacheClearResult {
|
||||
return CacheClearResult(
|
||||
deletedEntries = first.deletedEntries + second.deletedEntries,
|
||||
hadFailures = first.hadFailures || second.hadFailures,
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveEepromFile(): File {
|
||||
val base = getExternalFilesDir(null) ?: filesDir
|
||||
return File(File(base, "x1box"), "eeprom.bin")
|
||||
|
||||
@@ -16,6 +16,24 @@ internal object XboxEepromEditor {
|
||||
|
||||
private const val VIDEO_STANDARD_OFFSET = 0x58
|
||||
private const val LANGUAGE_OFFSET = 0x90
|
||||
private const val VIDEO_SETTINGS_OFFSET = 0x94
|
||||
|
||||
private const val VIDEO_SETTINGS_WIDESCREEN = 0x00010000u
|
||||
private const val VIDEO_SETTINGS_720P = 0x00020000u
|
||||
private const val VIDEO_SETTINGS_1080I = 0x00040000u
|
||||
private const val VIDEO_SETTINGS_480P = 0x00080000u
|
||||
private const val VIDEO_SETTINGS_LETTERBOX = 0x00100000u
|
||||
private const val VIDEO_SETTINGS_60HZ = 0x00400000u
|
||||
private const val VIDEO_SETTINGS_50HZ = 0x00800000u
|
||||
|
||||
private val MANAGED_VIDEO_SETTINGS_MASK =
|
||||
VIDEO_SETTINGS_WIDESCREEN or
|
||||
VIDEO_SETTINGS_720P or
|
||||
VIDEO_SETTINGS_1080I or
|
||||
VIDEO_SETTINGS_480P or
|
||||
VIDEO_SETTINGS_LETTERBOX or
|
||||
VIDEO_SETTINGS_60HZ or
|
||||
VIDEO_SETTINGS_50HZ
|
||||
|
||||
enum class Language(val id: UInt) {
|
||||
ENGLISH(0x00000001u),
|
||||
@@ -44,12 +62,71 @@ internal object XboxEepromEditor {
|
||||
}
|
||||
}
|
||||
|
||||
enum class AspectRatio(val bits: UInt) {
|
||||
NORMAL(0u),
|
||||
WIDESCREEN(VIDEO_SETTINGS_WIDESCREEN),
|
||||
LETTERBOX(VIDEO_SETTINGS_LETTERBOX);
|
||||
|
||||
companion object {
|
||||
fun fromBits(bits: UInt): AspectRatio? = when (bits and
|
||||
(VIDEO_SETTINGS_WIDESCREEN or VIDEO_SETTINGS_LETTERBOX)) {
|
||||
0u -> NORMAL
|
||||
VIDEO_SETTINGS_WIDESCREEN -> WIDESCREEN
|
||||
VIDEO_SETTINGS_LETTERBOX -> LETTERBOX
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class RefreshRate(val bits: UInt) {
|
||||
DEFAULT(0u),
|
||||
HZ_60(VIDEO_SETTINGS_60HZ),
|
||||
HZ_50(VIDEO_SETTINGS_50HZ);
|
||||
|
||||
companion object {
|
||||
fun fromBits(bits: UInt): RefreshRate? = when (bits and
|
||||
(VIDEO_SETTINGS_60HZ or VIDEO_SETTINGS_50HZ)) {
|
||||
0u -> DEFAULT
|
||||
VIDEO_SETTINGS_60HZ -> HZ_60
|
||||
VIDEO_SETTINGS_50HZ -> HZ_50
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class VideoSettings(
|
||||
val allow480p: Boolean,
|
||||
val allow720p: Boolean,
|
||||
val allow1080i: Boolean,
|
||||
val aspectRatio: AspectRatio,
|
||||
val refreshRate: RefreshRate,
|
||||
) {
|
||||
fun toManagedBits(): UInt {
|
||||
var bits = aspectRatio.bits or refreshRate.bits
|
||||
if (allow480p) {
|
||||
bits = bits or VIDEO_SETTINGS_480P
|
||||
}
|
||||
if (allow720p) {
|
||||
bits = bits or VIDEO_SETTINGS_720P
|
||||
}
|
||||
if (allow1080i) {
|
||||
bits = bits or VIDEO_SETTINGS_1080I
|
||||
}
|
||||
return bits
|
||||
}
|
||||
}
|
||||
|
||||
data class Snapshot(
|
||||
val language: Language,
|
||||
val videoStandard: VideoStandard,
|
||||
val videoSettings: VideoSettings,
|
||||
val rawLanguage: UInt,
|
||||
val rawVideoStandard: UInt,
|
||||
)
|
||||
val rawVideoSettings: UInt,
|
||||
) {
|
||||
val hasManagedVideoSettingsMismatch: Boolean
|
||||
get() = (rawVideoSettings and MANAGED_VIDEO_SETTINGS_MASK) != videoSettings.toManagedBits()
|
||||
}
|
||||
|
||||
@Throws(IOException::class, IllegalArgumentException::class)
|
||||
fun load(file: File): Snapshot {
|
||||
@@ -58,28 +135,49 @@ internal object XboxEepromEditor {
|
||||
|
||||
val rawLanguage = readLeUInt32(data, LANGUAGE_OFFSET)
|
||||
val rawVideoStandard = readLeUInt32(data, VIDEO_STANDARD_OFFSET)
|
||||
val rawVideoSettings = readLeUInt32(data, VIDEO_SETTINGS_OFFSET)
|
||||
|
||||
return Snapshot(
|
||||
language = Language.fromId(rawLanguage) ?: Language.ENGLISH,
|
||||
videoStandard = VideoStandard.fromId(rawVideoStandard) ?: VideoStandard.NTSC_M,
|
||||
videoSettings = VideoSettings(
|
||||
allow480p = (rawVideoSettings and VIDEO_SETTINGS_480P) != 0u,
|
||||
allow720p = (rawVideoSettings and VIDEO_SETTINGS_720P) != 0u,
|
||||
allow1080i = (rawVideoSettings and VIDEO_SETTINGS_1080I) != 0u,
|
||||
aspectRatio = AspectRatio.fromBits(rawVideoSettings) ?: AspectRatio.NORMAL,
|
||||
refreshRate = RefreshRate.fromBits(rawVideoSettings) ?: RefreshRate.DEFAULT,
|
||||
),
|
||||
rawLanguage = rawLanguage,
|
||||
rawVideoStandard = rawVideoStandard,
|
||||
rawVideoSettings = rawVideoSettings,
|
||||
)
|
||||
}
|
||||
|
||||
@Throws(IOException::class, IllegalArgumentException::class)
|
||||
fun apply(file: File, language: Language, videoStandard: VideoStandard): Boolean {
|
||||
fun apply(
|
||||
file: File,
|
||||
language: Language,
|
||||
videoStandard: VideoStandard,
|
||||
videoSettings: VideoSettings,
|
||||
): Boolean {
|
||||
val data = file.readBytes()
|
||||
ensureValidSize(file, data)
|
||||
|
||||
val currentLanguage = readLeUInt32(data, LANGUAGE_OFFSET)
|
||||
val currentVideoStandard = readLeUInt32(data, VIDEO_STANDARD_OFFSET)
|
||||
if (currentLanguage == language.id && currentVideoStandard == videoStandard.id) {
|
||||
val currentVideoSettings = readLeUInt32(data, VIDEO_SETTINGS_OFFSET)
|
||||
val nextVideoSettings =
|
||||
(currentVideoSettings and MANAGED_VIDEO_SETTINGS_MASK.inv()) or videoSettings.toManagedBits()
|
||||
if (currentLanguage == language.id &&
|
||||
currentVideoStandard == videoStandard.id &&
|
||||
currentVideoSettings == nextVideoSettings
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
writeLeUInt32(data, LANGUAGE_OFFSET, language.id)
|
||||
writeLeUInt32(data, VIDEO_STANDARD_OFFSET, videoStandard.id)
|
||||
writeLeUInt32(data, VIDEO_SETTINGS_OFFSET, nextVideoSettings)
|
||||
|
||||
val factoryChecksum = xboxChecksum(data, FACTORY_CHECKSUM_START, FACTORY_CHECKSUM_LENGTH)
|
||||
writeLeUInt32(data, FACTORY_CHECKSUM_OFFSET, factoryChecksum)
|
||||
|
||||
@@ -519,6 +519,7 @@
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:hint="@string/settings_eeprom_video_standard">
|
||||
|
||||
<AutoCompleteTextView
|
||||
@@ -529,6 +530,67 @@
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="@string/settings_eeprom_resolution_modes"
|
||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_eeprom_480p"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_eeprom_resolution_480p"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_eeprom_720p"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_eeprom_resolution_720p"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_eeprom_1080i"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/settings_eeprom_resolution_1080i"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_eeprom_aspect_ratio"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:hint="@string/settings_eeprom_aspect_ratio">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/dropdown_eeprom_aspect_ratio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_eeprom_refresh_rate"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/settings_eeprom_refresh_rate">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/dropdown_eeprom_refresh_rate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -537,6 +599,22 @@
|
||||
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/settings_section_maintenance"
|
||||
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||
android:textColor="@color/xemu_green" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_clear_system_cache"
|
||||
style="?attr/materialButtonOutlinedStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_clear_cache_action" />
|
||||
|
||||
<!-- Save button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_settings_save"
|
||||
|
||||
@@ -150,23 +150,46 @@
|
||||
<string name="settings_eeprom_video_standard_ntsc_j">NTSC-J</string>
|
||||
<string name="settings_eeprom_video_standard_pal_i">PAL-I</string>
|
||||
<string name="settings_eeprom_video_standard_pal_m">PAL-M</string>
|
||||
<string name="settings_eeprom_resolution_modes">EEPROM Resolution Modes</string>
|
||||
<string name="settings_eeprom_resolution_480p">Enable 480p</string>
|
||||
<string name="settings_eeprom_resolution_720p">Enable 720p</string>
|
||||
<string name="settings_eeprom_resolution_1080i">Enable 1080i</string>
|
||||
<string name="settings_eeprom_aspect_ratio">EEPROM Aspect Ratio</string>
|
||||
<string name="settings_eeprom_aspect_ratio_normal">Normal (4:3)</string>
|
||||
<string name="settings_eeprom_aspect_ratio_widescreen">Widescreen (16:9)</string>
|
||||
<string name="settings_eeprom_aspect_ratio_letterbox">Letterbox</string>
|
||||
<string name="settings_eeprom_refresh_rate">EEPROM Refresh Rate</string>
|
||||
<string name="settings_eeprom_refresh_rate_default">Default / Unset</string>
|
||||
<string name="settings_eeprom_refresh_rate_60">60 Hz</string>
|
||||
<string name="settings_eeprom_refresh_rate_50">50 Hz</string>
|
||||
<string name="settings_eeprom_status_ready">EEPROM file: %1$s</string>
|
||||
<string name="settings_eeprom_status_missing">EEPROM not found at %1$s. Launch a game once to create it.</string>
|
||||
<string name="settings_eeprom_status_invalid">EEPROM is invalid at %1$s.</string>
|
||||
<string name="settings_eeprom_status_error">Failed to read EEPROM at %1$s.</string>
|
||||
<string name="settings_eeprom_status_unknown">EEPROM file: %1$s (contains unknown values; saving will normalize them)</string>
|
||||
<string name="settings_eeprom_hint">Updates language and video standard in eeprom.bin. Restart emulation after saving.</string>
|
||||
<string name="settings_eeprom_hint">Updates language and Xbox video settings in eeprom.bin. HD modes depend on the game, cable, and video standard. Restart emulation after saving.</string>
|
||||
|
||||
<string name="settings_saved">Settings saved</string>
|
||||
<string name="settings_saved_with_eeprom">Settings and EEPROM saved</string>
|
||||
<string name="settings_saved_eeprom_missing">Settings saved. Launch a game once, then edit EEPROM.</string>
|
||||
<string name="settings_saved_eeprom_failed">Settings saved, but EEPROM update failed</string>
|
||||
<string name="settings_section_maintenance">Maintenance</string>
|
||||
<string name="settings_clear_cache_title">Clear Cache</string>
|
||||
<string name="settings_clear_cache_message">Clear emulator cache files now? This removes shader and app cache data, but keeps your games, saves, and HDD image.</string>
|
||||
<string name="settings_clear_cache_action">Clear Cache</string>
|
||||
<string name="settings_clear_cache_success">Cache cleared</string>
|
||||
<string name="settings_clear_cache_partial">Some cache files could not be removed</string>
|
||||
<string name="settings_clear_cache_empty">Cache is already clear</string>
|
||||
|
||||
<string name="in_game_menu_title">Game Menu</string>
|
||||
<string name="in_game_menu_resume">Resume</string>
|
||||
<string name="in_game_menu_restart_game">Restart Game</string>
|
||||
<string name="in_game_menu_show_touch_controls">Show Touch Controls</string>
|
||||
<string name="in_game_menu_hide_touch_controls">Hide Touch Controls</string>
|
||||
<string name="in_game_menu_reboot_system">Reboot System</string>
|
||||
<string name="in_game_menu_reboot_title">Reboot System</string>
|
||||
<string name="in_game_menu_reboot_message">Are you sure you want to reboot the emulated Xbox system?</string>
|
||||
<string name="in_game_menu_reboot_action">Reboot</string>
|
||||
<string name="in_game_menu_exit_to_library">Exit to Game Library</string>
|
||||
<string name="in_game_menu_quit_app">Quit App</string>
|
||||
<string name="in_game_menu_save_state">Save State</string>
|
||||
|
||||
@@ -563,7 +563,8 @@ static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument)
|
||||
DPRINTF("idle voice %d\n", argument);
|
||||
d->set_irq = true;
|
||||
} else {
|
||||
assert(false);
|
||||
DPRINTF("ignoring idle voice %d without FETFORCE1 request\n",
|
||||
argument);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user