2882b26a99
1) Adapt Rockboy to smaller screens (H10, X5, and iPod Nano). 2) Add the ability to use a preset palette on color targets. Choose 'Set Palette' from the main menu. 3) Clean up the code to remove any unused code and variables. 4) Changed tabs to spaces. 5) Disable reading and writing sound when sound is disabled. 6) Disbable writing to the RTC since it is not implemented yet. 7) Minor optimizations from WAC gnuboy CE and iBoy. 8) Massive clean up of code to make it appear consistent. 9) Change all C++ style comments to C style. 10) Completely reorganize dynarec. Add fixmes to all unimplemented opcodes. Add debug writes for all opcodes. Attempt to implement a few opcodes myself. 11) Silence some warnings when built using dynarec. 12) Minor reshuffling of IRAM, may or not offer a speed increase. 13) Include fixes found in the short-lived gnuboy CVS. All in all, there's about a 10% improvement on my test roms when sound is disabled and slight improvement with sound. Especially noticable when there are few sprites on screen and less action is occurring. See FS #6567. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12216 a1c6a512-1295-4272-9138-f99709370657
418 lines
10 KiB
C
418 lines
10 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
|
|
|
|
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
|
|
#define MENU_BUTTON_UP BUTTON_SCROLL_UP
|
|
#define MENU_BUTTON_DOWN BUTTON_SCROLL_DOWN
|
|
#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[]= {
|
|
{ "Unscaled", -1 },
|
|
{ "Scaled", -1 },
|
|
{ "Scaled - Maintain Ratio", -1 },
|
|
};
|
|
|
|
static const struct opt_items frameskip[]= {
|
|
{ "3 Max", -1 },
|
|
{ "4 Max", -1 },
|
|
{ "5 Max", -1 },
|
|
{ "6 Max", -1 },
|
|
};
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
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
|
|
|
|
static const struct menu_item items[] = {
|
|
{ "Max Frameskip", NULL },
|
|
{ "Sound" , NULL },
|
|
{ "Stats" , NULL },
|
|
{ "Screen Options" , NULL },
|
|
{ "Set Keys (Buggy)", NULL },
|
|
#ifdef HAVE_LCD_COLOR
|
|
{ "Set Palette" , NULL },
|
|
#endif
|
|
};
|
|
|
|
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 );
|
|
if(options.sound) sound_dirty();
|
|
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;
|
|
#ifdef HAVE_LCD_COLOR
|
|
case 5: /* Palette */
|
|
rb->set_option(items[5].desc, &options.pal, INT, palette, 17, NULL );
|
|
set_pal();
|
|
break;
|
|
#endif
|
|
default:
|
|
done=true;
|
|
break;
|
|
}
|
|
}
|
|
rb->menu_exit(m);
|
|
}
|