#include "common.h"\r
#include "input.h"\r
#include "emu.h"\r
+#include "plat.h"\r
\r
+#include <pico/patch.h>\r
\r
char menuErrorMsg[64] = { 0, };\r
\r
void text_out16(int x, int y, const char *texto, ...)\r
{\r
va_list args;\r
- char buffer[512];\r
+ char buffer[256];\r
+ int maxw = (SCREEN_WIDTH - x) / 8;\r
\r
- va_start(args,texto);\r
- vsprintf(buffer,texto,args);\r
+ va_start(args, texto);\r
+ vsnprintf(buffer, sizeof(buffer), texto, args);\r
va_end(args);\r
\r
+ if (maxw > 255)\r
+ maxw = 255;\r
+ buffer[maxw] = 0;\r
+\r
text_out16_(x,y,buffer,menu_text_color);\r
}\r
\r
}\r
\r
\r
-int me_id2offset(const menu_entry *entries, int count, menu_id id)\r
+int me_id2offset(const menu_entry *ent, menu_id id)\r
{\r
int i;\r
- for (i = 0; i < count; i++)\r
- {\r
- if (entries[i].id == id) return i;\r
- }\r
+ for (i = 0; ent->name; ent++, i++)\r
+ if (ent->id == id) return i;\r
\r
lprintf("%s: id %i not found\n", __FUNCTION__, id);\r
return 0;\r
}\r
\r
-void me_enable(menu_entry *entries, int count, menu_id id, int enable)\r
+void me_enable(menu_entry *entries, menu_id id, int enable)\r
{\r
- int i = me_id2offset(entries, count, id);\r
+ int i = me_id2offset(entries, id);\r
entries[i].enabled = enable;\r
}\r
\r
-int me_count_enabled(const menu_entry *entries, int count)\r
+int me_count(const menu_entry *ent)\r
{\r
- int i, ret = 0;\r
+ int ret;\r
\r
- for (i = 0; i < count; i++)\r
- {\r
- if (entries[i].enabled) ret++;\r
- }\r
+ for (ret = 0; ent->name; ent++, ret++)\r
+ ;\r
\r
return ret;\r
}\r
\r
-menu_id me_index2id(const menu_entry *entries, int count, int index)\r
+menu_id me_index2id(const menu_entry *ent, int index)\r
{\r
- int i;\r
+ const menu_entry *last;\r
\r
- for (i = 0; i < count; i++)\r
+ for (; ent->name; ent++)\r
{\r
- if (entries[i].enabled)\r
+ if (ent->enabled)\r
{\r
if (index == 0) break;\r
index--;\r
}\r
+ last = ent;\r
}\r
- if (i >= count) i = count - 1;\r
- return entries[i].id;\r
+ if (ent->name == NULL)\r
+ ent = last;\r
+ return ent->id;\r
}\r
\r
+/* TODO rm */\r
void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param)\r
{\r
int i, y1 = y;\r
continue;\r
}\r
text_out16(x, y1, entries[i].name);\r
- if (entries[i].beh == MB_ONOFF)\r
+ if (entries[i].beh == MB_OPT_ONOFF)\r
text_out16(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF");\r
- else if (entries[i].beh == MB_RANGE)\r
+ else if (entries[i].beh == MB_OPT_RANGE)\r
text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var);\r
y1 += 10;\r
}\r
+\r
+}\r
+\r
+static void me_draw2(const menu_entry *entries, int sel)\r
+{\r
+ const menu_entry *ent;\r
+ int x, y, w = 0, h = 0;\r
+ int opt_offs = 27*8;\r
+ const char *name;\r
+ int asel = 0;\r
+ int i, n;\r
+\r
+ /* calculate size of menu rect */\r
+ for (ent = entries, i = n = 0; ent->name; ent++, i++)\r
+ {\r
+ int wt;\r
+\r
+ if (!ent->enabled)\r
+ continue;\r
+\r
+ if (i == sel)\r
+ asel = n;\r
+\r
+ name = NULL;\r
+ wt = strlen(ent->name) * 8; /* FIXME: unhardcode font width */\r
+ if (wt == 0 && ent->generate_name)\r
+ name = ent->generate_name(1);\r
+ if (name != NULL)\r
+ wt = strlen(name) * 8;\r
+\r
+ if (ent->beh != MB_NONE)\r
+ {\r
+ if (wt > opt_offs)\r
+ opt_offs = wt + 8;\r
+ wt = opt_offs;\r
+\r
+ switch (ent->beh) {\r
+ case MB_NONE: break;\r
+ case MB_OPT_ONOFF:\r
+ case MB_OPT_RANGE: wt += 8*3; break;\r
+ case MB_OPT_CUSTOM:\r
+ name = NULL;\r
+ if (ent->generate_name != NULL)\r
+ name = ent->generate_name(0);\r
+ if (name != NULL)\r
+ wt += strlen(name) * 8;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (wt > w)\r
+ w = wt;\r
+ n++;\r
+ }\r
+ h = n * 10;\r
+ w += 16; /* selector */\r
+\r
+ if (w > SCREEN_WIDTH) {\r
+ lprintf("width %d > %d\n", w, SCREEN_WIDTH);\r
+ w = SCREEN_WIDTH;\r
+ }\r
+ if (h > SCREEN_HEIGHT) {\r
+ lprintf("height %d > %d\n", w, SCREEN_HEIGHT);\r
+ h = SCREEN_HEIGHT;\r
+ }\r
+\r
+ x = SCREEN_WIDTH / 2 - w / 2;\r
+ y = SCREEN_HEIGHT / 2 - h / 2;\r
+\r
+ /* draw */\r
+ plat_video_menu_begin();\r
+ menu_draw_selection(x, y + asel * 10, w);\r
+\r
+ for (ent = entries; ent->name; ent++)\r
+ {\r
+ if (!ent->enabled)\r
+ continue;\r
+\r
+ name = ent->name;\r
+ if (strlen(name) == 0) {\r
+ if (ent->generate_name)\r
+ name = ent->generate_name(1);\r
+ }\r
+ if (name != NULL)\r
+ text_out16(x + 16, y, name);\r
+\r
+ switch (ent->beh) {\r
+ case MB_NONE:\r
+ break;\r
+ case MB_OPT_ONOFF:\r
+ text_out16(x + 16 + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");\r
+ break;\r
+ case MB_OPT_RANGE:\r
+ text_out16(x + 16 + opt_offs, y, "%i", *(int *)ent->var);\r
+ break;\r
+ case MB_OPT_CUSTOM:\r
+ name = NULL;\r
+ if (ent->generate_name)\r
+ name = ent->generate_name(0);\r
+ if (name != NULL)\r
+ text_out16(x + 16 + opt_offs, y, "%s", name);\r
+ break;\r
+ }\r
+\r
+ y += 10;\r
+ }\r
+\r
+ plat_video_menu_end();\r
}\r
\r
-int me_process(menu_entry *entries, int count, menu_id id, int is_next)\r
+int me_process(menu_entry *entries, menu_id id, int is_next)\r
{\r
- int i = me_id2offset(entries, count, id);\r
+ int i = me_id2offset(entries, id);\r
menu_entry *entry = &entries[i];\r
switch (entry->beh)\r
{\r
- case MB_ONOFF:\r
+ case MB_OPT_ONOFF:\r
*(int *)entry->var ^= entry->mask;\r
return 1;\r
- case MB_RANGE:\r
+ case MB_OPT_RANGE:\r
*(int *)entry->var += is_next ? 1 : -1;\r
if (*(int *)entry->var < (int)entry->min) *(int *)entry->var = (int)entry->min;\r
if (*(int *)entry->var > (int)entry->max) *(int *)entry->var = (int)entry->max;\r
}\r
}\r
\r
+static void me_loop(menu_entry *menu, int *menu_sel)\r
+{\r
+ int ret, inp, sel = *menu_sel, menu_sel_max;\r
+\r
+ menu_sel_max = me_count(menu) - 1;\r
+ if (menu_sel_max < 1) {\r
+ lprintf("no enabled menu entries\n");\r
+ return;\r
+ }\r
+\r
+ while (!menu[sel].enabled && sel < menu_sel_max)\r
+ sel++;\r
+\r
+ /* make sure action buttons are not pressed on entering menu */\r
+ me_draw2(menu, sel);\r
+ while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU));\r
+\r
+ for (;;)\r
+ {\r
+ me_draw2(menu, sel);\r
+ inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R);\r
+ if (inp & PBTN_UP ) {\r
+ do {\r
+ sel--;\r
+ if (sel < 0)\r
+ sel = menu_sel_max;\r
+ }\r
+ while (!menu[sel].enabled);\r
+ }\r
+ if (inp & PBTN_DOWN) {\r
+ do {\r
+ sel++;\r
+ if (sel > menu_sel_max)\r
+ sel = 0;\r
+ }\r
+ while (!menu[sel].enabled);\r
+ }\r
+// if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R)) debug_menu_loop(); // TODO\r
+ if (inp & (PBTN_MENU|PBTN_MBACK))\r
+ break;\r
+\r
+ if (inp & PBTN_MOK)\r
+ {\r
+ if (menu[sel].submenu_handler != NULL) {\r
+ ret = menu[sel].submenu_handler(menu[sel].id);\r
+ if (ret) break;\r
+ }\r
+ }\r
+// menuErrorMsg[0] = 0; // TODO: clear error msg\r
+ }\r
+ *menu_sel = sel;\r
+}\r
+\r
+/* ***************************************** */\r
+\r
+/* TODO s */\r
+int menu_loop_tray(void) { return 0; }\r
+void menu_romload_prepare(const char *rom_name) {}\r
+void menu_romload_end(void) {}\r
+me_bind_action emuctrl_actions[1];\r
+menu_entry opt_entries[1];\r
+menu_entry opt2_entries[1];\r
+menu_entry cdopt_entries[1];\r
+menu_entry ctrlopt_entries[1];\r
+const int opt_entry_count = 0;\r
+const int opt2_entry_count = 0;\r
+const int cdopt_entry_count = 0;\r
+const int ctrlopt_entry_count = 0;\r
+\r
+extern int engineState;\r
+\r
+int savestate_menu_loop(int a) { return 1; }\r
+int menu_loop_options() { return 1; }\r
+void kc_sel_loop() {}\r
+void draw_menu_credits() {}\r
+void patches_menu_loop() {}\r
+\r
+// ------------ main menu ------------\r
+\r
+static int main_menu_handler(menu_id id)\r
+{\r
+ int ret;\r
+\r
+ switch (id)\r
+ {\r
+ case MA_MAIN_RESUME_GAME:\r
+ if (rom_loaded) {\r
+ while (in_menu_wait_any(50) & PBTN_MOK);\r
+ engineState = PGS_Running;\r
+ return 1;\r
+ }\r
+ break;\r
+ case MA_MAIN_SAVE_STATE:\r
+ if (rom_loaded) {\r
+ if (savestate_menu_loop(0))\r
+ break;\r
+ engineState = PGS_Running;\r
+ return 1;\r
+ }\r
+ break;\r
+ case MA_MAIN_LOAD_STATE:\r
+ if (rom_loaded) {\r
+ if (savestate_menu_loop(1))\r
+ break;\r
+ while (in_menu_wait_any(50) & PBTN_MOK);\r
+ engineState = PGS_Running;\r
+ return 1;\r
+ }\r
+ break;\r
+ case MA_MAIN_RESET_GAME:\r
+ if (rom_loaded) {\r
+ emu_ResetGame();\r
+ while (in_menu_wait_any(50) & PBTN_MOK);\r
+ engineState = PGS_Running;\r
+ return 1;\r
+ }\r
+ break;\r
+ case MA_MAIN_LOAD_ROM:\r
+ {\r
+/* char curr_path[PATH_MAX], *selfname;\r
+ FILE *tstf;\r
+ if ( (tstf = fopen(loadedRomFName, "rb")) )\r
+ {\r
+ fclose(tstf);\r
+ strcpy(curr_path, loadedRomFName);\r
+ }\r
+ else\r
+ getcwd(curr_path, PATH_MAX);\r
+ selfname = romsel_loop(curr_path);\r
+ if (selfname) {\r
+ printf("selected file: %s\n", selfname);\r
+ engineState = PGS_ReloadRom;\r
+ return;\r
+ }*/\r
+ break;\r
+ }\r
+ case MA_MAIN_OPTIONS:\r
+ ret = menu_loop_options();\r
+ if (ret == 1) break; // status update\r
+ if (engineState == PGS_ReloadRom)\r
+ return 1; // BIOS test\r
+ break;\r
+ case MA_MAIN_CONTROLS:\r
+ kc_sel_loop();\r
+ break;\r
+ case MA_MAIN_CREDITS:\r
+ draw_menu_credits();\r
+ usleep(500*1000); /* FIXME */\r
+ in_menu_wait(PBTN_MOK|PBTN_MBACK);\r
+ break;\r
+ case MA_MAIN_EXIT:\r
+ engineState = PGS_Quit;\r
+ return 1;\r
+ case MA_MAIN_PATCHES:\r
+ if (rom_loaded && PicoPatches) {\r
+ patches_menu_loop();\r
+ PicoPatchApply();\r
+ strcpy(menuErrorMsg, "Patches applied");\r
+ }\r
+ break;\r
+ default:\r
+ lprintf("%s: something unknown selected\n", __FUNCTION__);\r
+ break;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+menu_entry e_main_menu[] =\r
+{\r
+ mee_submenu_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),\r
+ mee_submenu_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),\r
+ mee_submenu_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),\r
+ mee_submenu_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),\r
+ mee_submenu_id("Load new ROM/ISO", MA_MAIN_LOAD_ROM, main_menu_handler),\r
+ mee_submenu_id("Change options", MA_MAIN_OPTIONS, main_menu_handler),\r
+ mee_submenu_id("Credits", MA_MAIN_CREDITS, main_menu_handler),\r
+ mee_submenu_id("Patches / GameGenie",MA_MAIN_PATCHES, main_menu_handler),\r
+ mee_submenu_id("Exit", MA_MAIN_EXIT, main_menu_handler),\r
+ mee_end,\r
+};\r
+\r
+void menu_loop(void)\r
+{\r
+ static int sel = 0;\r
+\r
+ me_enable(e_main_menu, MA_MAIN_RESUME_GAME, rom_loaded);\r
+ me_enable(e_main_menu, MA_MAIN_SAVE_STATE, rom_loaded);\r
+ me_enable(e_main_menu, MA_MAIN_LOAD_STATE, rom_loaded);\r
+ me_enable(e_main_menu, MA_MAIN_RESET_GAME, rom_loaded);\r
+ me_enable(e_main_menu, MA_MAIN_PATCHES, PicoPatches != NULL);\r
+\r
+ plat_video_menu_enter(rom_loaded);\r
+ in_set_blocking(1);\r
+ me_loop(e_main_menu, &sel);\r
+ in_set_blocking(0);\r
+\r
+ if (rom_loaded) {\r
+ while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MBACK)); // wait until select is released\r
+ engineState = PGS_Running;\r
+ }\r
+\r
+}\r
+\r
// ------------ debug menu ------------\r
\r
#include <sys/stat.h>\r
{\r
switch (mode)\r
{\r
- case 0: menu_draw_begin();\r
+ case 0: plat_video_menu_begin();\r
tmp = PDebugMain();\r
emu_platformDebugCat(tmp);\r
draw_text_debug(tmp, 0, 0);\r
draw_text_debug(PDebugSpriteList(), spr_offs, 6);\r
break;\r
}\r
- menu_draw_end();\r
+ plat_video_menu_end();\r
\r
inp = in_menu_wait(PBTN_EAST|PBTN_MBACK|PBTN_WEST|PBTN_NORTH|PBTN_L|PBTN_R|PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT);\r
if (inp & PBTN_MBACK) return;\r
}\r
}\r
\r
+/* TODO: rename */\r
+void menu_darken_bg(void *dst, int pixels, int darker)\r
+{\r
+ unsigned int *screen = dst;\r
+ pixels /= 2;\r
+ if (darker)\r
+ {\r
+ while (pixels--)\r
+ {\r
+ unsigned int p = *screen;\r
+ *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ while (pixels--)\r
+ {\r
+ unsigned int p = *screen;\r
+ *screen++ = (p&0xf79ef79e)>>1;\r
+ }\r
+ }\r
+}\r
\r
typedef enum
{
MB_NONE = 1, /* no auto processing */
- MB_ONOFF, /* ON/OFF setting */
- MB_RANGE, /* [min-max] setting */
+ MB_OPT_ONOFF, /* ON/OFF setting */
+ MB_OPT_RANGE, /* [min-max] setting */
+ MB_OPT_CUSTOM,
} menu_behavior;
typedef enum
char *name;
menu_behavior beh;
menu_id id;
- void *var; /* for on-off settings */
- int mask;
+ void *var; /* for on-off/range settings */
+ int mask; /* bit to toggle for on/off */
signed char min; /* for ranged integer settings, to be sign-extended */
signed char max;
char enabled;
char need_to_save;
+ int (*submenu_handler)(menu_id id);
+ const char * (*generate_name)(int is_left);
} menu_entry;
+#define mee_submenu_id(name, id, handler) \
+ { name, MB_NONE, id, NULL, 0, 0, 0, 1, 0, handler, NULL }
+
+#define mee_submenu(name, handler) \
+ mee_submenu_id(name, MA_NONE, handler)
+
+#define mee_end \
+ { NULL, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, NULL }
+
typedef struct
{
char *name;
typedef void (me_draw_custom_f)(const menu_entry *entry, int x, int y, void *param);
-int me_id2offset(const menu_entry *entries, int count, menu_id id);
-void me_enable(menu_entry *entries, int count, menu_id id, int enable);
-int me_count_enabled(const menu_entry *entries, int count);
-menu_id me_index2id(const menu_entry *entries, int count, int index);
+/* TODO: move? */
+int me_id2offset(const menu_entry *entries, menu_id id);
+void me_enable(menu_entry *entries, menu_id id, int enable);
+int me_count_enabled(const menu_entry *ent);
+menu_id me_index2id(const menu_entry *entries, int index);
void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param);
-int me_process(menu_entry *entries, int count, menu_id id, int is_next);
+int me_process(menu_entry *entries, menu_id id, int is_next);
const char *me_region_name(unsigned int code, int auto_order);
+void menu_darken_bg(void *dst, int pixels, int darker);
+