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 | |
parent | db44b294f49ab5705c5254d7a202d79dd342aa59 (diff) | |
download | rockbox-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.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 @@ | |||
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 */ |
13 | static void do_slot_menu(bool is_load); | 14 | static void do_slot_menu(bool is_load); |
15 | static void do_opt_menu(void); | ||
16 | static 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) |
16 | static int do_menu(char *title, char **items, size_t num_items, int sel_item); | 22 | static 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" |
20 | typedef enum { | 26 | typedef 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 | ||
38 | typedef enum { | 44 | typedef 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 */ | ||
47 | static const char *slot_menu[] = { | 58 | static 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" | ||
69 | typedef enum { | ||
70 | OM_ITEM_BACK, | ||
71 | OM_MENU_LAST | ||
72 | } OptMenuItem; | ||
73 | |||
74 | static 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 | */ | ||
130 | static 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 | */ | ||
149 | static 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 | */ | ||
170 | static 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 | */ | ||
215 | static 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 | */ | ||
237 | static 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 | */ |
107 | static void do_slot_menu(bool is_load) { | 263 | static 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 | ||
317 | static 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); | |||
44 | void doevents(void); | 44 | void doevents(void); |
45 | void ev_poll(void); | 45 | void ev_poll(void); |
46 | int do_user_menu(void); | 46 | int do_user_menu(void); |
47 | void loadstate(int fd); | ||
48 | void 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 | ||
220 | void savestate(int fd) | 227 | void 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 | ||