unified menu wip and some reorganization for it
[libpicofe.git] / common / menu.c
1 // (c) Copyright 2006,2007 notaz, All rights reserved.\r
2 // Free for non-commercial use.\r
3 \r
4 // For commercial use, separate licencing terms must be obtained.\r
5 \r
6 #include <stdio.h>\r
7 #include <string.h>\r
8 #include <stdlib.h>\r
9 #include <stdarg.h>\r
10 \r
11 #include "menu.h"\r
12 #include "fonts.h"\r
13 #include "readpng.h"\r
14 #include "lprintf.h"\r
15 #include "common.h"\r
16 #include "input.h"\r
17 #include "emu.h"\r
18 #include "plat.h"\r
19 \r
20 #include <pico/patch.h>\r
21 \r
22 char menuErrorMsg[64] = { 0, };\r
23 \r
24 // PicoPad[] format: MXYZ SACB RLDU\r
25 me_bind_action me_ctrl_actions[15] =\r
26 {\r
27         { "UP     ", 0x0001 },\r
28         { "DOWN   ", 0x0002 },\r
29         { "LEFT   ", 0x0004 },\r
30         { "RIGHT  ", 0x0008 },\r
31         { "A      ", 0x0040 },\r
32         { "B      ", 0x0010 },\r
33         { "C      ", 0x0020 },\r
34         { "A turbo", 0x4000 },\r
35         { "B turbo", 0x1000 },\r
36         { "C turbo", 0x2000 },\r
37         { "START  ", 0x0080 },\r
38         { "MODE   ", 0x0800 },\r
39         { "X      ", 0x0400 },\r
40         { "Y      ", 0x0200 },\r
41         { "Z      ", 0x0100 }\r
42 };\r
43 \r
44 \r
45 #ifndef UIQ3\r
46 \r
47 static unsigned char menu_font_data[10240];\r
48 static int menu_text_color = 0xffff; // default to white\r
49 static int menu_sel_color = -1; // disabled\r
50 \r
51 // draws text to current bbp16 screen\r
52 static void text_out16_(int x, int y, const char *text, int color)\r
53 {\r
54         int i, l, u, tr, tg, tb, len;\r
55         unsigned short *dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
56         tr = (color & 0xf800) >> 8;\r
57         tg = (color & 0x07e0) >> 3;\r
58         tb = (color & 0x001f) << 3;\r
59 \r
60         if (text == (void *)1)\r
61         {\r
62                 // selector symbol\r
63                 text = "";\r
64                 len = 1;\r
65         }\r
66         else\r
67                 len = strlen(text);\r
68 \r
69         for (i = 0; i < len; i++)\r
70         {\r
71                 unsigned char  *src = menu_font_data + (unsigned int)text[i]*4*10;\r
72                 unsigned short *dst = dest;\r
73                 for (l = 0; l < 10; l++, dst += SCREEN_WIDTH-8)\r
74                 {\r
75                         for (u = 8/2; u > 0; u--, src++)\r
76                         {\r
77                                 int c, r, g, b;\r
78                                 c = *src >> 4;\r
79                                 r = (*dst & 0xf800) >> 8;\r
80                                 g = (*dst & 0x07e0) >> 3;\r
81                                 b = (*dst & 0x001f) << 3;\r
82                                 r = (c^0xf)*r/15 + c*tr/15;\r
83                                 g = (c^0xf)*g/15 + c*tg/15;\r
84                                 b = (c^0xf)*b/15 + c*tb/15;\r
85                                 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
86                                 c = *src & 0xf;\r
87                                 r = (*dst & 0xf800) >> 8;\r
88                                 g = (*dst & 0x07e0) >> 3;\r
89                                 b = (*dst & 0x001f) << 3;\r
90                                 r = (c^0xf)*r/15 + c*tr/15;\r
91                                 g = (c^0xf)*g/15 + c*tg/15;\r
92                                 b = (c^0xf)*b/15 + c*tb/15;\r
93                                 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
94                         }\r
95                 }\r
96                 dest += 8;\r
97         }\r
98 }\r
99 \r
100 void text_out16(int x, int y, const char *texto, ...)\r
101 {\r
102         va_list args;\r
103         char    buffer[256];\r
104         int     maxw = (SCREEN_WIDTH - x) / 8;\r
105 \r
106         va_start(args, texto);\r
107         vsnprintf(buffer, sizeof(buffer), texto, args);\r
108         va_end(args);\r
109 \r
110         if (maxw > 255)\r
111                 maxw = 255;\r
112         buffer[maxw] = 0;\r
113 \r
114         text_out16_(x,y,buffer,menu_text_color);\r
115 }\r
116 \r
117 \r
118 void smalltext_out16(int x, int y, const char *texto, int color)\r
119 {\r
120         int i;\r
121         unsigned char  *src;\r
122         unsigned short *dst;\r
123 \r
124         for (i = 0;; i++, x += 6)\r
125         {\r
126                 unsigned char c = (unsigned char) texto[i];\r
127                 int h = 8;\r
128 \r
129                 if (!c) break;\r
130 \r
131                 src = fontdata6x8[c];\r
132                 dst = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
133 \r
134                 while (h--)\r
135                 {\r
136                         int w = 0x20;\r
137                         while (w)\r
138                         {\r
139                                 if( *src & w ) *dst = color;\r
140                                 dst++;\r
141                                 w>>=1;\r
142                         }\r
143                         src++;\r
144 \r
145                         dst += SCREEN_WIDTH-6;\r
146                 }\r
147         }\r
148 }\r
149 \r
150 void smalltext_out16_lim(int x, int y, const char *texto, int color, int max)\r
151 {\r
152         char    buffer[SCREEN_WIDTH/6+1];\r
153 \r
154         strncpy(buffer, texto, SCREEN_WIDTH/6);\r
155         if (max > SCREEN_WIDTH/6) max = SCREEN_WIDTH/6;\r
156         if (max < 0) max = 0;\r
157         buffer[max] = 0;\r
158 \r
159         smalltext_out16(x, y, buffer, color);\r
160 }\r
161 \r
162 void menu_draw_selection(int x, int y, int w)\r
163 {\r
164         int i, h;\r
165         unsigned short *dst, *dest;\r
166 \r
167         text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);\r
168 \r
169         if (menu_sel_color < 0) return; // no selection hilight\r
170 \r
171         if (y > 0) y--;\r
172         dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH + 14;\r
173         for (h = 11; h > 0; h--)\r
174         {\r
175                 dst = dest;\r
176                 for (i = w; i > 0; i--)\r
177                         *dst++ = menu_sel_color;\r
178                 dest += SCREEN_WIDTH;\r
179         }\r
180 }\r
181 \r
182 static int parse_hex_color(char *buff)\r
183 {\r
184         char *endp = buff;\r
185         int t = (int) strtoul(buff, &endp, 16);\r
186         if (endp != buff)\r
187 #ifdef PSP\r
188                 return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);\r
189 #else\r
190                 return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);\r
191 #endif\r
192         return -1;\r
193 }\r
194 \r
195 void menu_init(void)\r
196 {\r
197         int c, l;\r
198         unsigned char *fd = menu_font_data;\r
199         char buff[256];\r
200         FILE *f;\r
201 \r
202         // generate default font from fontdata8x8\r
203         memset(menu_font_data, 0, sizeof(menu_font_data));\r
204         for (c = 0; c < 256; c++)\r
205         {\r
206                 for (l = 0; l < 8; l++)\r
207                 {\r
208                         unsigned char fd8x8 = fontdata8x8[c*8+l];\r
209                         if (fd8x8&0x80) *fd |= 0xf0;\r
210                         if (fd8x8&0x40) *fd |= 0x0f; fd++;\r
211                         if (fd8x8&0x20) *fd |= 0xf0;\r
212                         if (fd8x8&0x10) *fd |= 0x0f; fd++;\r
213                         if (fd8x8&0x08) *fd |= 0xf0;\r
214                         if (fd8x8&0x04) *fd |= 0x0f; fd++;\r
215                         if (fd8x8&0x02) *fd |= 0xf0;\r
216                         if (fd8x8&0x01) *fd |= 0x0f; fd++;\r
217                 }\r
218                 fd += 8*2/2; // 2 empty lines\r
219         }\r
220 \r
221         // load custom font and selector (stored as 1st symbol in font table)\r
222         readpng(menu_font_data, "skin/font.png", READPNG_FONT);\r
223         memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>'\r
224         readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR);\r
225 \r
226         // load custom colors\r
227         f = fopen("skin/skin.txt", "r");\r
228         if (f != NULL)\r
229         {\r
230                 lprintf("found skin.txt\n");\r
231                 while (!feof(f))\r
232                 {\r
233                         fgets(buff, sizeof(buff), f);\r
234                         if (buff[0] == '#'  || buff[0] == '/')  continue; // comment\r
235                         if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line\r
236                         if (strncmp(buff, "text_color=", 11) == 0)\r
237                         {\r
238                                 int tmp = parse_hex_color(buff+11);\r
239                                 if (tmp >= 0) menu_text_color = tmp;\r
240                                 else lprintf("skin.txt: parse error for text_color\n");\r
241                         }\r
242                         else if (strncmp(buff, "selection_color=", 16) == 0)\r
243                         {\r
244                                 int tmp = parse_hex_color(buff+16);\r
245                                 if (tmp >= 0) menu_sel_color = tmp;\r
246                                 else lprintf("skin.txt: parse error for selection_color\n");\r
247                         }\r
248                         else\r
249                                 lprintf("skin.txt: parse error: %s\n", buff);\r
250                 }\r
251                 fclose(f);\r
252         }\r
253 }\r
254 \r
255 \r
256 int me_id2offset(const menu_entry *ent, menu_id id)\r
257 {\r
258         int i;\r
259         for (i = 0; ent->name; ent++, i++)\r
260                 if (ent->id == id) return i;\r
261 \r
262         lprintf("%s: id %i not found\n", __FUNCTION__, id);\r
263         return 0;\r
264 }\r
265 \r
266 void me_enable(menu_entry *entries, menu_id id, int enable)\r
267 {\r
268         int i = me_id2offset(entries, id);\r
269         entries[i].enabled = enable;\r
270 }\r
271 \r
272 int me_count(const menu_entry *ent)\r
273 {\r
274         int ret;\r
275 \r
276         for (ret = 0; ent->name; ent++, ret++)\r
277                 ;\r
278 \r
279         return ret;\r
280 }\r
281 \r
282 menu_id me_index2id(const menu_entry *ent, int index)\r
283 {\r
284         const menu_entry *last;\r
285 \r
286         for (; ent->name; ent++)\r
287         {\r
288                 if (ent->enabled)\r
289                 {\r
290                         if (index == 0) break;\r
291                         index--;\r
292                 }\r
293                 last = ent;\r
294         }\r
295         if (ent->name == NULL)\r
296                 ent = last;\r
297         return ent->id;\r
298 }\r
299 \r
300 /* TODO rm */\r
301 void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param)\r
302 {\r
303         int i, y1 = y;\r
304 \r
305         for (i = 0; i < count; i++)\r
306         {\r
307                 if (!entries[i].enabled) continue;\r
308                 if (entries[i].name == NULL)\r
309                 {\r
310                         if (cust_draw != NULL)\r
311                                 cust_draw(&entries[i], x, y1, param);\r
312                         y1 += 10;\r
313                         continue;\r
314                 }\r
315                 text_out16(x, y1, entries[i].name);\r
316                 if (entries[i].beh == MB_OPT_ONOFF)\r
317                         text_out16(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF");\r
318                 else if (entries[i].beh == MB_OPT_RANGE)\r
319                         text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var);\r
320                 y1 += 10;\r
321         }\r
322 \r
323 }\r
324 \r
325 static void me_draw2(const menu_entry *entries, int sel)\r
326 {\r
327         const menu_entry *ent;\r
328         int x, y, w = 0, h = 0;\r
329         int opt_offs = 27*8;\r
330         const char *name;\r
331         int asel = 0;\r
332         int i, n;\r
333 \r
334         /* calculate size of menu rect */\r
335         for (ent = entries, i = n = 0; ent->name; ent++, i++)\r
336         {\r
337                 int wt;\r
338 \r
339                 if (!ent->enabled)\r
340                         continue;\r
341 \r
342                 if (i == sel)\r
343                         asel = n;\r
344 \r
345                 name = NULL;\r
346                 wt = strlen(ent->name) * 8;     /* FIXME: unhardcode font width */\r
347                 if (wt == 0 && ent->generate_name)\r
348                         name = ent->generate_name(1);\r
349                 if (name != NULL)\r
350                         wt = strlen(name) * 8;\r
351 \r
352                 if (ent->beh != MB_NONE)\r
353                 {\r
354                         if (wt > opt_offs)\r
355                                 opt_offs = wt + 8;\r
356                         wt = opt_offs;\r
357 \r
358                         switch (ent->beh) {\r
359                         case MB_NONE: break;\r
360                         case MB_OPT_ONOFF:\r
361                         case MB_OPT_RANGE: wt += 8*3; break;\r
362                         case MB_OPT_CUSTOM:\r
363                                 name = NULL;\r
364                                 if (ent->generate_name != NULL)\r
365                                         name = ent->generate_name(0);\r
366                                 if (name != NULL)\r
367                                         wt += strlen(name) * 8;\r
368                                 break;\r
369                         }\r
370                 }\r
371 \r
372                 if (wt > w)\r
373                         w = wt;\r
374                 n++;\r
375         }\r
376         h = n * 10;\r
377         w += 16; /* selector */\r
378 \r
379         if (w > SCREEN_WIDTH) {\r
380                 lprintf("width %d > %d\n", w, SCREEN_WIDTH);\r
381                 w = SCREEN_WIDTH;\r
382         }\r
383         if (h > SCREEN_HEIGHT) {\r
384                 lprintf("height %d > %d\n", w, SCREEN_HEIGHT);\r
385                 h = SCREEN_HEIGHT;\r
386         }\r
387 \r
388         x = SCREEN_WIDTH  / 2 - w / 2;\r
389         y = SCREEN_HEIGHT / 2 - h / 2;\r
390 \r
391         /* draw */\r
392         plat_video_menu_begin();\r
393         menu_draw_selection(x, y + asel * 10, w);\r
394 \r
395         for (ent = entries; ent->name; ent++)\r
396         {\r
397                 if (!ent->enabled)\r
398                         continue;\r
399 \r
400                 name = ent->name;\r
401                 if (strlen(name) == 0) {\r
402                         if (ent->generate_name)\r
403                                 name = ent->generate_name(1);\r
404                 }\r
405                 if (name != NULL)\r
406                         text_out16(x + 16, y, name);\r
407 \r
408                 switch (ent->beh) {\r
409                 case MB_NONE:\r
410                         break;\r
411                 case MB_OPT_ONOFF:\r
412                         text_out16(x + 16 + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");\r
413                         break;\r
414                 case MB_OPT_RANGE:\r
415                         text_out16(x + 16 + opt_offs, y, "%i", *(int *)ent->var);\r
416                         break;\r
417                 case MB_OPT_CUSTOM:\r
418                         name = NULL;\r
419                         if (ent->generate_name)\r
420                                 name = ent->generate_name(0);\r
421                         if (name != NULL)\r
422                                 text_out16(x + 16 + opt_offs, y, "%s", name);\r
423                         break;\r
424                 }\r
425 \r
426                 y += 10;\r
427         }\r
428 \r
429         plat_video_menu_end();\r
430 }\r
431 \r
432 int me_process(menu_entry *entries, menu_id id, int is_next)\r
433 {\r
434         int i = me_id2offset(entries, id);\r
435         menu_entry *entry = &entries[i];\r
436         switch (entry->beh)\r
437         {\r
438                 case MB_OPT_ONOFF:\r
439                         *(int *)entry->var ^= entry->mask;\r
440                         return 1;\r
441                 case MB_OPT_RANGE:\r
442                         *(int *)entry->var += is_next ? 1 : -1;\r
443                         if (*(int *)entry->var < (int)entry->min) *(int *)entry->var = (int)entry->min;\r
444                         if (*(int *)entry->var > (int)entry->max) *(int *)entry->var = (int)entry->max;\r
445                         return 1;\r
446                 default:\r
447                         return 0;\r
448         }\r
449 }\r
450 \r
451 static void me_loop(menu_entry *menu, int *menu_sel)\r
452 {\r
453         int ret, inp, sel = *menu_sel, menu_sel_max;\r
454 \r
455         menu_sel_max = me_count(menu) - 1;\r
456         if (menu_sel_max < 1) {\r
457                 lprintf("no enabled menu entries\n");\r
458                 return;\r
459         }\r
460 \r
461         while (!menu[sel].enabled && sel < menu_sel_max)\r
462                 sel++;\r
463 \r
464         /* make sure action buttons are not pressed on entering menu */\r
465         me_draw2(menu, sel);\r
466         while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU));\r
467 \r
468         for (;;)\r
469         {\r
470                 me_draw2(menu, sel);\r
471                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R);\r
472                 if (inp & PBTN_UP  ) {\r
473                         do {\r
474                                 sel--;\r
475                                 if (sel < 0)\r
476                                         sel = menu_sel_max;\r
477                         }\r
478                         while (!menu[sel].enabled);\r
479                 }\r
480                 if (inp & PBTN_DOWN) {\r
481                         do {\r
482                                 sel++;\r
483                                 if (sel > menu_sel_max)\r
484                                         sel = 0;\r
485                         }\r
486                         while (!menu[sel].enabled);\r
487                 }\r
488 //              if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R)) debug_menu_loop(); // TODO\r
489                 if (inp & (PBTN_MENU|PBTN_MBACK))\r
490                         break;\r
491 \r
492                 if (inp & PBTN_MOK)\r
493                 {\r
494                         if (menu[sel].submenu_handler != NULL) {\r
495                                 ret = menu[sel].submenu_handler(menu[sel].id);\r
496                                 if (ret) break;\r
497                         }\r
498                 }\r
499 //              menuErrorMsg[0] = 0; // TODO: clear error msg\r
500         }\r
501         *menu_sel = sel;\r
502 }\r
503 \r
504 /* ***************************************** */\r
505 \r
506 /* TODO s */\r
507 int menu_loop_tray(void) { return 0; }\r
508 void menu_romload_prepare(const char *rom_name) {}\r
509 void menu_romload_end(void) {}\r
510 me_bind_action emuctrl_actions[1];\r
511 menu_entry opt_entries[1];\r
512 menu_entry opt2_entries[1];\r
513 menu_entry cdopt_entries[1];\r
514 menu_entry ctrlopt_entries[1];\r
515 const int opt_entry_count = 0;\r
516 const int opt2_entry_count = 0;\r
517 const int cdopt_entry_count = 0;\r
518 const int ctrlopt_entry_count = 0;\r
519 \r
520 extern int engineState;\r
521 \r
522 int savestate_menu_loop(int a) { return 1; }\r
523 int menu_loop_options() { return 1; }\r
524 void kc_sel_loop() {}\r
525 void draw_menu_credits() {}\r
526 void patches_menu_loop() {}\r
527 \r
528 // ------------ main menu ------------\r
529 \r
530 static int main_menu_handler(menu_id id)\r
531 {\r
532         int ret;\r
533 \r
534         switch (id)\r
535         {\r
536         case MA_MAIN_RESUME_GAME:\r
537                 if (rom_loaded) {\r
538                         while (in_menu_wait_any(50) & PBTN_MOK);\r
539                         engineState = PGS_Running;\r
540                         return 1;\r
541                 }\r
542                 break;\r
543         case MA_MAIN_SAVE_STATE:\r
544                 if (rom_loaded) {\r
545                         if (savestate_menu_loop(0))\r
546                                 break;\r
547                         engineState = PGS_Running;\r
548                         return 1;\r
549                 }\r
550                 break;\r
551         case MA_MAIN_LOAD_STATE:\r
552                 if (rom_loaded) {\r
553                         if (savestate_menu_loop(1))\r
554                                 break;\r
555                         while (in_menu_wait_any(50) & PBTN_MOK);\r
556                         engineState = PGS_Running;\r
557                         return 1;\r
558                 }\r
559                 break;\r
560         case MA_MAIN_RESET_GAME:\r
561                 if (rom_loaded) {\r
562                         emu_ResetGame();\r
563                         while (in_menu_wait_any(50) & PBTN_MOK);\r
564                         engineState = PGS_Running;\r
565                         return 1;\r
566                 }\r
567                 break;\r
568         case MA_MAIN_LOAD_ROM:\r
569                 {\r
570 /*                      char curr_path[PATH_MAX], *selfname;\r
571                         FILE *tstf;\r
572                         if ( (tstf = fopen(loadedRomFName, "rb")) )\r
573                         {\r
574                                 fclose(tstf);\r
575                                 strcpy(curr_path, loadedRomFName);\r
576                         }\r
577                         else\r
578                                 getcwd(curr_path, PATH_MAX);\r
579                         selfname = romsel_loop(curr_path);\r
580                         if (selfname) {\r
581                                 printf("selected file: %s\n", selfname);\r
582                                 engineState = PGS_ReloadRom;\r
583                                 return;\r
584                         }*/\r
585                         break;\r
586                 }\r
587         case MA_MAIN_OPTIONS:\r
588                 ret = menu_loop_options();\r
589                 if (ret == 1) break; // status update\r
590                 if (engineState == PGS_ReloadRom)\r
591                         return 1; // BIOS test\r
592                 break;\r
593         case MA_MAIN_CONTROLS:\r
594                 kc_sel_loop();\r
595                 break;\r
596         case MA_MAIN_CREDITS:\r
597                 draw_menu_credits();\r
598                 usleep(500*1000); /* FIXME */\r
599                 in_menu_wait(PBTN_MOK|PBTN_MBACK);\r
600                 break;\r
601         case MA_MAIN_EXIT:\r
602                 engineState = PGS_Quit;\r
603                 return 1;\r
604         case MA_MAIN_PATCHES:\r
605                 if (rom_loaded && PicoPatches) {\r
606                         patches_menu_loop();\r
607                         PicoPatchApply();\r
608                         strcpy(menuErrorMsg, "Patches applied");\r
609                 }\r
610                 break;\r
611         default:\r
612                 lprintf("%s: something unknown selected\n", __FUNCTION__);\r
613                 break;\r
614         }\r
615 \r
616         return 0;\r
617 }\r
618 \r
619 menu_entry e_main_menu[] =\r
620 {\r
621         mee_submenu_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),\r
622         mee_submenu_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),\r
623         mee_submenu_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),\r
624         mee_submenu_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),\r
625         mee_submenu_id("Load new ROM/ISO",   MA_MAIN_LOAD_ROM,    main_menu_handler),\r
626         mee_submenu_id("Change options",     MA_MAIN_OPTIONS,     main_menu_handler),\r
627         mee_submenu_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),\r
628         mee_submenu_id("Patches / GameGenie",MA_MAIN_PATCHES,     main_menu_handler),\r
629         mee_submenu_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),\r
630         mee_end,\r
631 };\r
632 \r
633 void menu_loop(void)\r
634 {\r
635         static int sel = 0;\r
636 \r
637         me_enable(e_main_menu, MA_MAIN_RESUME_GAME, rom_loaded);\r
638         me_enable(e_main_menu, MA_MAIN_SAVE_STATE,  rom_loaded);\r
639         me_enable(e_main_menu, MA_MAIN_LOAD_STATE,  rom_loaded);\r
640         me_enable(e_main_menu, MA_MAIN_RESET_GAME,  rom_loaded);\r
641         me_enable(e_main_menu, MA_MAIN_PATCHES, PicoPatches != NULL);\r
642 \r
643         plat_video_menu_enter(rom_loaded);\r
644         in_set_blocking(1);\r
645         me_loop(e_main_menu, &sel);\r
646         in_set_blocking(0);\r
647 \r
648         if (rom_loaded) {\r
649                 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MBACK)); // wait until select is released\r
650                 engineState = PGS_Running;\r
651         }\r
652 \r
653 }\r
654 \r
655 // ------------ debug menu ------------\r
656 \r
657 #include <sys/stat.h>\r
658 #include <sys/types.h>\r
659 \r
660 #include <pico/pico.h>\r
661 #include <pico/debug.h>\r
662 \r
663 void SekStepM68k(void);\r
664 \r
665 static void mplayer_loop(void)\r
666 {\r
667         emu_startSound();\r
668 \r
669         while (1)\r
670         {\r
671                 PDebugZ80Frame();\r
672                 if (in_menu_wait_any(0) & PBTN_NORTH) break;\r
673                 emu_waitSound();\r
674         }\r
675 \r
676         emu_endSound();\r
677 }\r
678 \r
679 static void draw_text_debug(const char *str, int skip, int from)\r
680 {\r
681         const char *p;\r
682         int len, line;\r
683 \r
684         p = str;\r
685         while (skip-- > 0)\r
686         {\r
687                 while (*p && *p != '\n') p++;\r
688                 if (*p == 0 || p[1] == 0) return;\r
689                 p++;\r
690         }\r
691 \r
692         str = p;\r
693         for (line = from; line < SCREEN_HEIGHT/10; line++)\r
694         {\r
695                 while (*p && *p != '\n') p++;\r
696                 len = p - str;\r
697                 if (len > 55) len = 55;\r
698                 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
699                 if (*p == 0) break;\r
700                 p++; str = p;\r
701         }\r
702 }\r
703 \r
704 static void draw_frame_debug(void)\r
705 {\r
706         char layer_str[48] = "layers:             ";\r
707         if (PicoDrawMask & PDRAW_LAYERB_ON)      memcpy(layer_str +  8, "B", 1);\r
708         if (PicoDrawMask & PDRAW_LAYERA_ON)      memcpy(layer_str + 10, "A", 1);\r
709         if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);\r
710         if (PicoDrawMask & PDRAW_SPRITES_HI_ON)  memcpy(layer_str + 19, "spr_hi", 6);\r
711 \r
712         clear_screen();\r
713         emu_forcedFrame(0);\r
714         smalltext_out16(4, SCREEN_HEIGHT-8, layer_str, 0xffff);\r
715 }\r
716 \r
717 void debug_menu_loop(void)\r
718 {\r
719         int inp, mode = 0;\r
720         int spr_offs = 0, dumped = 0;\r
721         char *tmp;\r
722 \r
723         while (1)\r
724         {\r
725                 switch (mode)\r
726                 {\r
727                         case 0: plat_video_menu_begin();\r
728                                 tmp = PDebugMain();\r
729                                 emu_platformDebugCat(tmp);\r
730                                 draw_text_debug(tmp, 0, 0);\r
731                                 if (dumped) {\r
732                                         smalltext_out16(SCREEN_WIDTH-6*10, SCREEN_HEIGHT-8, "dumped", 0xffff);\r
733                                         dumped = 0;\r
734                                 }\r
735                                 break;\r
736                         case 1: draw_frame_debug(); break;\r
737                         case 2: clear_screen();\r
738                                 emu_forcedFrame(0);\r
739                                 darken_screen();\r
740                                 PDebugShowSpriteStats((unsigned short *)SCREEN_BUFFER + (SCREEN_HEIGHT/2 - 240/2)*SCREEN_WIDTH +\r
741                                         SCREEN_WIDTH/2 - 320/2, SCREEN_WIDTH); break;\r
742                         case 3: clear_screen();\r
743                                 PDebugShowPalette(SCREEN_BUFFER, SCREEN_WIDTH);\r
744                                 PDebugShowSprite((unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH*120+SCREEN_WIDTH/2+16,\r
745                                         SCREEN_WIDTH, spr_offs);\r
746                                 draw_text_debug(PDebugSpriteList(), spr_offs, 6);\r
747                                 break;\r
748                 }\r
749                 plat_video_menu_end();\r
750 \r
751                 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
752                 if (inp & PBTN_MBACK) return;\r
753                 if (inp & PBTN_L) { mode--; if (mode < 0) mode = 3; }\r
754                 if (inp & PBTN_R) { mode++; if (mode > 3) mode = 0; }\r
755                 switch (mode)\r
756                 {\r
757                         case 0:\r
758                                 if (inp & PBTN_EAST) SekStepM68k();\r
759                                 if (inp & PBTN_NORTH) {\r
760                                         while (inp & PBTN_NORTH) inp = in_menu_wait_any(-1);\r
761                                         mplayer_loop();\r
762                                 }\r
763                                 if ((inp & (PBTN_WEST|PBTN_LEFT)) == (PBTN_WEST|PBTN_LEFT)) {\r
764                                         mkdir("dumps", 0777);\r
765                                         PDebugDumpMem();\r
766                                         while (inp & PBTN_WEST) inp = in_menu_wait_any(-1);\r
767                                         dumped = 1;\r
768                                 }\r
769                                 break;\r
770                         case 1:\r
771                                 if (inp & PBTN_LEFT)  PicoDrawMask ^= PDRAW_LAYERB_ON;\r
772                                 if (inp & PBTN_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;\r
773                                 if (inp & PBTN_DOWN)  PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;\r
774                                 if (inp & PBTN_UP)    PicoDrawMask ^= PDRAW_SPRITES_HI_ON;\r
775                                 if (inp & PBTN_EAST) {\r
776                                         PsndOut = NULL; // just in case\r
777                                         PicoSkipFrame = 1;\r
778                                         PicoFrame();\r
779                                         PicoSkipFrame = 0;\r
780                                         while (inp & PBTN_EAST) inp = in_menu_wait_any(-1);\r
781                                 }\r
782                                 break;\r
783                         case 3:\r
784                                 if (inp & PBTN_DOWN)  spr_offs++;\r
785                                 if (inp & PBTN_UP)    spr_offs--;\r
786                                 if (spr_offs < 0) spr_offs = 0;\r
787                                 break;\r
788                 }\r
789         }\r
790 }\r
791 \r
792 #endif // !UIQ3\r
793 \r
794 // ------------ util ------------\r
795 \r
796 const char *me_region_name(unsigned int code, int auto_order)\r
797 {\r
798         static const char *names[] = { "Auto", "      Japan NTSC", "      Japan PAL", "      USA", "      Europe" };\r
799         static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
800         int u, i = 0;\r
801         if (code) {\r
802                 code <<= 1;\r
803                 while((code >>= 1)) i++;\r
804                 if (i > 4) return "unknown";\r
805                 return names[i];\r
806         } else {\r
807                 static char name[24];\r
808                 strcpy(name, "Auto:");\r
809                 for (u = 0; u < 3; u++) {\r
810                         i = 0; code = ((auto_order >> u*4) & 0xf) << 1;\r
811                         while((code >>= 1)) i++;\r
812                         strcat(name, names_short[i]);\r
813                 }\r
814                 return name;\r
815         }\r
816 }\r
817 \r
818 /* TODO: rename */\r
819 void menu_darken_bg(void *dst, int pixels, int darker)\r
820 {\r
821         unsigned int *screen = dst;\r
822         pixels /= 2;\r
823         if (darker)\r
824         {\r
825                 while (pixels--)\r
826                 {\r
827                         unsigned int p = *screen;\r
828                         *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
829                 }\r
830         }\r
831         else\r
832         {\r
833                 while (pixels--)\r
834                 {\r
835                         unsigned int p = *screen;\r
836                         *screen++ = (p&0xf79ef79e)>>1;\r
837                 }\r
838         }\r
839 }\r
840 \r