701e262d3d
Now that all users have replaced setting IDs with direct pointers, find_setting() and friends don't need to return an ID value. Change-Id: I8c5c31bb68d3bca5350d43538335265a55fd5517
481 lines
13 KiB
C
481 lines
13 KiB
C
/*********************************************************************/
|
|
/* menu.c - user menu for rockboy */
|
|
/* */
|
|
/* Note: this file only exposes one function: do_user_menu(). */
|
|
/*********************************************************************/
|
|
|
|
#include "lib/helper.h"
|
|
#include "button.h"
|
|
#include "rockmacros.h"
|
|
#include "mem.h"
|
|
#include "save.h"
|
|
#include "rtc-gb.h"
|
|
#include "pcm.h"
|
|
#include "emu.h"
|
|
#include "loader.h"
|
|
|
|
#define SLOT_COUNT 50
|
|
#define DESC_SIZE 20
|
|
|
|
/* 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_DIR "/rockboy"
|
|
|
|
static int getbutton(char *text)
|
|
{
|
|
int fw, fh;
|
|
int button;
|
|
rb->lcd_clear_display();
|
|
rb->font_getstringsize(text, &fw, &fh,0);
|
|
rb->lcd_putsxy(LCD_WIDTH/2-fw/2, LCD_HEIGHT/2-fh/2, text);
|
|
rb->lcd_update();
|
|
rb->sleep(30);
|
|
|
|
while (rb->button_get(false) != BUTTON_NONE)
|
|
rb->yield();
|
|
|
|
while(true)
|
|
{
|
|
button = rb->button_get(true);
|
|
button = button&(BUTTON_MAIN|BUTTON_REMOTE);
|
|
|
|
return button;
|
|
}
|
|
}
|
|
|
|
static void setupkeys(void)
|
|
{
|
|
options.UP = getbutton("Press Up");
|
|
options.DOWN = getbutton("Press Down");
|
|
options.LEFT = getbutton("Press Left");
|
|
options.RIGHT = getbutton("Press Right");
|
|
|
|
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 selected=0, ret=0;
|
|
int result;
|
|
int time = 0;
|
|
|
|
#if CONFIG_RTC
|
|
time = rb->mktime(rb->get_time());
|
|
#endif
|
|
|
|
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
|
|
rb->lcd_set_mode(LCD_MODE_RGB565);
|
|
#endif
|
|
|
|
backlight_use_settings();
|
|
|
|
/* Clean out the button Queue */
|
|
while (rb->button_get(false) != BUTTON_NONE)
|
|
rb->yield();
|
|
|
|
MENUITEM_STRINGLIST(menu, "Rockboy Menu", NULL,
|
|
"Load Game", "Save Game",
|
|
"Options", "Reset", "Quit");
|
|
|
|
rockboy_pcm_init();
|
|
|
|
while(!done)
|
|
{
|
|
result = rb->do_menu(&menu, &selected, NULL, false);
|
|
|
|
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: /* Reset */
|
|
emu_reset();
|
|
done=true;
|
|
break;
|
|
case 4: /* Quit */
|
|
ret = USER_MENU_QUIT;
|
|
if(options.autosave) sn_save();
|
|
done=true;
|
|
break;
|
|
default:
|
|
done=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rb->lcd_setfont(FONT_SYSFIXED); /* Reset the font */
|
|
rb->lcd_clear_display(); /* Clear display for screen size changes */
|
|
|
|
/* Keep the RTC in sync */
|
|
#if CONFIG_RTC
|
|
time = (rb->mktime(rb->get_time()) - time) * 60;
|
|
#endif
|
|
while (time-- > 0) rtc_tick();
|
|
|
|
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
|
|
rb->lcd_set_mode(LCD_MODE_PAL256);
|
|
#endif
|
|
|
|
/* ignore backlight time out */
|
|
backlight_ignore_timeout();
|
|
|
|
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, int slot_id) {
|
|
char name_buf[17];
|
|
|
|
/* munge state file name */
|
|
strlcpy(name_buf, rom.name, sizeof(name_buf));
|
|
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 of the state.
|
|
* If no description is provided, set @desc to NULL.
|
|
*
|
|
*/
|
|
static bool do_file(char *path, char *desc, bool is_load) {
|
|
char desc_buf[DESC_SIZE];
|
|
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, 0666)) < 0)
|
|
return false;
|
|
|
|
/* load/save state */
|
|
if (is_load)
|
|
{
|
|
/* load description */
|
|
read(fd, desc_buf, sizeof(desc_buf));
|
|
|
|
/* load state */
|
|
loadstate(fd);
|
|
|
|
/* print out a status message so the user knows the state loaded */
|
|
rb->splashf(HZ * 1, "Loaded state from \"%s\"", path);
|
|
}
|
|
else
|
|
{
|
|
/* build description buffer */
|
|
memset(desc_buf, 0, sizeof(desc_buf));
|
|
if (desc)
|
|
strlcpy(desc_buf, desc, sizeof(desc_buf));
|
|
|
|
/* save state */
|
|
write(fd, desc_buf, sizeof(desc_buf));
|
|
savestate(fd);
|
|
}
|
|
|
|
/* close file descriptor */
|
|
close(fd);
|
|
|
|
/* return true (for success) */
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* get information on the given slot
|
|
*/
|
|
static void slot_info(char *info_buf, size_t info_bufsiz, int slot_id,
|
|
bool number) {
|
|
char buf[256];
|
|
int fd;
|
|
|
|
/* get slot file path */
|
|
build_slot_path(buf, sizeof(buf), 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, DESC_SIZE) == DESC_SIZE)
|
|
{
|
|
buf[DESC_SIZE] = '\0';
|
|
strlcpy(info_buf, buf, info_bufsiz);
|
|
}
|
|
else if(number)
|
|
strlcpy(info_buf, "ERROR", info_bufsiz);
|
|
|
|
close(fd);
|
|
}
|
|
else if(number)
|
|
{
|
|
/* if we couldn't open the file, then the slot is empty */
|
|
strlcpy(info_buf, "<Empty>", info_bufsiz);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* do_slot - load or save game data in the given slot
|
|
*
|
|
* Returns true on success and false on failure.
|
|
*/
|
|
static bool do_slot(int slot_id, bool is_load) {
|
|
char path_buf[256], desc_buf[DESC_SIZE];
|
|
|
|
/* build slot filename, clear desc buf */
|
|
build_slot_path(path_buf, sizeof(path_buf), slot_id);
|
|
memset(desc_buf, 0, sizeof(desc_buf));
|
|
|
|
/* if we're saving to a slot, then get a brief description */
|
|
if (!is_load)
|
|
{
|
|
slot_info(desc_buf, sizeof(desc_buf), slot_id, false);
|
|
if ( rb->kbd_input(desc_buf, sizeof(desc_buf), NULL) < 0 )
|
|
return false;
|
|
if ( !strlen(desc_buf) )
|
|
strlcpy(desc_buf, "Untitled", sizeof(desc_buf));
|
|
}
|
|
|
|
/* load/save file */
|
|
return do_file(path_buf, desc_buf, is_load);
|
|
}
|
|
|
|
/*
|
|
* slot_get_name
|
|
*/
|
|
static const char* slot_get_name(int selected_item, void * data,
|
|
char * buffer, size_t buffer_len)
|
|
{
|
|
const char (*items)[DESC_SIZE] = data;
|
|
snprintf(buffer, buffer_len, "%d. %s",
|
|
selected_item + 1, items[selected_item]);
|
|
return buffer;
|
|
}
|
|
|
|
/*
|
|
* list_action_callback
|
|
*/
|
|
static int list_action_callback(int action, struct gui_synclist *lists)
|
|
{
|
|
(void) lists;
|
|
if (action == ACTION_STD_OK)
|
|
return ACTION_STD_CANCEL;
|
|
return action;
|
|
}
|
|
|
|
/*
|
|
* do_slot_menu - prompt the user for a load/save memory slot
|
|
*/
|
|
static void do_slot_menu(bool is_load) {
|
|
bool done=false;
|
|
char items[SLOT_COUNT][DESC_SIZE];
|
|
int result;
|
|
int i;
|
|
struct simplelist_info info;
|
|
|
|
/* create menu items */
|
|
for (i = 0; i < SLOT_COUNT; i++)
|
|
slot_info(items[i], sizeof(*items), i, true);
|
|
|
|
rb->simplelist_info_init(&info, NULL, SLOT_COUNT, (void *)items);
|
|
info.get_name = slot_get_name;
|
|
info.action_callback = list_action_callback;
|
|
|
|
while(!done)
|
|
{
|
|
if(rb->simplelist_show_list(&info))
|
|
break;
|
|
|
|
result = info.selection;
|
|
if (result<SLOT_COUNT && result >= 0 )
|
|
done = do_slot(result, is_load);
|
|
else
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
static void do_opt_menu(void)
|
|
{
|
|
bool done=false;
|
|
int selected=0;
|
|
int result;
|
|
|
|
static const struct opt_items onoff[2] = {
|
|
{ "Off", -1 },
|
|
{ "On" , -1 },
|
|
};
|
|
|
|
static const struct opt_items stats[3] = {
|
|
{ "Off", -1 },
|
|
{ "Short" , -1 },
|
|
{ "Full" , -1 },
|
|
};
|
|
|
|
static const struct opt_items frameskip[]= {
|
|
{ "0 Max", -1 },
|
|
{ "1 Max", -1 },
|
|
{ "2 Max", -1 },
|
|
{ "3 Max", -1 },
|
|
{ "4 Max", -1 },
|
|
{ "5 Max", -1 },
|
|
{ "6 Max", -1 },
|
|
{ "7 Max", -1 },
|
|
{ "8 Max", -1 },
|
|
{ "9 Max", -1 },
|
|
{ "10 Max", -1 },
|
|
{ "11 Max", -1 },
|
|
{ "12 Max", -1 },
|
|
{ "13 Max", -1 },
|
|
{ "14 Max", -1 },
|
|
{ "15 Max", -1 },
|
|
{ "16 Max", -1 },
|
|
{ "17 Max", -1 },
|
|
{ "18 Max", -1 },
|
|
{ "19 Max", -1 },
|
|
{ "20 Max", -1 },
|
|
};
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
static const struct opt_items rotate[] = {
|
|
{ "No rotation", -1 },
|
|
{ "Rotate Right" , -1 },
|
|
{ "Rotate Left" , -1 },
|
|
};
|
|
|
|
static const struct opt_items scaling[]= {
|
|
{ "Scaled", -1 },
|
|
{ "Scaled - Maintain Ratio", -1 },
|
|
#if (LCD_WIDTH>=160) && (LCD_HEIGHT>=144)
|
|
{ "Unscaled", -1 },
|
|
#endif
|
|
};
|
|
|
|
static const struct opt_items palette[]= {
|
|
{ "Brown (Default)", -1 },
|
|
{ "Gray", -1 },
|
|
{ "Light Gray", -1 },
|
|
{ "Multi-Color 1", -1 },
|
|
{ "Multi-Color 2", -1 },
|
|
{ "Adventure Island", -1 },
|
|
{ "Adventure Island 2", -1 },
|
|
{ "Balloon Kid", -1 },
|
|
{ "Batman", -1 },
|
|
{ "Batman: Return of Joker", -1 },
|
|
{ "Bionic Commando", -1 },
|
|
{ "Castlvania Adventure", -1 },
|
|
{ "Donkey Kong Land", -1 },
|
|
{ "Dr. Mario", -1 },
|
|
{ "Kirby", -1 },
|
|
{ "Metroid", -1 },
|
|
{ "Zelda", -1 },
|
|
};
|
|
#endif
|
|
|
|
MENUITEM_STRINGLIST(menu, "Options", NULL,
|
|
"Max Frameskip", "Autosave", "Sound", "Volume",
|
|
"Stats", "Set Keys (Buggy)",
|
|
#ifdef HAVE_LCD_COLOR
|
|
"Screen Size", "Screen Rotate", "Set Palette",
|
|
#endif
|
|
);
|
|
|
|
options.dirty=1; /* Assume that the settings have been changed */
|
|
|
|
struct viewport *parentvp = NULL;
|
|
const struct settings_list* vol = rb->find_setting(&rb->global_settings->volume);
|
|
|
|
while(!done)
|
|
{
|
|
result = rb->do_menu(&menu, &selected, NULL, false);
|
|
|
|
switch (result)
|
|
{
|
|
case 0: /* Frameskip */
|
|
rb->set_option("Max Frameskip", &options.maxskip, INT, frameskip,
|
|
sizeof(frameskip)/sizeof(*frameskip), NULL );
|
|
break;
|
|
case 1: /* Autosave */
|
|
rb->set_option("Autosave", &options.autosave, INT, onoff, 2, NULL );
|
|
break;
|
|
case 2: /* Sound */
|
|
if(options.sound>1) options.sound=1;
|
|
rb->set_option("Sound", &options.sound, INT, onoff, 2, NULL );
|
|
if(options.sound) sound_dirty();
|
|
break;
|
|
case 3: /* Volume */
|
|
rb->option_screen((struct settings_list*)vol, parentvp, false, "Volume");
|
|
break;
|
|
case 4: /* Stats */
|
|
rb->set_option("Stats", &options.showstats, INT, stats, 3, NULL );
|
|
break;
|
|
case 5: /* Keys */
|
|
setupkeys();
|
|
break;
|
|
#ifdef HAVE_LCD_COLOR
|
|
case 6: /* Screen Size */
|
|
rb->set_option("Screen Size", &options.scaling, INT, scaling,
|
|
sizeof(scaling)/sizeof(*scaling), NULL );
|
|
setvidmode();
|
|
break;
|
|
case 7: /* Screen rotate */
|
|
rb->set_option("Screen Rotate", &options.rotate, INT, rotate,
|
|
sizeof(rotate)/sizeof(*rotate), NULL );
|
|
setvidmode();
|
|
break;
|
|
case 8: /* Palette */
|
|
rb->set_option("Set Palette", &options.pal, INT, palette, 17, NULL );
|
|
set_pal();
|
|
break;
|
|
#endif
|
|
default:
|
|
done=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|