24 static struct ezmenu ezm
;
25 static enum menu_page currpage
;
26 static unsigned char screen
[160*144];
27 static char statusline
[64];
30 void menu_init(void) {
31 ezmenu_init(&ezm
, 160, 144, FONTW
, FONTH
);
35 static int allowed_ext(char *fn
) {
36 static const char *exttab
[] = {".gb", ".gbc", ".xz", ".gz", ".zip", 0};
37 char *e
= strrchr(fn
, '.');
40 for(i
=0; exttab
[i
]; ++i
)
41 if(!strcmp(exttab
[i
], e
)) return 1;
45 static int strlistcmp(const void* a
, const void *b
) {
46 return strcmp(*(char* const*) a
, *(char* const*) b
);
49 static int strendswith(char *s
, char *end
) {
50 size_t ls
= strlen(s
), le
= strlen(end
);
51 return ls
>= le
&& !strcmp(s
+ ls
- le
, end
);
54 /* this is defined this way so a couple 0 bytes can be added that can
55 be overwritten with the mapped key name, if desired */
56 static char* controller_menu_items
[] = {
57 (char[]){'+', 'a', 0},
58 (char[]){'+', 'b', 0},
59 (char[]){'+', 's', 'e', 'l', 'e', 'c', 't', 0},
60 (char[]){'+', 's', 't', 'a', 'r', 't', 0},
61 (char[]){'m', 'e', 'n', 'u', 0},
62 (char[]){'b', 'a', 'c', 'k', 0},
65 void menu_initpage(enum menu_page page
) {
66 static const char* loaderr_menu_items
[] = {"back"};
67 static const char* main_menu_items
[] = {
76 static const char* state_menu_items
[] = {
77 "state 0", "state 1", "state 2", "state 3", "state 4",
78 "state 5", "state 6", "state 7", "state 8", "state 9",
84 unsigned dirlistcnt
= 0, i
;
85 if(currpage
== mp_romsel
) {
86 for(i
=0; i
<ezm
.linecount
; ++i
)
93 ezmenu_setheader(&ezm
, page
== mp_savestate
? "Save state" : "Load state");
94 ezmenu_setlines(&ezm
, (void*)state_menu_items
, sizeof(state_menu_items
)/sizeof(main_menu_items
[0]));
95 ezmenu_setfooter(&ezm
, " ");
99 ezmenu_setheader(&ezm
, loader_get_error());
100 ezmenu_setlines(&ezm
, (void*)loaderr_menu_items
, 1);
103 ezmenu_setheader(&ezm
, "GNUBOY MAIN MENU");
104 ezmenu_setlines(&ezm
, (void*)main_menu_items
, sizeof(main_menu_items
)/sizeof(main_menu_items
[0]));
105 ezmenu_setfooter(&ezm
, " ");
108 ezmenu_setheader(&ezm
, "Controller config");
109 ezmenu_setlines(&ezm
, controller_menu_items
, sizeof(controller_menu_items
)/sizeof(controller_menu_items
[0]));
110 ezmenu_setfooter(&ezm
, " ");
113 dir
= opendir(romdir
);
115 loader_set_error("failed to open directory");
116 if(strendswith(romdir
, "/.."))
117 romdir
[strlen(romdir
)-3] = 0;
121 ezmenu_setheader(&ezm
, "GNUBOY ROM Selection");
122 ezmenu_setfooter(&ezm
, " ");
123 dirlist
= malloc(sizeof(char*));
124 dirlist
[0] = strdup("..");
126 while((file
= readdir(dir
))) {
127 if(file
->d_name
[0] == '.') continue;
128 if (!allowed_ext(file
->d_name
)) {
131 snprintf(path
, sizeof path
, "%s/%s", romdir
, file
->d_name
);
132 if(!stat(path
, &st
) && S_ISDIR(st
.st_mode
));
135 dirlist
= realloc(dirlist
, sizeof(char*)*(++dirlistcnt
));
136 dirlist
[dirlistcnt
-1] = strdup(file
->d_name
);
139 qsort(dirlist
+1, dirlistcnt
-1, sizeof(char*), strlistcmp
);
140 ezmenu_setlines(&ezm
, dirlist
, dirlistcnt
);
146 static void font_blit(unsigned char* screen
, int dx
, int dy
, int ch
, int hl
) {
147 unsigned char* dest
= screen
+ dy
*160 + dx
;
148 unsigned const char* font
= font5x7
+ (ch
>FONTMAX
?0:ch
)*FONTH
*FONTW
;
150 for(y
= 0; y
< FONTH
; ++y
, dest
+= 160)
151 for(x
= 0; x
< FONTW
; ++x
)
152 dest
[x
] = hl
&&!font
[y
*FONTW
+x
]?2:font
[y
*FONTW
+x
];
161 static void bkup_pal(struct palbkup
*bk
) {
162 bk
->pal1
[0] = scan
.pal1
[0];
163 bk
->pal1
[1] = scan
.pal1
[1];
164 bk
->pal1
[2] = scan
.pal1
[2];
165 bk
->pal2
[0] = scan
.pal2
[0];
166 bk
->pal2
[1] = scan
.pal2
[1];
167 bk
->pal2
[2] = scan
.pal2
[2];
168 bk
->pal4
[0] = scan
.pal4
[0];
169 bk
->pal4
[1] = scan
.pal4
[1];
170 bk
->pal4
[2] = scan
.pal4
[2];
173 static void restore_pal(struct palbkup
*bk
) {
174 scan
.pal1
[0] = bk
->pal1
[0];
175 scan
.pal1
[1] = bk
->pal1
[1];
176 scan
.pal1
[2] = bk
->pal1
[2];
177 scan
.pal2
[0] = bk
->pal2
[0];
178 scan
.pal2
[1] = bk
->pal2
[1];
179 scan
.pal2
[2] = bk
->pal2
[2];
180 scan
.pal4
[0] = bk
->pal4
[0];
181 scan
.pal4
[1] = bk
->pal4
[1];
182 scan
.pal4
[2] = bk
->pal4
[2];
185 static void menu_paint(void) {
187 /* since we use gb's lcd routines to draw to vram, we have to backup
188 previous palette entries */
194 scan
.pal2
[1] = 0xffff;
195 scan
.pal2
[2] = 0x6666;
197 scan
.pal4
[1] = 0xffffffff; // alpha left or right ?
198 scan
.pal4
[2] = 0x66666666;
201 for(y
= 0; y
< ezm
.h
; ++y
) {
202 l
=strlen(ezm
.vislines
[y
]);
203 for(x
= 0; x
< ezm
.w
; ++x
)
204 font_blit(screen
, x
*FONTW
, y
*FONTH
, x
>=l
?' ':ezm
.vislines
[y
][x
], y
==ezm
.vissel
);
210 for(y
= 0; y
< 144; ++y
) {
211 memcpy(scan
.buf
, screen
+160*y
, 160);
219 static int menu_getevent(int *st
) {
223 if(!ev_getevent(&ev
)) {
230 if (ev
.type
!= EV_PRESS
&& ev
.type
!= EV_RELEASE
)
232 *st
= (ev
.type
!= EV_RELEASE
);
247 static enum menu_key
menu_translate_key(int k
) {
261 bind
= rc_getkeybind(k
);
262 if(!bind
|| (bind
[0] != '+' && bind
[0] != '-'))
265 if(!strcmp(bind
, "a")) return mk_ok
;
266 if(!strcmp(bind
, "b")) return mk_cancel
;
267 if(!strcmp(bind
, "start")) return mk_ok
;
268 if(!strcmp(bind
, "up")) return mk_up
;
269 if(!strcmp(bind
, "down")) return mk_down
;
274 void menu_enter(void) {
279 int st
, k
= menu_getevent(&st
);
280 if (!k
|| !st
) goto next
;
281 switch(menu_translate_key(k
)) {
283 ezmenu_userinput(&ezm
, EZM_UP
);
287 ezmenu_userinput(&ezm
, EZM_DOWN
);
291 if(currpage
== mp_main
) break;
292 menu_initpage(mp_main
);
295 if(currpage
== mp_romsel
) {
298 snprintf(rd
, sizeof rd
, "%s/%s", romdir
, ezm
.vislines
[ezm
.vissel
]);
299 if(!stat(rd
, &st
) && S_ISDIR(st
.st_mode
)) {
302 menu_initpage(mp_romsel
);
306 if(load_rom_and_rc(rd
)) {
307 menu_initpage(mp_loaderr
);
312 } else if (currpage
== mp_main
) {
313 if(!strcmp(ezm
.vislines
[ezm
.vissel
], "continue")) {
314 if(emu_paused()) goto out
;
316 else if(!strcmp(ezm
.vislines
[ezm
.vissel
], "reset")) {
322 else if(!strcmp(ezm
.vislines
[ezm
.vissel
], "select rom")) {
323 menu_initpage(mp_romsel
);
326 else if(!strcmp(ezm
.vislines
[ezm
.vissel
], "controller config")) {
327 menu_initpage(mp_controller
);
330 else if(!strcmp(ezm
.vislines
[ezm
.vissel
], "load state")) {
331 menu_initpage(mp_loadstate
);
334 else if(!strcmp(ezm
.vislines
[ezm
.vissel
], "save state")) {
335 menu_initpage(mp_savestate
);
338 else if(!strcmp(ezm
.vislines
[ezm
.vissel
], "quit")) {
342 } else if (currpage
== mp_loaderr
) {
343 menu_initpage(mp_romsel
);
345 } else if (currpage
== mp_controller
) {
346 if(!strcmp(ezm
.vislines
[ezm
.vissel
], "back")) {
347 menu_initpage(mp_main
);
350 ezmenu_setfooter(&ezm
, "press a button to override");
353 while((k
= menu_getevent(&st
)) == 0 || !st
);
354 rc_unbindkey(k_keyname(k
));
355 rc_bindkey(k_keyname(k
), ezm
.vislines
[ezm
.vissel
]);
356 snprintf(statusline
, sizeof statusline
, "key assigned: %s", k_keyname(k
));
357 ezmenu_setfooter(&ezm
, statusline
);
360 } else if (currpage
== mp_savestate
|| currpage
== mp_loadstate
) {
361 if(!strcmp(ezm
.vislines
[ezm
.vissel
], "back")) {
362 menu_initpage(mp_main
);
365 if(!emu_paused()) break;
366 int n
= atoi(ezm
.vislines
[ezm
.vissel
]+6);
367 if(currpage
== mp_savestate
) state_save(n
);
380 rcvar_t menu_exports
[] =
382 RCV_STRING("romdir", &romdir
, "rom directory"),