summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichiel Van Der Kolk <not.valid@email.address>2005-03-04 09:30:27 +0000
committerMichiel Van Der Kolk <not.valid@email.address>2005-03-04 09:30:27 +0000
commit254100adf4a7118997b8c3fd583ee9909f73c48d (patch)
tree73a776564cce175d9b004d9e8509938eb19acf09
parentdb44b294f49ab5705c5254d7a202d79dd342aa59 (diff)
downloadrockbox-254100adf4a7118997b8c3fd583ee9909f73c48d.tar.gz
rockbox-254100adf4a7118997b8c3fd583ee9909f73c48d.zip
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
-rw-r--r--apps/plugins/rockboy/menu.c261
-rw-r--r--apps/plugins/rockboy/rockmacros.h2
-rw-r--r--apps/plugins/rockboy/save.c24
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 @@
8#include "string.h" 8#include "string.h"
9#include "button.h" 9#include "button.h"
10#include "rockmacros.h" 10#include "rockmacros.h"
11#include "mem.h"
11 12
12/* load/save state function declarations */ 13/* load/save state function declarations */
13static void do_slot_menu(bool is_load); 14static void do_slot_menu(bool is_load);
15static void do_opt_menu(void);
16static void munge_name(char *buf, size_t bufsiz);
17
18/* directory ROM save slots belong in */
19#define STATE_DIR "/.rockbox/rockboy"
14 20
15#define MENU_CANCEL (-1) 21#define MENU_CANCEL (-1)
16static int do_menu(char *title, char **items, size_t num_items, int sel_item); 22static int do_menu(char *title, char **items, size_t num_items, int sel_item);
17 23
18/* main menu items */ 24/* main menu items */
19#define MAIN_MENU_TITLE "RockBoy Menu" 25#define MAIN_MENU_TITLE "Rockboy"
20typedef enum { 26typedef enum {
21 ITEM_BACK, 27 MM_ITEM_BACK,
22 ITEM_LOAD, 28 MM_ITEM_LOAD,
23 ITEM_SAVE, 29 MM_ITEM_SAVE,
24 ITEM_OPT, 30 MM_ITEM_OPT,
25 ITEM_QUIT, 31 MM_ITEM_QUIT,
26 ITEM_LAST 32 MM_ITEM_LAST
27} MainMenuItem; 33} MainMenuItem;
28 34
29/* strings for the main menu */ 35/* strings for the main menu */
@@ -36,20 +42,37 @@ static const char *main_menu[] = {
36}; 42};
37 43
38typedef enum { 44typedef enum {
39 ITEM_SLOT1, 45 SM_ITEM_SLOT1,
40 ITEM_SLOT2, 46 SM_ITEM_SLOT2,
41 ITEM_SLOT3, 47 SM_ITEM_SLOT3,
42 ITEM_SLOT4, 48 SM_ITEM_SLOT4,
43 ITEM_SLOT5 49 SM_ITEM_SLOT5,
50 SM_ITEM_FILE,
51 SM_ITEM_BACK,
52 SM_ITEM_LAST
44} SlotMenuItem; 53} SlotMenuItem;
45 54
46/* this is evil, but we snprintf() into it later :( */ 55/* this semi-evil, but we snprintf() into these strings later
56 * Note: if you want more save slots, just add more lines
57 * to this array */
47static const char *slot_menu[] = { 58static const char *slot_menu[] = {
48 "1. ", 59 "1. ",
49 "2. ", 60 "2. ",
50 "3. ", 61 "3. ",
51 "4. ", 62 "4. ",
52 "5. " 63 "5. ",
64 "Save to File... ",
65 "Previous Menu... "
66};
67
68#define OPT_MENU_TITLE "Options"
69typedef enum {
70 OM_ITEM_BACK,
71 OM_MENU_LAST
72} OptMenuItem;
73
74static const char *opt_menu[] = {
75 "Previous Menu..."
53}; 76};
54 77
55/* 78/*
@@ -79,17 +102,20 @@ int do_user_menu(void) {
79 102
80 /* handle selected menu item */ 103 /* handle selected menu item */
81 switch (mi) { 104 switch (mi) {
82 case ITEM_QUIT: 105 case MM_ITEM_QUIT:
83 ret = USER_MENU_QUIT; 106 ret = USER_MENU_QUIT;
84 case MENU_CANCEL: 107 case MENU_CANCEL:
85 case ITEM_BACK: 108 case MM_ITEM_BACK:
86 done = true; 109 done = true;
87 break; 110 break;
88 case ITEM_LOAD: 111 case MM_ITEM_LOAD:
89 do_slot_menu(1); 112 do_slot_menu(true);
113 break;
114 case MM_ITEM_SAVE:
115 do_slot_menu(false);
90 break; 116 break;
91 case ITEM_SAVE: 117 case MM_ITEM_OPT:
92 do_slot_menu(0); 118 do_opt_menu();
93 break; 119 break;
94 } 120 }
95 } 121 }
@@ -99,26 +125,158 @@ int do_user_menu(void) {
99} 125}
100 126
101/* 127/*
102 * do_load_menu - prompt the user for a memory slot 128 * munge_name - munge a string into a filesystem-safe name
129 */
130static void munge_name(char *buf, const size_t bufsiz) {
131 unsigned int i, max;
132
133 /* check strlen */
134 max = strlen(buf);
135 max = (max < bufsiz) ? max : bufsiz;
136
137 /* iterate over characters and munge them (if necessary) */
138 for (i = 0; i < max; i++)
139 if (!isalnum(buf[i]))
140 buf[i] = '_';
141}
142
143/*
144 * build_slot_path - build a path to an slot state file for this rom
145 *
146 * Note: uses rom.name. Is there a safer way of doing this? Like a ROM
147 * checksum or something like that?
148 */
149static void build_slot_path(char *buf, size_t bufsiz, size_t slot_id) {
150 char name_buf[40];
151
152 /* munge state file name */
153 strncpy(name_buf, rom.name, sizeof(name_buf));
154 name_buf[16] = '\0';
155 munge_name(name_buf, strlen(name_buf));
156
157 /* glom the whole mess together */
158 snprintf(buf, bufsiz, "%s/%s-%d.rbs", STATE_DIR, name_buf, slot_id + 1);
159}
160
161/*
162 * do_file - load or save game data in the given file
103 * 163 *
104 * TOOD 164 * Returns true on success and false on failure.
165 *
166 * @desc is a brief user-provided description (<20 bytes) of the state.
167 * If no description is provided, set @desc to NULL.
168 *
169 */
170static bool do_file(char *path, char *desc, bool is_load) {
171 char buf[200], desc_buf[20];
172 int fd, file_mode;
173
174 /* set file mode */
175 file_mode = is_load ? O_RDONLY : (O_WRONLY | O_CREAT);
176
177 /* attempt to open file descriptor here */
178 if ((fd = open(path, file_mode)) <= 0)
179 return false;
180
181 /* load/save state */
182 if (is_load) {
183 /* load description */
184 read(fd, desc_buf, 20);
185
186 /* load state */
187 loadstate(fd);
188
189 /* print out a status message so the user knows the state loaded */
190 snprintf(buf, sizeof(buf), "Loaded state from \"%s\"", path);
191 rb->splash(HZ * 1, true, buf);
192 } else {
193 /* build description buffer */
194 memset(desc_buf, 0, sizeof(desc_buf));
195 if (desc)
196 strncpy(desc_buf, desc, sizeof(desc_buf));
197
198 /* save state */
199 write(fd, desc_buf, 20);
200 savestate(fd);
201 }
202
203 /* close file descriptor */
204 close(fd);
205
206 /* return true (for success) */
207 return true;
208}
209
210/*
211 * do_slot - load or save game data in the given slot
105 * 212 *
213 * Returns true on success and false on failure.
214 */
215static bool do_slot(size_t slot_id, bool is_load) {
216 char path_buf[256], desc_buf[20];
217
218 /* build slot filename, clear desc buf */
219 build_slot_path(path_buf, sizeof(path_buf), slot_id);
220 memset(desc_buf, 0, sizeof(desc_buf));
221
222 /* if we're saving to a slot, then get a brief description */
223 if (!is_load) {
224 if (rb->kbd_input(desc_buf, sizeof(desc_buf)) || !strlen(desc_buf)) {
225 memset(desc_buf, 0, sizeof(desc_buf));
226 strncpy(desc_buf, "Untitled", sizeof(desc_buf));
227 }
228 }
229
230 /* load/save file */
231 return do_file(path_buf, desc_buf, is_load);
232}
233
234/*
235 * get information on the given slot
236 */
237static void slot_info(char *info_buf, size_t info_bufsiz, size_t slot_id) {
238 char buf[256];
239 int fd;
240
241 /* get slot file path */
242 build_slot_path(buf, sizeof(buf), slot_id);
243
244 /* attempt to open slot */
245 if ((fd = open(buf, O_RDONLY)) >= 0) {
246 /* this slot has a some data in it, read it */
247 if (read(fd, buf, 20) > 0) {
248 buf[20] = '\0';
249 snprintf(info_buf, info_bufsiz, "%2d. %s", slot_id + 1, buf);
250 } else {
251 snprintf(info_buf, info_bufsiz, "%2d. ERROR", slot_id + 1);
252 }
253 close(fd);
254 } else {
255 /* if we couldn't open the file, then the slot is empty */
256 snprintf(info_buf, info_bufsiz, "%2d.", slot_id + 1);
257 }
258}
259
260/*
261 * do_slot_menu - prompt the user for a load/save memory slot
106 */ 262 */
107static void do_slot_menu(bool is_load) { 263static void do_slot_menu(bool is_load) {
108 int i, mi, ret, num_items; 264 int i, mi, ret, num_items;
109 bool done = false; 265 bool done = false;
110 char *title; 266 char *title, buf[256];
111 267
112 /* set defaults */ 268 /* set defaults */
113 ret = 0; /* return value */ 269 ret = 0; /* return value */
114 mi = 0; /* initial menu selection */ 270 mi = 0; /* initial menu selection */
115 num_items = sizeof(slot_menu) / sizeof(char*); 271 num_items = sizeof(slot_menu) / sizeof(char*);
116 272
117 /* create menu items */ 273 /* create menu items (the last two are file and previous menu,
118 for (i = 0; i < num_items; i++) { 274 * so don't populate those) */
119 /* TODO: get slot info here */ 275 for (i = 0; i < num_items - 2; i++)
120 snprintf((char*) slot_menu[i], sizeof(slot_menu[i]), "%2d. %s", i + 1, "<empty>"); 276 slot_info((char*) slot_menu[i], 20, i);
121 } 277
278 /* set text of file item */
279 snprintf((char*) slot_menu[SM_ITEM_FILE], 20, "%s File...", is_load ? "Load from" : "Save to");
122 280
123 /* set menu title */ 281 /* set menu title */
124 title = is_load ? "Load State" : "Save State"; 282 title = is_load ? "Load State" : "Save State";
@@ -129,26 +287,55 @@ static void do_slot_menu(bool is_load) {
129 mi = do_menu(title, (char**) slot_menu, num_items, mi); 287 mi = do_menu(title, (char**) slot_menu, num_items, mi);
130 288
131 /* handle selected menu item */ 289 /* handle selected menu item */
132 if (mi == MENU_CANCEL) { 290 done = true;
133 done = true; 291 if (mi != MENU_CANCEL && mi != SM_ITEM_BACK) {
134 break; 292 if (mi == SM_ITEM_FILE) {
135 } else { 293 char rom_name_buf[40];
136 if (is_load) { 294
137 /* TODO: load slot here */ 295 /* munge rom name to valid filename */
296 strncpy(rom_name_buf, rom.name, 16);
297 munge_name(rom_name_buf, sizeof(rom_name_buf));
298
299 /* create default filename */
300 snprintf(buf, sizeof(buf), "/%s.rbs", rom_name_buf);
301
302 /* prompt for output filename, save to file */
303 if (!rb->kbd_input(buf, sizeof(buf)))
304 done = do_file(buf, NULL, is_load);
138 } else { 305 } else {
139 /* TODO: save slot here */ 306 done = do_slot(mi, is_load);
140 } 307 }
308
309 /* if we couldn't save the state file, then print out an
310 * error message */
311 if (!is_load && !done)
312 rb->splash(HZ * 2, true, "Couldn't save state file.");
141 } 313 }
142 } 314 }
143} 315}
144 316
317static void do_opt_menu(void) {
318 int mi, num_items;
319 bool done = false;
320
321 /* set a couple of defaults */
322 num_items = sizeof(opt_menu) / sizeof(char*);
323 mi = 0;
324
325 while (!done) {
326 mi = do_menu(OPT_MENU_TITLE, (char**) opt_menu, num_items, mi);
327 if (mi == MENU_CANCEL || mi == OM_ITEM_BACK)
328 done = true;
329 }
330}
331
145/*********************************************************************/ 332/*********************************************************************/
146/* MENU FUNCTIONS */ 333/* MENU FUNCTIONS */
147/*********************************************************************/ 334/*********************************************************************/
148/* at some point i'll make this a generic menu interface, but for now, 335/* at some point i'll make this a generic menu interface, but for now,
149 * these defines will suffice */ 336 * these defines will suffice */
150#define MENU_X 10 337#define MENU_X 10
151#define MENU_Y 10 338#define MENU_Y 8
152#define MENU_WIDTH (LCD_WIDTH - 2 * MENU_X) 339#define MENU_WIDTH (LCD_WIDTH - 2 * MENU_X)
153#define MENU_HEIGHT (LCD_HEIGHT - 2 * MENU_Y) 340#define MENU_HEIGHT (LCD_HEIGHT - 2 * MENU_Y)
154#define MENU_RECT MENU_X, MENU_Y, MENU_WIDTH, MENU_HEIGHT 341#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) {
201 int x, y, w, h, by; 388 int x, y, w, h, by;
202 389
203 /* set to default? font */ 390 /* set to default? font */
204 /* rb->lcd_setfont(0); */ 391 rb->lcd_setfont(0);
205 392
206 /* draw the outline */ 393 /* draw the outline */
207 rb->lcd_fillrect(SHADOW_RECT); 394 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);
44void doevents(void); 44void doevents(void);
45void ev_poll(void); 45void ev_poll(void);
46int do_user_menu(void); 46int do_user_menu(void);
47void loadstate(int fd);
48void savestate(int fd);
47#define USER_MENU_QUIT -2 49#define USER_MENU_QUIT -2
48 50
49 51
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)
169 int irl = hw.cgb ? 8 : 2; 169 int irl = hw.cgb ? 8 : 2;
170 int vrl = hw.cgb ? 4 : 2; 170 int vrl = hw.cgb ? 4 : 2;
171 int srl = mbc.ramsize << 1; 171 int srl = mbc.ramsize << 1;
172 size_t base_offset;
172 173
173 ver = hramofs = hiofs = palofs = oamofs = wavofs = 0; 174 ver = hramofs = hiofs = palofs = oamofs = wavofs = 0;
174 175
176 base_offset = lseek(fd, 0, SEEK_CUR);
177
175 read(fd,buf, 4096); 178 read(fd,buf, 4096);
176 179
177 for (j = 0; header[j][0]; j++) 180 for (j = 0; header[j][0]; j++)
@@ -207,14 +210,18 @@ void loadstate(int fd)
207 if (wavofs) memcpy(snd.wave, buf+wavofs, sizeof snd.wave); 210 if (wavofs) memcpy(snd.wave, buf+wavofs, sizeof snd.wave);
208 else memcpy(snd.wave, ram.hi+0x30, 16); /* patch data from older files */ 211 else memcpy(snd.wave, ram.hi+0x30, 16); /* patch data from older files */
209 212
210 lseek(fd, iramblock<<12, SEEK_SET); 213 lseek(fd, base_offset + (iramblock << 12), SEEK_SET);
211 read(fd,ram.ibank, 4096*irl); 214 read(fd,ram.ibank, 4096*irl);
212 215
213 lseek(fd, vramblock<<12, SEEK_SET); 216 lseek(fd, base_offset + (vramblock << 12), SEEK_SET);
214 read(fd,lcd.vbank, 4096*vrl); 217 read(fd,lcd.vbank, 4096*vrl);
215 218
216 lseek(fd, sramblock<<12, SEEK_SET); 219 lseek(fd, base_offset + (sramblock << 12), SEEK_SET);
217 read(fd,ram.sbank, 4096*srl); 220 read(fd,ram.sbank, 4096*srl);
221 vram_dirty();
222 pal_dirty();
223 sound_dirty();
224 mem_updatemap();
218} 225}
219 226
220void savestate(int fd) 227void savestate(int fd)
@@ -226,6 +233,7 @@ void savestate(int fd)
226 int irl = hw.cgb ? 8 : 2; 233 int irl = hw.cgb ? 8 : 2;
227 int vrl = hw.cgb ? 4 : 2; 234 int vrl = hw.cgb ? 4 : 2;
228 int srl = mbc.ramsize << 1; 235 int srl = mbc.ramsize << 1;
236 size_t base_offset;
229 237
230 ver = 0x105; 238 ver = 0x105;
231 iramblock = 1; 239 iramblock = 1;
@@ -261,16 +269,18 @@ void savestate(int fd)
261 memcpy(buf+oamofs, lcd.oam.mem, sizeof lcd.oam); 269 memcpy(buf+oamofs, lcd.oam.mem, sizeof lcd.oam);
262 memcpy(buf+wavofs, snd.wave, sizeof snd.wave); 270 memcpy(buf+wavofs, snd.wave, sizeof snd.wave);
263 271
264 lseek(fd, 0, SEEK_SET); 272 /* calculate base offset for output file */
273 /* (we'll seek relative to that from now on) */
274 base_offset = lseek(fd, 0, SEEK_CUR);
265 write(fd,buf, 4096); 275 write(fd,buf, 4096);
266 276
267 lseek(fd, iramblock<<12, SEEK_SET); 277 lseek(fd, base_offset + (iramblock << 12), SEEK_SET);
268 write(fd,ram.ibank, 4096*irl); 278 write(fd,ram.ibank, 4096*irl);
269 279
270 lseek(fd, vramblock<<12, SEEK_SET); 280 lseek(fd, base_offset + (vramblock << 12), SEEK_SET);
271 write(fd,lcd.vbank, 4096*vrl); 281 write(fd,lcd.vbank, 4096*vrl);
272 282
273 lseek(fd, sramblock<<12, SEEK_SET); 283 lseek(fd, base_offset + (sramblock << 12), SEEK_SET);
274 write(fd,ram.sbank, 4096*srl); 284 write(fd,ram.sbank, 4096*srl);
275} 285}
276 286