1 /*********************************************************************/
2 /* menu.c - user menu for rockboy */
4 /* Note: this file only exposes one function: do_user_menu(). */
5 /*********************************************************************/
8 #include "rockmacros.h"
11 #include "lib/oldmenuapi.h"
15 #if CONFIG_KEYPAD == IPOD_4G_PAD
16 #define MENU_BUTTON_UP BUTTON_SCROLL_BACK
17 #define MENU_BUTTON_DOWN BUTTON_SCROLL_FWD
18 #define MENU_BUTTON_LEFT BUTTON_LEFT
19 #define MENU_BUTTON_RIGHT BUTTON_RIGHT
21 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
22 #define MENU_BUTTON_UP BUTTON_SCROLL_UP
23 #define MENU_BUTTON_DOWN BUTTON_SCROLL_DOWN
24 #define MENU_BUTTON_LEFT BUTTON_LEFT
25 #define MENU_BUTTON_RIGHT BUTTON_RIGHT
28 #define MENU_BUTTON_UP BUTTON_UP
29 #define MENU_BUTTON_DOWN BUTTON_DOWN
30 #define MENU_BUTTON_LEFT BUTTON_LEFT
31 #define MENU_BUTTON_RIGHT BUTTON_RIGHT
34 /* load/save state function declarations */
35 static void do_opt_menu(void);
36 static void do_slot_menu(bool is_load
);
37 static void munge_name(char *buf
, size_t bufsiz
);
39 /* directory ROM save slots belong in */
40 #define STATE_DIR ROCKBOX_DIR "/rockboy"
42 static int getbutton(char *text
)
45 rb
->lcd_clear_display();
46 rb
->font_getstringsize(text
, &fw
, &fh
,0);
47 rb
->lcd_putsxy(LCD_WIDTH
/2-fw
/2, LCD_HEIGHT
/2-fh
/2, text
);
51 while (rb
->button_get(false) != BUTTON_NONE
)
57 button
= rb
->button_get(true);
58 button
=button
&0x00000FFF;
64 static void setupkeys(void)
66 options
.UP
=getbutton ("Press Up");
67 options
.DOWN
=getbutton ("Press Down");
68 options
.LEFT
=getbutton ("Press Left");
69 options
.RIGHT
=getbutton ("Press Right");
71 options
.A
=getbutton ("Press A");
72 options
.B
=getbutton ("Press B");
74 options
.START
=getbutton ("Press Start");
75 options
.SELECT
=getbutton("Press Select");
77 options
.MENU
=getbutton ("Press Menu");
81 * do_user_menu - create the user menu on the screen.
83 * Returns USER_MENU_QUIT if the user selected "quit", otherwise
86 int do_user_menu(void) {
93 time
= rb
->mktime(rb
->get_time());
96 /* Clean out the button Queue */
97 while (rb
->button_get(false) != BUTTON_NONE
)
100 static const struct menu_item items
[] = {
101 {"Load Game", NULL
},
102 {"Save Game", NULL
},
109 m
= menu_init(items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
117 case 0: /* Load Game */
120 case 1: /* Save Game */
123 case 2: /* Options */
127 ret
= USER_MENU_QUIT
;
138 rb
->lcd_setfont(0); /* Reset the font */
139 rb
->lcd_clear_display(); /* Clear display for screen size changes */
141 /* Keep the RTC in sync */
143 time
= (rb
->mktime(rb
->get_time()) - time
) * 60;
145 while (time
-- > 0) rtc_tick();
151 * munge_name - munge a string into a filesystem-safe name
153 static void munge_name(char *buf
, const size_t bufsiz
) {
158 max
= (max
< bufsiz
) ? max
: bufsiz
;
160 /* iterate over characters and munge them (if necessary) */
161 for (i
= 0; i
< max
; i
++)
162 if (!isalnum(buf
[i
]))
167 * build_slot_path - build a path to an slot state file for this rom
169 * Note: uses rom.name. Is there a safer way of doing this? Like a ROM
170 * checksum or something like that?
172 static void build_slot_path(char *buf
, size_t bufsiz
, size_t slot_id
) {
175 /* munge state file name */
176 strncpy(name_buf
, rom
.name
, 40);
178 munge_name(name_buf
, strlen(name_buf
));
180 /* glom the whole mess together */
181 snprintf(buf
, bufsiz
, "%s/%s-%ld.rbs", STATE_DIR
, name_buf
, slot_id
+ 1);
185 * do_file - load or save game data in the given file
187 * Returns true on success and false on failure.
189 * @desc is a brief user-provided description (<20 bytes) of the state.
190 * If no description is provided, set @desc to NULL.
193 static bool do_file(char *path
, char *desc
, bool is_load
) {
194 char buf
[200], desc_buf
[20];
198 file_mode
= is_load
? O_RDONLY
: (O_WRONLY
| O_CREAT
);
200 /* attempt to open file descriptor here */
201 if ((fd
= open(path
, file_mode
)) <= 0)
204 /* load/save state */
207 /* load description */
208 read(fd
, desc_buf
, 20);
213 /* print out a status message so the user knows the state loaded */
214 snprintf(buf
, 200, "Loaded state from \"%s\"", path
);
215 rb
->splash(HZ
* 1, buf
);
219 /* build description buffer */
220 memset(desc_buf
, 0, 20);
222 strncpy(desc_buf
, desc
, 20);
225 write(fd
, desc_buf
, 20);
229 /* close file descriptor */
232 /* return true (for success) */
237 * do_slot - load or save game data in the given slot
239 * Returns true on success and false on failure.
241 static bool do_slot(size_t slot_id
, bool is_load
) {
242 char path_buf
[256], desc_buf
[20];
244 /* build slot filename, clear desc buf */
245 build_slot_path(path_buf
, 256, slot_id
);
246 memset(desc_buf
, 0, 20);
248 /* if we're saving to a slot, then get a brief description */
250 if (rb
->kbd_input(desc_buf
, 20) || !strlen(desc_buf
))
252 memset(desc_buf
, 0, 20);
253 strncpy(desc_buf
, "Untitled", 20);
257 return do_file(path_buf
, desc_buf
, is_load
);
261 * get information on the given slot
263 static void slot_info(char *info_buf
, size_t info_bufsiz
, size_t slot_id
) {
267 /* get slot file path */
268 build_slot_path(buf
, 256, slot_id
);
270 /* attempt to open slot */
271 if ((fd
= open(buf
, O_RDONLY
)) >= 0)
273 /* this slot has a some data in it, read it */
274 if (read(fd
, buf
, 20) > 0)
277 snprintf(info_buf
, info_bufsiz
, "%ld. %s", slot_id
+ 1, buf
);
280 snprintf(info_buf
, info_bufsiz
, "%ld. ERROR", slot_id
+ 1);
286 /* if we couldn't open the file, then the slot is empty */
287 snprintf(info_buf
, info_bufsiz
, "%ld. %s", slot_id
+ 1, "<Empty>");
292 * do_slot_menu - prompt the user for a load/save memory slot
294 static void do_slot_menu(bool is_load
) {
303 struct menu_item items
[] = {
311 int num_items
= sizeof(items
) / sizeof(*items
);
313 /* create menu items */
314 for (i
= 0; i
< num_items
; i
++)
315 slot_info(buf
[i
], 20, i
);
317 m
= menu_init(items
, num_items
, NULL
, NULL
, NULL
, NULL
);
323 if (result
<num_items
&& result
>= 0 )
324 done
= do_slot(result
, is_load
);
331 static void do_opt_menu(void)
337 static const struct opt_items onoff
[2] = {
342 static const struct opt_items frameskip
[]= {
352 #ifdef HAVE_LCD_COLOR
353 static const struct opt_items scaling
[]= {
355 { "Scaled - Maintain Ratio", -1 },
356 #if (LCD_WIDTH>=160) && (LCD_HEIGHT>=144)
361 static const struct opt_items palette
[]= {
362 { "Brown (Default)", -1 },
364 { "Light Gray", -1 },
365 { "Multi-Color 1", -1 },
366 { "Multi-Color 2", -1 },
367 { "Adventure Island", -1 },
368 { "Adventure Island 2", -1 },
369 { "Balloon Kid", -1 },
371 { "Batman: Return of Joker", -1 },
372 { "Bionic Commando", -1 },
373 { "Castlvania Adventure", -1 },
374 { "Donkey Kong Land", -1 },
382 static const struct menu_item items
[] = {
383 { "Max Frameskip", NULL
},
386 { "Set Keys (Buggy)", NULL
},
387 #ifdef HAVE_LCD_COLOR
388 { "Screen Size" , NULL
},
389 { "Screen Rotate" , NULL
},
390 { "Set Palette" , NULL
},
394 m
= menu_init(items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
396 options
.dirty
=1; /* Assume that the settings have been changed */
405 case 0: /* Frameskip */
406 rb
->set_option(items
[0].desc
, &options
.maxskip
, INT
, frameskip
,
407 sizeof(frameskip
)/sizeof(*frameskip
), NULL
);
410 if(options
.sound
>1) options
.sound
=1;
411 rb
->set_option(items
[1].desc
, &options
.sound
, INT
, onoff
, 2, NULL
);
412 if(options
.sound
) sound_dirty();
415 rb
->set_option(items
[2].desc
, &options
.showstats
, INT
, onoff
, 2, NULL
);
420 #ifdef HAVE_LCD_COLOR
421 case 4: /* Screen Size */
422 rb
->set_option(items
[4].desc
, &options
.scaling
, INT
, scaling
,
423 sizeof(scaling
)/sizeof(*scaling
), NULL
);
426 case 5: /* Screen rotate */
427 rb
->set_option(items
[5].desc
, &options
.rotate
, INT
, onoff
, 2, NULL
);
430 case 6: /* Palette */
431 rb
->set_option(items
[6].desc
, &options
.pal
, INT
, palette
, 17, NULL
);