1775d4f438
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11663 a1c6a512-1295-4272-9138-f99709370657
379 lines
9.3 KiB
C
379 lines
9.3 KiB
C
/*********************************************************************/
|
|
/* menu.c - user menu for rockboy */
|
|
/* */
|
|
/* Note: this file only exposes one function: do_user_menu(). */
|
|
/*********************************************************************/
|
|
|
|
#include "button.h"
|
|
#include "rockmacros.h"
|
|
#include "mem.h"
|
|
|
|
#if (CONFIG_KEYPAD == IPOD_4G_PAD)
|
|
#define MENU_BUTTON_UP BUTTON_SCROLL_BACK
|
|
#define MENU_BUTTON_DOWN BUTTON_SCROLL_FWD
|
|
#define MENU_BUTTON_LEFT BUTTON_LEFT
|
|
#define MENU_BUTTON_RIGHT BUTTON_RIGHT
|
|
#else
|
|
#define MENU_BUTTON_UP BUTTON_UP
|
|
#define MENU_BUTTON_DOWN BUTTON_DOWN
|
|
#define MENU_BUTTON_LEFT BUTTON_LEFT
|
|
#define MENU_BUTTON_RIGHT BUTTON_RIGHT
|
|
#endif
|
|
|
|
/* load/save state function declarations */
|
|
static void do_opt_menu(void);
|
|
static void do_slot_menu(bool is_load);
|
|
static void munge_name(char *buf, size_t bufsiz);
|
|
|
|
/* directory ROM save slots belong in */
|
|
#define STATE_DIR "/.rockbox/rockboy"
|
|
|
|
int getbutton(char *text)
|
|
{
|
|
rb->lcd_putsxy(0, 0, text);
|
|
rb->lcd_update();
|
|
rb->sleep(30);
|
|
|
|
while (rb->button_get(false) != BUTTON_NONE)
|
|
rb->yield();
|
|
|
|
int button;
|
|
while(true)
|
|
{
|
|
button = rb->button_get(true);
|
|
button=button&0x00000FFF;
|
|
|
|
switch(button)
|
|
{
|
|
case MENU_BUTTON_LEFT:
|
|
case MENU_BUTTON_RIGHT:
|
|
case MENU_BUTTON_UP:
|
|
case MENU_BUTTON_DOWN:
|
|
break;
|
|
default:
|
|
return button;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setupkeys(void)
|
|
{
|
|
options.A=getbutton("Press A");
|
|
|
|
options.B=getbutton("Press B");
|
|
|
|
options.START=getbutton("Press Start");
|
|
|
|
options.SELECT=getbutton("Press Select");
|
|
|
|
options.MENU=getbutton("Press Menu");
|
|
}
|
|
|
|
/*
|
|
* do_user_menu - create the user menu on the screen.
|
|
*
|
|
* Returns USER_MENU_QUIT if the user selected "quit", otherwise
|
|
* returns zero.
|
|
*/
|
|
int do_user_menu(void) {
|
|
bool done=false;
|
|
int m, ret=0;
|
|
int result;
|
|
|
|
/* Clean out the button Queue */
|
|
while (rb->button_get(false) != BUTTON_NONE)
|
|
rb->yield();
|
|
|
|
static const struct menu_item items[] = {
|
|
{"Load Game", NULL },
|
|
{"Save Game", NULL },
|
|
{"Options", NULL },
|
|
{"Quit", NULL },
|
|
};
|
|
|
|
pcm_init();
|
|
|
|
m = rb->menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
|
|
|
|
while(!done)
|
|
{
|
|
result=rb->menu_show(m);
|
|
|
|
switch (result)
|
|
{
|
|
case 0: /* Load Game */
|
|
do_slot_menu(true);
|
|
break;
|
|
case 1: /* Save Game */
|
|
do_slot_menu(false);
|
|
break;
|
|
case 2: /* Options */
|
|
do_opt_menu();
|
|
break;
|
|
case 3: /* Quit */
|
|
ret = USER_MENU_QUIT;
|
|
done=true;
|
|
break;
|
|
default:
|
|
done=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rb->menu_exit(m);
|
|
|
|
rb->lcd_setfont(0); /* Reset the font */
|
|
rb->lcd_clear_display(); /* Clear display for screen size changes */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* munge_name - munge a string into a filesystem-safe name
|
|
*/
|
|
static void munge_name(char *buf, const size_t bufsiz) {
|
|
unsigned int i, max;
|
|
|
|
/* check strlen */
|
|
max = strlen(buf);
|
|
max = (max < bufsiz) ? max : bufsiz;
|
|
|
|
/* iterate over characters and munge them (if necessary) */
|
|
for (i = 0; i < max; i++)
|
|
if (!isalnum(buf[i]))
|
|
buf[i] = '_';
|
|
}
|
|
|
|
/*
|
|
* build_slot_path - build a path to an slot state file for this rom
|
|
*
|
|
* Note: uses rom.name. Is there a safer way of doing this? Like a ROM
|
|
* checksum or something like that?
|
|
*/
|
|
static void build_slot_path(char *buf, size_t bufsiz, size_t slot_id) {
|
|
char name_buf[40];
|
|
|
|
/* munge state file name */
|
|
strncpy(name_buf, rom.name, 40);
|
|
name_buf[16] = '\0';
|
|
munge_name(name_buf, strlen(name_buf));
|
|
|
|
/* glom the whole mess together */
|
|
snprintf(buf, bufsiz, "%s/%s-%d.rbs", STATE_DIR, name_buf, slot_id + 1);
|
|
}
|
|
|
|
/*
|
|
* do_file - load or save game data in the given file
|
|
*
|
|
* Returns true on success and false on failure.
|
|
*
|
|
* @desc is a brief user-provided description (<20 bytes) of the state.
|
|
* If no description is provided, set @desc to NULL.
|
|
*
|
|
*/
|
|
static bool do_file(char *path, char *desc, bool is_load) {
|
|
char buf[200], desc_buf[20];
|
|
int fd, file_mode;
|
|
|
|
/* set file mode */
|
|
file_mode = is_load ? O_RDONLY : (O_WRONLY | O_CREAT);
|
|
|
|
/* attempt to open file descriptor here */
|
|
if ((fd = open(path, file_mode)) <= 0)
|
|
return false;
|
|
|
|
/* load/save state */
|
|
if (is_load)
|
|
{
|
|
/* load description */
|
|
read(fd, desc_buf, 20);
|
|
|
|
/* load state */
|
|
loadstate(fd);
|
|
|
|
/* print out a status message so the user knows the state loaded */
|
|
snprintf(buf, 200, "Loaded state from \"%s\"", path);
|
|
rb->splash(HZ * 1, true, buf);
|
|
}
|
|
else
|
|
{
|
|
/* build description buffer */
|
|
memset(desc_buf, 0, 20);
|
|
if (desc)
|
|
strncpy(desc_buf, desc, 20);
|
|
|
|
/* save state */
|
|
write(fd, desc_buf, 20);
|
|
savestate(fd);
|
|
}
|
|
|
|
/* close file descriptor */
|
|
close(fd);
|
|
|
|
/* return true (for success) */
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* do_slot - load or save game data in the given slot
|
|
*
|
|
* Returns true on success and false on failure.
|
|
*/
|
|
static bool do_slot(size_t slot_id, bool is_load) {
|
|
char path_buf[256], desc_buf[20];
|
|
|
|
/* build slot filename, clear desc buf */
|
|
build_slot_path(path_buf, 256, slot_id);
|
|
memset(desc_buf, 0, 20);
|
|
|
|
/* if we're saving to a slot, then get a brief description */
|
|
if (!is_load)
|
|
if (rb->kbd_input(desc_buf, 20) || !strlen(desc_buf))
|
|
{
|
|
memset(desc_buf, 0, 20);
|
|
strncpy(desc_buf, "Untitled", 20);
|
|
}
|
|
|
|
/* load/save file */
|
|
return do_file(path_buf, desc_buf, is_load);
|
|
}
|
|
|
|
/*
|
|
* get information on the given slot
|
|
*/
|
|
static void slot_info(char *info_buf, size_t info_bufsiz, size_t slot_id) {
|
|
char buf[256];
|
|
int fd;
|
|
|
|
/* get slot file path */
|
|
build_slot_path(buf, 256, slot_id);
|
|
|
|
/* attempt to open slot */
|
|
if ((fd = open(buf, O_RDONLY)) >= 0)
|
|
{
|
|
/* this slot has a some data in it, read it */
|
|
if (read(fd, buf, 20) > 0)
|
|
{
|
|
buf[20] = '\0';
|
|
snprintf(info_buf, info_bufsiz, "%d. %s", slot_id + 1, buf);
|
|
}
|
|
else
|
|
snprintf(info_buf, info_bufsiz, "%d. ERROR", slot_id + 1);
|
|
|
|
close(fd);
|
|
}
|
|
else
|
|
{
|
|
/* if we couldn't open the file, then the slot is empty */
|
|
snprintf(info_buf, info_bufsiz, "%d. %s", slot_id + 1, "<Empty>");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* do_slot_menu - prompt the user for a load/save memory slot
|
|
*/
|
|
static void do_slot_menu(bool is_load) {
|
|
bool done=false;
|
|
|
|
char buf[5][20];
|
|
|
|
int m;
|
|
int result;
|
|
int i;
|
|
|
|
struct menu_item items[] = {
|
|
{ buf[0] , NULL },
|
|
{ buf[1] , NULL },
|
|
{ buf[2] , NULL },
|
|
{ buf[3] , NULL },
|
|
{ buf[4] , NULL },
|
|
};
|
|
|
|
int num_items = sizeof(items) / sizeof(*items);
|
|
|
|
/* create menu items */
|
|
for (i = 0; i < num_items; i++)
|
|
slot_info(buf[i], 20, i);
|
|
|
|
m = rb->menu_init(items, num_items, NULL, NULL, NULL, NULL);
|
|
|
|
while(!done)
|
|
{
|
|
result=rb->menu_show(m);
|
|
|
|
if (result<num_items && result >= 0 )
|
|
done = do_slot(result, is_load);
|
|
else
|
|
done = true;
|
|
}
|
|
rb->menu_exit(m);
|
|
}
|
|
|
|
static void do_opt_menu(void)
|
|
{
|
|
bool done=false;
|
|
int m;
|
|
int result;
|
|
|
|
static const struct opt_items onoff[2] = {
|
|
{ "Off", -1 },
|
|
{ "On" , -1 },
|
|
};
|
|
|
|
static const struct opt_items fullscreen[]= {
|
|
{ "Off", -1 },
|
|
{ "Fullscreen", -1 },
|
|
{ "Full - Maintain Ratio", -1 },
|
|
};
|
|
|
|
static const struct opt_items frameskip[]= {
|
|
{ "3 Max", -1 },
|
|
{ "4 Max", -1 },
|
|
{ "5 Max", -1 },
|
|
{ "6 Max", -1 },
|
|
};
|
|
|
|
static const struct menu_item items[] = {
|
|
{ "Max Frameskip", NULL },
|
|
{ "Sound" , NULL },
|
|
{ "Stats" , NULL },
|
|
{ "Fullscreen" , NULL },
|
|
{ "Set Keys (Buggy)", NULL },
|
|
};
|
|
|
|
m = rb->menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
|
|
|
|
while(!done)
|
|
{
|
|
|
|
result=rb->menu_show(m);
|
|
|
|
switch (result)
|
|
{
|
|
case 0: /* Frameskip */
|
|
options.maxskip-=3;
|
|
rb->set_option(items[0].desc, &options.maxskip, INT, frameskip, 4, NULL );
|
|
options.maxskip+=3;
|
|
break;
|
|
case 1: /* Sound */
|
|
if(options.sound>1) options.sound=1;
|
|
rb->set_option(items[1].desc, &options.sound, INT, onoff, 2, NULL );
|
|
break;
|
|
case 2: /* Stats */
|
|
rb->set_option(items[2].desc, &options.showstats, INT, onoff, 2, NULL );
|
|
break;
|
|
case 3: /* Fullscreen */
|
|
rb->set_option(items[3].desc, &options.fullscreen, INT, fullscreen, 3, NULL );
|
|
setvidmode(options.fullscreen);
|
|
break;
|
|
case 4: /* Keys */
|
|
setupkeys();
|
|
break;
|
|
default:
|
|
done=true;
|
|
break;
|
|
}
|
|
}
|
|
rb->menu_exit(m);
|
|
}
|