From 78a71959f6104b32d50a887bbc9713f25f673b0b Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Sun, 18 May 2025 11:28:26 -0700 Subject: [PATCH] Add in-game menu --- src/linux/port.c | 85 +++++++++++++++++++++++++++++++++++++++++++++--- src/menu.c | 20 ++++++++++-- 2 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/linux/port.c b/src/linux/port.c index 5fc7544..17981e0 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -280,7 +280,7 @@ int64_t time_sleep_until(int64_t t_target) { return t_diff * 1000; } -static nese_Action select_rom(platform_data* plat, char* file, int sz_file); +static nese_Action game_menu(platform_data* plat); int nese_frame_ready(void* plat_data) { platform_data* plat = (platform_data*)plat_data; @@ -320,6 +320,7 @@ int nese_frame_ready(void* plat_data) { nese_Action action = process_events(plat->sys); switch (action) { case Action_Quit: + // TODO: Save SRAM status = -1; break; @@ -345,18 +346,23 @@ int nese_frame_ready(void* plat_data) { break; case Action_Menu: - action = select_rom(plat, plat->filename, PLAT_FILENAME_SIZE); - if (Action_OK == action) { + action = game_menu(plat); + if (Action_Reset == action) { nes_reset(plat->sys); + } else if (Action_Save == action) { + status = save_state(plat->sys, plat->cart.filename); + } else if (Action_Load == action) { + status = load_state(plat->sys, plat->cart.filename); } else if (Action_Quit == action || NULL == plat->sys->cart_header) { status = -1; } + // TODO: Other Actions? break; default: } - // TODO: Perform more actions + // TODO: Perform more actions? if (0 == status) { plat->t_target += FRAME_TIME_NS; @@ -501,6 +507,8 @@ static nese_Action select_rom(platform_data* plat, char* file, int sz_file) { nese_Action action = Action_OK; Menu_State menu = {0}; + // TODO: Save SRAM + while (Action_OK == action || NULL == plat->sys->cart_header) { action = run_rom_menu(plat, &menu, file, sz_file); if (Action_OK == action) { @@ -522,6 +530,75 @@ static nese_Action select_rom(platform_data* plat, char* file, int sz_file) { return action; } +static nese_Action run_game_menu(platform_data* plat, Menu_State* state) { + static char* items[] = { + "Resume", + "Save", + "Restore", + "Reset", + "Select ROM", + "Exit", + }; + static const nese_Action choices[] = { + Action_Cancel, + Action_Save, + Action_Load, + Action_Reset, + Action_Menu, + Action_Quit, + }; + static const File_List options = { + .files = items, + .count = (sizeof(items) / sizeof(*items)), + }; + + Menu_State menu = {0}; + if (NULL != state) menu = *state; + + nese_Action action = run_menu(plat, &menu, &options, 100); + if (Action_Menu == action) { + action = Action_Cancel; + } else if (Action_OK == action) { + if ( menu.cursor >= 0 && + menu.cursor < (sizeof(choices) / sizeof(*choices))) { + action = choices[menu.cursor]; + } + } + + if (NULL != state) *state = menu; + + return action; +} + +static nese_Action game_menu(platform_data* plat) { + nese_Action action = Action_OK; + Menu_State menu = {0}; + + while (Action_OK == action) { + action = run_game_menu(plat, &menu); + + if (Action_Menu == action) { + // Select ROM + action = select_rom(plat, plat->filename, PLAT_FILENAME_SIZE); + if (Action_OK == action) { + // New ROM selected - Exit loop and reset + action = Action_Reset; + + } else if (Action_Cancel == action) { + // No ROM selected - Keep the menu running + action = Action_OK; + } + + if (NULL == plat->sys->cart_header) { + // A failed ROM load means we shouldn't return to the game menu + action = Action_Quit; + } + } + } + + return action; +} + /* Platform Data */ diff --git a/src/menu.c b/src/menu.c index a187cbe..086a5a5 100644 --- a/src/menu.c +++ b/src/menu.c @@ -11,8 +11,8 @@ static nese_Action wait_for_input(void* plat, uint8_t* new_buttons) { uint8_t changed = 0; while (0 == changed && Action_OK == action) { - // TODO: Sleep uint8_t buttons = input.gamepads[0].buttons; + // Sleep is implicit in nese_update_input action = nese_update_input(plat, &input); changed = (~buttons & input.gamepads[0].buttons); } @@ -22,6 +22,19 @@ static nese_Action wait_for_input(void* plat, uint8_t* new_buttons) { return action; } +static nese_Action wait_for_input_quiet(void* plat) { + nes_Input input = {0}; + nese_Action action = nese_update_input(plat, &input); + uint8_t buttons = input.gamepads[0].buttons; + + while (Action_Quit != action && buttons) { + action = nese_update_input(plat, &input); + buttons = input.gamepads[0].buttons; + } + + return action; +} + nese_Action modal_popup(void* plat, const char* message, uint32_t color) { int w = 0; @@ -98,7 +111,8 @@ static void show_menu(void* plat, const Menu_State* menu, nese_draw_finish(plat); } -int run_menu(void* plat, Menu_State* state, const File_List* files, int x) { +nese_Action run_menu(void* plat, Menu_State* state, + const File_List* files, int x) { Menu_State menu = {0}; if (NULL != state) { menu = *state; @@ -160,5 +174,7 @@ int run_menu(void* plat, Menu_State* state, const File_List* files, int x) { if (NULL != state) *state = menu; + wait_for_input_quiet(plat); + return action; }