diff --git a/workspace/all/minarch/ma_config.c b/workspace/all/minarch/ma_config.c index 438a0a65b..2d478a996 100644 --- a/workspace/all/minarch/ma_config.c +++ b/workspace/all/minarch/ma_config.c @@ -1707,6 +1707,7 @@ struct Config config = { .shortcuts = (ButtonMapping[]){ [SHORTCUT_SAVE_STATE] = {"Save State", -1, BTN_ID_NONE, 0}, [SHORTCUT_LOAD_STATE] = {"Load State", -1, BTN_ID_NONE, 0}, + [SHORTCUT_UNDO_LOAD_STATE] = {"Undo Load State", -1, BTN_ID_NONE, 0}, [SHORTCUT_RESET_GAME] = {"Reset Game", -1, BTN_ID_NONE, 0}, [SHORTCUT_SAVE_QUIT] = {"Save & Quit", -1, BTN_ID_NONE, 0}, [SHORTCUT_CYCLE_SCALE] = {"Cycle Scaling", -1, BTN_ID_NONE, 0}, diff --git a/workspace/all/minarch/ma_game.c b/workspace/all/minarch/ma_game.c index d971c388f..858a0a9e5 100644 --- a/workspace/all/minarch/ma_game.c +++ b/workspace/all/minarch/ma_game.c @@ -8,6 +8,7 @@ #include "ma_internal.h" #include "ma_game.h" +#include "ma_saves.h" struct Game game; struct retro_disk_control_ext_callback disk_control_ext; @@ -158,6 +159,9 @@ void Game_changeDisc(char* path) { disk_control_ext.replace_image_index(0, &game_info); putFile(CHANGE_DISC_PATH, path); // NextUI still needs to know this to update recents.txt + + // any undo snapshot belongs to the disc we just ejected + State_invalidateUndo(); } int extract_zip(char** extensions) diff --git a/workspace/all/minarch/ma_input.c b/workspace/all/minarch/ma_input.c index 3233b214d..4e9b5542b 100644 --- a/workspace/all/minarch/ma_input.c +++ b/workspace/all/minarch/ma_input.c @@ -144,6 +144,7 @@ void input_poll_callback(void) { Menu_saveState(); break; case SHORTCUT_LOAD_STATE: Menu_loadState(); break; + case SHORTCUT_UNDO_LOAD_STATE: Menu_undoLoadState(); break; case SHORTCUT_SCREENSHOT: Menu_screenshot(); break; diff --git a/workspace/all/minarch/ma_internal.h b/workspace/all/minarch/ma_internal.h index 49d4a8935..a734db5cf 100644 --- a/workspace/all/minarch/ma_internal.h +++ b/workspace/all/minarch/ma_internal.h @@ -235,6 +235,7 @@ enum { enum { SHORTCUT_SAVE_STATE, SHORTCUT_LOAD_STATE, + SHORTCUT_UNDO_LOAD_STATE, SHORTCUT_RESET_GAME, SHORTCUT_SAVE_QUIT, SHORTCUT_CYCLE_SCALE, diff --git a/workspace/all/minarch/ma_menu.c b/workspace/all/minarch/ma_menu.c index 5a5c130d0..978112fd1 100644 --- a/workspace/all/minarch/ma_menu.c +++ b/workspace/all/minarch/ma_menu.c @@ -1688,6 +1688,7 @@ void Menu_loadState(void) { Menu_updateState(); if (menu.save_exists) { + int disc_changed = 0; if (menu.total_discs) { char slot_disc_name[256]; getFile(menu.txt_path, slot_disc_name, 256); @@ -1699,12 +1700,22 @@ void Menu_loadState(void) { char* disc_path = menu.disc_paths[menu.disc]; if (!exactMatch(slot_disc_path, disc_path)) { Game_changeDisc(slot_disc_path); + disc_changed = 1; } } state_slot = menu.slot; putInt(menu.slot_path, menu.slot); - int success = State_read(); + int success; + if (disc_changed) { + // the state we'd be undoing back to belongs to the disc that was just + // ejected, so there's nothing safe to offer an undo for + State_invalidateUndo(); + success = State_read(); + } + else { + success = State_readWithUndo(); + } Rewind_on_state_change(); // Show notification if enabled @@ -1716,6 +1727,15 @@ void Menu_loadState(void) { } } } +void Menu_undoLoadState(void) { + int success = State_undoLoad(); + + // Show notification if enabled (hardcore mode pushes its own message) + if (CFG_getNotifyLoad() && !RA_isHardcoreModeActive()) { + Notification_push(NOTIFICATION_LOAD_STATE, + success ? "Load State Undone" : "Nothing To Undo", NULL); + } +} void Menu_loop(void) { @@ -1838,6 +1858,13 @@ void Menu_loop(void) { status = STATUS_CONT; show_menu = 0; } + else if (PAD_justPressed(BTN_X)) { + if (selected==ITEM_LOAD && State_hasUndo()) { + Menu_undoLoadState(); + status = STATUS_LOAD; + show_menu = 0; + } + } else if (PAD_justPressed(BTN_A)) { switch(selected) { case ITEM_CONT: @@ -1933,14 +1960,19 @@ void Menu_loop(void) { if (show_setting && !GetHDMI()) GFX_blitHardwareHints(screen, show_setting); else GFX_blitButtonGroup((char*[]){ BTN_SLEEP==BTN_POWER?"POWER":"MENU","SLEEP", NULL }, 0, screen, 0); - GFX_blitButtonGroup((char*[]){ "B","BACK", "A","OKAY", NULL }, 1, screen, 1); + if (selected==ITEM_LOAD && State_hasUndo()) { + GFX_blitButtonGroup((char*[]){ "X","UNDO LOAD", "B","BACK", "A","LOAD", NULL }, 1, screen, 1); + } + else { + GFX_blitButtonGroup((char*[]){ "B","BACK", "A","OKAY", NULL }, 1, screen, 1); + } // list oy = (((DEVICE_HEIGHT / FIXED_SCALE) - PADDING * 2) - (MENU_ITEM_COUNT * PILL_SIZE)) / 2; for (int i=0; i