1
0
Fork 0
forked from len0rd/rockbox
foxbox/apps/plugins/rockboy/menu.c
William Wilgus 7100090f99 Remove NVRAM infavor of a separate cfg file
remove nvram and use the existing settings framework for it

add a crc check to the user_settings data to see if we need to save
the user setting file or if we can just save the status file (resume.cfg)

move volume to the system_status struct so we don't write the whole settings file
over volume changes

allow user to still export volume with save sound settings

allow the user to also export pitch and speed

name the file .resume.cfg

Rename all the SYSTEM_STATUS save file variables to TLAs to save space and
discourage tinkering

Cleanup DEBUG_AVAIL_SETTINGS output

when saving user_settings it calls status_save as well this cause the resume
file to be written twice. instead remove the callback for status_save
when setting_save is called

remove header text when saving .resume.cfg

convert status_save() to status_save(bool force)
add SYSTEM_STATUS_UPDATE_TICKS

for ATA device set this to 5 minutes
since we arlready wait for the disk to be up before saving
we don't want to miss our window

for all other every 15 minutes

that way if the battery is too low by the time shutdown comes around you
don't lose much progress

Change-Id: I27214ffd6e5d5494ee5ca83b14f04a41ba426ad7
2025-01-21 00:04:32 -05:00

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_status->volume);
while(!done)
{
result = rb->do_menu(&menu, &selected, NULL, false);
switch (result)
{
case 0: /* Frameskip */
rb->set_option("Max Frameskip", &options.maxskip, RB_INT, frameskip,
sizeof(frameskip)/sizeof(*frameskip), NULL );
break;
case 1: /* Autosave */
rb->set_option("Autosave", &options.autosave, RB_INT, onoff, 2, NULL );
break;
case 2: /* Sound */
if(options.sound>1) options.sound=1;
rb->set_option("Sound", &options.sound, RB_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, RB_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, RB_INT, scaling,
sizeof(scaling)/sizeof(*scaling), NULL );
setvidmode();
break;
case 7: /* Screen rotate */
rb->set_option("Screen Rotate", &options.rotate, RB_INT, rotate,
sizeof(rotate)/sizeof(*rotate), NULL );
setvidmode();
break;
case 8: /* Palette */
rb->set_option("Set Palette", &options.pal, RB_INT, palette, 17, NULL );
set_pal();
break;
#endif
default:
done=true;
break;
}
}
}