mirror of
https://github.com/PabloMK7/citra
synced 2024-11-14 20:58:23 +00:00
android: add quicksave hotkeys (#181)
This commit is contained in:
parent
ed3d5a9f7f
commit
45f52709a6
10 changed files with 96 additions and 18 deletions
|
@ -527,12 +527,28 @@ object NativeLibrary {
|
|||
|
||||
external fun removeAmiibo()
|
||||
|
||||
const val SAVESTATE_SLOT_COUNT = 10
|
||||
const val SAVESTATE_SLOT_COUNT = 11
|
||||
const val QUICKSAVE_SLOT = 0
|
||||
|
||||
external fun getSavestateInfo(): Array<SaveStateInfo>?
|
||||
|
||||
external fun saveState(slot: Int)
|
||||
|
||||
fun loadStateIfAvailable(slot: Int): Boolean {
|
||||
var available = false
|
||||
getSavestateInfo()?.forEach {
|
||||
if (it.slot == slot){
|
||||
available = true
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
if (available) {
|
||||
loadState(slot)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
external fun loadState(slot: Int)
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,7 +66,7 @@ class EmulationActivity : AppCompatActivity() {
|
|||
|
||||
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
||||
screenAdjustmentUtil = ScreenAdjustmentUtil(windowManager, settingsViewModel.settings)
|
||||
hotkeyUtility = HotkeyUtility(screenAdjustmentUtil)
|
||||
hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this)
|
||||
setContentView(binding.root)
|
||||
|
||||
val navHostFragment =
|
||||
|
|
|
@ -8,5 +8,7 @@ enum class Hotkey(val button: Int) {
|
|||
SWAP_SCREEN(10001),
|
||||
CYCLE_LAYOUT(10002),
|
||||
CLOSE_GAME(10003),
|
||||
PAUSE_OR_RESUME(10004);
|
||||
PAUSE_OR_RESUME(10004),
|
||||
QUICKSAVE(10005),
|
||||
QUICKLOAD(10006);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,14 @@
|
|||
|
||||
package org.citra.citra_emu.features.hotkeys
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||
|
||||
class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil) {
|
||||
class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil, private val context: Context) {
|
||||
|
||||
val hotkeyButtons = Hotkey.entries.map { it.button }
|
||||
|
||||
|
@ -18,6 +22,23 @@ class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil) {
|
|||
Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts()
|
||||
Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame()
|
||||
Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume()
|
||||
Hotkey.QUICKSAVE.button -> {
|
||||
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
|
||||
Toast.makeText(context,
|
||||
context.getString(R.string.quicksave_saving),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
Hotkey.QUICKLOAD.button -> {
|
||||
val wasLoaded = NativeLibrary.loadStateIfAvailable(NativeLibrary.QUICKSAVE_SLOT)
|
||||
val stringRes = if(wasLoaded) {
|
||||
R.string.quickload_loading
|
||||
} else {
|
||||
R.string.quickload_not_found
|
||||
}
|
||||
Toast.makeText(context,
|
||||
context.getString(stringRes),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -136,6 +136,8 @@ class Settings {
|
|||
const val HOTKEY_CYCLE_LAYOUT = "hotkey_toggle_layout"
|
||||
const val HOTKEY_CLOSE_GAME = "hotkey_close_game"
|
||||
const val HOTKEY_PAUSE_OR_RESUME = "hotkey_pause_or_resume_game"
|
||||
const val HOTKEY_QUICKSAVE = "hotkey_quickload"
|
||||
const val HOTKEY_QUICKlOAD = "hotkey_quickpause"
|
||||
|
||||
val buttonKeys = listOf(
|
||||
KEY_BUTTON_A,
|
||||
|
@ -187,13 +189,17 @@ class Settings {
|
|||
HOTKEY_SCREEN_SWAP,
|
||||
HOTKEY_CYCLE_LAYOUT,
|
||||
HOTKEY_CLOSE_GAME,
|
||||
HOTKEY_PAUSE_OR_RESUME
|
||||
HOTKEY_PAUSE_OR_RESUME,
|
||||
HOTKEY_QUICKSAVE,
|
||||
HOTKEY_QUICKlOAD
|
||||
)
|
||||
val hotkeyTitles = listOf(
|
||||
R.string.emulation_swap_screens,
|
||||
R.string.emulation_cycle_landscape_layouts,
|
||||
R.string.emulation_close_game,
|
||||
R.string.emulation_toggle_pause
|
||||
R.string.emulation_toggle_pause,
|
||||
R.string.emulation_quicksave,
|
||||
R.string.emulation_quickload,
|
||||
)
|
||||
|
||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||
|
|
|
@ -133,6 +133,8 @@ class InputBindingSetting(
|
|||
Settings.HOTKEY_CYCLE_LAYOUT -> Hotkey.CYCLE_LAYOUT.button
|
||||
Settings.HOTKEY_CLOSE_GAME -> Hotkey.CLOSE_GAME.button
|
||||
Settings.HOTKEY_PAUSE_OR_RESUME -> Hotkey.PAUSE_OR_RESUME.button
|
||||
Settings.HOTKEY_QUICKSAVE -> Hotkey.QUICKSAVE.button
|
||||
Settings.HOTKEY_QUICKlOAD -> Hotkey.QUICKLOAD.button
|
||||
else -> -1
|
||||
}
|
||||
|
||||
|
|
|
@ -481,12 +481,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
popupMenu.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_emulation_save_state -> {
|
||||
showSaveStateSubmenu()
|
||||
showStateSubmenu(true)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_emulation_load_state -> {
|
||||
showLoadStateSubmenu()
|
||||
showStateSubmenu(false)
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -497,7 +497,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
popupMenu.show()
|
||||
}
|
||||
|
||||
private fun showSaveStateSubmenu() {
|
||||
private fun showStateSubmenu(isSaving: Boolean) {
|
||||
|
||||
val savestates = NativeLibrary.getSavestateInfo()
|
||||
|
||||
val popupMenu = PopupMenu(
|
||||
|
@ -507,19 +508,40 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
|
||||
popupMenu.menu.apply {
|
||||
for (i in 0 until NativeLibrary.SAVESTATE_SLOT_COUNT) {
|
||||
val slot = i + 1
|
||||
val text = getString(R.string.emulation_empty_state_slot, slot)
|
||||
add(text).setEnabled(true).setOnMenuItemClickListener {
|
||||
displaySavestateWarning()
|
||||
NativeLibrary.saveState(slot)
|
||||
val slot = i
|
||||
var enableClick = isSaving
|
||||
val text = if (slot == NativeLibrary.QUICKSAVE_SLOT) {
|
||||
enableClick = false
|
||||
getString(R.string.emulation_quicksave_slot)
|
||||
} else {
|
||||
getString(R.string.emulation_empty_state_slot, slot)
|
||||
}
|
||||
|
||||
add(text).setEnabled(enableClick).setOnMenuItemClickListener {
|
||||
if(isSaving) {
|
||||
NativeLibrary.saveState(slot)
|
||||
} else {
|
||||
NativeLibrary.loadState(slot)
|
||||
binding.drawerLayout.close()
|
||||
Toast.makeText(context,
|
||||
getString(R.string.quickload_loading),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
savestates?.forEach {
|
||||
val text = getString(R.string.emulation_occupied_state_slot, it.slot, it.time)
|
||||
popupMenu.menu.getItem(it.slot - 1).setTitle(text)
|
||||
var enableClick = true
|
||||
val text = if(it.slot == NativeLibrary.QUICKSAVE_SLOT) {
|
||||
// do not allow saving in quicksave slot
|
||||
enableClick = !isSaving
|
||||
getString(R.string.emulation_occupied_quicksave_slot, it.time)
|
||||
} else{
|
||||
getString(R.string.emulation_occupied_state_slot, it.slot, it.time)
|
||||
}
|
||||
popupMenu.menu.getItem(it.slot).setTitle(text).setEnabled(enableClick)
|
||||
}
|
||||
|
||||
popupMenu.show()
|
||||
|
|
|
@ -692,4 +692,13 @@
|
|||
<string name="delay_render_thread">Delay game render thread</string>
|
||||
<string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) dynamic-fps games.</string>
|
||||
|
||||
<!-- Quickload&Save-->
|
||||
<string name="emulation_quicksave_slot">Quicksave</string>
|
||||
<string name="emulation_quicksave">Quicksave</string>
|
||||
<string name="emulation_quickload">Quickload</string>
|
||||
<string name="emulation_occupied_quicksave_slot">Quicksave - %1$tF %1$tR</string>
|
||||
<string name="quicksave_saving">Saving…</string>
|
||||
<string name="quickload_loading">Loading…</string>
|
||||
<string name="quickload_not_found">No Quicksave available.</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -90,7 +90,7 @@ static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64
|
|||
std::vector<SaveStateInfo> ListSaveStates(u64 program_id, u64 movie_id) {
|
||||
std::vector<SaveStateInfo> result;
|
||||
result.reserve(SaveStateSlotCount);
|
||||
for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) {
|
||||
for (u32 slot = 0; slot <= SaveStateSlotCount; ++slot) {
|
||||
const auto path = GetSaveStatePath(program_id, movie_id, slot);
|
||||
if (!FileUtil::Exists(path)) {
|
||||
continue;
|
||||
|
|
|
@ -20,7 +20,7 @@ struct SaveStateInfo {
|
|||
std::string build_name;
|
||||
};
|
||||
|
||||
constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots
|
||||
constexpr u32 SaveStateSlotCount = 11; // Maximum count of savestate slots
|
||||
|
||||
std::vector<SaveStateInfo> ListSaveStates(u64 program_id, u64 movie_id);
|
||||
|
||||
|
|
Loading…
Reference in a new issue