diff --git a/core/input/gamepad.h b/core/input/gamepad.h index e40bba430..a488fbfb7 100644 --- a/core/input/gamepad.h +++ b/core/input/gamepad.h @@ -47,6 +47,8 @@ enum DreamcastKey EMU_BTN_FFORWARD, EMU_BTN_ESCAPE, EMU_BTN_INSERT_CARD, + EMU_BTN_LOADSTATE, + EMU_BTN_SAVESTATE, // Real axes DC_AXIS_TRIGGERS = 0x1000000, diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 65fc747f6..f4ed30ab8 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -91,6 +91,14 @@ bool GamepadDevice::handleButtonInput(int port, DreamcastKey key, bool pressed) if (pressed && settings.platform.isNaomi()) card_reader::insertCard(); break; + case EMU_BTN_LOADSTATE: + if (pressed) + gui_loadState(); + break; + case EMU_BTN_SAVESTATE: + if (pressed) + gui_saveState(); + break; case DC_AXIS_LT: if (port >= 0) lt[port] = pressed ? 255 : 0; diff --git a/core/input/mapping.cpp b/core/input/mapping.cpp index 44216566a..1175879b0 100644 --- a/core/input/mapping.cpp +++ b/core/input/mapping.cpp @@ -56,6 +56,8 @@ button_list[] = { DC_AXIS_RIGHT, "compat", "btn_analog_right" }, { DC_BTN_RELOAD, "dreamcast", "reload" }, { EMU_BTN_INSERT_CARD, "emulator", "insert_card" }, + { EMU_BTN_LOADSTATE, "emulator", "btn_jump_state" }, + { EMU_BTN_SAVESTATE, "emulator", "btn_quick_save" }, }; static struct diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index bab2f549f..9f4bc58fd 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -517,6 +517,11 @@ void gui_stop_game(const std::string& message) } } +static bool savestateAllowed() +{ + return !settings.content.path.empty() && !settings.network.online && !settings.naomi.multiboard; +} + static void gui_display_commands() { imguiDriver->displayVmus(); @@ -527,10 +532,10 @@ static void gui_display_commands() ImGui::Begin("##commands", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize); { - DisabledScope scope(settings.content.path.empty() || settings.network.online || settings.naomi.multiboard); + DisabledScope scope(!savestateAllowed()); // Load State - if (ImGui::Button("Load State", ScaledVec2(110, 50)) && !scope.isDisabled()) + if (ImGui::Button("Load State", ScaledVec2(110, 50)) && savestateAllowed()) { gui_state = GuiState::Closed; dc_loadstate(config::SavestateSlot); @@ -554,7 +559,7 @@ static void gui_display_commands() ImGui::SameLine(); // Save State - if (ImGui::Button("Save State", ScaledVec2(110, 50)) && !scope.isDisabled()) + if (ImGui::Button("Save State", ScaledVec2(110, 50)) && savestateAllowed()) { gui_state = GuiState::Closed; dc_savestate(config::SavestateSlot); @@ -727,6 +732,8 @@ const Mapping dcButtons[] = { { EMU_BTN_MENU, "Menu" }, { EMU_BTN_ESCAPE, "Exit" }, { EMU_BTN_FFORWARD, "Fast-forward" }, + { EMU_BTN_LOADSTATE, "Load State" }, + { EMU_BTN_SAVESTATE, "Save State" }, { EMU_BTN_NONE, nullptr } }; @@ -774,6 +781,8 @@ const Mapping arcadeButtons[] = { { EMU_BTN_MENU, "Menu" }, { EMU_BTN_ESCAPE, "Exit" }, { EMU_BTN_FFORWARD, "Fast-forward" }, + { EMU_BTN_LOADSTATE, "Load State" }, + { EMU_BTN_SAVESTATE, "Save State" }, { EMU_BTN_INSERT_CARD, "Insert Card" }, { EMU_BTN_NONE, nullptr } @@ -1034,6 +1043,10 @@ static void controller_mapping_popup(const std::shared_ptr& gamep const char* items[] = { "Dreamcast Controls", "Arcade Controls" }; + if (last_item_current_map_idx == 2 && game_started) + // Select the right mappings for the current game + item_current_map_idx = settings.platform.isArcade() ? 1 : 0; + // Here our selection data is an index. ImGui::SetNextItemWidth(comboWidth); @@ -2998,6 +3011,37 @@ void gui_save() boxart.saveDatabase(); } +void gui_loadState() +{ + const LockGuard lock(guiMutex); + if (gui_state == GuiState::Closed && savestateAllowed()) + { + try { + emu.stop(); + dc_loadstate(config::SavestateSlot); + emu.start(); + } catch (const FlycastException& e) { + gui_stop_game(e.what()); + } + } +} + +void gui_saveState() +{ + const LockGuard lock(guiMutex); + if (gui_state == GuiState::Closed && savestateAllowed()) + { + try { + emu.stop(); + dc_savestate(config::SavestateSlot); + emu.start(); + } catch (const FlycastException& e) { + gui_stop_game(e.what()); + } + } +} + + #ifdef TARGET_UWP // Ugly but a good workaround for MS stupidity // UWP doesn't allow the UI thread to wait on a thread/task. When an std::future is ready, it is possible diff --git a/core/rend/gui.h b/core/rend/gui.h index ca80d3ba2..2da21f567 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -48,6 +48,8 @@ void gui_start_game(const std::string& path); void gui_error(const std::string& what); void gui_setOnScreenKeyboardCallback(void (*callback)(bool show)); void gui_save(); +void gui_loadState(); +void gui_saveState(); enum class GuiState { Closed,