1 /*********************************************************************/
2 /* menu.c - user menu for rockboy */
4 /* Note: this file only exposes one function: do_user_menu(). */
5 /*********************************************************************/
7 #include "lib/helper.h"
9 #include "rockmacros.h"
20 /* load/save state function declarations */
21 static void do_opt_menu(void);
22 static void do_slot_menu(bool is_load
);
23 static void munge_name(char *buf
, size_t bufsiz
);
25 /* directory ROM save slots belong in */
26 #define STATE_DIR ROCKBOX_DIR "/rockboy"
28 static int getbutton(char *text
)
32 rb
->lcd_clear_display();
33 rb
->font_getstringsize(text
, &fw
, &fh
,0);
34 rb
->lcd_putsxy(LCD_WIDTH
/2-fw
/2, LCD_HEIGHT
/2-fh
/2, text
);
38 while (rb
->button_get(false) != BUTTON_NONE
)
43 button
= rb
->button_get(true);
44 button
= button
&(BUTTON_MAIN
|BUTTON_REMOTE
);
50 static void setupkeys(void)
52 options
.UP
= getbutton("Press Up");
53 options
.DOWN
= getbutton("Press Down");
54 options
.LEFT
= getbutton("Press Left");
55 options
.RIGHT
= getbutton("Press Right");
57 options
.A
= getbutton("Press A");
58 options
.B
= getbutton("Press B");
60 options
.START
= getbutton("Press Start");
61 options
.SELECT
= getbutton("Press Select");
63 options
.MENU
= getbutton("Press Menu");
67 * do_user_menu - create the user menu on the screen.
69 * Returns USER_MENU_QUIT if the user selected "quit", otherwise
72 int do_user_menu(void) {
74 int selected
=0, ret
=0;
79 time
= rb
->mktime(rb
->get_time());
82 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
83 rb
->lcd_set_mode(LCD_MODE_RGB565
);
86 backlight_use_settings();
88 /* Clean out the button Queue */
89 while (rb
->button_get(false) != BUTTON_NONE
)
92 MENUITEM_STRINGLIST(menu
, "Rockboy Menu", NULL
,
93 "Load Game", "Save Game",
94 "Options", "Reset", "Quit");
100 result
= rb
->do_menu(&menu
, &selected
, NULL
, false);
104 case 0: /* Load Game */
107 case 1: /* Save Game */
110 case 2: /* Options */
118 ret
= USER_MENU_QUIT
;
119 if(options
.autosave
) sn_save();
128 rb
->lcd_setfont(0); /* Reset the font */
129 rb
->lcd_clear_display(); /* Clear display for screen size changes */
131 /* Keep the RTC in sync */
133 time
= (rb
->mktime(rb
->get_time()) - time
) * 60;
135 while (time
-- > 0) rtc_tick();
137 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
138 rb
->lcd_set_mode(LCD_MODE_PAL256
);
141 /* ignore backlight time out */
142 backlight_force_on();
148 * munge_name - munge a string into a filesystem-safe name
150 static void munge_name(char *buf
, const size_t bufsiz
) {
155 max
= (max
< bufsiz
) ? max
: bufsiz
;
157 /* iterate over characters and munge them (if necessary) */
158 for (i
= 0; i
< max
; i
++)
159 if (!isalnum(buf
[i
]))
164 * build_slot_path - build a path to an slot state file for this rom
166 * Note: uses rom.name. Is there a safer way of doing this? Like a ROM
167 * checksum or something like that?
169 static void build_slot_path(char *buf
, size_t bufsiz
, int slot_id
) {
172 /* munge state file name */
173 strlcpy(name_buf
, rom
.name
, sizeof(name_buf
));
174 munge_name(name_buf
, strlen(name_buf
));
176 /* glom the whole mess together */
177 snprintf(buf
, bufsiz
, "%s/%s-%d.rbs", STATE_DIR
, name_buf
, slot_id
+ 1);
181 * do_file - load or save game data in the given file
183 * Returns true on success and false on failure.
185 * @desc is a brief user-provided description of the state.
186 * If no description is provided, set @desc to NULL.
189 static bool do_file(char *path
, char *desc
, bool is_load
) {
190 char desc_buf
[DESC_SIZE
];
194 file_mode
= is_load
? O_RDONLY
: (O_WRONLY
| O_CREAT
);
196 /* attempt to open file descriptor here */
197 if ((fd
= open(path
, file_mode
, 0666)) < 0)
200 /* load/save state */
203 /* load description */
204 read(fd
, desc_buf
, sizeof(desc_buf
));
209 /* print out a status message so the user knows the state loaded */
210 rb
->splashf(HZ
* 1, "Loaded state from \"%s\"", path
);
214 /* build description buffer */
215 memset(desc_buf
, 0, sizeof(desc_buf
));
217 strlcpy(desc_buf
, desc
, sizeof(desc_buf
));
220 write(fd
, desc_buf
, sizeof(desc_buf
));
224 /* close file descriptor */
227 /* return true (for success) */
232 * get information on the given slot
234 static void slot_info(char *info_buf
, size_t info_bufsiz
, int slot_id
,
239 /* get slot file path */
240 build_slot_path(buf
, sizeof(buf
), slot_id
);
242 /* attempt to open slot */
243 if ((fd
= open(buf
, O_RDONLY
)) >= 0)
245 /* this slot has a some data in it, read it */
246 if (read(fd
, buf
, DESC_SIZE
) == DESC_SIZE
)
248 buf
[DESC_SIZE
] = '\0';
249 strlcpy(info_buf
, buf
, info_bufsiz
);
252 strlcpy(info_buf
, "ERROR", info_bufsiz
);
258 /* if we couldn't open the file, then the slot is empty */
259 strlcpy(info_buf
, "<Empty>", info_bufsiz
);
264 * do_slot - load or save game data in the given slot
266 * Returns true on success and false on failure.
268 static bool do_slot(int slot_id
, bool is_load
) {
269 char path_buf
[256], desc_buf
[DESC_SIZE
];
271 /* build slot filename, clear desc buf */
272 build_slot_path(path_buf
, sizeof(path_buf
), slot_id
);
273 memset(desc_buf
, 0, sizeof(desc_buf
));
275 /* if we're saving to a slot, then get a brief description */
278 slot_info(desc_buf
, sizeof(desc_buf
), slot_id
, false);
279 if ( rb
->kbd_input(desc_buf
, sizeof(desc_buf
)) < 0 )
281 if ( !strlen(desc_buf
) )
282 strlcpy(desc_buf
, "Untitled", sizeof(desc_buf
));
286 return do_file(path_buf
, desc_buf
, is_load
);
292 static const char* slot_get_name(int selected_item
, void * data
,
293 char * buffer
, size_t buffer_len
)
295 const char (*items
)[DESC_SIZE
] = data
;
296 snprintf(buffer
, buffer_len
, "%d. %s",
297 selected_item
+ 1, items
[selected_item
]);
302 * list_action_callback
304 static int list_action_callback(int action
, struct gui_synclist
*lists
)
307 if (action
== ACTION_STD_OK
)
308 return ACTION_STD_CANCEL
;
313 * do_slot_menu - prompt the user for a load/save memory slot
315 static void do_slot_menu(bool is_load
) {
317 char items
[SLOT_COUNT
][DESC_SIZE
];
320 struct simplelist_info info
;
322 /* create menu items */
323 for (i
= 0; i
< SLOT_COUNT
; i
++)
324 slot_info(items
[i
], sizeof(*items
), i
, true);
326 rb
->simplelist_info_init(&info
, NULL
, SLOT_COUNT
, (void *)items
);
327 info
.get_name
= slot_get_name
;
328 info
.action_callback
= list_action_callback
;
332 if(rb
->simplelist_show_list(&info
))
335 result
= info
.selection
;
336 if (result
<SLOT_COUNT
&& result
>= 0 )
337 done
= do_slot(result
, is_load
);
343 static void do_opt_menu(void)
349 static const struct opt_items onoff
[2] = {
354 static const struct opt_items stats
[3] = {
360 static const struct opt_items frameskip
[]= {
384 #ifdef HAVE_LCD_COLOR
385 static const struct opt_items rotate
[] = {
386 { "No rotation", -1 },
387 { "Rotate Right" , -1 },
388 { "Rotate Left" , -1 },
391 static const struct opt_items scaling
[]= {
393 { "Scaled - Maintain Ratio", -1 },
394 #if (LCD_WIDTH>=160) && (LCD_HEIGHT>=144)
399 static const struct opt_items palette
[]= {
400 { "Brown (Default)", -1 },
402 { "Light Gray", -1 },
403 { "Multi-Color 1", -1 },
404 { "Multi-Color 2", -1 },
405 { "Adventure Island", -1 },
406 { "Adventure Island 2", -1 },
407 { "Balloon Kid", -1 },
409 { "Batman: Return of Joker", -1 },
410 { "Bionic Commando", -1 },
411 { "Castlvania Adventure", -1 },
412 { "Donkey Kong Land", -1 },
420 MENUITEM_STRINGLIST(menu
, "Options", NULL
,
421 "Max Frameskip", "Autosave", "Sound", "Volume",
422 "Stats", "Set Keys (Buggy)",
423 #ifdef HAVE_LCD_COLOR
424 "Screen Size", "Screen Rotate", "Set Palette",
428 options
.dirty
=1; /* Assume that the settings have been changed */
430 struct viewport
*parentvp
= NULL
;
431 const struct settings_list
* vol
= rb
->find_setting(&rb
->global_settings
->volume
, NULL
);
435 result
= rb
->do_menu(&menu
, &selected
, NULL
, false);
439 case 0: /* Frameskip */
440 rb
->set_option("Max Frameskip", &options
.maxskip
, INT
, frameskip
,
441 sizeof(frameskip
)/sizeof(*frameskip
), NULL
);
443 case 1: /* Autosave */
444 rb
->set_option("Autosave", &options
.autosave
, INT
, onoff
, 2, NULL
);
447 if(options
.sound
>1) options
.sound
=1;
448 rb
->set_option("Sound", &options
.sound
, INT
, onoff
, 2, NULL
);
449 if(options
.sound
) sound_dirty();
452 rb
->option_screen((struct settings_list
*)vol
, parentvp
, false, "Volume");
455 rb
->set_option("Stats", &options
.showstats
, INT
, stats
, 3, NULL
);
460 #ifdef HAVE_LCD_COLOR
461 case 6: /* Screen Size */
462 rb
->set_option("Screen Size", &options
.scaling
, INT
, scaling
,
463 sizeof(scaling
)/sizeof(*scaling
), NULL
);
466 case 7: /* Screen rotate */
467 rb
->set_option("Screen Rotate", &options
.rotate
, INT
, rotate
,
468 sizeof(rotate
)/sizeof(*rotate
), NULL
);
471 case 8: /* Palette */
472 rb
->set_option("Set Palette", &options
.pal
, INT
, palette
, 17, NULL
);