From 860c6322c346c8e2ec2a4626e79f26d8b0d23777 Mon Sep 17 00:00:00 2001 From: notaz Date: Sun, 25 Feb 2007 19:20:45 +0000 Subject: [PATCH] savestate loader in menu git-svn-id: file:///home/notaz/opt/svn/PicoDrive@56 be3aeb3a-fb24-0410-a615-afba39da0efa --- Pico/Area.c | 9 +- Pico/Pico.h | 2 + Pico/PicoInt.h | 1 + Pico/cd/Area.c | 34 ++++++++ platform/gp2x/emu.c | 198 ++++++++++++++++++++++++++++++------------- platform/gp2x/emu.h | 4 + platform/gp2x/gp2x.c | 14 ++- platform/gp2x/gp2x.h | 1 + platform/gp2x/menu.c | 160 +++++++++++++++++++++++++++++++--- 9 files changed, 344 insertions(+), 79 deletions(-) diff --git a/Pico/Area.c b/Pico/Area.c index 23d9af13..e4f680b1 100644 --- a/Pico/Area.c +++ b/Pico/Area.c @@ -22,10 +22,11 @@ struct PicoArea { void *data; int len; char *name; }; // taking an address of fread or fwrite causes "application could't be started" error // on startup randomly depending on binary layout of executable file. -arearw *areaRead = (arearw *) 0; // fread; // read and write function pointers for -arearw *areaWrite = (arearw *) 0; // fwrite; // gzip save state ability -areaeof *areaEof = (areaeof *) 0; -areaseek *areaSeek = (areaseek *) 0; +arearw *areaRead = (arearw *) 0; // fread; // read and write function pointers for +arearw *areaWrite = (arearw *) 0; // fwrite; // gzip save state ability +areaeof *areaEof = (areaeof *) 0; +areaseek *areaSeek = (areaseek *) 0; +areaclose *areaClose = (areaclose *) 0; // Scan one variable and callback diff --git a/Pico/Pico.h b/Pico/Pico.h index 7341a4c9..0d8bcf0c 100644 --- a/Pico/Pico.h +++ b/Pico/Pico.h @@ -50,12 +50,14 @@ int PicoFrameMCD(void); typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file); typedef size_t (areaeof)(void *file); typedef int (areaseek)(void *file, long offset, int whence); +typedef int (areaclose)(void *file); // Save or load the state from PmovFile: int PmovState(int PmovAction, void *PmovFile); // &1=for reading &2=for writing &4=volatile &8=non-volatile extern arearw *areaRead; // external read and write function pointers for extern arearw *areaWrite; // gzip save state ability extern areaeof *areaEof; extern areaseek *areaSeek; +extern areaclose *areaClose; extern void (*PicoStateProgressCB)(const char *str); // Cart.c diff --git a/Pico/PicoInt.h b/Pico/PicoInt.h index 0cbeb803..0b067567 100644 --- a/Pico/PicoInt.h +++ b/Pico/PicoInt.h @@ -236,6 +236,7 @@ int PicoAreaUnpackCpu(unsigned char *cpu, int is_sub); // cd/Area.c int PicoCdSaveState(void *file); int PicoCdLoadState(void *file); +int PicoCdLoadStateGfx(void *file); // Draw.c int PicoLine(int scan); diff --git a/Pico/cd/Area.c b/Pico/cd/Area.c index 073033f0..51da6160 100644 --- a/Pico/cd/Area.c +++ b/Pico/cd/Area.c @@ -250,3 +250,37 @@ int PicoCdLoadState(void *file) return 0; } + +int PicoCdLoadStateGfx(void *file) +{ + int ver, len, found = 0; + char buff[8]; + + g_read_offs = 0; + CHECKED_READ(8, buff); + if (strncmp(buff, "PicoSMCD", 8)) R_ERROR_RETURN("bad header"); + CHECKED_READ(4, &ver); + + while (!areaEof(file) && found < 4) + { + CHECKED_READ(1, buff); + CHECKED_READ(4, &len); + if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length"); + if (buff[0] > CHUNK_FM && !(PicoMCD & 1)) R_ERROR_RETURN("cd chunk in non CD state?"); + + switch (buff[0]) + { + case CHUNK_VRAM: CHECKED_READ_BUFF(Pico.vram); found++; break; + case CHUNK_CRAM: CHECKED_READ_BUFF(Pico.cram); found++; break; + case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); found++; break; + case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break; + default: + areaSeek(file, len, SEEK_CUR); + break; + } + } + + return 0; +} + + diff --git a/platform/gp2x/emu.c b/platform/gp2x/emu.c index 1c81c870..cdb69b75 100644 --- a/platform/gp2x/emu.c +++ b/platform/gp2x/emu.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -394,22 +396,36 @@ void emu_Init(void) printf("framebuff == 0\n"); } + // make dirs for saves, cfgs, etc. + mkdir("mds", 0777); + mkdir("srm", 0777); + mkdir("brm", 0777); + mkdir("cfg", 0777); + PicoInit(); // logf = fopen("log.txt", "w"); } -static void romfname_ext(char *dst, char *ext) +static void romfname_ext(char *dst, const char *prefix, const char *ext) { char *p; + int prefix_len = 0; // make save filename for (p = romFileName+strlen(romFileName)-1; p >= romFileName && *p != '/'; p--); p++; - strncpy(dst, p, 511); + *dst = 0; + if (prefix) { + strcpy(dst, prefix); + prefix_len = strlen(prefix); + } + strncpy(dst + prefix_len, p, 511-prefix_len); dst[511-8] = 0; - if(dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0; - strcat(dst, ext); + if (dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0; + if (ext) strcat(dst, ext); + + printf("romfname_ext: %s\n", dst); } @@ -479,7 +495,10 @@ int emu_ReadConfig(int game) strncpy(cfg, PicoConfigFile, 511); cfg[511] = 0; } else { - romfname_ext(cfg, ".pbcfg"); + romfname_ext(cfg, "cfg/", ".pbcfg"); + f = fopen(cfg, "rb"); + if (!f) romfname_ext(cfg, NULL, ".pbcfg"); + else fclose(f); } printf("emu_ReadConfig: %s ", cfg); @@ -522,7 +541,7 @@ int emu_WriteConfig(int game) strncpy(cfg, PicoConfigFile, 511); cfg[511] = 0; } else { - romfname_ext(cfg, ".pbcfg"); + romfname_ext(cfg, "cfg", ".pbcfg"); } printf("emu_WriteConfig: %s ", cfg); @@ -745,25 +764,6 @@ static void vidResetMode(void) } -static int check_save_file(void) -{ - char saveFname[512]; - char ext[16]; - FILE *f; - - ext[0] = 0; - if(state_slot > 0 && state_slot < 10) sprintf(ext, ".%i", state_slot); - strcat(ext, ".mds"); - if(currentConfig.EmuOpt & 8) strcat(ext, ".gz"); - - romfname_ext(saveFname, ext); - if ((f = fopen(saveFname, "rb"))) { - fclose(f); - return 1; - } - return 0; -} - static void emu_state_cb(const char *str) { clearArea(0); @@ -774,7 +774,7 @@ static void RunEvents(unsigned int which) { if(which & 0x1800) { // save or load (but not both) int do_it = 1; - if (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200) && check_save_file()) { + if (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200) && emu_check_save_file(state_slot)) { unsigned long keys; blit("", "OVERWRITE SAVE? (Y=yes, X=no)"); while( !((keys = gp2x_joystick_read(1)) & (GP2X_X|GP2X_Y)) ) @@ -817,7 +817,7 @@ static void RunEvents(unsigned int which) state_slot += 1; if(state_slot > 9) state_slot = 0; } - sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, check_save_file() ? "USED" : "FREE"); + sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, emu_check_save_file(state_slot) ? "USED" : "FREE"); gettimeofday(¬iceMsgTime, 0); } if(which & 0x0080) { @@ -964,6 +964,24 @@ static void SkipFrame(int do_sound) } +void emu_forced_frame(void) +{ + int po_old = PicoOpt; + + PicoOpt |= 0x10; + PicoFrameFull(); + PicoOpt = po_old; + + if (!(Pico.video.reg[12]&1)) { + vidCpyM2 = vidCpyM2_40col; + clearArea(1); + } else vidCpyM2 = vidCpyM2_32col; + + vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8); + vidConvCpyRGB32(localPal, Pico.cram, 0x40); + gp2x_video_setpalette(localPal, 0x40); +} + static void simpleWait(int thissec, int lim_time) { struct timeval tval; @@ -1239,18 +1257,11 @@ if (Pico.m.frame_count == 31563) { } // if in 16bit mode, generate 8it image for menu background - if (!(PicoOpt&0x10) && (currentConfig.EmuOpt&0x80)) { - PicoOpt |= 0x10; - if (!(Pico.video.reg[12]&1)) clearArea(1); - PicoFrameFull(); - vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8); - vidConvCpyRGB32(localPal, Pico.cram, 0x40); - gp2x_video_setpalette(localPal, 0x40); - PicoOpt &= ~0x10; - } + if (!(PicoOpt&0x10) && (currentConfig.EmuOpt&0x80)) + emu_forced_frame(); // for menu bg - gp2x_memcpy_all_buffers(gp2x_screen, 0, 320*240*2); + gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2); } @@ -1272,18 +1283,95 @@ size_t gzWrite2(void *p, size_t _size, size_t _n, void *file) return gzwrite(file, p, _n); } +static int try_ropen_file(const char *fname) +{ + FILE *f; + + f = fopen(fname, "rb"); + if (f) { + fclose(f); + return 1; + } + return 0; +} + +char *emu_GetSaveFName(int load, int is_sram, int slot) +{ + static char saveFname[512]; + char ext[16]; + + if (is_sram) + { + romfname_ext(saveFname, (PicoMCD&1) ? "brm/" : "srm/", (PicoMCD&1) ? ".brm" : ".srm"); + if (load) { + if (try_ropen_file(saveFname)) return saveFname; + // try in current dir.. + romfname_ext(saveFname, NULL, (PicoMCD&1) ? ".brm" : ".srm"); + if (try_ropen_file(saveFname)) return saveFname; + return NULL; // give up + } + } + else + { + ext[0] = 0; + if(slot > 0 && slot < 10) sprintf(ext, ".%i", slot); + strcat(ext, (currentConfig.EmuOpt & 8) ? ".mds.gz" : ".mds"); + + romfname_ext(saveFname, "mds/", ext); + if (load) { + if (try_ropen_file(saveFname)) return saveFname; + romfname_ext(saveFname, NULL, ext); + if (try_ropen_file(saveFname)) return saveFname; + if (currentConfig.EmuOpt & 8) { + ext[0] = 0; + if(slot > 0 && slot < 10) sprintf(ext, ".%i", slot); + strcat(ext, ".mds"); + + romfname_ext(saveFname, "mds/", ext); + if (try_ropen_file(saveFname)) return saveFname; + romfname_ext(saveFname, NULL, ext); + if (try_ropen_file(saveFname)) return saveFname; + } + return NULL; + } + } + + return saveFname; +} + +int emu_check_save_file(int slot) +{ + return emu_GetSaveFName(1, 0, slot) ? 1 : 0; +} + +void emu_set_save_cbs(int gz) +{ + if (gz) { + areaRead = gzRead2; + areaWrite = gzWrite2; + areaEof = (areaeof *) gzeof; + areaSeek = (areaseek *) gzseek; + areaClose = (areaclose *) gzclose; + } else { + areaRead = (arearw *) fread; + areaWrite = (arearw *) fwrite; + areaEof = (areaeof *) feof; + areaSeek = (areaseek *) fseek; + areaClose = (areaclose *) fclose; + } +} int emu_SaveLoadGame(int load, int sram) { int ret = 0; - char saveFname[512]; + char *saveFname; // make save filename - romfname_ext(saveFname, ""); - if(sram) strcat(saveFname, (PicoMCD&1) ? ".brm" : ".srm"); - else { - if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot); - strcat(saveFname, ".mds"); + saveFname = emu_GetSaveFName(load, sram, state_slot); + if (saveFname == NULL) { + strcpy(noticeMsg, load ? "LOAD FAILED (missing file)" : "SAVE FAILED "); + gettimeofday(¬iceMsgTime, 0); + return -1; } printf("saveLoad (%i, %i): %s\n", load, sram, saveFname); @@ -1326,31 +1414,21 @@ int emu_SaveLoadGame(int load, int sram) else { void *PmovFile = NULL; - // try gzip first - if(currentConfig.EmuOpt & 8) { - strcat(saveFname, ".gz"); + if (strcmp(saveFname + strlen(saveFname) - 3, ".gz") == 0) { if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) { - areaRead = gzRead2; - areaWrite = gzWrite2; - areaEof = (areaeof *) gzeof; - areaSeek = (areaseek *) gzseek; + emu_set_save_cbs(1); if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY); - } else - saveFname[strlen(saveFname)-3] = 0; + } } - if(!PmovFile) { // gzip failed or was disabled + else + { if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) { - areaRead = (arearw *) fread; - areaWrite = (arearw *) fwrite; - areaEof = (areaeof *) feof; - areaSeek = (areaseek *) fseek; + emu_set_save_cbs(0); } } if(PmovFile) { ret = PmovState(load ? 6 : 5, PmovFile); - if(areaRead == gzRead2) - gzclose(PmovFile); - else fclose ((FILE *) PmovFile); + areaClose(PmovFile); PmovFile = 0; if (!load) sync(); else Pico.m.dirtyPal=1; diff --git a/platform/gp2x/emu.h b/platform/gp2x/emu.h index 674929b0..42d732bd 100644 --- a/platform/gp2x/emu.h +++ b/platform/gp2x/emu.h @@ -46,5 +46,9 @@ void emu_Loop(void); void emu_ResetGame(void); int emu_ReadConfig(int game); int emu_WriteConfig(int game); +char *emu_GetSaveFName(int load, int is_sram, int slot); +int emu_check_save_file(int slot); +void emu_set_save_cbs(int gz); +void emu_forced_frame(void); int find_bios(int region, char **bios_file); diff --git a/platform/gp2x/gp2x.c b/platform/gp2x/gp2x.c index 552318c7..28c0a938 100644 --- a/platform/gp2x/gp2x.c +++ b/platform/gp2x/gp2x.c @@ -145,12 +145,18 @@ void gp2x_video_wait_vsync(void) } +void gp2x_memcpy_buffers(int buffers, void *data, int offset, int len) +{ + if (buffers & (1<<0)) memcpy((char *)gp2x_screens[0] + offset, data, len); + if (buffers & (1<<1)) memcpy((char *)gp2x_screens[1] + offset, data, len); + if (buffers & (1<<2)) memcpy((char *)gp2x_screens[2] + offset, data, len); + if (buffers & (1<<3)) memcpy((char *)gp2x_screens[3] + offset, data, len); +} + + void gp2x_memcpy_all_buffers(void *data, int offset, int len) { - memcpy((char *)gp2x_screens[0] + offset, data, len); - memcpy((char *)gp2x_screens[1] + offset, data, len); - memcpy((char *)gp2x_screens[2] + offset, data, len); - memcpy((char *)gp2x_screens[3] + offset, data, len); + gp2x_memcpy_buffers(0xf, data, offset, len); } diff --git a/platform/gp2x/gp2x.h b/platform/gp2x/gp2x.h index bddfc29c..9883b22a 100644 --- a/platform/gp2x/gp2x.h +++ b/platform/gp2x/gp2x.h @@ -14,6 +14,7 @@ void gp2x_video_changemode2(int bpp); void gp2x_video_setpalette(int *pal, int len); void gp2x_video_RGB_setscaling(int W, int H); void gp2x_video_wait_vsync(void); +void gp2x_memcpy_buffers(int buffers, void *data, int offset, int len); void gp2x_memcpy_all_buffers(void *data, int offset, int len); void gp2x_memset_all_buffers(int offset, int byte, int len); void gp2x_pd_clone_buffer2(void); diff --git a/platform/gp2x/menu.c b/platform/gp2x/menu.c index 9ffbd40c..9a2914a7 100644 --- a/platform/gp2x/menu.c +++ b/platform/gp2x/menu.c @@ -18,6 +18,7 @@ #include "version.h" #include "Pico/PicoInt.h" +#include "zlib/zlib.h" #ifndef _DIRENT_HAVE_D_TYPE #error "need d_type for file browser @@ -390,6 +391,139 @@ static char *romsel_loop(char *curr_path) return ret; } +// ------------ savestate loader ------------ + +static void menu_prepare_bg(void); + +static int state_slot_flags = 0; + +static void state_check_slots(void) +{ + int slot; + + state_slot_flags = 0; + + for (slot = 0; slot < 10; slot++) + { + if (emu_check_save_file(slot)) + { + state_slot_flags |= 1 << slot; + } + } +} + +static void draw_savestate_bg(int slot) +{ + struct PicoVideo tmp_pv; + unsigned short tmp_cram[0x40]; + unsigned short tmp_vsram[0x40]; + void *tmp_vram, *file; + char *fname; + + fname = emu_GetSaveFName(1, 0, slot); + if (!fname) return; + + tmp_vram = malloc(sizeof(Pico.vram)); + if (tmp_vram == NULL) return; + + memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram)); + memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram)); + memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram)); + memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video)); + + if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) { + file = gzopen(fname, "rb"); + emu_set_save_cbs(1); + } else { + file = fopen(fname, "rb"); + emu_set_save_cbs(0); + } + + if (file) { + if (PicoMCD & 1) { + PicoCdLoadStateGfx(file); + } else { + areaSeek(file, 0x10020, SEEK_SET); // skip header and RAM in state file + areaRead(Pico.vram, 1, sizeof(Pico.vram), file); + areaSeek(file, 0x2000, SEEK_CUR); + areaRead(Pico.cram, 1, sizeof(Pico.cram), file); + areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file); + areaSeek(file, 0x221a0, SEEK_SET); + areaRead(&Pico.video, 1, sizeof(Pico.video), file); + } + areaClose(file); + } + + emu_forced_frame(); + gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2); + menu_prepare_bg(); + + memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram)); + memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram)); + memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram)); + memcpy(&Pico.video, &tmp_pv, sizeof(Pico.video)); + free(tmp_vram); +} + +static void draw_savestate_menu(int menu_sel, int is_loading) +{ + int tl_x = 25, tl_y = 60, y, i; + + if (state_slot_flags & (1 << menu_sel)) + draw_savestate_bg(menu_sel); + gp2x_pd_clone_buffer2(); + + gp2x_text_out8(tl_x, 30, is_loading ? "Load state" : "Save state"); + + /* draw all 10 slots */ + y = tl_y; + for (i = 0; i < 10; i++, y+=10) + { + gp2x_text_out8(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free"); + } + gp2x_text_out8(tl_x, y, "back"); + + // draw cursor + gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + + gp2x_video_flip2(); +} + +static int savestate_menu_loop(int is_loading) +{ + int menu_sel = 10, menu_sel_max = 10; + unsigned long inp = 0; + + state_check_slots(); + + for(;;) + { + draw_savestate_menu(menu_sel, is_loading); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X); + if(inp & GP2X_UP ) { + do { + menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; + } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading); + } + if(inp & GP2X_DOWN) { + do { + menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; + } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading); + } + if(inp & GP2X_B) { // save/load + if (menu_sel < 10) { + state_slot = menu_sel; + if (emu_SaveLoadGame(is_loading, 0)) { + strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed"); + return 1; + } + return 0; + } else return 1; + } + if(inp & GP2X_X) return 1; + } +} + // -------------- key config -------------- static char *usb_joy_key_name(int joy, int num) @@ -1018,20 +1152,16 @@ static void menu_loop_root(void) break; case 1: // save state if (rom_data) { - if(emu_SaveLoadGame(0, 0)) { - strcpy(menuErrorMsg, "save failed"); + if(savestate_menu_loop(0)) continue; - } engineState = PGS_Running; return; } break; case 2: // load state if (rom_data) { - if(emu_SaveLoadGame(1, 0)) { - strcpy(menuErrorMsg, "load failed"); + if(savestate_menu_loop(1)) continue; - } engineState = PGS_Running; return; } @@ -1074,21 +1204,29 @@ static void menu_loop_root(void) } -static void menu_gfx_prepare(void) +static void menu_prepare_bg(void) { extern int localPal[0x100]; - int i; + int c, i; // don't clear old palette just for fun (but make it dark) - for (i = 0x100-1; i >= 0; i--) - localPal[i] = (localPal[i] >> 2) & 0x003f3f3f; + for (i = 0x100-1; i >= 0; i--) { + c = localPal[i]; + localPal[i] = ((c >> 1) & 0x007f7f7f) - ((c >> 3) & 0x001f1f1f); + } localPal[0xe0] = 0x00000000; // reserved pixels for OSD localPal[0xf0] = 0x00ffffff; + gp2x_video_setpalette(localPal, 0x100); +} + +static void menu_gfx_prepare(void) +{ + menu_prepare_bg(); + // switch to 8bpp gp2x_video_changemode2(8); gp2x_video_RGB_setscaling(320, 240); - gp2x_video_setpalette(localPal, 0x100); gp2x_video_flip2(); } -- 2.39.5