final tweaks for 1.55 release
[libpicofe.git] / common / emu.c
index 1c7d1b7..167f2f0 100644 (file)
@@ -5,6 +5,7 @@
 \r
 #include <stdio.h>\r
 #include <stdlib.h>\r
+#include <stdarg.h>\r
 #ifndef NO_SYNC\r
 #include <unistd.h>\r
 #endif\r
@@ -24,6 +25,8 @@
 #include <zlib/zlib.h>\r
 \r
 \r
+#define STATUS_MSG_TIMEOUT 2000\r
+\r
 void *g_screen_ptr;\r
 \r
 #if !SCREEN_SIZE_FIXED\r
@@ -44,6 +47,8 @@ char rom_fname_reload[512] = { 0, };
 char rom_fname_loaded[512] = { 0, };\r
 int rom_loaded = 0;\r
 int reset_timing = 0;\r
+static unsigned int notice_msg_time;   /* when started showing */\r
+static char noticeMsg[40];\r
 \r
 unsigned char *movie_data = NULL;\r
 static int movie_size = 0;\r
@@ -75,9 +80,9 @@ static int try_rfn_cut(char *fname)
        return 0;\r
 }\r
 \r
-static void get_ext(char *file, char *ext)\r
+static void get_ext(const char *file, char *ext)\r
 {\r
-       char *p;\r
+       const char *p;\r
 \r
        p = file + strlen(file) - 4;\r
        if (p < file) p = file;\r
@@ -86,15 +91,32 @@ static void get_ext(char *file, char *ext)
        strlwr_(ext);\r
 }\r
 \r
