diff options
author | Michiel Van Der Kolk <not.valid@email.address> | 2005-03-04 09:30:27 +0000 |
---|---|---|
committer | Michiel Van Der Kolk <not.valid@email.address> | 2005-03-04 09:30:27 +0000 |
commit | 254100adf4a7118997b8c3fd583ee9909f73c48d (patch) | |
tree | 73a776564cce175d9b004d9e8509938eb19acf09 /apps/plugins/rockboy | |
parent | db44b294f49ab5705c5254d7a202d79dd342aa59 (diff) |
load/savestate support added to the menu, contributed by pabs
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6129 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/rockboy')
-rw-r--r-- | apps/plugins/rockboy/menu.c | 261 | ||||
-rw-r--r-- | apps/plugins/rockboy/rockmacros.h | 2 | ||||
-rw-r--r-- | apps/plugins/rockboy/save.c | 24 |
3 files changed, 243 insertions, 44 deletions
diff --git a/apps/plugins/rockboy/menu.c b/apps/plugins/rockboy/menu.c index b43e33bedf..2334f0d17e 100644 --- a/apps/plugins/rockboy/menu.c +++ b/apps/plugins/rockboy/menu.c @@ -8,22 +8,28 @@ #include "string.h" #include "button.h" #include "rockmacros.h" +#include "mem.h" /* load/save state function declarations */ static void do_slot_menu(bool is_load); +static void do_opt_menu(void); +static void munge_name(char *buf, size_t bufsiz); + +/* directory ROM save slots belong in */ +#define STATE_DIR "/.rockbox/rockboy" #define MENU_CANCEL (-1) static int do_menu(char *title, char **items, size_t num_items, int sel_item); /* main menu items */ -#define MAIN_MENU_TITLE "RockBoy Menu" +#define MAIN_MENU_TITLE "Rockboy" typedef enum { - ITEM_BACK, - ITEM_LOAD, - ITEM_SAVE, - ITEM_OPT, - ITEM_QUIT, - ITEM_LAST + MM_ITEM_BACK, + MM_ITEM_LOAD, + MM_ITEM_SAVE, + MM_ITEM_OPT, + MM_ITEM_QUIT, + MM_ITEM_LAST } MainMenuItem; /* strings for the main menu */ @@ -36,20 +42,37 @@ static const char *main_menu[] = { }; typedef enum { - ITEM_SLOT1, - ITEM_SLOT2, - ITEM_SLOT3, - ITEM_SLOT4, - ITEM_SLOT5 + SM_ITEM_SLOT1, + SM_ITEM_SLOT2, + SM_ITEM_SLOT3, + SM_ITEM_SLOT4, + SM_ITEM_SLOT5, + SM_ITEM_FILE, + SM_ITEM_BACK, + SM_ITEM_LAST } SlotMenuItem; -/* this is evil, but we snprintf() into it later :( */ +/* this semi-evil, but we snprintf() into these strings later + * Note: if you want more save slots, just add more lines + * to this array */ static const char *slot_menu[] = { "1. ", "2. ", "3. ", "4. ", - "5. " + "5. ", + "Save to File... ", + "Previous Menu... " +}; + +#define OPT_MENU_TITLE "Options" +typedef enum { + OM_ITEM_BACK, + OM_MENU_LAST +} OptMenuItem; + +static const char *opt_menu[] = { + "Previous Menu..." }; /* @@ -79,17 +102,20 @@ int do_user_menu(void) { /* handle selected menu item */ switch (mi) { - case ITEM_QUIT: + case MM_ITEM_QUIT: ret = USER_MENU_QUIT; case MENU_CANCEL: - case ITEM_BACK: + case MM_ITEM_BACK: done = true; break; - case ITEM_LOAD: - do_slot_menu(1); + case MM_ITEM_LOAD: + do_slot_menu(true); + break; + case MM_ITEM_SAVE: + do_slot_menu(false); break; - case ITEM_SAVE: - do_slot_menu(0); + case MM_ITEM_OPT: + do_opt_menu(); break; } } @@ -99,26 +125,158 @@ int do_user_menu(void) { } /* - * do_load_menu - prompt the user for a memory slot + * munge_name - munge a string into a filesystem-safe name + */ +static void munge_name(char *buf, const size_t bufsiz) { + unsigned int i, max; + + /* check strlen */ + max = strlen(buf); + max = (max < bufsiz) ? max : bufsiz; + + /* iterate over characters and munge them (if necessary) */ + for (i = 0; i < max; i++) + if (!isalnum(buf[i])) + buf[i] = '_'; +} + +/* + * build_slot_path - build a path to an slot state file for this rom + * + * Note: uses rom.name. Is there a safer way of doing this? Like a ROM + * checksum or something like that? + */ +static void build_slot_path(char *buf, size_t bufsiz, size_t slot_id) { + char name_buf[40]; + + /* munge state file name */ + strncpy(name_buf, rom.name, sizeof(name_buf)); + name_buf[16] = '\0'; + munge_name(name_buf, strlen(name_buf)); + + /* glom the whole mess together */ + snprintf(buf, bufsiz, "%s/%s-%d.rbs", STATE_DIR, name_buf, slot_id + 1); +} + +/* + * do_file - load or save game data in the given file * - * TOOD + * Returns true on success and false on failure. + * + * @desc is a brief user-provided description (<20 bytes) of the state. + * If no description is provided, set @desc to NULL. + * + */ +static bool do_file(char *path, char *desc, bool is_load) { + char buf[200], desc_buf[20]; + int fd, file_mode; + + /* set file mode */ + file_mode = is_load ? O_RDONLY : (O_WRONLY | O_CREAT); + + /* attempt to open file descriptor here */ + if ((fd = open(path, file_mode)) <= 0) + return false; + + /* load/save state */ + if (is_load) { + /* load description */ + read(fd, desc_buf, 20); + + /* load state */ + loadstate(fd); + + /* print out a status message so the user knows the state loaded */ + snprintf(buf, sizeof(buf), "Loaded state from \"%s\"", path); + rb->splash(HZ * 1, true, buf); + } else { + /* build description buffer */ + memset(desc_buf, 0, sizeof(desc_buf)); + if (desc) + strncpy(desc_buf, desc, sizeof(desc_buf)); + + /* save state */ + write(fd, desc_buf, 20); + savestate(fd); + } + + /* close file descriptor */ + close(fd); + + /* return true (for success) */ + return true; +} + +/* + * do_slot - load or save game data in the given slot * + * Returns true on success and false on failure. + */ +static bool do_slot(size_t slot_id, bool is_load) { + char path_buf[256], desc_buf[20]; + + /* build slot filename, clear desc buf */ + build_slot_path(path_buf, sizeof(path_buf), slot_id); + memset(desc_buf, 0, sizeof(desc_buf)); + + /* if we're saving to a slot, then get a brief description */ + if (!is_load) { + if (rb->kbd_input(desc_buf, sizeof(desc_buf)) || !strlen(desc_buf)) { + memset(desc_buf, 0, sizeof(desc_buf)); + strncpy(desc_buf, "Untitled", sizeof(desc_buf)); + } + } + + /* load/save file */ + return do_file(path_buf, desc_buf, is_load); +} + +/* + * get information on the given slot + */ +static void slot_info(char *info_buf, size_t info_bufsiz, size_t slot_id) { + char buf[256]; + int fd; + + /* get slot file path */ + build_slot_path(buf, sizeof(buf), slot_id); + + /* attempt to open slot */ + if ((fd = open(buf, O_RDONLY)) >= 0) { + /* this slot has a some data in it, read it */ + if (read(fd, buf, 20) > 0) { + buf[20] = '\0'; + snprintf(info_buf, info_bufsiz, "%2d. %s", slot_id + 1, buf); + } else { + snprintf(info_buf, info_bufsiz, "%2d. ERROR", slot_id + 1); + } + close(fd); + } else { + /* if we couldn't open the file, then the slot is empty */ + snprintf(info_buf, info_bufsiz, "%2d.", slot_id + 1); + } +} + +/* + * do_slot_menu - prompt the user for a load/save memory slot */ static void do_slot_menu(bool is_load) { int i, mi, ret, num_items; bool done = false; - char *title; + char *title, buf[256]; /* set defaults */ ret = 0; /* return value */ mi = 0; /* initial menu selection */ num_items = sizeof(slot_menu) / sizeof(char*); - /* create menu items */ - for (i = 0; i < num_items; i++) { - /* TODO: get slot info here */ - snprintf((char*) slot_menu[i], sizeof(slot_menu[i]), "%2d. %s", i + 1, "<empty>"); - } + /* create menu items (the last two are file and previous menu, + * so don't populate those) */ + for (i = 0; i < num_items - 2; i++) + slot_info((char*) slot_menu[i], 20, i); + + /* set text of file item */ + snprintf((char*) slot_menu[SM_ITEM_FILE], 20, "%s File...", is_load ? "Load from" : "Save to"); /* set menu title */ title = is_load ? "Load State" : "Save State"; @@ -129,26 +287,55 @@ static void do_slot_menu(bool is_load) { mi = do_menu(title, (char**) slot_menu, num_items, mi); /* handle selected menu item */ - if (mi == MENU_CANCEL) { - done = true; - break; - } else { - if (is_load) { - /* TODO: load slot here */ + done = true; + if (mi != MENU_CANCEL && mi != SM_ITEM_BACK) { + if (mi == SM_ITEM_FILE) { + char rom_name_buf[40]; + + /* munge rom name to valid filename */ + strncpy(rom_name_buf, rom.name, 16); + munge_name(rom_name_buf, sizeof(rom_name_buf)); + + /* create default filename */ + snprintf(buf, sizeof(buf), "/%s.rbs", rom_name_buf); + + /* prompt for output filename, save to file */ + if (!rb->kbd_input(buf, sizeof(buf))) + done = do_file(buf, NULL, is_load); } else { - /* TODO: save slot here */ + done = do_slot(mi, is_load); } + + /* if we couldn't save the state file, then print out an + * error message */ + if (!is_load && !done) + rb->splash(HZ * 2, true, "Couldn't save state file."); } } } +static void do_opt_menu(void) { + int mi, num_items; + bool done = false; + + /* set a couple of defaults */ + num_items = sizeof(opt_menu) / sizeof(char*); + mi = 0; + + while (!done) { + mi = do_menu(OPT_MENU_TITLE, (char**) opt_menu, num_items, mi); + if (mi == MENU_CANCEL || mi == OM_ITEM_BACK) + done = true; + } +} + /*********************************************************************/ /* MENU FUNCTIONS */ /*********************************************************************/ /* at some point i'll make this a generic menu interface, but for now, * these defines will suffice */ #define MENU_X 10 -#define MENU_Y 10 +#define MENU_Y 8 #define MENU_WIDTH (LCD_WIDTH - 2 * MENU_X) #define MENU_HEIGHT (LCD_HEIGHT - 2 * MENU_Y) #define MENU_RECT MENU_X, MENU_Y, MENU_WIDTH, MENU_HEIGHT @@ -201,7 +388,7 @@ static void draw_menu(char *title, char **items, size_t num_items) { int x, y, w, h, by; /* set to default? font */ - /* rb->lcd_setfont(0); */ + rb->lcd_setfont(0); /* draw the outline */ rb->lcd_fillrect(SHADOW_RECT); diff --git a/apps/plugins/rockboy/rockmacros.h b/apps/plugins/rockboy/rockmacros.h index b14ba63783..9f8cdae59f 100644 --- a/apps/plugins/rockboy/rockmacros.h +++ b/apps/plugins/rockboy/rockmacros.h @@ -44,6 +44,8 @@ void pcm_init(void); void doevents(void); void ev_poll(void); int do_user_menu(void); +void loadstate(int fd); +void savestate(int fd); #define USER_MENU_QUIT -2 diff --git a/apps/plugins/rockboy/save.c b/apps/plugins/rockboy/save.c index 11fc3507c9..dc94ce69ff 100644 --- a/apps/plugins/rockboy/save.c +++ b/apps/plugins/rockboy/save.c @@ -169,9 +169,12 @@ void loadstate(int fd) int irl = hw.cgb ? 8 : 2; int vrl = hw.cgb ? 4 : 2; int srl = mbc.ramsize << 1; + size_t base_offset; ver = hramofs = hiofs = palofs = oamofs = wavofs = 0; + base_offset = lseek(fd, 0, SEEK_CUR); + read(fd,buf, 4096); for (j = 0; header[j][0]; j++) @@ -207,14 +210,18 @@ void loadstate(int fd) if (wavofs) memcpy(snd.wave, buf+wavofs, sizeof snd.wave); else memcpy(snd.wave, ram.hi+0x30, 16); /* patch data from older files */ - lseek(fd, iramblock<<12, SEEK_SET); + lseek(fd, base_offset + (iramblock << 12), SEEK_SET); read(fd,ram.ibank, 4096*irl); - lseek(fd, vramblock<<12, SEEK_SET); + lseek(fd, base_offset + (vramblock << 12), SEEK_SET); read(fd,lcd.vbank, 4096*vrl); - lseek(fd, sramblock<<12, SEEK_SET); + lseek(fd, base_offset + (sramblock << 12), SEEK_SET); read(fd,ram.sbank, 4096*srl); + vram_dirty(); + pal_dirty(); + sound_dirty(); + mem_updatemap(); } void savestate(int fd) @@ -226,6 +233,7 @@ void savestate(int fd) int irl = hw.cgb ? 8 : 2; int vrl = hw.cgb ? 4 : 2; int srl = mbc.ramsize << 1; + size_t base_offset; ver = 0x105; iramblock = 1; @@ -261,16 +269,18 @@ void savestate(int fd) memcpy(buf+oamofs, lcd.oam.mem, sizeof lcd.oam); memcpy(buf+wavofs, snd.wave, sizeof snd.wave); - lseek(fd, 0, SEEK_SET); + /* calculate base offset for output file */ + /* (we'll seek relative to that from now on) */ + base_offset = lseek(fd, 0, SEEK_CUR); write(fd,buf, 4096); - lseek(fd, iramblock<<12, SEEK_SET); + lseek(fd, base_offset + (iramblock << 12), SEEK_SET); write(fd,ram.ibank, 4096*irl); - lseek(fd, vramblock<<12, SEEK_SET); + lseek(fd, base_offset + (vramblock << 12), SEEK_SET); write(fd,lcd.vbank, 4096*vrl); - lseek(fd, sramblock<<12, SEEK_SET); + lseek(fd, base_offset + (sramblock << 12), SEEK_SET); write(fd,ram.sbank, 4096*srl); } |