| @@ -18,6 +18,7 @@ typedef enum { | |||||
| input_Result_Cancel, | input_Result_Cancel, | ||||
| input_Result_View, | input_Result_View, | ||||
| input_Result_Refresh, | input_Result_Refresh, | ||||
| input_Result_Scale, | |||||
| } nes_Input_Result; | } nes_Input_Result; | ||||
| #define nes_controller_num_buttons (8U) | #define nes_controller_num_buttons (8U) | ||||
| @@ -268,19 +268,22 @@ char* run_main_menu(menu_state* state, nese_Components* comp, | |||||
| } | } | ||||
| int run_game_menu(menu_state* state, nese_Components* comp, | int run_game_menu(menu_state* state, nese_Components* comp, | ||||
| const cart_info* cart) { | |||||
| static char* items[] = { | |||||
| const nese_State* nese) { | |||||
| char* items[] = { | |||||
| "Resume", | "Resume", | ||||
| "Save", | "Save", | ||||
| "Restore", | "Restore", | ||||
| "Reset", | "Reset", | ||||
| "Select ROM", | "Select ROM", | ||||
| #ifndef STANDALONE | #ifndef STANDALONE | ||||
| "Toggle Fullscreen", | |||||
| (nese->flags & (1 << State_Bit_Fullscreen)) ? | |||||
| "Windowed" : "Fullscreen", | |||||
| #endif | #endif | ||||
| (nese->flags & (1 << State_Bit_Integer_Scale)) ? | |||||
| "Force Aspect" : "Integer Scaling", | |||||
| "Exit", | "Exit", | ||||
| }; | }; | ||||
| static int choices[] = { | |||||
| static const int choices[] = { | |||||
| input_Result_OK, | input_Result_OK, | ||||
| input_Result_Save, | input_Result_Save, | ||||
| input_Result_Load, | input_Result_Load, | ||||
| @@ -289,9 +292,10 @@ int run_game_menu(menu_state* state, nese_Components* comp, | |||||
| #ifndef STANDALONE | #ifndef STANDALONE | ||||
| input_Result_View, | input_Result_View, | ||||
| #endif | #endif | ||||
| input_Result_Scale, | |||||
| input_Result_Quit, | input_Result_Quit, | ||||
| }; | }; | ||||
| static const file_list options = { | |||||
| const file_list options = { | |||||
| .files = items, | .files = items, | ||||
| .count = (sizeof(items) / sizeof(*items)), | .count = (sizeof(items) / sizeof(*items)), | ||||
| }; | }; | ||||
| @@ -299,7 +303,8 @@ int run_game_menu(menu_state* state, nese_Components* comp, | |||||
| menu_state menu = {0}; | menu_state menu = {0}; | ||||
| if (NULL != state) menu = *state; | if (NULL != state) menu = *state; | ||||
| int status = run_menu(&menu, &options, 100, comp, cart); | |||||
| int status = run_menu(&menu, &options, 100, | |||||
| comp, &nese->cart); | |||||
| if (input_Result_Menu == status) { | if (input_Result_Menu == status) { | ||||
| status = input_Result_Cancel; | status = input_Result_Cancel; | ||||
| @@ -35,7 +35,7 @@ char* run_main_menu(menu_state*, nese_Components*, | |||||
| // Returns nes_Input_Result indicating the choice | // Returns nes_Input_Result indicating the choice | ||||
| int run_game_menu(menu_state*, nese_Components*, | int run_game_menu(menu_state*, nese_Components*, | ||||
| const cart_info*); | |||||
| const nese_State*); | |||||
| int modal_popup(const char* message, nese_Components*, | int modal_popup(const char* message, nese_Components*, | ||||
| const cart_info*); | const cart_info*); | ||||
| @@ -169,18 +169,19 @@ static int do_game_menu(menu_state* menu, | |||||
| menu_state rom_menu = {0}; | menu_state rom_menu = {0}; | ||||
| while (1) { | while (1) { | ||||
| status = run_game_menu(menu, comp, &state->cart); | |||||
| status = run_game_menu(menu, comp, state); | |||||
| if (input_Result_View == status) { | |||||
| if ( input_Result_View == status || | |||||
| input_Result_Scale == status ) { | |||||
| if (input_Result_View == status) { | |||||
| #ifndef STANDALONE | #ifndef STANDALONE | ||||
| state->flags ^= (1 << State_Bit_Fullscreen); | |||||
| state->flags ^= (1 << State_Bit_Fullscreen); | |||||
| #endif | #endif | ||||
| // We call this both times since it does both | |||||
| // the toggle and the recalculation. | |||||
| nes_render_fullscreen( | |||||
| comp->rend, | |||||
| state->flags & (1 << State_Bit_Fullscreen) | |||||
| ); | |||||
| } else { | |||||
| state->flags ^= (1 << State_Bit_Integer_Scale); | |||||
| } | |||||
| nes_render_set_flags(comp->rend, state->flags); | |||||
| continue; | continue; | ||||
| } else if (input_Result_Menu == status) { | } else if (input_Result_Menu == status) { | ||||
| @@ -223,11 +224,7 @@ int main(int argc, char* argv[]) { | |||||
| if (status == 0) { | if (status == 0) { | ||||
| status = nes_render_init(components.rend); | status = nes_render_init(components.rend); | ||||
| if (0 == status) { | if (0 == status) { | ||||
| nes_render_fullscreen( | |||||
| components.rend, | |||||
| (state.flags & (1 << State_Bit_Fullscreen)) | |||||
| ); | |||||
| nes_render_refresh(components.rend); | |||||
| nes_render_set_flags( components.rend, state.flags); | |||||
| } | } | ||||
| } | } | ||||
| @@ -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 (*fullscreen)(struct nes_Renderer_t*, int enable); | |||||
| void (*set_flags)(struct nes_Renderer_t*, uint32_t flags); | |||||
| void (*refresh)(struct nes_Renderer_t*); | void (*refresh)(struct nes_Renderer_t*); | ||||
| void (*draw_last_frame)(struct nes_Renderer_t*, int dim); | void (*draw_last_frame)(struct nes_Renderer_t*, int dim); | ||||
| @@ -19,6 +19,7 @@ typedef struct nes_Renderer_t { | |||||
| void (*draw_done)(struct nes_Renderer_t*); | void (*draw_done)(struct nes_Renderer_t*); | ||||
| Overlay overlay; | Overlay overlay; | ||||
| uint32_t flags; | |||||
| void* data; | void* data; | ||||
| } nes_Renderer; | } nes_Renderer; | ||||
| @@ -34,9 +35,13 @@ 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_render_fullscreen(nes_Renderer* rend, | |||||
| int enable) { | |||||
| rend->fullscreen(rend, enable); | |||||
| static inline uint32_t nes_render_set_flags(nes_Renderer* rend, | |||||
| uint32_t flags) { | |||||
| uint32_t old_flags = rend->flags; | |||||
| rend->set_flags(rend, flags); | |||||
| rend->flags = flags; | |||||
| return old_flags; | |||||
| } | } | ||||
| static inline void nes_render_refresh(nes_Renderer* rend) { | static inline void nes_render_refresh(nes_Renderer* rend) { | ||||
| @@ -152,14 +152,7 @@ static int sdl_input_update(nes_Input_Reader* reader, | |||||
| } else if (SDL_WINDOWEVENT == event.type) { | } else if (SDL_WINDOWEVENT == event.type) { | ||||
| if ( SDL_WINDOWEVENT_EXPOSED == | if ( SDL_WINDOWEVENT_EXPOSED == | ||||
| event.window.event) { | event.window.event) { | ||||
| // We need to do this to flush both buffers | |||||
| // nes_draw_last_frame(comp->rend, 1); | |||||
| // nes_draw_done(comp->rend); | |||||
| // We call this both times since it does both | |||||
| // the toggle and the recalculation. | |||||
| nes_render_refresh(comp->rend); | nes_render_refresh(comp->rend); | ||||
| status = input_Result_Refresh; | status = input_Result_Refresh; | ||||
| } | } | ||||
| @@ -37,7 +37,6 @@ static inline uint8_t* chr_mem(const nes_ppu* ppu, | |||||
| return ppu->mapper->chr_addr(ppu->map_data, addr); | return ppu->mapper->chr_addr(ppu->map_data, addr); | ||||
| } | } | ||||
| typedef struct { | typedef struct { | ||||
| SDL_Window* window; | SDL_Window* window; | ||||
| SDL_Renderer* renderer; | SDL_Renderer* renderer; | ||||
| @@ -47,17 +46,84 @@ typedef struct { | |||||
| sdl_overlay_font font; | sdl_overlay_font font; | ||||
| SDL_Texture* texture; | SDL_Texture* texture; | ||||
| int win_w; | |||||
| int win_h; | |||||
| SDL_Rect view; | SDL_Rect view; | ||||
| } sdl_render_data; | } sdl_render_data; | ||||
| static sdl_render_data the_render_data = {0}; | static sdl_render_data the_render_data = {0}; | ||||
| static void sdl_render_refresh(nes_Renderer* rend) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | |||||
| int win_w = 0, win_h = 0; | |||||
| SDL_GetWindowSize(data->window, &win_w, &win_h); | |||||
| // Determine the viewport within the screen | |||||
| int w = win_w; | |||||
| int h = win_h; | |||||
| if (!(rend->flags & (1 << State_Bit_Integer_Scale))) { | |||||
| // Fullscreen | |||||
| if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) { | |||||
| w = (h * nes_ppu_scan_w) / nes_ppu_scan_h; | |||||
| } else { | |||||
| h = (w * nes_ppu_scan_h) / nes_ppu_scan_w; | |||||
| } | |||||
| } else { | |||||
| // Integer Scale | |||||
| int scale_y = 1, scale_x = 1; | |||||
| if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) { | |||||
| // Wide Window | |||||
| // printf("Wide\n"); | |||||
| scale_y = h / nes_ppu_scan_h; | |||||
| scale_x = ( (scale_y * nes_ppu_scan_w) + | |||||
| (nes_ppu_render_w / 2) ) / | |||||
| nes_ppu_render_w; | |||||
| if ((scale_x * nes_ppu_render_w) > w) { | |||||
| scale_x = w / nes_ppu_render_w; | |||||
| } | |||||
| } else { | |||||
| // Tall Window | |||||
| // printf("Tall\n"); | |||||
| scale_x = w / nes_ppu_render_w; | |||||
| scale_y = ( (scale_x * nes_ppu_render_w) + | |||||
| (nes_ppu_scan_w / 2) ) / nes_ppu_scan_w; | |||||
| if ((scale_y * nes_ppu_render_h) > h) { | |||||
| scale_y = h / nes_ppu_render_h; | |||||
| } | |||||
| } | |||||
| h = scale_y * nes_ppu_render_h; | |||||
| w = scale_x * nes_ppu_render_w; | |||||
| /* | |||||
| printf("Scale %d, %d : %d, %d -> %d, %d\n", | |||||
| scale_x, scale_y, win_w, win_h, w, h); | |||||
| */ | |||||
| if (!(rend->flags & (1 << State_Bit_Fullscreen))) { | |||||
| SDL_SetWindowSize(data->window, w, h); | |||||
| int x = 0, y = 0; | |||||
| SDL_GetWindowPosition(data->window, &x, &y); | |||||
| SDL_SetWindowPosition(data->window, | |||||
| x - ((w - win_w) / 2), | |||||
| y - ((h - win_h) / 2)); | |||||
| } | |||||
| } | |||||
| data->view.x = (win_w - w) / 2; | |||||
| data->view.y = (win_h - h) / 2; | |||||
| data->view.w = w; | |||||
| data->view.h = h; | |||||
| SDL_RenderSetClipRect(data->renderer, &data->view); | |||||
| } | |||||
| static int sdl_render_init(nes_Renderer* rend) { | static int sdl_render_init(nes_Renderer* rend) { | ||||
| sdl_render_data* data = &the_render_data; | sdl_render_data* data = &the_render_data; | ||||
| rend->data = &the_render_data; | |||||
| int status = SDL_Init(SDL_INIT_VIDEO); | int status = SDL_Init(SDL_INIT_VIDEO); | ||||
| if (0 != status) { | if (0 != status) { | ||||
| @@ -70,21 +136,11 @@ static int sdl_render_init(nes_Renderer* rend) { | |||||
| SDL_GetCurrentDisplayMode(0, &mode); | SDL_GetCurrentDisplayMode(0, &mode); | ||||
| int yscale = (mode.h - 1) / nes_ppu_scan_h; | |||||
| int xscale = mode.w / nes_ppu_scan_w; | |||||
| int scale = (xscale < yscale ? xscale : yscale); | |||||
| data->win_w = (nes_ppu_scan_w * scale); | |||||
| data->win_h = (nes_ppu_scan_h * scale); | |||||
| data->view.w = data->win_w; | |||||
| data->view.h = data->win_h; | |||||
| data->window = SDL_CreateWindow( | data->window = SDL_CreateWindow( | ||||
| "NESe", | "NESe", | ||||
| SDL_WINDOWPOS_UNDEFINED, | |||||
| SDL_WINDOWPOS_UNDEFINED, | |||||
| data->win_w, data->win_h, 0 | |||||
| SDL_WINDOWPOS_CENTERED, | |||||
| SDL_WINDOWPOS_CENTERED, | |||||
| mode.w, mode.h, 0 | |||||
| ); | ); | ||||
| if (NULL == data->window) { | if (NULL == data->window) { | ||||
| fprintf(stderr, "SDL: Failed to create window\n"); | fprintf(stderr, "SDL: Failed to create window\n"); | ||||
| @@ -106,6 +162,7 @@ static int sdl_render_init(nes_Renderer* rend) { | |||||
| status = -1; | status = -1; | ||||
| } else { | } else { | ||||
| sdl_render_refresh(rend); | |||||
| overlay_init(&rend->overlay); | overlay_init(&rend->overlay); | ||||
| sdl_overlay_font_init(data->renderer, &data->font); | sdl_overlay_font_init(data->renderer, &data->font); | ||||
| } | } | ||||
| @@ -171,8 +228,6 @@ static int sdl_render_init(nes_Renderer* rend) { | |||||
| SDL_SetPaletteColors(data->foreground->format->palette, | SDL_SetPaletteColors(data->foreground->format->palette, | ||||
| nes_palette, 0U, 64U); | nes_palette, 0U, 64U); | ||||
| SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU); | SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU); | ||||
| rend->data = &the_render_data; | |||||
| } | } | ||||
| return status; | return status; | ||||
| @@ -190,38 +245,16 @@ static void sdl_render_done(nes_Renderer* rend) { | |||||
| SDL_Quit(); | SDL_Quit(); | ||||
| } | } | ||||
| static void sdl_render_fullscreen(nes_Renderer* rend, int on) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | |||||
| SDL_SetWindowFullscreen(data->window, | |||||
| on ? SDL_WINDOW_FULLSCREEN : 0); | |||||
| } | |||||
| static void sdl_render_refresh(nes_Renderer* rend) { | |||||
| static void sdl_render_set_flags(nes_Renderer* rend, | |||||
| uint32_t flags) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | sdl_render_data* data = (sdl_render_data*)rend->data; | ||||
| SDL_GetWindowSize(data->window, | |||||
| &data->win_w, &data->win_h); | |||||
| // Determine the viewport within the screen | |||||
| int w = data->win_w; | |||||
| int h = data->win_h; | |||||
| if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) { | |||||
| w = (h * nes_ppu_scan_w) / nes_ppu_scan_h; | |||||
| } else { | |||||
| h = (w * nes_ppu_scan_h) / nes_ppu_scan_w; | |||||
| } | |||||
| data->view.x = (data->win_w - w) / 2; | |||||
| data->view.y = (data->win_h - h) / 2; | |||||
| data->view.w = w; | |||||
| data->view.h = h; | |||||
| SDL_RenderSetClipRect(data->renderer, &data->view); | |||||
| // We need to do this to flush both buffers | |||||
| // nes_draw_last_frame(rend, 1); | |||||
| // nes_draw_done(rend); | |||||
| SDL_SetWindowFullscreen( | |||||
| data->window, | |||||
| (flags & (1 << State_Bit_Fullscreen)) ? | |||||
| SDL_WINDOW_FULLSCREEN : 0 | |||||
| ); | |||||
| rend->flags = flags; | |||||
| sdl_render_refresh(rend); | |||||
| } | } | ||||
| static inline void render_sprite_line( | static inline void render_sprite_line( | ||||
| @@ -661,7 +694,7 @@ nes_Renderer sdl_renderer = { | |||||
| .init = sdl_render_init, | .init = sdl_render_init, | ||||
| .done = sdl_render_done, | .done = sdl_render_done, | ||||
| .render = sdl_render, | .render = sdl_render, | ||||
| .fullscreen = sdl_render_fullscreen, | |||||
| .set_flags = sdl_render_set_flags, | |||||
| .refresh = sdl_render_refresh, | .refresh = sdl_render_refresh, | ||||
| .draw_last_frame = sdl_redraw_frame, | .draw_last_frame = sdl_redraw_frame, | ||||
| .draw_text = sdl_draw_text, | .draw_text = sdl_draw_text, | ||||
| @@ -43,12 +43,16 @@ static const ini_datum prefs_schema = { | |||||
| .type = ini_section, | .type = ini_section, | ||||
| .name = ".flags", | .name = ".flags", | ||||
| .offset = offsetof(nese_State, flags), | .offset = offsetof(nese_State, flags), | ||||
| .count = 1, | |||||
| .count = 2, | |||||
| .data = (ini_datum[]){ | .data = (ini_datum[]){ | ||||
| { | { | ||||
| .type = ini_flag, | .type = ini_flag, | ||||
| .name = "fullscreen", | .name = "fullscreen", | ||||
| .shift = State_Bit_Fullscreen, | .shift = State_Bit_Fullscreen, | ||||
| }, { | |||||
| .type = ini_flag, | |||||
| .name = "integer_scale", | |||||
| .shift = State_Bit_Integer_Scale, | |||||
| }, | }, | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -34,11 +34,12 @@ void cart_info_done(cart_info*); | |||||
| typedef enum { | typedef enum { | ||||
| State_Bit_Fullscreen = 0, | State_Bit_Fullscreen = 0, | ||||
| } nese_State_Flags; | |||||
| State_Bit_Integer_Scale = 1, | |||||
| } nese_State_Flag_Bits; | |||||
| typedef struct { | typedef struct { | ||||
| cart_info cart; | cart_info cart; | ||||
| nese_State_Flags flags; | |||||
| uint32_t flags; // nese_State_Flag_Bits | |||||
| } nese_State; | } nese_State; | ||||