-static const char *biosfiles_us[] = { "us_scd1_9210", "us_scd2_9306", "SegaCDBIOS9303" };\r
-static const char *biosfiles_eu[] = { "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303"   };\r
-static const char *biosfiles_jp[] = { "jp_mcd1_9112", "jp_mcd1_9111" };\r
+void emu_status_msg(const char *format, ...)\r
+{\r
+       va_list vl;\r
+       int ret;\r
+\r
+       va_start(vl, format);\r
+       ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);\r
+       va_end(vl);\r
+\r
+       /* be sure old text gets overwritten */\r
+       for (; ret < 28; ret++)\r
+               noticeMsg[ret] = ' ';\r
+       noticeMsg[ret] = 0;\r
+\r
+       notice_msg_time = plat_get_ticks_ms();\r
+}\r
+\r
+static const char * const biosfiles_us[] = { "us_scd1_9210", "us_scd2_9306", "SegaCDBIOS9303" };\r
+static const char * const biosfiles_eu[] = { "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303"   };\r
+static const char * const biosfiles_jp[] = { "jp_mcd1_9112", "jp_mcd1_9111" };\r
 \r
 static int find_bios(int region, char **bios_file)\r
 {\r
        static char bios_path[1024];\r
        int i, count;\r
-       const char **files;\r
+       const char * const *files;\r
        FILE *f = NULL;\r
 \r
        if (region == 4) { // US\r
@@ -155,12 +177,13 @@ static unsigned char id_header[0x100];
 \r
 /* checks if fname points to valid MegaCD image\r
  * if so, checks for suitable BIOS */\r
-int emu_cd_check(int *pregion, char *fname_in)\r
+static int emu_cd_check(int *pregion, const char *fname_in)\r
 {\r
+       const char *fname = fname_in;\r
        unsigned char buf[32];\r
        pm_file *cd_f;\r
        int region = 4; // 1: Japan, 4: US, 8: Europe\r
-       char ext[5], *fname = fname_in;\r
+       char ext[5];\r
        cue_track_type type = CT_UNKNOWN;\r
        cue_data_t *cue_data = NULL;\r
 \r
@@ -384,8 +407,8 @@ int emu_reload_rom(char *rom_fname)
                // valid CD image, check for BIOS..\r
 \r
                // we need to have config loaded at this point\r
-               ret = emu_read_config(1, 1);\r
-               if (!ret) emu_read_config(0, 1);\r
+               ret = emu_read_config(1, 0);\r
+               if (!ret) emu_read_config(0, 0);\r
                cfg_loaded = 1;\r
 \r
                if (PicoRegionOverride) {\r
@@ -442,8 +465,8 @@ int emu_reload_rom(char *rom_fname)
        if (!(PicoAHW & PAHW_MCD))\r
                memcpy(id_header, rom_data + 0x100, sizeof(id_header));\r
        if (!cfg_loaded) {\r
-               ret = emu_read_config(1, 1);\r
-               if (!ret) emu_read_config(0, 1);\r
+               ret = emu_read_config(1, 0);\r
+               if (!ret) emu_read_config(0, 0);\r
        }\r
 \r
        lprintf("PicoCartInsert(%p, %d);\n", rom_data, rom_size);\r
@@ -485,12 +508,12 @@ int emu_reload_rom(char *rom_fname)
                        // TODO: bits 6 & 5\r
                }\r
                movie_data[0x18+30] = 0;\r
-               plat_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
+               emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
        }\r
        else\r
        {\r
                PicoOpt &= ~POPT_DIS_VDP_FIFO;\r
-               plat_status_msg(Pico.m.pal ? "PAL SYSTEM / 50 FPS" : "NTSC SYSTEM / 60 FPS");\r
+               emu_status_msg(Pico.m.pal ? "PAL SYSTEM / 50 FPS" : "NTSC SYSTEM / 60 FPS");\r
        }\r
 \r
        // load SRAM for this ROM\r
@@ -509,6 +532,24 @@ fail:
        return 0;\r
 }\r
 \r
+int emu_swap_cd(const char *fname)\r
+{\r
+       cd_img_type cd_type;\r
+       int ret = -1;\r
+\r
+       cd_type = emu_cd_check(NULL, fname);\r
+       if (cd_type != CIT_NOT_CD)\r
+               ret = Insert_CD(fname, cd_type);\r
+       if (ret != 0) {\r
+               me_update_msg("Load failed, invalid CD image?");\r
+               return 0;\r
+       }\r
+\r
+       strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);\r
+       rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;\r
+       return 1;\r
+}\r
+\r
 static void romfname_ext(char *dst, const char *prefix, const char *ext)\r
 {\r
        char *p;\r
@@ -572,10 +613,11 @@ int emu_read_config(int game, int no_defaults)
        char cfg[512];\r
        int ret;\r
 \r
+       if (!no_defaults)\r
+               emu_set_defconfig();\r
+\r
        if (!game)\r
        {\r
-               if (!no_defaults)\r
-                       emu_set_defconfig();\r
                make_config_cfg(cfg);\r
                ret = config_readsect(cfg, NULL);\r
        }\r
@@ -602,7 +644,8 @@ int emu_read_config(int game, int no_defaults)
                        // read global config, and apply game_def.cfg on top\r
                        make_config_cfg(cfg);\r
                        config_readsect(cfg, NULL);\r
-                       ret = config_readsect("game_def.cfg", sect);\r
+                       emu_make_path(cfg, "game_def.cfg", sizeof(cfg));\r
+                       ret = config_readsect(cfg, sect);\r
                }\r
 \r
                if (ret == 0)\r
@@ -702,7 +745,7 @@ void update_movie(void)
        if (offs+3 > movie_size) {\r
                free(movie_data);\r
                movie_data = 0;\r
-               plat_status_msg("END OF MOVIE.");\r
+               emu_status_msg("END OF MOVIE.");\r
                lprintf("END OF MOVIE.\n");\r
        } else {\r
                // MXYZ SACB RLDU\r
@@ -824,7 +867,7 @@ int emu_save_load_game(int load, int sram)
        saveFname = emu_get_save_fname(load, sram, state_slot);\r
        if (saveFname == NULL) {\r
                if (!sram)\r
-                       plat_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
+                       emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
                return -1;\r
        }\r
 \r
@@ -910,10 +953,10 @@ int emu_save_load_game(int load, int sram)
                }\r
                else    ret = -1;\r
                if (!ret)\r
