diff --git a/src/game/level_update.c b/src/game/level_update.c index 3fa795f9..052959c4 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -29,8 +29,7 @@ #include "course_table.h" #include "thread6.h" #include "../../include/libc/stdlib.h" -#include "../pc/configfile.h" -#define CONFIG_FILE "sm64config.txt" +#include "../pc/pc_main.h" #include "pc/cliopts.h" @@ -1020,36 +1019,27 @@ s32 play_mode_normal(void) { s32 play_mode_paused(void) { if (gPauseScreenMode == 0) { set_menu_mode(RENDER_PAUSE_SCREEN); - } else if (gPauseScreenMode == 1) { + } else if (gPauseScreenMode == 1) { raise_background_noise(1); gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN; set_play_mode(PLAY_MODE_NORMAL); - } + } else if (gPauseScreenMode == 2) { // Exit level - else if (gPauseScreenMode == 2) { - - if (gDebugLevelSelect) { - fade_into_special_warp(-9, 1); - } - - else { - initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0); - fade_into_special_warp(0, 0); - gSavedCourseNum = COURSE_NONE; - } - - } // end of gPauseScreenMode == 2 for "EXIT COURSE" option - - if (gPauseScreenMode == 3) { // We should only be getting "int 3" to here - initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0); + if (gDebugLevelSelect) { + fade_into_special_warp(-9, 1); + } else { + initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0); fade_into_special_warp(0, 0); + gSavedCourseNum = COURSE_NONE; + } + } else if (gPauseScreenMode == 3) { + // We should only be getting "int 3" to here + initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0); + fade_into_special_warp(0, 0); + game_exit(); + } - //configfile_save(CONFIG_FILE); - exit(0); // Appears to automatically save config on exit! - } - - gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN; - // } + gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN; return 0; } diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index cf623f91..6c3a8b0a 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -29,8 +29,7 @@ #include "dialog_ids.h" #include "thread6.h" #include "../../include/libc/stdlib.h" -#include "../pc/configfile.h" -#define CONFIG_FILE "sm64config.txt" +#include "../pc/pc_main.h" // TODO: put this elsewhere enum SaveOption { SAVE_OPT_SAVE_AND_CONTINUE = 1, SAVE_OPT_SAVE_AND_QUIT, SAVE_OPT_SAVE_EXIT_GAME, SAVE_OPT_CONTINUE_DONT_SAVE }; @@ -263,17 +262,11 @@ void handle_save_menu(struct MarioState *m) { if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) { fade_into_special_warp(-2, 0); // reset game + } else if (gSaveOptSelectIndex == SAVE_OPT_SAVE_EXIT_GAME) { + //initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0); + fade_into_special_warp(0, 0); + game_exit(); } - - if (gSaveOptSelectIndex == SAVE_OPT_SAVE_EXIT_GAME) { - //configfile_save(CONFIG_FILE); //Redundant, save_file implies save_config? Save config file before fully exiting - //initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0); - fade_into_special_warp(0, 0); - - //fade_into_special_warp(-2, 0); // do the reset game thing - exit(0); // exit after saving game - } - } // not quitting diff --git a/src/game/options_menu.c b/src/game/options_menu.c index 59dfc23e..752a5396 100644 --- a/src/game/options_menu.c +++ b/src/game/options_menu.c @@ -16,6 +16,7 @@ #include "game/game_init.h" #include "game/ingame_menu.h" #include "game/options_menu.h" +#include "pc/pc_main.h" #include "pc/cliopts.h" #include "pc/configfile.h" #include "pc/controller/controller_api.h" @@ -161,7 +162,7 @@ struct SubMenu { /* button action functions */ static void optmenu_act_exit(UNUSED struct Option *self, s32 arg) { - if (!arg) exit(0); // only exit on A press and not directions + if (!arg) game_exit(); // only exit on A press and not directions } /* submenu option lists */ diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index c732eab6..b73b6aeb 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -24,17 +24,26 @@ #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" +#include "../pc_main.h" #include "../configfile.h" #include "../cliopts.h" #include "src/pc/controller/controller_keyboard.h" +// TODO: figure out if this shit even works +#ifdef VERSION_EU +# define FRAMERATE 25 +#else +# define FRAMERATE 30 +#endif + static SDL_Window *wnd; static SDL_GLContext ctx = NULL; static int inverted_scancode_table[512]; -static bool cur_fullscreen; -static uint32_t cur_width, cur_height; +static bool window_fullscreen; +static bool window_vsync; +static int window_width, window_height; const SDL_Scancode windows_scancode_table[] = { @@ -88,7 +97,7 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = { }; static void gfx_sdl_set_fullscreen(bool fullscreen) { - if (fullscreen == cur_fullscreen) return; + if (fullscreen == window_fullscreen) return; if (fullscreen) { SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP); @@ -96,15 +105,34 @@ static void gfx_sdl_set_fullscreen(bool fullscreen) { } else { SDL_SetWindowFullscreen(wnd, 0); SDL_ShowCursor(SDL_ENABLE); + // reset back to small window just in case + window_width = DESIRED_SCREEN_WIDTH; + window_height = DESIRED_SCREEN_HEIGHT; + SDL_SetWindowSize(wnd, window_width, window_height); } - cur_fullscreen = fullscreen; + window_fullscreen = fullscreen; +} + +static bool test_vsync(void) { + // Even if SDL_GL_SetSwapInterval succeeds, it doesn't mean that VSync actually works. + // A 60 Hz monitor should have a swap interval of 16.67 milliseconds. + // If it takes less than 12 milliseconds, assume that VSync is not working. + // SDL_GetTicks() probably does not offer enough precision for this kind of shit. + Uint32 start, end; + + // do an extra swap, sometimes the first one takes longer (maybe creates buffers?) + SDL_GL_SwapWindow(wnd); + + SDL_GL_SwapWindow(wnd); + start = SDL_GetTicks(); + SDL_GL_SwapWindow(wnd); + end = SDL_GetTicks(); + + return (end - start >= 12); } static void gfx_sdl_init(void) { - Uint32 window_flags = 0; - u8 Fullscreen; - SDL_Init(SDL_INIT_VIDEO); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); @@ -119,49 +147,48 @@ static void gfx_sdl_init(void) { //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); - window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + if (gCLIOpts.FullScreen) + configFullscreen = true; - Fullscreen = configFullscreen; - if (gCLIOpts.FullScreen == 1) - Fullscreen = true; - else if (gCLIOpts.FullScreen == 2) - Fullscreen = false; - - if (Fullscreen) { - window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } - - SDL_DisplayMode sdl_displaymode; - SDL_GetCurrentDisplayMode(0, &sdl_displaymode); - - const char* window_title = + const char* window_title = #ifndef USE_GLES "Super Mario 64 PC port (OpenGL)"; #else "Super Mario 64 PC port (OpenGL_ES2)"; #endif + Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + + window_fullscreen = configFullscreen; + if (configFullscreen) { - wnd = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - sdl_displaymode.w, sdl_displaymode.h, window_flags); + window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SDL_ShowCursor(SDL_DISABLE); } else { - wnd = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT, window_flags); SDL_ShowCursor(SDL_ENABLE); } - SDL_GL_CreateContext(wnd); - SDL_GL_SetSwapInterval(1); // We have a double buffered GL context, it makes no sense to want tearing. + wnd = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT, window_flags); + + ctx = SDL_GL_CreateContext(wnd); + SDL_GL_SetSwapInterval(2); + + // in case FULLSCREEN_DESKTOP set our size to god knows what + SDL_GetWindowSize(wnd, &window_width, &window_height); + + window_vsync = test_vsync(); + if (!window_vsync) + printf("Warning: VSync is not enabled or not working. Falling back to timer for synchronization\n"); for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; } - + for (size_t i = 0; i < sizeof(scancode_rmapping_extended) / sizeof(scancode_rmapping_extended[0]); i++) { inverted_scancode_table[scancode_rmapping_extended[i][0]] = inverted_scancode_table[scancode_rmapping_extended[i][1]] + 0x100; } - + for (size_t i = 0; i < sizeof(scancode_rmapping_nonextended) / sizeof(scancode_rmapping_nonextended[0]); i++) { inverted_scancode_table[scancode_rmapping_nonextended[i][0]] = inverted_scancode_table[scancode_rmapping_nonextended[i][1]]; inverted_scancode_table[scancode_rmapping_nonextended[i][1]] += 0x100; @@ -169,20 +196,13 @@ static void gfx_sdl_init(void) { } static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { - int t; - while (1) { - t = SDL_GetTicks(); + while (1) run_one_game_iter(); - t = SDL_GetTicks() - t; - - if (t < 1000 / 30) { - SDL_Delay ((1000 / 30) - t); - } - } } static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) { - SDL_GetWindowSize(wnd, width, height); + *width = window_width; + *height = window_height; } static int translate_scancode(int scancode) { @@ -221,12 +241,19 @@ static void gfx_sdl_handle_events(void) { gfx_sdl_onkeyup(event.key.keysym.scancode); break; #endif + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + window_width = event.window.data1; + window_height = event.window.data2; + } + break; case SDL_QUIT: - exit(0); + game_exit(); + break; } } // just check if the fullscreen value has changed and toggle fullscreen if it has - if (configFullscreen != cur_fullscreen) + if (configFullscreen != window_fullscreen) gfx_sdl_set_fullscreen(configFullscreen); } @@ -234,7 +261,21 @@ static bool gfx_sdl_start_frame(void) { return true; } +static void sync_framerate_with_timer(void) { + // Number of milliseconds a frame should take (30 fps) + const Uint32 FRAME_TIME = 1000 / FRAMERATE; + static Uint32 last_time; + + Uint32 elapsed = SDL_GetTicks() - last_time; + if (elapsed < FRAME_TIME) + SDL_Delay(FRAME_TIME - elapsed); + + last_time = SDL_GetTicks(); +} + static void gfx_sdl_swap_buffers_begin(void) { + if (!window_vsync) + sync_framerate_with_timer(); SDL_GL_SwapWindow(wnd); } diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index c198f210..c4162c32 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -18,6 +18,7 @@ #include "audio/audio_sdl.h" #include "audio/audio_null.h" +#include "pc_main.h" #include "cliopts.h" #include "configfile.h" #include "controller/controller_api.h" @@ -46,13 +47,11 @@ void dispatch_audio_sptask(struct SPTask *spTask) { void set_vblank_handler(s32 index, struct VblankHandler *handler, OSMesgQueue *queue, OSMesg *msg) { } -static uint8_t inited = 0; +static bool inited = false; #include "game/display.h" // for gGlobalTimer void send_display_list(struct SPTask *spTask) { - if (!inited) { - return; - } + if (!inited) return; gfx_run((Gfx *)spTask->task.t.data_ptr); } @@ -92,11 +91,19 @@ void audio_shutdown(void) { } } -void game_shutdown(void) { +void game_deinit(void) { configfile_save(gCLIOpts.ConfigFile);; controller_shutdown(); audio_shutdown(); gfx_shutdown(); + inited = false; +} + +void game_exit(void) { + game_deinit(); +#ifndef TARGET_WEB + exit(0); +#endif } #ifdef TARGET_WEB @@ -128,7 +135,8 @@ static void on_anim_frame(double time) { } } - request_anim_frame(on_anim_frame); + if (inited) // only continue if the init flag is still set + request_anim_frame(on_anim_frame); } #endif @@ -138,12 +146,7 @@ void main_func(void) { gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); configfile_load(gCLIOpts.ConfigFile); - atexit(game_shutdown); -#ifdef TARGET_WEB - emscripten_set_main_loop(em_main_loop, 0, 0); - request_anim_frame(on_anim_frame); -#endif wm_api = &gfx_sdl; rendering_api = &gfx_opengl_api; gfx_init(wm_api, rendering_api); @@ -159,16 +162,14 @@ void main_func(void) { sound_init(); thread5_game_loop(NULL); + + inited = true; + #ifdef TARGET_WEB - /*for (int i = 0; i < atoi(argv[1]); i++) { - game_loop_one_iteration(); - }*/ - inited = 1; + emscripten_set_main_loop(em_main_loop, 0, 0); + request_anim_frame(on_anim_frame); #else - inited = 1; - while (1) { - wm_api->main_loop(produce_one_frame); - } + wm_api->main_loop(produce_one_frame); #endif } diff --git a/src/pc/pc_main.h b/src/pc/pc_main.h new file mode 100644 index 00000000..00ab34be --- /dev/null +++ b/src/pc/pc_main.h @@ -0,0 +1,7 @@ +#ifndef _PC_MAIN_H +#define _PC_MAIN_H + +void game_deinit(void); +void game_exit(void); + +#endif // _PC_MAIN_H