| @@ -108,30 +108,3 @@ int nes_cart_init_file(nes_cart* cart, FILE* file) { | |||||
| return status; | return status; | ||||
| } | } | ||||
| FILE* nes_load_cart(nes_cart* cart, const char* cart_filename) { | |||||
| int status = 0; | |||||
| FILE* cart_file = fopen(cart_filename, "rb"); | |||||
| if (NULL == cart_file) { | |||||
| status = -1; | |||||
| fprintf(stderr, "Could not open %s\n", cart_filename); | |||||
| } | |||||
| if (status == 0) { | |||||
| status = nes_cart_init_file(cart, cart_file); | |||||
| } | |||||
| if (status == 0) { | |||||
| // Failure might mean there's nothing to load | |||||
| load_sram(cart, cart_filename); | |||||
| } | |||||
| if (status != 0 && NULL != cart_file) { | |||||
| fclose(cart_file); | |||||
| cart_file = NULL; | |||||
| } | |||||
| return cart_file; | |||||
| } | |||||
| @@ -28,7 +28,5 @@ int nes_cart_init_file(nes_cart*, FILE* file); | |||||
| int nes_cart_init_mem(nes_cart*, void*, int len); | int nes_cart_init_mem(nes_cart*, void*, int len); | ||||
| void nes_cart_done(nes_cart*); | void nes_cart_done(nes_cart*); | ||||
| FILE* nes_load_cart(nes_cart* cart, const char* cart_filename); | |||||
| #endif // NES_CART_H_ | #endif // NES_CART_H_ | ||||
| @@ -9,12 +9,13 @@ | |||||
| typedef enum { | typedef enum { | ||||
| input_Result_Error = -1, | input_Result_Error = -1, | ||||
| input_Result_OK = 0, | |||||
| input_Result_Quit = 1, | |||||
| input_Result_Reset = 2, | |||||
| input_Result_Save = 3, | |||||
| input_Result_Load = 4, | |||||
| input_Result_Cancel = 5, | |||||
| input_Result_OK, | |||||
| input_Result_Quit, | |||||
| input_Result_Menu, | |||||
| input_Result_Reset, | |||||
| input_Result_Save, | |||||
| input_Result_Load, | |||||
| input_Result_Cancel, | |||||
| } nes_Input_Result; | } nes_Input_Result; | ||||
| #define nes_controller_num_buttons (8U) | #define nes_controller_num_buttons (8U) | ||||
| @@ -66,7 +66,6 @@ static void nes_irq(void* sys, int active) { | |||||
| int nes_init(nes* sys, int audio_freq) { | int nes_init(nes* sys, int audio_freq) { | ||||
| e6502_init(&sys->cpu, (e6502_Read*)nes_mem_read, | e6502_init(&sys->cpu, (e6502_Read*)nes_mem_read, | ||||
| (e6502_Write*)nes_mem_write, sys); | (e6502_Write*)nes_mem_write, sys); | ||||
| nes_map_set_irq(sys->cart.mapper, nes_irq, sys); | |||||
| nes_ppu_init(&sys->ppu, &sys->cart); | nes_ppu_init(&sys->ppu, &sys->cart); | ||||
| return nes_apu_init( | return nes_apu_init( | ||||
| &sys->apu, | &sys->apu, | ||||
| @@ -77,6 +76,18 @@ int nes_init(nes* sys, int audio_freq) { | |||||
| ); | ); | ||||
| } | } | ||||
| void nes_done(nes* sys) { | |||||
| nes_cart_done(&sys->cart); | |||||
| nes_apu_done(&sys->apu); | |||||
| } | |||||
| int nes_setup_cart(nes* sys) { | |||||
| nes_map_set_irq(sys->cart.mapper, nes_irq, sys); | |||||
| sys->ppu.mapper = sys->cart.mapper; | |||||
| nes_reset(sys); | |||||
| return 0; | |||||
| } | |||||
| void nes_reset(nes* sys) { | void nes_reset(nes* sys) { | ||||
| e6502_reset(&sys->cpu); | e6502_reset(&sys->cpu); | ||||
| nes_ppu_reset(&sys->ppu); | nes_ppu_reset(&sys->ppu); | ||||
| @@ -58,6 +58,10 @@ void nes_mem_write(nes*, uint16_t addr, uint8_t); | |||||
| int nes_init(nes*, int audio_freq); | int nes_init(nes*, int audio_freq); | ||||
| void nes_done(nes*); | |||||
| int nes_setup_cart(nes*); | |||||
| void nes_reset(nes*); | void nes_reset(nes*); | ||||
| nes_ppu_Result nes_step(nes*, int* run); | nes_ppu_Result nes_step(nes*, int* run); | ||||
| @@ -22,7 +22,32 @@ extern nes_Input_Reader sdl_input; | |||||
| extern nes_Audio_Stream sdl_audio; | extern nes_Audio_Stream sdl_audio; | ||||
| static nes sys = {0}; | |||||
| static FILE* nes_load_cart(nes_cart* cart, | |||||
| const char* cart_filename) { | |||||
| int status = 0; | |||||
| FILE* cart_file = fopen(cart_filename, "rb"); | |||||
| if (NULL == cart_file) { | |||||
| status = -1; | |||||
| fprintf(stderr, "Could not open %s\n", cart_filename); | |||||
| } | |||||
| if (status == 0) { | |||||
| status = nes_cart_init_file(cart, cart_file); | |||||
| } | |||||
| if (status == 0) { | |||||
| // Failure might mean there's nothing to load | |||||
| load_sram(cart, cart_filename); | |||||
| } | |||||
| if (status != 0 && NULL != cart_file) { | |||||
| fclose(cart_file); | |||||
| cart_file = NULL; | |||||
| } | |||||
| return cart_file; | |||||
| } | |||||
| typedef struct { | typedef struct { | ||||
| @@ -32,7 +57,7 @@ typedef struct { | |||||
| int mode; | int mode; | ||||
| } loadsave_state; | } loadsave_state; | ||||
| void loadsave_start(loadsave_state* loadsave, int mode) { | |||||
| static void loadsave_start(loadsave_state* loadsave, int mode) { | |||||
| if (0 != loadsave->mode) { | if (0 != loadsave->mode) { | ||||
| // We can't do two things at once. | // We can't do two things at once. | ||||
| overlay_clear_message(loadsave->overlay, loadsave->id); | overlay_clear_message(loadsave->overlay, loadsave->id); | ||||
| @@ -54,18 +79,15 @@ void loadsave_start(loadsave_state* loadsave, int mode) { | |||||
| } | } | ||||
| } | } | ||||
| int loadsave_tick(loadsave_state* loadsave) { | |||||
| static int loadsave_tick(loadsave_state* loadsave) { | |||||
| int action = 0; | int action = 0; | ||||
| if (loadsave->mode != 0 && --loadsave->timer <= 0) { | if (loadsave->mode != 0 && --loadsave->timer <= 0) { | ||||
| const char *op = NULL; | const char *op = NULL; | ||||
| if (input_Result_Load == loadsave->mode) { | if (input_Result_Load == loadsave->mode) { | ||||
| //load_state(&sys, cart_filename); | |||||
| op = "restored"; | op = "restored"; | ||||
| } else if (input_Result_Save == loadsave->mode) { | } else if (input_Result_Save == loadsave->mode) { | ||||
| //save_state(&sys, cart_filename); | |||||
| op = "saved"; | op = "saved"; | ||||
| } | } | ||||
| @@ -84,73 +106,138 @@ int loadsave_tick(loadsave_state* loadsave) { | |||||
| return action; | return action; | ||||
| } | } | ||||
| typedef struct { | |||||
| char* filename; | |||||
| FILE* file; | |||||
| } cart_info; | |||||
| int main(int argc, char* argv[]) { | |||||
| int status = 0; | |||||
| nes_Renderer* rend = &sdl_renderer; | |||||
| if (status == 0) { | |||||
| status = nes_render_init(rend); | |||||
| } | |||||
| nes_Input_Reader* input = &sdl_input; | |||||
| if (status == 0) { | |||||
| status = nes_input_init(input); | |||||
| } | |||||
| nes_Audio_Stream* audio = &sdl_audio; | |||||
| if (status == 0) { | |||||
| status = nes_audio_init(audio, audio_freq); | |||||
| } | |||||
| static void cleanup_cart_info(cart_info* cart) { | |||||
| if (cart->file) fclose(cart->file); | |||||
| free(cart->filename); | |||||
| } | |||||
| char* cart_filename = NULL; | |||||
| FILE* cart_file = NULL; | |||||
| if (0 == status && argc > 1) { | |||||
| cart_filename = strdup(argv[1]); | |||||
| cart_file = nes_load_cart(&sys.cart, argv[1]); | |||||
| } | |||||
| static int select_rom(menu_state* menu, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys, | |||||
| cart_info* cur_cart) { | |||||
| int status = 0; | |||||
| cart_info cart = {0}; | |||||
| nes_cart new_cart = {0}; | |||||
| menu_state menu = {0}; | |||||
| while (0 == status && NULL == cart_file) { | |||||
| while (0 == status && NULL == cart.file) { | |||||
| // Display a load failure message? | // Display a load failure message? | ||||
| if (NULL != cart_filename) { | |||||
| if (NULL != cart.filename) { | |||||
| char message[1024]; | char message[1024]; | ||||
| snprintf(message, sizeof(message) - 1, | snprintf(message, sizeof(message) - 1, | ||||
| "Could not load\n%s", cart_filename); | |||||
| int button = modal_popup(message, rend, input, &sys); | |||||
| "Could not load\n%s", cart.filename); | |||||
| int button = modal_popup(message, rend, input, sys); | |||||
| if (input_Result_Quit == (button >> 8)) { | if (input_Result_Quit == (button >> 8)) { | ||||
| // Program closed inside modal | // Program closed inside modal | ||||
| status = -1; | |||||
| status = input_Result_Quit; | |||||
| } | } | ||||
| } | } | ||||
| if (0 == status) { | if (0 == status) { | ||||
| // If we didn't launch with a file, run the loader | // If we didn't launch with a file, run the loader | ||||
| cart_filename = run_main_menu(&menu, rend, | |||||
| input, &sys); | |||||
| cart.filename = run_main_menu(menu, rend, | |||||
| input, sys); | |||||
| if (NULL == cart_filename) { | |||||
| if ( NULL == cart.filename || | |||||
| (char*)-1 == cart.filename ) { | |||||
| // This means that we dumped out of the loader | // This means that we dumped out of the loader | ||||
| cart.filename = NULL; | |||||
| status = -1; | status = -1; | ||||
| } else { | } else { | ||||
| cart_file = nes_load_cart(&sys.cart, | |||||
| cart_filename); | |||||
| cart.file = nes_load_cart(&new_cart, | |||||
| cart.filename); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (0 == status && NULL != cart.file) { | |||||
| save_sram(&sys->cart, cur_cart->filename); | |||||
| nes_cart_done(&sys->cart); | |||||
| cleanup_cart_info(cur_cart); | |||||
| sys->cart = new_cart; | |||||
| *cur_cart = cart; | |||||
| nes_setup_cart(sys); | |||||
| } | |||||
| return status; | |||||
| } | |||||
| static int do_game_menu(menu_state* state, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys, | |||||
| cart_info* cart) { | |||||
| int status = 0; | |||||
| menu_state rom_menu = {0}; | |||||
| while (1) { | |||||
| status = run_game_menu(state, rend, input, sys); | |||||
| if (input_Result_Menu == status) { | |||||
| status = select_rom(&rom_menu, rend, input, | |||||
| sys, cart); | |||||
| if (input_Result_Cancel == status) { | |||||
| continue; | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| return status; | |||||
| } | |||||
| int main(int argc, char* argv[]) { | |||||
| int status = 0; | |||||
| nes_Renderer* rend = &sdl_renderer; | |||||
| if (status == 0) { | |||||
| status = nes_render_init(rend); | |||||
| } | |||||
| nes_Input_Reader* input = &sdl_input; | |||||
| if (status == 0) { | |||||
| status = nes_input_init(input); | |||||
| } | |||||
| nes_Audio_Stream* audio = &sdl_audio; | |||||
| if (status == 0) { | |||||
| status = nes_audio_init(audio, audio_freq); | |||||
| } | |||||
| nes sys = {0}; | |||||
| if (status == 0) { | if (status == 0) { | ||||
| status = nes_init(&sys, audio_freq); | status = nes_init(&sys, audio_freq); | ||||
| } | } | ||||
| cart_info cart = {0}; | |||||
| if (0 == status && argc > 1) { | |||||
| cart.filename = strdup(argv[1]); | |||||
| cart.file = nes_load_cart(&sys.cart, cart.filename); | |||||
| if (NULL != cart.file) { | |||||
| nes_setup_cart(&sys); | |||||
| } | |||||
| } | |||||
| menu_state main_menu = {0}; | |||||
| if (0 == status && NULL == cart.file) { | |||||
| status = select_rom(&main_menu, rend, input, | |||||
| &sys, &cart); | |||||
| } | |||||
| if (status == 0) { | if (status == 0) { | ||||
| menu_state game_menu = {0}; | |||||
| loadsave_state loadsave = { | loadsave_state loadsave = { | ||||
| .overlay = &rend->overlay, | .overlay = &rend->overlay, | ||||
| }; | }; | ||||
| nes_reset(&sys); | |||||
| nes_render(rend, &sys.ppu); | nes_render(rend, &sys.ppu); | ||||
| time_us t_target = time_now(); | time_us t_target = time_now(); | ||||
| @@ -170,9 +257,9 @@ int main(int argc, char* argv[]) { | |||||
| // Load/Save Operations | // Load/Save Operations | ||||
| int action = loadsave_tick(&loadsave); | int action = loadsave_tick(&loadsave); | ||||
| if (input_Result_Load == action) { | if (input_Result_Load == action) { | ||||
| load_state(&sys, cart_filename); | |||||
| load_state(&sys, cart.filename); | |||||
| } else if (input_Result_Save == action) { | } else if (input_Result_Save == action) { | ||||
| save_state(&sys, cart_filename); | |||||
| save_state(&sys, cart.filename); | |||||
| } | } | ||||
| // Sleep to catch up to master clock | // Sleep to catch up to master clock | ||||
| @@ -197,6 +284,20 @@ int main(int argc, char* argv[]) { | |||||
| // Update button states every rendered frame | // Update button states every rendered frame | ||||
| status = nes_input_update(input, &sys.input); | status = nes_input_update(input, &sys.input); | ||||
| if (input_Result_Menu == status) { | |||||
| status = do_game_menu( | |||||
| &game_menu, rend, | |||||
| input, &sys, &cart | |||||
| ); | |||||
| if ( input_Result_Load == status || | |||||
| input_Result_Save == status) { | |||||
| loadsave.mode = status; | |||||
| loadsave.timer = 0; | |||||
| status = 0; | |||||
| } | |||||
| // Allow other options to fall through | |||||
| } | |||||
| if (input_Result_Reset == status) { | if (input_Result_Reset == status) { | ||||
| overlay_add_message( | overlay_add_message( | ||||
| &rend->overlay, | &rend->overlay, | ||||
| @@ -239,11 +340,15 @@ int main(int argc, char* argv[]) { | |||||
| status == 0 ? "OK" : "Halted"); | status == 0 ? "OK" : "Halted"); | ||||
| // Failure might mean there's nothing to save | // Failure might mean there's nothing to save | ||||
| save_sram(&sys.cart, cart_filename); | |||||
| save_sram(&sys.cart, cart.filename); | |||||
| } | } | ||||
| if (NULL != cart_file) fclose(cart_file); | |||||
| free(cart_filename); | |||||
| nes_done(&sys); | |||||
| cleanup_cart_info(&cart); | |||||
| nes_audio_done(audio); | |||||
| nes_input_done(input); | |||||
| nes_render_done(rend); | |||||
| return status; | return status; | ||||
| } | } | ||||
| @@ -10,7 +10,7 @@ typedef struct nes_Renderer_t { | |||||
| void (*done)(struct nes_Renderer_t*); | void (*done)(struct nes_Renderer_t*); | ||||
| int (*render)(struct nes_Renderer_t*, nes_ppu*); | int (*render)(struct nes_Renderer_t*, nes_ppu*); | ||||
| void (*draw_last_frame)(struct nes_Renderer_t*); | |||||
| void (*draw_last_frame)(struct nes_Renderer_t*, int dim); | |||||
| void (*draw_text)(struct nes_Renderer_t*, const char*, int x, int y); | void (*draw_text)(struct nes_Renderer_t*, const char*, int x, int y); | ||||
| void (*text_size)(struct nes_Renderer_t*, const char*, int* w, int* h); | void (*text_size)(struct nes_Renderer_t*, const char*, int* w, int* h); | ||||
| void (*draw_done)(struct nes_Renderer_t*); | void (*draw_done)(struct nes_Renderer_t*); | ||||
| @@ -31,8 +31,9 @@ static inline int nes_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| return rend->render(rend, ppu); | return rend->render(rend, ppu); | ||||
| } | } | ||||
| static inline void nes_draw_last_frame(nes_Renderer* rend) { | |||||
| rend->draw_last_frame(rend); | |||||
| static inline void nes_draw_last_frame(nes_Renderer* rend, | |||||
| int dim) { | |||||
| rend->draw_last_frame(rend, dim); | |||||
| } | } | ||||
| static inline void nes_text_size(nes_Renderer* rend, | static inline void nes_text_size(nes_Renderer* rend, | ||||
| @@ -37,16 +37,18 @@ int load_sram(nes_cart* cart, const char* cart_filename) { | |||||
| int save_sram(const nes_cart* cart, const char* cart_filename) { | int save_sram(const nes_cart* cart, const char* cart_filename) { | ||||
| int status = -1; | int status = -1; | ||||
| int sram_size = cart->mapper->sram_size ? | |||||
| cart->mapper->sram_size(cart->mapper) : 0; | |||||
| const void* sram = cart->mapper->sram ? | |||||
| cart->mapper->sram(cart->mapper) : NULL; | |||||
| if (sram_size > 0 && NULL != sram) { | |||||
| char sram_filename[FILENAME_MAX] = {0}; | |||||
| make_sram_filename(sram_filename, FILENAME_MAX - 1, | |||||
| cart_filename); | |||||
| status = write_file(sram_filename, sram, sram_size); | |||||
| if (NULL != cart->mapper) { | |||||
| int sram_size = cart->mapper->sram_size ? | |||||
| cart->mapper->sram_size(cart->mapper) : 0; | |||||
| const void* sram = cart->mapper->sram ? | |||||
| cart->mapper->sram(cart->mapper) : NULL; | |||||
| if (sram_size > 0 && NULL != sram) { | |||||
| char sram_filename[FILENAME_MAX] = {0}; | |||||
| make_sram_filename(sram_filename, FILENAME_MAX - 1, | |||||
| cart_filename); | |||||
| status = write_file(sram_filename, sram, sram_size); | |||||
| } | |||||
| } | } | ||||
| return status; | return status; | ||||
| @@ -53,7 +53,7 @@ static void sdl_input_done(nes_Input_Reader* input) { | |||||
| sdl_lose_gamepad(input->data); | sdl_lose_gamepad(input->data); | ||||
| } | } | ||||
| static const int sdl_reset_key = SDLK_ESCAPE; | |||||
| static const int sdl_menu_key = SDLK_ESCAPE; | |||||
| static const int sdl_save_key = SDLK_F1; | static const int sdl_save_key = SDLK_F1; | ||||
| static const int sdl_load_key = SDLK_F2; | static const int sdl_load_key = SDLK_F2; | ||||
| @@ -111,9 +111,9 @@ static int sdl_input_update(nes_Input_Reader* reader, | |||||
| input->controllers[0].buttons &= ~mask; | input->controllers[0].buttons &= ~mask; | ||||
| } | } | ||||
| } else if ( sdl_reset_key == event.key.keysym.sym && | |||||
| } else if ( sdl_menu_key == event.key.keysym.sym && | |||||
| SDL_KEYDOWN == event.type) { | SDL_KEYDOWN == event.type) { | ||||
| status = input_Result_Reset; | |||||
| status = input_Result_Menu; | |||||
| } else if (sdl_save_key == event.key.keysym.sym) { | } else if (sdl_save_key == event.key.keysym.sym) { | ||||
| if (SDL_KEYDOWN == event.type) { | if (SDL_KEYDOWN == event.type) { | ||||
| @@ -22,7 +22,7 @@ static int get_input(nes_Input_Reader* reader, | |||||
| } | } | ||||
| static int wait_for_input(nes_Input_Reader* reader, | static int wait_for_input(nes_Input_Reader* reader, | ||||
| nes_input* input) { | |||||
| nes_input* input) { | |||||
| int buttons = input->controllers[0].buttons; | int buttons = input->controllers[0].buttons; | ||||
| int status = 0; | int status = 0; | ||||
| for ( ; | for ( ; | ||||
| @@ -95,16 +95,21 @@ static void fix_filename(char* dst, int n, const char* src) { | |||||
| } | } | ||||
| static inline int n_visible(void) { | static inline int n_visible(void) { | ||||
| return (((nes_ppu_render_h - 30) - 1) / 11) - 1; | |||||
| return ((nes_ppu_render_h - 20) / 11); | |||||
| } | } | ||||
| static void show_menu(const menu_state* menu, | |||||
| nes_Renderer* rend, file_list* files) { | |||||
| nes_draw_last_frame(rend); | |||||
| static void show_menu(const menu_state* menu, int dim, int x, | |||||
| nes_Renderer* rend, | |||||
| const file_list* files) { | |||||
| nes_draw_last_frame(rend, dim); | |||||
| int bottom = menu->top + n_visible(); | |||||
| int bottom = menu->top + n_visible() - 1; | |||||
| for ( int n = menu->top, y = 10; | |||||
| int max = n_visible(); | |||||
| if (max > files->count) max = files->count; | |||||
| int y = (nes_ppu_render_h - (max * 11)) / 2; | |||||
| for ( int n = menu->top; | |||||
| n < files->count && n <= bottom; | n < files->count && n <= bottom; | ||||
| ++n, y += 11 ) { | ++n, y += 11 ) { | ||||
| char filename[100]; | char filename[100]; | ||||
| @@ -115,13 +120,73 @@ static void show_menu(const menu_state* menu, | |||||
| ( (menu->top == n && 0 != menu->top) || | ( (menu->top == n && 0 != menu->top) || | ||||
| (bottom == n && files->count - 1 > bottom) ) ? | (bottom == n && files->count - 1 > bottom) ) ? | ||||
| "..." : filename, | "..." : filename, | ||||
| 20, y); | |||||
| if (menu->cursor == n) nes_draw_text(rend, ">", 10, y); | |||||
| x, y); | |||||
| if (menu->cursor == n) { | |||||
| nes_draw_text(rend, ">", x - 10, y); | |||||
| } | |||||
| } | } | ||||
| nes_draw_done(rend); | nes_draw_done(rend); | ||||
| } | } | ||||
| static int run_menu(menu_state* state, const file_list* files, | |||||
| int x, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys) { | |||||
| menu_state menu = {0}; | |||||
| if (NULL != state) { | |||||
| menu = *state; | |||||
| if (menu.cursor < 0) { | |||||
| menu.cursor = 0; | |||||
| } else if (menu.cursor >= files->count) { | |||||
| menu.cursor = files->count - 1; | |||||
| } | |||||
| } | |||||
| int status = 0; | |||||
| while (0 == status) { | |||||
| // Scrolling (do this first to ensure menu is valid) | |||||
| const int visible = n_visible() - 1; | |||||
| menu.top = menu.cursor - (visible / 2); | |||||
| if (menu.top <= 0) { | |||||
| // We use <= so we don't readjust the top from 0 | |||||
| menu.top = 0; | |||||
| } else if (menu.top + visible >= files->count) { | |||||
| menu.top = (files->count - 1) - visible; | |||||
| } | |||||
| show_menu(&menu, NULL != sys->cart.mapper, | |||||
| x, rend, files); | |||||
| int buttons = wait_for_input(input, &sys->input); | |||||
| int special = (buttons >> 8); | |||||
| if (input_Result_Quit == special) { | |||||
| status = special; | |||||
| } else if (buttons & (1 << Button_B)) { | |||||
| status = input_Result_Cancel; | |||||
| } else if (buttons & ( (1 << Button_A) | | |||||
| (1 << Button_Start) )) { | |||||
| // Select | |||||
| break; | |||||
| } else if (buttons & (1 << Button_Up)) { | |||||
| if (menu.cursor > 0) --menu.cursor; | |||||
| } else if (buttons & ( (1 << Button_Down) | | |||||
| (1 << Button_Select) )) { | |||||
| if (menu.cursor < (files->count - 1)) { | |||||
| ++menu.cursor; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (NULL != state) *state = menu; | |||||
| return status; | |||||
| } | |||||
| char* run_main_menu(menu_state* state, nes_Renderer* rend, | char* run_main_menu(menu_state* state, nes_Renderer* rend, | ||||
| nes_Input_Reader* input, nes* sys) { | nes_Input_Reader* input, nes* sys) { | ||||
| @@ -130,7 +195,7 @@ char* run_main_menu(menu_state* state, nes_Renderer* rend, | |||||
| DIR* dir = opendir("rom"); | DIR* dir = opendir("rom"); | ||||
| if (NULL == dir) { | if (NULL == dir) { | ||||
| nes_draw_last_frame(rend); | |||||
| nes_draw_last_frame(rend, NULL != sys->cart.mapper); | |||||
| nes_draw_text(rend, "No ROMS found!", 10, 10); | nes_draw_text(rend, "No ROMS found!", 10, 10); | ||||
| nes_draw_text(rend, "Press any key to exit", 10, 21); | nes_draw_text(rend, "Press any key to exit", 10, 21); | ||||
| nes_draw_done(rend); | nes_draw_done(rend); | ||||
| @@ -142,55 +207,19 @@ char* run_main_menu(menu_state* state, nes_Renderer* rend, | |||||
| closedir(dir); | closedir(dir); | ||||
| menu_state menu = {0}; | menu_state menu = {0}; | ||||
| if (NULL != state) { | |||||
| menu = *state; | |||||
| if (menu.cursor < 0) { | |||||
| menu.cursor = 0; | |||||
| } else if (menu.cursor >= files.count) { | |||||
| menu.cursor = files.count - 1; | |||||
| } | |||||
| } | |||||
| while (1) { | |||||
| // Scrolling (do this first to ensure menu is valid) | |||||
| const int visible = n_visible(); | |||||
| menu.top = menu.cursor - (visible / 2); | |||||
| if (menu.top <= 0) { | |||||
| // We use <= so we don't readjust the top from 0 | |||||
| menu.top = 0; | |||||
| } else if (menu.top + visible >= files.count) { | |||||
| menu.top = (files.count - 1) - visible; | |||||
| } | |||||
| show_menu(&menu, rend, &files); | |||||
| if (NULL != state) menu = *state; | |||||
| int buttons = wait_for_input(input, &sys->input); | |||||
| int special = (buttons >> 8); | |||||
| int status = run_menu(&menu, &files, 20, | |||||
| rend, input, sys); | |||||
| if ( input_Result_Quit == special || | |||||
| (buttons & (1 << Button_B))) { | |||||
| // Cancel | |||||
| menu.cursor = -1; | |||||
| break; | |||||
| if (input_Result_Quit == status) { | |||||
| cart_filename = (char*)-1; | |||||
| } else if (buttons & ( (1 << Button_A) | | |||||
| (1 << Button_Start) )) { | |||||
| // Select | |||||
| break; | |||||
| } else if (input_Result_Cancel == status) { | |||||
| cart_filename = NULL; | |||||
| } else if (buttons & (1 << Button_Up)) { | |||||
| if (menu.cursor > 0) --menu.cursor; | |||||
| } else if (buttons & ( (1 << Button_Down) | | |||||
| (1 << Button_Select) )) { | |||||
| if (menu.cursor < (files.count - 1)) { | |||||
| ++menu.cursor; | |||||
| } | |||||
| } | |||||
| } | |||||
| // Selection has been made (or cancelled) | |||||
| if (menu.cursor >= 0 && menu.cursor < files.count) { | |||||
| } else if ( menu.cursor >= 0 && | |||||
| menu.cursor < files.count) { | |||||
| char filename[1024]; | char filename[1024]; | ||||
| snprintf(filename, sizeof(filename) - 1, | snprintf(filename, sizeof(filename) - 1, | ||||
| "%s/%s", "rom", files.files[menu.cursor]); | "%s/%s", "rom", files.files[menu.cursor]); | ||||
| @@ -199,15 +228,48 @@ char* run_main_menu(menu_state* state, nes_Renderer* rend, | |||||
| free_file_list(&files); | free_file_list(&files); | ||||
| if (NULL != state) { | |||||
| *state = menu; | |||||
| if (menu.cursor < 0) state->cursor = 0; | |||||
| } | |||||
| if (NULL != state) *state = menu; | |||||
| } | } | ||||
| return cart_filename; | return cart_filename; | ||||
| } | } | ||||
| int run_game_menu(menu_state* state, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys) { | |||||
| static char* items[] = { | |||||
| "Resume", | |||||
| "Save", | |||||
| "Load", | |||||
| "Reset", | |||||
| "Select ROM", | |||||
| "Exit", | |||||
| }; | |||||
| static int choices[] = { | |||||
| input_Result_OK, | |||||
| input_Result_Save, | |||||
| input_Result_Load, | |||||
| input_Result_Reset, | |||||
| input_Result_Menu, | |||||
| input_Result_Quit, | |||||
| }; | |||||
| static const file_list options = { | |||||
| .files = items, | |||||
| .count = (sizeof(items) / sizeof(*items)), | |||||
| }; | |||||
| menu_state menu = {0}; | |||||
| if (NULL != state) menu = *state; | |||||
| int status = run_menu(&menu, &options, 100, | |||||
| rend, input, sys); | |||||
| if (NULL != state) *state = menu; | |||||
| if (0 == status) status = choices[menu.cursor]; | |||||
| return status; | |||||
| } | |||||
| int modal_popup(const char* message, nes_Renderer* rend, | int modal_popup(const char* message, nes_Renderer* rend, | ||||
| nes_Input_Reader* input, nes* sys) { | nes_Input_Reader* input, nes* sys) { | ||||
| int w = 0; | int w = 0; | ||||
| @@ -218,7 +280,7 @@ int modal_popup(const char* message, nes_Renderer* rend, | |||||
| int x = (nes_ppu_render_w - w) / 2; | int x = (nes_ppu_render_w - w) / 2; | ||||
| int y = (nes_ppu_render_h - h) / 2; | int y = (nes_ppu_render_h - h) / 2; | ||||
| nes_draw_last_frame(rend); | |||||
| nes_draw_last_frame(rend, NULL != sys->cart.mapper); | |||||
| nes_draw_text(rend, message, x, y); | nes_draw_text(rend, message, x, y); | ||||
| nes_draw_done(rend); | nes_draw_done(rend); | ||||
| @@ -148,7 +148,7 @@ static int sdl_render_init(nes_Renderer* rend) { | |||||
| } else { | } else { | ||||
| SDL_LockTextureToSurface(data->texture, NULL, | SDL_LockTextureToSurface(data->texture, NULL, | ||||
| &data->target); | &data->target); | ||||
| SDL_FillRect(data->target, NULL, 0x556677FF); | |||||
| SDL_FillRect(data->target, NULL, 0x006677FF); | |||||
| } | } | ||||
| } | } | ||||
| @@ -560,11 +560,24 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| } | } | ||||
| static void sdl_redraw_frame(nes_Renderer* rend) { | |||||
| static void sdl_redraw_frame(nes_Renderer* rend, int dim) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | sdl_render_data* data = (sdl_render_data*)rend->data; | ||||
| if (dim) { | |||||
| SDL_SetTextureAlphaMod(data->texture, 0x7F); | |||||
| SDL_SetTextureBlendMode(data->texture, | |||||
| SDL_BLENDMODE_BLEND); | |||||
| SDL_RenderClear(data->renderer); | |||||
| } | |||||
| SDL_UnlockTexture(data->texture); | SDL_UnlockTexture(data->texture); | ||||
| SDL_RenderCopy(data->renderer, data->texture, NULL, NULL); | SDL_RenderCopy(data->renderer, data->texture, NULL, NULL); | ||||
| SDL_LockTextureToSurface(data->texture, NULL, &data->target); | SDL_LockTextureToSurface(data->texture, NULL, &data->target); | ||||
| if (dim) { | |||||
| SDL_SetTextureBlendMode(data->texture, | |||||
| SDL_BLENDMODE_NONE); | |||||
| } | |||||
| } | } | ||||
| static void sdl_draw_present(nes_Renderer* rend) { | static void sdl_draw_present(nes_Renderer* rend) { | ||||