-                       plat_status_msg(load ? "GAME LOADED" : "GAME SAVED");\r
+                       emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");\r
                else\r
                {\r
-                       plat_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
+                       emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
                        ret = -1;\r
                }\r
 \r
@@ -935,7 +978,7 @@ void emu_set_fastforward(int set_on)
                currentConfig.EmuOpt &= ~4;\r
                currentConfig.EmuOpt |= 0x40000;\r
                is_on = 1;\r
-               plat_status_msg("FAST FORWARD");\r
+               emu_status_msg("FAST FORWARD");\r
        }\r
        else if (!set_on && is_on) {\r
                PsndOut = set_PsndOut;\r
@@ -948,7 +991,7 @@ void emu_set_fastforward(int set_on)
 \r
 static void emu_msg_tray_open(void)\r
 {\r
-       plat_status_msg("CD tray opened");\r
+       emu_status_msg("CD tray opened");\r
 }\r
 \r
 void emu_reset_game(void)\r
@@ -966,9 +1009,9 @@ void run_events_pico(unsigned int events)
                if (pico_inp_mode > 2)\r
                        pico_inp_mode = 0;\r
                switch (pico_inp_mode) {\r
-                       case 2: plat_status_msg("Input: Pen on Pad"); break;\r
-                       case 1: plat_status_msg("Input: Pen on Storyware"); break;\r
-                       case 0: plat_status_msg("Input: Joystick");\r
+                       case 2: emu_status_msg("Input: Pen on Pad"); break;\r
+                       case 1: emu_status_msg("Input: Pen on Storyware"); break;\r
+                       case 0: emu_status_msg("Input: Joystick");\r
                                PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;\r
                                break;\r
                }\r
@@ -977,13 +1020,13 @@ void run_events_pico(unsigned int events)
                PicoPicohw.page--;\r
                if (PicoPicohw.page < 0)\r
                        PicoPicohw.page = 0;\r
-               plat_status_msg("Page %i", PicoPicohw.page);\r
+               emu_status_msg("Page %i", PicoPicohw.page);\r
        }\r
        if (events & PEV_PICO_PNEXT) {\r
                PicoPicohw.page++;\r
                if (PicoPicohw.page > 6)\r
                        PicoPicohw.page = 6;\r
-               plat_status_msg("Page %i", PicoPicohw.page);\r
+               emu_status_msg("Page %i", PicoPicohw.page);\r
        }\r
 \r
        if (pico_inp_mode == 0)\r
@@ -1072,7 +1115,7 @@ static void run_events_ui(unsigned int which)
                        in_set_blocking(0);\r
                }\r
                if (do_it) {\r
-                       plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING GAME" : "SAVING GAME");\r
+                       plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");\r
                        PicoStateProgressCB = plat_status_msg_busy_next;\r
                        emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);\r
                        PicoStateProgressCB = NULL;\r
@@ -1094,7 +1137,7 @@ static void run_events_ui(unsigned int which)
                                state_slot = 0;\r
                }\r
 \r
-               plat_status_msg("SAVE SLOT %i [%s]", state_slot,\r
+               emu_status_msg("SAVE SLOT %i [%s]", state_slot,\r
                        emu_check_save_file(state_slot) ? "USED" : "FREE");\r
        }\r
        if (which & PEV_MENU)\r
@@ -1194,3 +1237,208 @@ void emu_finish(void)
        PicoExit();\r
 }\r
 \r
