1 /*********************************************************************/
2 /* menu.c - user menu for rockboy */
4 /* Note: this file only exposes one function: do_user_menu(). */
5 /*********************************************************************/
8 #include "rockmacros.h"
14 /* load/save state function declarations */
15 static void do_opt_menu(void);
16 static void do_slot_menu(bool is_load
);
17 static void munge_name(char *buf
, size_t bufsiz
);
19 /* directory ROM save slots belong in */
20 #define STATE_DIR ROCKBOX_DIR "/rockboy"
22 static int getbutton(char *text
)
25 rb
->lcd_clear_display();
26 rb
->font_getstringsize(text
, &fw
, &fh
,0);
27 rb
->lcd_putsxy(LCD_WIDTH
/2-fw
/2, LCD_HEIGHT
/2-fh
/2, text
);
31 while (rb
->button_get(false) != BUTTON_NONE
)
37 button
= rb
->button_get(true);
38 button
=button
&0x00000FFF;
44 static void setupkeys(void)
46 options
.UP
=getbutton ("Press Up");
47 options
.DOWN
=getbutton ("Press Down");
48 options
.LEFT
=getbutton ("Press Left");
49 options
.RIGHT
=getbutton ("Press Right");
51 options
.A
=getbutton ("Press A");
52 options
.B
=getbutton ("Press B");
54 options
.START
=getbutton ("Press Start");
55 options
.SELECT
=getbutton("Press Select");
57 options
.MENU
=getbutton ("Press Menu");
61 * do_user_menu - create the user menu on the screen.
63 * Returns USER_MENU_QUIT if the user selected "quit", otherwise
66 int do_user_menu(void) {
68 int selected
=0, ret
=0;
73 time
= rb
->mktime(rb
->get_time());
76 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
77 rb
->lcd_set_mode(LCD_MODE_RGB565
);
80 /* Clean out the button Queue */
81 while (rb
->button_get(false) != BUTTON_NONE
)
84 MENUITEM_STRINGLIST(menu
, "Rockboy Menu", NULL
,
85 "Load Game", "Save Game",
92 result
= rb
->do_menu(&menu
, &selected
, NULL
, false);
96 case 0: /* Load Game */
99 case 1: /* Save Game */
102 case 2: /* Options */
106 ret
= USER_MENU_QUIT
;
115 rb
->lcd_setfont(0); /* Reset the font */
116 rb
->lcd_clear_display(); /* Clear display for screen size changes */
118 /* Keep the RTC in sync */
120 time
= (rb
->mktime(rb
->get_time()) - time
) * 60;
122 while (time
-- > 0) rtc_tick();
124 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
125 rb
->lcd_set_mode(LCD_MODE_PAL256
);
132 * munge_name - munge a string into a filesystem-safe name
134 static void munge_name(char *buf
, const size_t bufsiz
) {
139 max
= (max
< bufsiz
) ? max
: bufsiz
;
141 /* iterate over characters and munge them (if necessary) */
142 for (i
= 0; i
< max
; i
++)
143 if (!isalnum(buf
[i
]))
148 * build_slot_path - build a path to an slot state file for this rom
150 * Note: uses rom.name. Is there a safer way of doing this? Like a ROM
151 * checksum or something like that?
153 static void build_slot_path(char *buf
, size_t bufsiz
, size_t slot_id
) {
156 /* munge state file name */
157 strlcpy(name_buf
, rom
.name
, sizeof(name_buf
));
158 munge_name(name_buf
, strlen(name_buf
));
160 /* glom the whole mess together */
161 snprintf(buf
, bufsiz
, "%s/%s-%ld.rbs", STATE_DIR
, name_buf
, slot_id
+ 1);
165 * do_file - load or save game data in the given file
167 * Returns true on success and false on failure.
169 * @desc is a brief user-provided description (<20 bytes) of the state.
170 * If no description is provided, set @desc to NULL.
173 static bool do_file(char *path
, char *desc
, bool is_load
) {
174 char buf
[200], desc_buf
[20];
178 file_mode
= is_load
? O_RDONLY
: (O_WRONLY
| O_CREAT
);
180 /* attempt to open file descriptor here */
181 if ((fd
= open(path
, file_mode
)) <= 0)
184 /* load/save state */
187 /* load description */
188 read(fd
, desc_buf
, 20);
193 /* print out a status message so the user knows the state loaded */
194 snprintf(buf
, 200, "Loaded state from \"%s\"", path
);
195 rb
->splash(HZ
* 1, buf
);
199 /* build description buffer */
200 memset(desc_buf
, 0, 20);
202 strlcpy(desc_buf
, desc
, 20);
205 write(fd
, desc_buf
, 20);
209 /* close file descriptor */
212 /* return true (for success) */
217 * do_slot - load or save game data in the given slot
219 * Returns true on success and false on failure.
221 static bool do_slot(size_t slot_id
, bool is_load
) {
222 char path_buf
[256], desc_buf
[20];
224 /* build slot filename, clear desc buf */
225 build_slot_path(path_buf
, 256, slot_id
);
226 memset(desc_buf
, 0, 20);
228 /* if we're saving to a slot, then get a brief description */
230 if ( (rb
->kbd_input(desc_buf
, 20) < 0) || !strlen(desc_buf
) )
232 strlcpy(desc_buf
, "Untitled", 20);
236 return do_file(path_buf
, desc_buf
, is_load
);
240 * get information on the given slot
242 static void slot_info(char *info_buf
, size_t info_bufsiz
, size_t slot_id
) {
246 /* get slot file path */
247 build_slot_path(buf
, 256, slot_id
);
249 /* attempt to open slot */
250 if ((fd
= open(buf
, O_RDONLY
)) >= 0)
252 /* this slot has a some data in it, read it */
253 if (read(fd
, buf
, 20) > 0)
256 snprintf(info_buf
, info_bufsiz
, "%ld. %s", slot_id
+ 1, buf
);
259 snprintf(info_buf
, info_bufsiz
, "%ld. ERROR", slot_id
+ 1);
265 /* if we couldn't open the file, then the slot is empty */
266 snprintf(info_buf
, info_bufsiz
, "%ld. %s", slot_id
+ 1, "<Empty>");
273 static const char* slot_get_name(int selected_item
, void * data
,
274 char * buffer
, size_t buffer_len
)
276 const char (*items
)[20] = data
;
279 return items
[selected_item
];
283 * list_action_callback
285 static int list_action_callback(int action
, struct gui_synclist
*lists
)
288 if (action
== ACTION_STD_OK
)
289 return ACTION_STD_CANCEL
;
294 * do_slot_menu - prompt the user for a load/save memory slot
296 static void do_slot_menu(bool is_load
) {
301 int num_items
= sizeof(items
) / sizeof(*items
);
302 struct simplelist_info info
;
304 /* create menu items */
305 for (i
= 0; i
< num_items
; i
++)
306 slot_info(items
[i
], 20, i
);
308 rb
->simplelist_info_init(&info
, NULL
, num_items
, (void *)items
);
309 info
.get_name
= slot_get_name
;
310 info
.action_callback
= list_action_callback
;
314 if(rb
->simplelist_show_list(&info
))
317 result
= info
.selection
;
318 if (result
<num_items
&& result
>= 0 )
319 done
= do_slot(result
, is_load
);
325 static void do_opt_menu(void)
331 static const struct opt_items onoff
[2] = {
336 static const struct opt_items frameskip
[]= {
346 #ifdef HAVE_LCD_COLOR
347 static const struct opt_items rotate
[] = {
348 { "No rotation", -1 },
349 { "Rotate Right" , -1 },
350 { "Rotate Left" , -1 },
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 MENUITEM_STRINGLIST(menu
, "Options", NULL
,
383 "Max Frameskip", "Sound", "Stats", "Set Keys (Buggy)",
384 #ifdef HAVE_LCD_COLOR
385 "Screen Size", "Screen Rotate", "Set Palette",
389 options
.dirty
=1; /* Assume that the settings have been changed */
393 result
= rb
->do_menu(&menu
, &selected
, NULL
, false);
397 case 0: /* Frameskip */
398 rb
->set_option("Max Frameskip", &options
.maxskip
, INT
, frameskip
,
399 sizeof(frameskip
)/sizeof(*frameskip
), NULL
);
402 if(options
.sound
>1) options
.sound
=1;
403 rb
->set_option("Sound", &options
.sound
, INT
, onoff
, 2, NULL
);
404 if(options
.sound
) sound_dirty();
407 rb
->set_option("Stats", &options
.showstats
, INT
, onoff
, 2, NULL
);
412 #ifdef HAVE_LCD_COLOR
413 case 4: /* Screen Size */
414 rb
->set_option("Screen Size", &options
.scaling
, INT
, scaling
,
415 sizeof(scaling
)/sizeof(*scaling
), NULL
);
418 case 5: /* Screen rotate */
419 rb
->set_option("Screen Rotate", &options
.rotate
, INT
, rotate
,
420 sizeof(rotate
)/sizeof(*rotate
), NULL
);
423 case 6: /* Palette */
424 rb
->set_option("Set Palette", &options
.pal
, INT
, palette
, 17, NULL
);