+static void skip_frame(int do_audio)\r
+{\r
+       PicoSkipFrame = do_audio ? 1 : 2;\r
+       PicoFrame();\r
+       PicoSkipFrame = 0;\r
+}\r
+\r
+/* our tick here is 1 us right now */\r
+#define ms_to_ticks(x) (unsigned int)(x * 1000)\r
+#define get_ticks() plat_get_ticks_us()\r
+\r
+void emu_loop(void)\r
+{\r
+       int pframes_done;               /* "period" frames, used for sync */\r
+       int frames_done, frames_shown;  /* actual frames for fps counter */\r
+       int oldmodes, target_fps, target_frametime;\r
+       unsigned int timestamp_base = 0, timestamp_fps;\r
+       char *notice_msg = NULL;\r
+       char fpsbuff[24];\r
+       int i;\r
+\r
+       fpsbuff[0] = 0;\r
+\r
+       /* make sure we are in correct mode */\r
+       oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;\r
+       Pico.m.dirtyPal = 1;\r
+\r
+       /* number of ticks per frame */\r
+       if (Pico.m.pal) {\r
+               target_fps = 50;\r
+               target_frametime = ms_to_ticks(1000) / 50;\r
+       } else {\r
+               target_fps = 60;\r
+               target_frametime = ms_to_ticks(1000) / 60 + 1;\r
+       }\r
+\r
+       // prepare CD buffer\r
+       if (PicoAHW & PAHW_MCD)\r
+               PicoCDBufferInit();\r
+\r
+       pemu_loop_prep();\r
+\r
+       timestamp_fps = get_ticks();\r
+       reset_timing = 1;\r
+\r
+       frames_done = frames_shown = pframes_done = 0;\r
+\r
+       plat_video_wait_vsync();\r
+\r
+       /* loop with resync every 1 sec. */\r
+       while (engineState == PGS_Running)\r
+       {\r
+               unsigned int timestamp;\r
+               int diff, diff_lim;\r
+               int modes;\r
+\r
+               timestamp = get_ticks();\r
+               if (reset_timing) {\r
+                       reset_timing = 0;\r
+                       timestamp_base = timestamp;\r
+                       pframes_done = 0;\r
+               }\r
+\r
+               // show notice_msg message?\r
+               if (notice_msg_time != 0)\r
+               {\r
+                       static int noticeMsgSum;\r
+                       if (timestamp - ms_to_ticks(notice_msg_time) > ms_to_ticks(STATUS_MSG_TIMEOUT)) {\r
+                               notice_msg_time = 0;\r
+                               plat_status_msg_clear();\r
+                               notice_msg = NULL;\r
+                       } else {\r
+                               int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];\r
+                               if (sum != noticeMsgSum) {\r
+                                       plat_status_msg_clear();\r
+                                       noticeMsgSum = sum;\r
+                               }\r
+                               notice_msg = noticeMsg;\r
+                       }\r
+               }\r
+\r
+               // check for mode changes\r
+               modes = ((Pico.video.reg[12]&1)<<2) | (Pico.video.reg[1]&8);\r
+               if (modes != oldmodes) {\r
+                       oldmodes = modes;\r
+                       pemu_video_mode_change(!(modes & 4), (modes & 8));\r
+               }\r
+\r
+               // second changed?\r
+               if (timestamp - timestamp_fps >= ms_to_ticks(1000))\r
+               {\r
+#ifdef BENCHMARK\r
+                       static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
+                       if (++bench == 10) {\r
+                               bench = 0;\r
+                               bench_fps_s = bench_fps;\r
+                               bf[bfp++ & 3] = bench_fps;\r
+                               bench_fps = 0;\r
+                       }\r
+                       bench_fps += frames_shown;\r
+                       sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
+#else\r
+                       if (currentConfig.EmuOpt & EOPT_SHOW_FPS) {\r
+                               sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);\r
+                               if (fpsbuff[5] == 0) { fpsbuff[5] = fpsbuff[6] = ' '; fpsbuff[7] = 0; }\r
+                       }\r
+#endif\r
+                       frames_shown = frames_done = 0;\r
+                       timestamp_fps += ms_to_ticks(1000);\r
+               }\r
+#ifdef PFRAMES\r
+               sprintf(fpsbuff, "%i", Pico.m.frame_count);\r
+#endif\r
+\r
+               if (timestamp - timestamp_base >= ms_to_ticks(1000))\r
+               {\r
+                       if ((currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) && currentConfig.Frameskip >= 0)\r
+                               pframes_done = 0;\r
+                       else {\r
+                               pframes_done -= target_fps;\r
+                               /* don't allow it to drift during heavy slowdowns */\r
+                               if (pframes_done < -5) {\r
+                                       reset_timing = 1;\r
+                                       continue;\r
+                               }\r
+                               if (pframes_done < -2)\r
+                                       pframes_done = -2;\r
+                       }\r
+                       timestamp_base += ms_to_ticks(1000);\r
+               }\r
+\r
+               diff = timestamp - timestamp_base;\r
+               diff_lim = (pframes_done + 1) * target_frametime;\r
+\r
+               if (currentConfig.Frameskip >= 0) // frameskip enabled\r
+               {\r
+                       for (i = 0; i < currentConfig.Frameskip; i++) {\r
+                               emu_update_input();\r
+                               skip_frame(1);\r
+                               pframes_done++; frames_done++;\r
+                               diff_lim += target_frametime;\r
+\r
+                               if (!(currentConfig.EmuOpt & EOPT_NO_FRMLIMIT)) {\r
+                                       timestamp = get_ticks();\r
+                                       diff = timestamp - timestamp_base;\r
+                                       if (diff < diff_lim) // we are too fast\r
+                                               plat_wait_till_us(timestamp_base + diff_lim);\r
+                               }\r
+                       }\r
+               }\r
+               else if (diff > diff_lim)\r
+               {\r
+                       /* no time left for this frame - skip */\r
+                       if (diff - diff_lim >= ms_to_ticks(200)) {\r
+                               /* if too much behind, reset instead */\r
+                               reset_timing = 1;\r
+                               continue;\r
+                       }\r
+                       emu_update_input();\r
+                       skip_frame(diff < diff_lim + target_frametime * 2);\r
+                       pframes_done++; frames_done++;\r
+                       continue;\r
+               }\r
+\r
+               emu_update_input();\r
+               PicoFrame();\r
+\r
+               /* frame limiter */\r
+               if (!reset_timing && !(currentConfig.EmuOpt & EOPT_NO_FRMLIMIT))\r
+               {\r
+                       timestamp = get_ticks();\r
+                       diff = timestamp - timestamp_base;\r
+\r
+                       // sleep or vsync if we are still too fast\r
+                       if (diff < diff_lim)\r
+                       {\r
+                               // we are too fast\r
+                               plat_wait_till_us(timestamp_base + diff_lim - target_frametime / 4);\r
+                               if (currentConfig.EmuOpt & EOPT_VSYNC)\r
+                                       plat_video_wait_vsync();\r
+                       }\r
+               }\r
+\r
+               pemu_update_display(fpsbuff, notice_msg);\r
+\r
+               pframes_done++; frames_done++; frames_shown++;\r
+       }\r
+\r
+       emu_set_fastforward(0);\r
+\r
+       // save SRAM\r
+       if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && SRam.changed) {\r
+               plat_status_msg_busy_first("Writing SRAM/BRAM...");\r
+               emu_save_load_game(0, 1);\r
+               SRam.changed = 0;\r
+       }\r
+\r
+       pemu_loop_end();\r
+\r
+       // pemu_loop_end() might want to do 1 frame for bg image,\r
+       // so free CD buffer here\r
+       if (PicoAHW & PAHW_MCD)\r
+               PicoCDBufferFree();\r
+}\r
+\r