proper timeout handling for input subsys
[libpicofe.git] / common / menu.c
CommitLineData
c7a4ff64 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
b8464531 15#include "common.h"\r
fce20e73 16#include "input.h"\r
b8464531 17#include "emu.h"\r
24b24674 18#include "plat.h"\r
049a6b3e 19#include "posix.h"\r
c7a4ff64 20\r
049a6b3e 21#include <pico/pico_int.h>\r
24b24674 22#include <pico/patch.h>\r
049a6b3e 23#include <zlib/zlib.h>\r
c7a4ff64 24\r
049a6b3e 25#define array_size(x) (sizeof(x) / sizeof(x[0]))\r
f013066e 26\r
049a6b3e 27static char static_buff[64];\r
28char menuErrorMsg[64] = { 0, };\r
367b6f1f 29\r
30\r
36f6fd5a 31#ifndef UIQ3\r
32\r
c7a4ff64 33static unsigned char menu_font_data[10240];\r
34static int menu_text_color = 0xffff; // default to white\r
35static int menu_sel_color = -1; // disabled\r
36\r
37// draws text to current bbp16 screen\r
38static void text_out16_(int x, int y, const char *text, int color)\r
39{\r
40 int i, l, u, tr, tg, tb, len;\r
41 unsigned short *dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
42 tr = (color & 0xf800) >> 8;\r
43 tg = (color & 0x07e0) >> 3;\r
44 tb = (color & 0x001f) << 3;\r
45\r
46 if (text == (void *)1)\r
47 {\r
48 // selector symbol\r
49 text = "";\r
50 len = 1;\r
51 }\r
52 else\r
049a6b3e 53 {\r
54 const char *p;\r
55 for (p = text; *p != 0 && *p != '\n'; p++)\r
56 ;\r
57 len = p - text;\r
58 }\r
c7a4ff64 59\r
60 for (i = 0; i < len; i++)\r
61 {\r
62 unsigned char *src = menu_font_data + (unsigned int)text[i]*4*10;\r
63 unsigned short *dst = dest;\r
64 for (l = 0; l < 10; l++, dst += SCREEN_WIDTH-8)\r
65 {\r
66 for (u = 8/2; u > 0; u--, src++)\r
67 {\r
68 int c, r, g, b;\r
69 c = *src >> 4;\r
70 r = (*dst & 0xf800) >> 8;\r
71 g = (*dst & 0x07e0) >> 3;\r
72 b = (*dst & 0x001f) << 3;\r
73 r = (c^0xf)*r/15 + c*tr/15;\r
74 g = (c^0xf)*g/15 + c*tg/15;\r
75 b = (c^0xf)*b/15 + c*tb/15;\r
76 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
77 c = *src & 0xf;\r
78 r = (*dst & 0xf800) >> 8;\r
79 g = (*dst & 0x07e0) >> 3;\r
80 b = (*dst & 0x001f) << 3;\r
81 r = (c^0xf)*r/15 + c*tr/15;\r
82 g = (c^0xf)*g/15 + c*tg/15;\r
83 b = (c^0xf)*b/15 + c*tb/15;\r
84 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
85 }\r
86 }\r
87 dest += 8;\r
88 }\r
89}\r
90\r
91void text_out16(int x, int y, const char *texto, ...)\r
92{\r
93 va_list args;\r
24b24674 94 char buffer[256];\r
95 int maxw = (SCREEN_WIDTH - x) / 8;\r
c7a4ff64 96\r
24b24674 97 va_start(args, texto);\r
98 vsnprintf(buffer, sizeof(buffer), texto, args);\r
c7a4ff64 99 va_end(args);\r
100\r
24b24674 101 if (maxw > 255)\r
102 maxw = 255;\r
103 buffer[maxw] = 0;\r
104\r
c7a4ff64 105 text_out16_(x,y,buffer,menu_text_color);\r
106}\r
107\r
108\r
049a6b3e 109static void smalltext_out16_(int x, int y, const char *texto, int color)\r
c7a4ff64 110{\r
111 int i;\r
112 unsigned char *src;\r
113 unsigned short *dst;\r
114\r
115 for (i = 0;; i++, x += 6)\r
116 {\r
117 unsigned char c = (unsigned char) texto[i];\r
118 int h = 8;\r
119\r
120 if (!c) break;\r
121\r
122 src = fontdata6x8[c];\r
123 dst = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
124\r
125 while (h--)\r
126 {\r
127 int w = 0x20;\r
128 while (w)\r
129 {\r
130 if( *src & w ) *dst = color;\r
131 dst++;\r
132 w>>=1;\r
133 }\r
134 src++;\r
135\r
136 dst += SCREEN_WIDTH-6;\r
137 }\r
138 }\r
139}\r
140\r
049a6b3e 141void smalltext_out16(int x, int y, const char *texto, int color)\r
c7a4ff64 142{\r
049a6b3e 143 char buffer[SCREEN_WIDTH/6+1];\r
c7a4ff64 144\r
049a6b3e 145 strncpy(buffer, texto, sizeof(buffer));\r
146 buffer[sizeof(buffer) - 1] = 0;\r
c7a4ff64 147\r
049a6b3e 148 smalltext_out16_(x, y, buffer, color);\r
c7a4ff64 149}\r
150\r
049a6b3e 151static void menu_draw_selection(int x, int y, int w)\r
c7a4ff64 152{\r
153 int i, h;\r
154 unsigned short *dst, *dest;\r
155\r
156 text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);\r
157\r
158 if (menu_sel_color < 0) return; // no selection hilight\r
159\r
160 if (y > 0) y--;\r
161 dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH + 14;\r
162 for (h = 11; h > 0; h--)\r
163 {\r
164 dst = dest;\r
165 for (i = w; i > 0; i--)\r
166 *dst++ = menu_sel_color;\r
167 dest += SCREEN_WIDTH;\r
168 }\r
169}\r
170\r
171static int parse_hex_color(char *buff)\r
172{\r
173 char *endp = buff;\r
174 int t = (int) strtoul(buff, &endp, 16);\r
703e4c7b 175 if (endp != buff)\r
176#ifdef PSP\r
177 return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);\r
178#else\r
179 return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);\r
180#endif\r
c7a4ff64 181 return -1;\r
182}\r
183\r
184void menu_init(void)\r
185{\r
186 int c, l;\r
187 unsigned char *fd = menu_font_data;\r
188 char buff[256];\r
189 FILE *f;\r
190\r
191 // generate default font from fontdata8x8\r
192 memset(menu_font_data, 0, sizeof(menu_font_data));\r
193 for (c = 0; c < 256; c++)\r
194 {\r
195 for (l = 0; l < 8; l++)\r
196 {\r
197 unsigned char fd8x8 = fontdata8x8[c*8+l];\r
198 if (fd8x8&0x80) *fd |= 0xf0;\r
199 if (fd8x8&0x40) *fd |= 0x0f; fd++;\r
200 if (fd8x8&0x20) *fd |= 0xf0;\r
201 if (fd8x8&0x10) *fd |= 0x0f; fd++;\r
202 if (fd8x8&0x08) *fd |= 0xf0;\r
203 if (fd8x8&0x04) *fd |= 0x0f; fd++;\r
204 if (fd8x8&0x02) *fd |= 0xf0;\r
205 if (fd8x8&0x01) *fd |= 0x0f; fd++;\r
206 }\r
207 fd += 8*2/2; // 2 empty lines\r
208 }\r
209\r
210 // load custom font and selector (stored as 1st symbol in font table)\r
211 readpng(menu_font_data, "skin/font.png", READPNG_FONT);\r
212 memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>'\r
213 readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR);\r
214\r
215 // load custom colors\r
216 f = fopen("skin/skin.txt", "r");\r
217 if (f != NULL)\r
218 {\r
219 lprintf("found skin.txt\n");\r
220 while (!feof(f))\r
221 {\r
222 fgets(buff, sizeof(buff), f);\r
223 if (buff[0] == '#' || buff[0] == '/') continue; // comment\r
224 if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line\r
225 if (strncmp(buff, "text_color=", 11) == 0)\r
226 {\r
227 int tmp = parse_hex_color(buff+11);\r
228 if (tmp >= 0) menu_text_color = tmp;\r
229 else lprintf("skin.txt: parse error for text_color\n");\r
230 }\r
231 else if (strncmp(buff, "selection_color=", 16) == 0)\r
232 {\r
233 int tmp = parse_hex_color(buff+16);\r
234 if (tmp >= 0) menu_sel_color = tmp;\r
235 else lprintf("skin.txt: parse error for selection_color\n");\r
236 }\r
237 else\r
238 lprintf("skin.txt: parse error: %s\n", buff);\r
239 }\r
240 fclose(f);\r
241 }\r
242}\r
243\r
244\r
049a6b3e 245static int me_id2offset(const menu_entry *ent, menu_id id)\r
c7a4ff64 246{\r
247 int i;\r
24b24674 248 for (i = 0; ent->name; ent++, i++)\r
249 if (ent->id == id) return i;\r
c7a4ff64 250\r
251 lprintf("%s: id %i not found\n", __FUNCTION__, id);\r
252 return 0;\r
253}\r
254\r
049a6b3e 255static void me_enable(menu_entry *entries, menu_id id, int enable)\r
c7a4ff64 256{\r
24b24674 257 int i = me_id2offset(entries, id);\r
c7a4ff64 258 entries[i].enabled = enable;\r
259}\r
260\r
049a6b3e 261static int me_count(const menu_entry *ent)\r
c7a4ff64 262{\r
24b24674 263 int ret;\r
c7a4ff64 264\r
24b24674 265 for (ret = 0; ent->name; ent++, ret++)\r
266 ;\r
c7a4ff64 267\r
268 return ret;\r
269}\r
270\r
049a6b3e 271static void me_draw(const menu_entry *entries, int sel)\r
24b24674 272{\r
273 const menu_entry *ent;\r
274 int x, y, w = 0, h = 0;\r
049a6b3e 275 int offs, opt_offs = 27*8;\r
24b24674 276 const char *name;\r
277 int asel = 0;\r
278 int i, n;\r
279\r
280 /* calculate size of menu rect */\r
281 for (ent = entries, i = n = 0; ent->name; ent++, i++)\r
282 {\r
283 int wt;\r
284\r
285 if (!ent->enabled)\r
286 continue;\r
287\r
288 if (i == sel)\r
289 asel = n;\r
290\r
291 name = NULL;\r
292 wt = strlen(ent->name) * 8; /* FIXME: unhardcode font width */\r
293 if (wt == 0 && ent->generate_name)\r
049a6b3e 294 name = ent->generate_name(ent->id, &offs);\r
24b24674 295 if (name != NULL)\r
296 wt = strlen(name) * 8;\r
297\r
298 if (ent->beh != MB_NONE)\r
299 {\r
300 if (wt > opt_offs)\r
301 opt_offs = wt + 8;\r
302 wt = opt_offs;\r
303\r
304 switch (ent->beh) {\r
305 case MB_NONE: break;\r
306 case MB_OPT_ONOFF:\r
307 case MB_OPT_RANGE: wt += 8*3; break;\r
308 case MB_OPT_CUSTOM:\r
049a6b3e 309 case MB_OPT_CUSTONOFF:\r
310 case MB_OPT_CUSTRANGE:\r
24b24674 311 name = NULL;\r
049a6b3e 312 offs = 0;\r
24b24674 313 if (ent->generate_name != NULL)\r
049a6b3e 314 name = ent->generate_name(ent->id, &offs);\r
24b24674 315 if (name != NULL)\r
049a6b3e 316 wt += (strlen(name) + offs) * 8;\r
24b24674 317 break;\r
318 }\r
319 }\r
320\r
321 if (wt > w)\r
322 w = wt;\r
323 n++;\r
324 }\r
325 h = n * 10;\r
326 w += 16; /* selector */\r
327\r
328 if (w > SCREEN_WIDTH) {\r
329 lprintf("width %d > %d\n", w, SCREEN_WIDTH);\r
330 w = SCREEN_WIDTH;\r
331 }\r
332 if (h > SCREEN_HEIGHT) {\r
333 lprintf("height %d > %d\n", w, SCREEN_HEIGHT);\r
334 h = SCREEN_HEIGHT;\r
335 }\r
336\r
337 x = SCREEN_WIDTH / 2 - w / 2;\r
338 y = SCREEN_HEIGHT / 2 - h / 2;\r
339\r
340 /* draw */\r
341 plat_video_menu_begin();\r
342 menu_draw_selection(x, y + asel * 10, w);\r
343\r
344 for (ent = entries; ent->name; ent++)\r
345 {\r
346 if (!ent->enabled)\r
347 continue;\r
348\r
349 name = ent->name;\r
350 if (strlen(name) == 0) {\r
351 if (ent->generate_name)\r
049a6b3e 352 name = ent->generate_name(ent->id, &offs);\r
24b24674 353 }\r
354 if (name != NULL)\r
355 text_out16(x + 16, y, name);\r
356\r
357 switch (ent->beh) {\r
358 case MB_NONE:\r
359 break;\r
360 case MB_OPT_ONOFF:\r
361 text_out16(x + 16 + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");\r
362 break;\r
363 case MB_OPT_RANGE:\r
364 text_out16(x + 16 + opt_offs, y, "%i", *(int *)ent->var);\r
365 break;\r
366 case MB_OPT_CUSTOM:\r
049a6b3e 367 case MB_OPT_CUSTONOFF:\r
368 case MB_OPT_CUSTRANGE:\r
24b24674 369 name = NULL;\r
049a6b3e 370 offs = 0;\r
24b24674 371 if (ent->generate_name)\r
049a6b3e 372 name = ent->generate_name(ent->id, &offs);\r
24b24674 373 if (name != NULL)\r
049a6b3e 374 text_out16(x + 16 + opt_offs + offs * 8, y, "%s", name);\r
24b24674 375 break;\r
376 }\r
377\r
378 y += 10;\r
379 }\r
380\r
049a6b3e 381 /* display message if we have one */\r
382 if (menuErrorMsg[0] != 0) {\r
383 static int msg_redraws = 0;\r
384 if (SCREEN_HEIGHT - h >= 2*10)\r
385 text_out16(5, 226, menuErrorMsg);\r
386 else\r
387 lprintf("menu msg doesn't fit!\n");\r
388\r
389 if (++msg_redraws > 4) {\r
390 menuErrorMsg[0] = 0;\r
391 msg_redraws = 0;\r
392 }\r
393 }\r
394\r
24b24674 395 plat_video_menu_end();\r
c7a4ff64 396}\r
397\r
049a6b3e 398static int me_process(menu_entry *entry, int is_next)\r
c7a4ff64 399{\r
c7a4ff64 400 switch (entry->beh)\r
401 {\r
24b24674 402 case MB_OPT_ONOFF:\r
049a6b3e 403 case MB_OPT_CUSTONOFF:\r
c7a4ff64 404 *(int *)entry->var ^= entry->mask;\r
405 return 1;\r
24b24674 406 case MB_OPT_RANGE:\r
049a6b3e 407 case MB_OPT_CUSTRANGE:\r
c7a4ff64 408 *(int *)entry->var += is_next ? 1 : -1;\r
049a6b3e 409 if (*(int *)entry->var < (int)entry->min)\r
410 *(int *)entry->var = (int)entry->max;\r
411 if (*(int *)entry->var > (int)entry->max)\r
412 *(int *)entry->var = (int)entry->min;\r
c7a4ff64 413 return 1;\r
414 default:\r
415 return 0;\r
416 }\r
417}\r
418\r
049a6b3e 419static void debug_menu_loop(void);\r
420\r
24b24674 421static void me_loop(menu_entry *menu, int *menu_sel)\r
422{\r
423 int ret, inp, sel = *menu_sel, menu_sel_max;\r
424\r
425 menu_sel_max = me_count(menu) - 1;\r
426 if (menu_sel_max < 1) {\r
427 lprintf("no enabled menu entries\n");\r
428 return;\r
429 }\r
430\r
431 while (!menu[sel].enabled && sel < menu_sel_max)\r
432 sel++;\r
433\r
434 /* make sure action buttons are not pressed on entering menu */\r
049a6b3e 435 me_draw(menu, sel);\r
24b24674 436 while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU));\r
437\r
438 for (;;)\r
439 {\r
049a6b3e 440 me_draw(menu, sel);\r
441 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|\r
442 PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R, 70);\r
443 if (inp & (PBTN_MENU|PBTN_MBACK))\r
444 break;\r
445\r
24b24674 446 if (inp & PBTN_UP ) {\r
447 do {\r
448 sel--;\r
449 if (sel < 0)\r
450 sel = menu_sel_max;\r
451 }\r
049a6b3e 452 while (!menu[sel].enabled || !menu[sel].selectable);\r
24b24674 453 }\r
454 if (inp & PBTN_DOWN) {\r
455 do {\r
456 sel++;\r
457 if (sel > menu_sel_max)\r
458 sel = 0;\r
459 }\r
049a6b3e 460 while (!menu[sel].enabled || !menu[sel].selectable);\r
461 }\r
462\r
463 /* a bit hacky but oh well */\r
464 if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R))\r
465 debug_menu_loop();\r
466\r
467 if (inp & (PBTN_LEFT|PBTN_RIGHT)) { /* multi choice */\r
468 if (me_process(&menu[sel], (inp & PBTN_RIGHT) ? 1 : 0))\r
469 continue;\r
24b24674 470 }\r
24b24674 471\r
049a6b3e 472 if (inp & (PBTN_MOK|PBTN_LEFT|PBTN_RIGHT))\r
24b24674 473 {\r
049a6b3e 474 if (menu[sel].handler != NULL) {\r
475 ret = menu[sel].handler(menu[sel].id, inp);\r
24b24674 476 if (ret) break;\r
049a6b3e 477 menu_sel_max = me_count(menu) - 1; /* might change */\r
24b24674 478 }\r
479 }\r
24b24674 480 }\r
481 *menu_sel = sel;\r
482}\r
483\r
484/* ***************************************** */\r
485\r
049a6b3e 486static void draw_menu_credits(void)\r
487{\r
488 const char *creds, *p;\r
489 int x, y, h, w, wt;\r
24b24674 490\r
049a6b3e 491 p = creds = plat_get_credits();\r
24b24674 492\r
049a6b3e 493 for (h = 1, w = 0; *p != 0; h++) {\r
494 for (wt = 0; *p != 0 && *p != '\n'; p++)\r
495 wt++;\r
24b24674 496\r
049a6b3e 497 if (wt > w)\r
498 w = wt;\r
499 if (*p == 0)\r
24b24674 500 break;\r
049a6b3e 501 p++;\r
24b24674 502 }\r
503\r
049a6b3e 504 x = SCREEN_WIDTH / 2 - w * 8 / 2;\r
505 y = SCREEN_HEIGHT / 2 - h * 10 / 2;\r
506 if (x < 0) x = 0;\r
507 if (y < 0) y = 0;\r
508\r
509 plat_video_menu_begin();\r
510\r
511 for (p = creds; *p != 0 && y <= SCREEN_HEIGHT - 10; y += 10) {\r
512 text_out16(x, y, p);\r
513\r
514 for (; *p != 0 && *p != '\n'; p++)\r
515 ;\r
516 if (*p != 0)\r
517 p++;\r
518 }\r
519\r
520 plat_video_menu_end();\r
24b24674 521}\r
522\r
049a6b3e 523// --------- loading ROM screen ----------\r
524\r
525static int cdload_called = 0;\r
526\r
527static void load_progress_cb(int percent)\r
24b24674 528{\r
049a6b3e 529 int ln, len = percent * SCREEN_WIDTH / 100;\r
530 unsigned short *dst = (unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH * 10 * 2;\r
24b24674 531\r
049a6b3e 532 if (len > SCREEN_WIDTH)\r
533 len = SCREEN_WIDTH;\r
534 for (ln = 10 - 2; ln > 0; ln--, dst += SCREEN_WIDTH)\r
535 memset(dst, 0xff, len * 2);\r
536 plat_video_menu_end();\r
537}\r
538\r
539static void cdload_progress_cb(int percent)\r
24b24674 540{\r
049a6b3e 541 int ln, len = percent * SCREEN_WIDTH / 100;\r
542 unsigned short *dst = (unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH * 10 * 2;\r
24b24674 543\r
049a6b3e 544 memset(dst, 0xff, SCREEN_WIDTH * (10 - 2) * 2);\r
24b24674 545\r
049a6b3e 546 smalltext_out16(1, 3 * 10, "Processing CD image / MP3s", 0xffff);\r
547 smalltext_out16(1, 4 * 10, rom_fname_loaded, 0xffff);\r
548 dst += SCREEN_WIDTH * 30;\r
24b24674 549\r
049a6b3e 550 if (len > SCREEN_WIDTH)\r
551 len = SCREEN_WIDTH;\r
552 for (ln = (10 - 2); ln > 0; ln--, dst += SCREEN_WIDTH)\r
553 memset(dst, 0xff, len * 2);\r
24b24674 554\r
049a6b3e 555 plat_video_menu_end();\r
556 cdload_called = 1;\r
24b24674 557}\r
558\r
049a6b3e 559void menu_romload_prepare(const char *rom_name)\r
560{\r
561 const char *p = rom_name + strlen(rom_name);\r
b8464531 562\r
049a6b3e 563 plat_video_menu_begin();\r
b8464531 564\r
049a6b3e 565 while (p > rom_name && *p != '/')\r
566 p--;\r
b8464531 567\r
049a6b3e 568 /* fill both buffers, callbacks won't update in full */\r
569 smalltext_out16(1, 1, "Loading", 0xffff);\r
570 smalltext_out16(1, 10, p, 0xffff);\r
571 plat_video_menu_end();\r
b8464531 572\r
049a6b3e 573 smalltext_out16(1, 1, "Loading", 0xffff);\r
574 smalltext_out16(1, 10, p, 0xffff);\r
575 plat_video_menu_end();\r
576\r
577 PicoCartLoadProgressCB = load_progress_cb;\r
578 PicoCDLoadProgressCB = cdload_progress_cb;\r
579 cdload_called = 0;\r
580}\r
581\r
582void menu_romload_end(void)\r
725d7f6c 583{\r
049a6b3e 584 PicoCartLoadProgressCB = PicoCDLoadProgressCB = NULL;\r
585 smalltext_out16(1, (cdload_called ? 6 : 3) * 10,\r
586 "Starting emulation...", 0xffff);\r
587 plat_video_menu_end();\r
588}\r
725d7f6c 589\r
049a6b3e 590// -------------- ROM selector --------------\r
725d7f6c 591\r
049a6b3e 592// rrrr rggg gggb bbbb\r
593static unsigned short file2color(const char *fname)\r
594{\r
595 const char *ext = fname + strlen(fname) - 3;\r
596 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso", "cue" };\r
597 static const char *other_exts[] = { "gmv", "pat" };\r
598 int i;\r
599\r
600 if (ext < fname) ext = fname;\r
601 for (i = 0; i < array_size(rom_exts); i++)\r
602 if (strcasecmp(ext, rom_exts[i]) == 0) return 0xbdff; // FIXME: mk defines\r
603 for (i = 0; i < array_size(other_exts); i++)\r
604 if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5;\r
605 return 0xffff;\r
725d7f6c 606}\r
607\r
049a6b3e 608static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
b8464531 609{\r
049a6b3e 610 int max_cnt, start, i, pos;\r
b8464531 611\r
049a6b3e 612 max_cnt = SCREEN_HEIGHT / 10;\r
613 start = max_cnt / 2 - sel;\r
614 n--; // exclude current dir (".")\r
b8464531 615\r
049a6b3e 616 plat_video_menu_begin();\r
617\r
618// if (!rom_loaded)\r
619// menu_darken_bg(gp2x_screen, 320*240, 0);\r
620\r
621 menu_darken_bg((short *)SCREEN_BUFFER + SCREEN_WIDTH * max_cnt/2 * 10, SCREEN_WIDTH * 8, 0);\r
622\r
623 if (start - 2 >= 0)\r
624 smalltext_out16(14, (start - 2)*10, curdir, 0xffff);\r
625 for (i = 0; i < n; i++) {\r
626 pos = start + i;\r
627 if (pos < 0) continue;\r
628 if (pos >= max_cnt) break;\r
629 if (namelist[i+1]->d_type == DT_DIR) {\r
630 smalltext_out16(14, pos*10, "/", 0xfff6);\r
631 smalltext_out16(14+6, pos*10, namelist[i+1]->d_name, 0xfff6);\r
632 } else {\r
633 unsigned short color = file2color(namelist[i+1]->d_name);\r
634 smalltext_out16(14, pos*10, namelist[i+1]->d_name, color);\r
635 }\r
b8464531 636 }\r
049a6b3e 637 text_out16(5, max_cnt/2 * 10, ">");\r
638 plat_video_menu_end();\r
b8464531 639}\r
640\r
049a6b3e 641static int scandir_cmp(const void *p1, const void *p2)\r
b8464531 642{\r
049a6b3e 643 struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
644 if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
645 if ((*d1)->d_type == DT_DIR) return -1; // put before\r
646 if ((*d2)->d_type == DT_DIR) return 1;\r
647 return alphasort(d1, d2);\r
648}\r
b8464531 649\r
049a6b3e 650static const char *filter_exts[] = {\r
651 ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",\r
652 ".jpg", ".gpe"\r
653};\r
654\r
655static int scandir_filter(const struct dirent *ent)\r
656{\r
657 const char *p;\r
658 int i;\r
659\r
660 if (ent == NULL || ent->d_name == NULL) return 0;\r
661 if (strlen(ent->d_name) < 5) return 1;\r
662\r
663 p = ent->d_name + strlen(ent->d_name) - 4;\r
664\r
665 for (i = 0; i < array_size(filter_exts); i++)\r
666 if (strcmp(p, filter_exts[i]) == 0)\r
667 return 0;\r
668\r
669 return 1;\r
b8464531 670}\r
671\r
049a6b3e 672static char *menu_loop_romsel(char *curr_path, int len)\r
b8464531 673{\r
049a6b3e 674 struct dirent **namelist;\r
675 int n, inp, sel = 0;\r
676 char *ret = NULL, *fname = NULL;\r
677\r
678rescan:\r
679 // is this a dir or a full path?\r
680 if (!plat_is_dir(curr_path)) {\r
681 char *p = curr_path + strlen(curr_path) - 1;\r
682 for (; p > curr_path && *p != '/'; p--)\r
683 ;\r
684 *p = 0;\r
685 fname = p+1;\r
686 }\r
b8464531 687\r
049a6b3e 688 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
689 if (n < 0) {\r
690 lprintf("menu_loop_romsel failed, dir: %s\n", curr_path);\r
691\r
692 // try root\r
693 getcwd(curr_path, len);\r
694 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
695 if (n < 0) {\r
696 // oops, we failed\r
697 lprintf("menu_loop_romsel failed, dir: %s\n", curr_path);\r
698 return NULL;\r
699 }\r
700 }\r
701\r
702 // try to find sel\r
703 if (fname != NULL) {\r
704 int i;\r
705 for (i = 1; i < n; i++) {\r
706 if (strcmp(namelist[i]->d_name, fname) == 0) {\r
707 sel = i - 1;\r
b8464531 708 break;\r
049a6b3e 709 }\r
b8464531 710 }\r
049a6b3e 711 }\r
b8464531 712\r
049a6b3e 713 for (;;)\r
714 {\r
715 draw_dirlist(curr_path, namelist, n, sel);\r
716 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|\r
717 PBTN_L|PBTN_R|PBTN_WEST|PBTN_MOK|PBTN_MBACK|PBTN_MENU, 33); // TODO L R\r
718 if (inp & PBTN_UP ) { sel--; if (sel < 0) sel = n-2; }\r
719 if (inp & PBTN_DOWN) { sel++; if (sel > n-2) sel = 0; }\r
720 if (inp & PBTN_LEFT) { sel-=10; if (sel < 0) sel = 0; }\r
721 if (inp & PBTN_L) { sel-=24; if (sel < 0) sel = 0; }\r
722 if (inp & PBTN_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
723 if (inp & PBTN_R) { sel+=24; if (sel > n-2) sel = n-2; }\r
724 if ((inp & PBTN_MOK) || (inp & (PBTN_MENU|PBTN_WEST)) == (PBTN_MENU|PBTN_WEST)) // enter dir/select || delete\r
b8464531 725 {\r
049a6b3e 726 again:\r
727 if (namelist[sel+1]->d_type == DT_REG)\r
728 {\r
729 strcpy(rom_fname_reload, curr_path);\r
730 strcat(rom_fname_reload, "/");\r
731 strcat(rom_fname_reload, namelist[sel+1]->d_name);\r
732 if (inp & PBTN_MOK) { // return sel\r
733 ret = rom_fname_reload;\r
734 break;\r
725d7f6c 735 }\r
049a6b3e 736// do_delete(rom_fname_reload, namelist[sel+1]->d_name); // TODO\r
737 if (n > 0) {\r
738 while (n--) free(namelist[n]);\r
739 free(namelist);\r
740 }\r
741 goto rescan;\r
742 }\r
743 else if (namelist[sel+1]->d_type == DT_DIR)\r
744 {\r
745 int newlen;\r
746 char *p, *newdir;\r
747 if (!(inp & PBTN_MOK)) continue;\r
748 newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
749 newdir = malloc(newlen);\r
750 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
751 char *start = curr_path;\r
752 p = start + strlen(start) - 1;\r
753 while (*p == '/' && p > start) p--;\r
754 while (*p != '/' && p > start) p--;\r
755 if (p <= start) strcpy(newdir, "/");\r
756 else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
757 } else {\r
758 strcpy(newdir, curr_path);\r
759 p = newdir + strlen(newdir) - 1;\r
760 while (*p == '/' && p >= newdir) *p-- = 0;\r
761 strcat(newdir, "/");\r
762 strcat(newdir, namelist[sel+1]->d_name);\r
b8464531 763 }\r
049a6b3e 764 ret = menu_loop_romsel(newdir, newlen);\r
765 free(newdir);\r
b8464531 766 break;\r
049a6b3e 767 }\r
768 else\r
769 {\r
770 // unknown file type, happens on NTFS mounts. Try to guess.\r
771 FILE *tstf; int tmp;\r
772 strcpy(rom_fname_reload, curr_path);\r
773 strcat(rom_fname_reload, "/");\r
774 strcat(rom_fname_reload, namelist[sel+1]->d_name);\r
775 tstf = fopen(rom_fname_reload, "rb");\r
776 if (tstf != NULL)\r
777 {\r
778 if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
779 namelist[sel+1]->d_type = DT_REG;\r
780 else namelist[sel+1]->d_type = DT_DIR;\r
781 fclose(tstf);\r
782 goto again;\r
783 }\r
784 }\r
785 }\r
786 if (inp & PBTN_MBACK)\r
787 break;\r
788 }\r
789\r
790 if (n > 0) {\r
791 while (n--) free(namelist[n]);\r
792 free(namelist);\r
793 }\r
794\r
795 return ret;\r
796}\r
797\r
798// ------------ patch/gg menu ------------\r
799\r
800static void draw_patchlist(int sel)\r
801{\r
802 int max_cnt, start, i, pos, active;\r
803\r
804 max_cnt = SCREEN_HEIGHT / 10;\r
805 start = max_cnt / 2 - sel;\r
806\r
807 plat_video_menu_begin();\r
808\r
809 for (i = 0; i < PicoPatchCount; i++) {\r
810 pos = start + i;\r
811 if (pos < 0) continue;\r
812 if (pos >= max_cnt) break;\r
813 active = PicoPatches[i].active;\r
814 smalltext_out16(14, pos*10, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);\r
815 smalltext_out16(14+6*4, pos*10, PicoPatches[i].name, active ? 0xfff6 : 0xffff);\r
816 }\r
817 pos = start + i;\r
818 if (pos < max_cnt)\r
819 smalltext_out16(14, pos * 10, "done", 0xffff);\r
820\r
821 text_out16(5, max_cnt / 2 * 10, ">");\r
822 plat_video_menu_end();\r
823}\r
824\r
825static void menu_loop_patches(void)\r
826{\r
827 static int menu_sel = 0;\r
828 int inp;\r
829\r
830 for (;;)\r
831 {\r
832 draw_patchlist(menu_sel);\r
833 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R|PBTN_MOK|PBTN_MBACK, 33);\r
834 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
835 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
836 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
837 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
838 if (inp & PBTN_MOK) { // action\r
839 if (menu_sel < PicoPatchCount)\r
840 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
841 else break;\r
842 }\r
843 if (inp & PBTN_MBACK)\r
844 break;\r
845 }\r
846}\r
847\r
848// ------------ savestate loader ------------\r
849\r
850static int state_slot_flags = 0;\r
851\r
852static void state_check_slots(void)\r
853{\r
854 int slot;\r
855\r
856 state_slot_flags = 0;\r
857\r
858 for (slot = 0; slot < 10; slot++) {\r
859 if (emu_checkSaveFile(slot))\r
860 state_slot_flags |= 1 << slot;\r
861 }\r
862}\r
863\r
864static void draw_savestate_bg(int slot)\r
865{\r
866 struct PicoVideo tmp_pv;\r
867 unsigned short tmp_cram[0x40];\r
868 unsigned short tmp_vsram[0x40];\r
869 void *tmp_vram, *file;\r
870 char *fname;\r
871\r
872 fname = emu_GetSaveFName(1, 0, slot);\r
873 if (!fname) return;\r
874\r
875 tmp_vram = malloc(sizeof(Pico.vram));\r
876 if (tmp_vram == NULL) return;\r
877\r
878 memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
879 memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
880 memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
881 memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
882\r
883 if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
884 file = gzopen(fname, "rb");\r
885 emu_setSaveStateCbs(1);\r
886 } else {\r
887 file = fopen(fname, "rb");\r
888 emu_setSaveStateCbs(0);\r
889 }\r
890\r
891 if (file) {\r
892 if (PicoAHW & PAHW_MCD) {\r
893 PicoCdLoadStateGfx(file);\r
894 } else {\r
895 areaSeek(file, 0x10020, SEEK_SET); // skip header and RAM in state file\r
896 areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
897 areaSeek(file, 0x2000, SEEK_CUR);\r
898 areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
899 areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
900 areaSeek(file, 0x221a0, SEEK_SET);\r
901 areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
902 }\r
903 areaClose(file);\r
904 }\r
905\r
906 /* do a frame and fetch menu bg */\r
907 emu_forcedFrame(POPT_EN_SOFTSCALE);\r
908 plat_video_menu_enter(1);\r
909\r
910 memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
911 memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
912 memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
913 memcpy(&Pico.video, &tmp_pv, sizeof(Pico.video));\r
914 free(tmp_vram);\r
915}\r
916\r
917static void draw_savestate_menu(int menu_sel, int is_loading)\r
918{\r
919 int i, x, y, w, h;\r
920\r
921 if (state_slot_flags & (1 << menu_sel))\r
922 draw_savestate_bg(menu_sel);\r
923\r
924 w = 13 * 8 + 16;\r
925 h = (1+2+10+1) * 10;\r
926 x = SCREEN_WIDTH / 2 - w / 2;\r
927 if (x < 0) x = 0;\r
928 y = SCREEN_HEIGHT / 2 - h / 2;\r
929 if (y < 0) y = 0;\r
930\r
931 plat_video_menu_begin();\r
932\r
933 text_out16(x, y, is_loading ? "Load state" : "Save state");\r
934 y += 3*10;\r
935\r
936 menu_draw_selection(x - 16, y + menu_sel * 10, 13 * 8 + 4);\r
937\r
938 /* draw all 10 slots */\r
939 y += 10;\r
940 for (i = 0; i < 10; i++, y += 10)\r
941 {\r
942 text_out16(x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
943 }\r
944 text_out16(x, y, "back");\r
945\r
946 plat_video_menu_end();\r
947}\r
948\r
949static int menu_loop_savestate(int is_loading)\r
950{\r
951 static int menu_sel = 10;\r
952 int menu_sel_max = 10;\r
953 unsigned long inp = 0;\r
954\r
955 state_check_slots();\r
956\r
957 for (;;)\r
958 {\r
959 draw_savestate_menu(menu_sel, is_loading);\r
960 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK, 100);\r
961 if (inp & PBTN_UP) {\r
962 do {\r
963 menu_sel--;\r
964 if (menu_sel < 0)\r
965 menu_sel = menu_sel_max;\r
966 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
967 }\r
968 if (inp & PBTN_DOWN) {\r
969 do {\r
970 menu_sel++;\r
971 if (menu_sel > menu_sel_max)\r
972 menu_sel = 0;\r
973 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
974 }\r
975 if (inp & PBTN_MOK) { // save/load\r
976 if (menu_sel < 10) {\r
977 state_slot = menu_sel;\r
978 if (emu_SaveLoadGame(is_loading, 0)) {\r
979 strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
980 return 0;\r
981 }\r
982 return 1;\r
983 }\r
984 return 0;\r
985 }\r
986 if (inp & PBTN_MBACK)\r
987 return 0;\r
988 }\r
989}\r
990\r
991// -------------- key config --------------\r
992\r
993static char *action_binds(int player_idx, int action_mask, int dev_id)\r
994{\r
995 const int *binds;\r
996 int k, count;\r
997\r
998 static_buff[0] = 0;\r
999\r
1000 binds = in_get_dev_binds(dev_id);\r
1001 if (binds == NULL)\r
1002 return static_buff;\r
1003\r
1004 count = in_get_dev_bind_count(dev_id);\r
1005 for (k = 0; k < count; k++)\r
1006 {\r
1007 const char *xname;\r
1008 if (!(binds[k] & action_mask))\r
1009 continue;\r
1010\r
1011 if (player_idx >= 0 && ((binds[k] >> 16) & 3) != player_idx)\r
1012 continue;\r
1013\r
1014 xname = in_get_key_name(dev_id, k);\r
1015 if (static_buff[0])\r
1016 strncat(static_buff, " + ", sizeof(static_buff));\r
1017 strncat(static_buff, xname, sizeof(static_buff));\r
1018 }\r
1019\r
1020 return static_buff;\r
1021}\r
1022\r
1023static int count_bound_keys(int dev_id, int action_mask, int player_idx)\r
1024{\r
1025 const int *binds;\r
1026 int k, keys = 0;\r
1027 int count;\r
1028\r
1029 binds = in_get_dev_binds(dev_id);\r
1030 if (binds == NULL)\r
1031 return 0;\r
1032\r
1033 count = in_get_dev_bind_count(dev_id);\r
1034 for (k = 0; k < count; k++)\r
1035 {\r
1036 if (!(binds[k] & action_mask))\r
1037 continue;\r
1038\r
1039 if (player_idx >= 0 && ((binds[k] >> 16) & 3) != player_idx)\r
1040 continue;\r
1041\r
1042 keys++;\r
1043 }\r
1044\r
1045 return keys;\r
1046}\r
1047\r
1048static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx,\r
1049 int sel, int dev_id, int dev_count, int is_bind)\r
1050{\r
1051 int x, y = 30, w, i;\r
1052 const char *dev_name;\r
1053\r
1054 x = SCREEN_WIDTH / 2 - 32*8 / 2;\r
1055 if (x < 0) x = 0;\r
1056\r
1057 plat_video_menu_begin();\r
1058 if (player_idx >= 0)\r
1059 text_out16(x, 10, "Player %i controls", player_idx + 1);\r
1060 else\r
1061 text_out16(x, 10, "Emulator controls");\r
1062\r
1063 menu_draw_selection(x - 16, y + sel*10, (player_idx >= 0) ? 66 : 140);\r
1064\r
1065 for (i = 0; i < opt_cnt; i++, y+=10)\r
1066 text_out16(x, y, "%s : %s", opts[i].name,\r
1067 action_binds(player_idx, opts[i].mask, dev_id));\r
1068\r
1069 dev_name = in_get_dev_name(dev_id, 1, 1);\r
1070 w = strlen(dev_name) * 8;\r
1071 if (w < 30 * 8)\r
1072 w = 30 * 8;\r
1073 if (w > SCREEN_WIDTH)\r
1074 w = SCREEN_WIDTH;\r
1075\r
1076 x = SCREEN_WIDTH / 2 - w / 2;\r
1077\r
1078 if (dev_count > 1) {\r
1079 text_out16(x, SCREEN_HEIGHT - 4*10, "Viewing binds for:");\r
1080 text_out16(x, SCREEN_HEIGHT - 3*10, dev_name);\r
1081 }\r
1082\r
1083 if (is_bind)\r
1084 text_out16(x, SCREEN_HEIGHT - 2*10, "Press a button to bind/unbind");\r
1085 else if (dev_count > 1)\r
1086 text_out16(x, SCREEN_HEIGHT - 2*10, "Press left/right for other devs");\r
1087\r
1088 plat_video_menu_end();\r
1089}\r
1090\r
1091static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)\r
1092{\r
1093 int i, sel = 0, menu_sel_max = opt_cnt - 1;\r
1094 int dev_id, dev_count, kc, is_down, mkey, unbind;\r
1095\r
1096 for (i = 0, dev_id = -1, dev_count = 0; i < IN_MAX_DEVS; i++) {\r
1097 if (in_get_dev_name(i, 1, 0) != NULL) {\r
1098 dev_count++;\r
1099 if (dev_id < 0)\r
1100 dev_id = i;\r
1101 }\r
1102 }\r
1103\r
1104 if (dev_id == -1) {\r
1105 lprintf("no devs, can't do config\n");\r
1106 return;\r
1107 }\r
1108\r
1109 for (;;)\r
1110 {\r
1111 draw_key_config(opts, opt_cnt, player_idx, sel, dev_id, dev_count, 0);\r
1112 mkey = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_MBACK|PBTN_MOK, 100);\r
1113 switch (mkey) {\r
1114 case PBTN_UP: sel--; if (sel < 0) sel = menu_sel_max; continue;\r
1115 case PBTN_DOWN: sel++; if (sel > menu_sel_max) sel = 0; continue;\r
1116 case PBTN_LEFT:\r
1117 for (i = 0, dev_id--; i < IN_MAX_DEVS; i++, dev_id--) {\r
1118 if (dev_id < 0)\r
1119 dev_id = IN_MAX_DEVS - 1;\r
1120 if (in_get_dev_name(dev_id, 1, 0) != NULL)\r
1121 break;\r
1122 }\r
1123 continue;\r
1124 case PBTN_RIGHT:\r
1125 for (i = 0, dev_id++; i < IN_MAX_DEVS; i++, dev_id++) {\r
1126 if (dev_id >= IN_MAX_DEVS)\r
1127 dev_id = 0;\r
1128 if (in_get_dev_name(dev_id, 1, 0) != NULL)\r
1129 break;\r
1130 }\r
1131 continue;\r
1132 case PBTN_MBACK: return;\r
1133 case PBTN_MOK:\r
1134 if (sel >= opt_cnt)\r
1135 return;\r
1136 while (in_menu_wait_any(30) & PBTN_MOK);\r
1137 break;\r
1138 default:continue;\r
1139 }\r
1140\r
1141 draw_key_config(opts, opt_cnt, player_idx, sel, dev_id, dev_count, 1);\r
1142\r
1143 /* wait for some up event */\r
1144 for (is_down = 1; is_down; )\r
1145 kc = in_update_keycode(&dev_id, &is_down, -1);\r
1146\r
1147 unbind = count_bound_keys(dev_id, opts[sel].mask, player_idx) >= 2;\r
1148\r
1149 in_bind_key(dev_id, kc, opts[sel].mask, unbind);\r
1150 if (player_idx >= 0) {\r
1151 /* FIXME */\r
1152 in_bind_key(dev_id, kc, 3 << 16, 1);\r
1153 in_bind_key(dev_id, kc, player_idx << 16, 0);\r
1154 }\r
1155 }\r
1156}\r
1157\r
1158// PicoPad[] format: MXYZ SACB RLDU\r
1159me_bind_action me_ctrl_actions[15] =\r
1160{\r
1161 { "UP ", 0x0001 },\r
1162 { "DOWN ", 0x0002 },\r
1163 { "LEFT ", 0x0004 },\r
1164 { "RIGHT ", 0x0008 },\r
1165 { "A ", 0x0040 },\r
1166 { "B ", 0x0010 },\r
1167 { "C ", 0x0020 },\r
1168 { "A turbo", 0x4000 },\r
1169 { "B turbo", 0x1000 },\r
1170 { "C turbo", 0x2000 },\r
1171 { "START ", 0x0080 },\r
1172 { "MODE ", 0x0800 },\r
1173 { "X ", 0x0400 },\r
1174 { "Y ", 0x0200 },\r
1175 { "Z ", 0x0100 }\r
1176};\r
1177\r
1178// player2_flag, reserved, ?, ?,\r
1179// ?, ?, fast forward, menu\r
1180// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
1181// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
1182me_bind_action emuctrl_actions[] =\r
1183{\r
1184 { "Load State ", 1<<28 },\r
1185 { "Save State ", 1<<27 },\r
1186 { "Prev Save Slot ", 1<<25 },\r
1187 { "Next Save Slot ", 1<<24 },\r
1188 { "Switch Renderer ", 1<<26 },\r
1189 { "Volume Down ", 1<<30 },\r
1190 { "Volume Up ", 1<<29 },\r
1191 { "Fast forward ", 1<<22 },\r
1192 { "Enter Menu ", 1<<23 },\r
1193 { "Pico Next page ", 1<<21 },\r
1194 { "Pico Prev page ", 1<<20 },\r
1195 { "Pico Switch input", 1<<19 },\r
1196 { NULL, 0 }\r
1197};\r
1198\r
1199static int key_config_loop_wrap(menu_id id, int keys)\r
1200{\r
1201 switch (id) {\r
1202 case MA_CTRL_PLAYER1:\r
1203 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions), 0);\r
1204 break;\r
1205 case MA_CTRL_PLAYER2:\r
1206 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions), 1);\r
1207 break;\r
1208 case MA_CTRL_EMU:\r
1209 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);\r
1210 break;\r
1211 default:\r
1212 break;\r
1213 }\r
1214 return 0;\r
1215}\r
1216\r
1217static const char *mgn_dev_name(menu_id id, int *offs)\r
1218{\r
1219 const char *name = NULL;\r
1220 static int it = 0;\r
1221\r
1222 if (id == MA_CTRL_DEV_FIRST)\r
1223 it = 0;\r
1224\r
1225 for (; it < IN_MAX_DEVS; it++) {\r
1226 name = in_get_dev_name(it, 1, 1);\r
1227 if (name != NULL)\r
1228 break;\r
1229 }\r
1230\r
1231 it++;\r
1232 return name;\r
1233}\r
1234\r
1235static int mh_saveloadcfg(menu_id id, int keys);\r
1236static const char *mgn_savecfg(menu_id id, int *offs);\r
1237\r
1238static menu_entry e_menu_keyconfig[] =\r
1239{\r
1240 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),\r
1241 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),\r
1242 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),\r
1243 mee_onoff ("6 button pad", MA_OPT_6BUTTON_PAD, PicoOpt, POPT_6BTN_PAD),\r
1244 mee_range ("Turbo rate", MA_CTRL_TURBO_RATE, currentConfig.turbo_rate, 1, 30),\r
1245 mee_handler_mkname_id(MA_OPT_SAVECFG, mh_saveloadcfg, mgn_savecfg),\r
1246 mee_handler_id("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_saveloadcfg),\r
1247 mee_label (""),\r
1248 mee_label ("Input devices:"),\r
1249 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),\r
1250 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),\r
1251 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),\r
1252 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),\r
1253 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),\r
1254 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),\r
1255 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),\r
1256 mee_end,\r
1257};\r
1258\r
1259static int menu_loop_keyconfig(menu_id id, int keys)\r
1260{\r
1261 static int sel = 0;\r
1262 me_loop(e_menu_keyconfig, &sel);\r
1263 return 0;\r
1264}\r
1265\r
1266// ------------ SCD options menu ------------\r
1267\r
1268static const char *mgn_cdopt_ra(menu_id id, int *offs)\r
1269{\r
1270 *offs = -5;\r
1271 if (PicoCDBuffers <= 0)\r
1272 return " OFF";\r
1273 sprintf(static_buff, "%5iK", PicoCDBuffers * 2);\r
1274 return static_buff;\r
1275}\r
1276\r
1277static int mh_cdopt_ra(menu_id id, int keys)\r
1278{\r
1279 if (keys & PBTN_LEFT) {\r
1280 PicoCDBuffers >>= 1;\r
1281 if (PicoCDBuffers < 2)\r
1282 PicoCDBuffers = 0;\r
1283 } else {\r
1284 if (PicoCDBuffers <= 0)\r
1285 PicoCDBuffers = 1;\r
1286 PicoCDBuffers <<= 1;\r
1287 if (PicoCDBuffers > 8*1024)\r
1288 PicoCDBuffers = 8*1024; // 16M\r
1289 }\r
1290 return 0;\r
1291}\r
1292\r
1293static menu_entry e_menu_cd_options[] =\r
1294{\r
1295 mee_onoff("CD LEDs", MA_CDOPT_LEDS, currentConfig.EmuOpt, 0x0400),\r
1296 mee_onoff("CDDA audio", MA_CDOPT_CDDA, PicoOpt, POPT_EN_MCD_CDDA),\r
1297 mee_onoff("PCM audio", MA_CDOPT_PCM, PicoOpt, POPT_EN_MCD_PCM),\r
1298 mee_cust ("ReadAhead buffer", MA_CDOPT_READAHEAD, mh_cdopt_ra, mgn_cdopt_ra),\r
1299 mee_onoff("SaveRAM cart", MA_CDOPT_SAVERAM, PicoOpt, POPT_EN_MCD_RAMCART),\r
1300 mee_onoff("Scale/Rot. fx (slow)", MA_CDOPT_SCALEROT_CHIP, PicoOpt, POPT_EN_MCD_GFX),\r
1301 mee_onoff("Better sync (slow)", MA_CDOPT_BETTER_SYNC, PicoOpt, POPT_EN_MCD_PSYNC),\r
1302 mee_end,\r
1303};\r
1304\r
1305static int menu_loop_cd_options(menu_id id, int keys)\r
1306{\r
1307 static int sel = 0;\r
1308 me_loop(e_menu_cd_options, &sel);\r
1309 return 0;\r
1310}\r
1311\r
1312// ------------ adv options menu ------------\r
1313\r
1314// TODO FIXME fix if and mv\r
1315static const char *mgn_aopt_sqhack(menu_id id, int *offs)\r
1316{\r
1317 *offs = -10;\r
1318 sprintf(static_buff, "%s, %s", 111 ? " active" : "inactive",\r
1319 (currentConfig.EmuOpt & 0x10) ? "ON" : "OFF");\r
1320 return static_buff;\r
1321}\r
1322\r
1323static menu_entry e_menu_adv_options[] =\r
1324{\r
1325 mee_onoff ("SRAM/BRAM saves", MA_OPT_SRAM_STATES, currentConfig.EmuOpt, EOPT_USE_SRAM),\r
1326 mee_onoff ("Disable sprite limit", MA_OPT2_NO_SPRITE_LIM, PicoOpt, POPT_DIS_SPRITE_LIM),\r
1327 mee_onoff ("Use second CPU for sound", MA_OPT_ARM940_SOUND, PicoOpt, POPT_EXT_FM),\r
1328 mee_onoff ("Emulate Z80", MA_OPT2_ENABLE_Z80, PicoOpt, POPT_EN_Z80),\r
1329 mee_onoff ("Emulate YM2612 (FM)", MA_OPT2_ENABLE_YM2612, PicoOpt, POPT_EN_FM),\r
1330 mee_onoff ("Emulate SN76496 (PSG)", MA_OPT2_ENABLE_SN76496,PicoOpt, POPT_EN_PSG),\r
1331 mee_onoff ("gzip savestates", MA_OPT2_GZIP_STATES, currentConfig.EmuOpt, EOPT_GZIP_SAVES),\r
1332 mee_onoff ("Don't save last used ROM", MA_OPT2_NO_LAST_ROM, currentConfig.EmuOpt, EOPT_NO_AUTOSVCFG),\r
1333 mee_label ("- needs restart -"),\r
1334 mee_onoff ("craigix's RAM timings", MA_OPT2_RAMTIMINGS, currentConfig.EmuOpt, 0x0100),\r
1335 mee_onoff_cust("Squidgehack", MA_OPT2_SQUIDGEHACK, currentConfig.EmuOpt, 0x0010, mgn_aopt_sqhack),\r
1336 mee_onoff ("SVP dynarec", MA_OPT2_SVP_DYNAREC, PicoOpt, POPT_EN_SVP_DRC),\r
1337 mee_onoff ("Disable idle loop patching",MA_OPT2_NO_IDLE_LOOPS,PicoOpt, POPT_DIS_IDLE_DET),\r
1338 mee_end,\r
1339};\r
1340\r
1341static int menu_loop_adv_options(menu_id id, int keys)\r
1342{\r
1343 static int sel = 0;\r
1344 me_loop(e_menu_adv_options, &sel);\r
1345 return 0;\r
1346}\r
1347\r
1348// ------------ gfx options menu ------------\r
1349\r
1350static const char *mgn_opt_scaling(menu_id id, int *offs)\r
1351{\r
1352 *offs = -12;\r
1353 switch (currentConfig.scaling) {\r
1354 default: return " OFF";\r
1355 case 1: return "hw horizontal";\r
1356 case 2: return "hw horiz. + vert.";\r
1357 case 3: return "sw horizontal";\r
1358 }\r
1359}\r
1360\r
1361static const char *mgn_aopt_gamma(menu_id id, int *offs)\r
1362{\r
1363 sprintf(static_buff, "%i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
1364 return static_buff;\r
1365}\r
1366\r
1367static menu_entry e_menu_gfx_options[] =\r
1368{\r
1369 mee_range_cust("Scaling", MA_OPT_SCALING, currentConfig.scaling, 0, 3, mgn_opt_scaling),\r
1370 mee_range_cust("Gamma correction", MA_OPT2_GAMMA, currentConfig.gamma, 1, 300, mgn_aopt_gamma),\r
1371 mee_onoff ("A_SN's gamma curve", MA_OPT2_A_SN_GAMMA, currentConfig.EmuOpt, 0x1000),\r
1372 mee_onoff ("Perfect vsync", MA_OPT2_VSYNC, currentConfig.EmuOpt, 0x2000),\r
1373 mee_end,\r
1374};\r
1375\r
1376static int menu_loop_gfx_options(menu_id id, int keys)\r
1377{\r
1378 static int sel = 0;\r
1379 me_loop(e_menu_gfx_options, &sel);\r
1380 return 0;\r
1381}\r
1382\r
1383// ------------ options menu ------------\r
1384\r
1385static menu_entry e_menu_options[];\r
1386\r
1387/* TODO: move to plat */\r
1388static int mh_opt_render(menu_id id, int keys)\r
1389{\r
1390 if (keys & PBTN_LEFT) {\r
1391 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
1392 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1393 } else {\r
1394 if (PicoOpt&0x10) return 0;\r
1395 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
1396 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1397 }\r
1398 return 0;\r
1399}\r
1400\r
1401static int sndrate_prevnext(int rate, int dir)\r
1402{\r
1403 static const int rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1404 int i;\r
1405\r
1406 for (i = 0; i < 5; i++)\r
1407 if (rates[i] == rate) break;\r
1408\r
1409 i += dir ? 1 : -1;\r
1410 if (i > 4) {\r
1411 if (!(PicoOpt & POPT_EN_STEREO)) {\r
1412 PicoOpt |= POPT_EN_STEREO;\r
1413 return rates[0];\r
1414 }\r
1415 return rates[4];\r
1416 }\r
1417 if (i < 0) {\r
1418 if (PicoOpt & POPT_EN_STEREO) {\r
1419 PicoOpt &= ~POPT_EN_STEREO;\r
1420 return rates[4];\r
1421 }\r
1422 return rates[0];\r
1423 }\r
1424 return rates[i];\r
1425}\r
1426\r
1427static void region_prevnext(int right)\r
1428{\r
1429 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1430 static const int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1431 int i;\r
1432\r
1433 if (right) {\r
1434 if (!PicoRegionOverride) {\r
1435 for (i = 0; i < 6; i++)\r
1436 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1437 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
1438 else PicoRegionOverride=1;\r
1439 }\r
1440 else\r
1441 PicoRegionOverride <<= 1;\r
1442 if (PicoRegionOverride > 8)\r
1443 PicoRegionOverride = 8;\r
1444 } else {\r
1445 if (!PicoRegionOverride) {\r
1446 for (i = 0; i < 6; i++)\r
1447 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1448 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1449 }\r
1450 else\r
1451 PicoRegionOverride >>= 1;\r
1452 }\r
1453}\r
1454\r
1455static int mh_opt_misc(menu_id id, int keys)\r
1456{\r
1457 int i;\r
1458\r
1459 switch (id) {\r
1460 case MA_OPT_SOUND_QUALITY:\r
1461 PsndRate = sndrate_prevnext(PsndRate, keys & PBTN_RIGHT);\r
1462 break;\r
1463 case MA_OPT_REGION:\r
1464 region_prevnext(keys & PBTN_RIGHT);\r
1465 break;\r
1466 case MA_OPT_CONFIRM_STATES:\r
1467 i = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1468 i += (keys & PBTN_LEFT) ? -1 : 1;\r
1469 if (i < 0) i = 0; else if (i > 3) i = 3;\r
1470 i |= i << 1; i &= ~2;\r
1471 currentConfig.EmuOpt &= ~0xa00;\r
1472 currentConfig.EmuOpt |= i << 9;\r
1473 break;\r
1474 default:\r
1475 break;\r
1476 }\r
1477 return 0;\r
1478}\r
1479\r
1480static int mh_saveloadcfg(menu_id id, int keys)\r
1481{\r
1482 int ret;\r
1483\r
1484 if (keys & (PBTN_LEFT|PBTN_RIGHT)) { // multi choice\r
1485 config_slot += (keys & PBTN_LEFT) ? -1 : 1;\r
1486 if (config_slot < 0) config_slot = 9;\r
1487 else if (config_slot > 9) config_slot = 0;\r
1488 me_enable(e_menu_options, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1489 return 0;\r
1490 }\r
1491\r
1492 switch (id) {\r
1493 case MA_OPT_SAVECFG:\r
1494 case MA_OPT_SAVECFG_GAME:\r
1495 if (emu_WriteConfig(id == MA_OPT_SAVECFG_GAME ? 1 : 0))\r
1496 strcpy(menuErrorMsg, "config saved");\r
1497 else\r
1498 strcpy(menuErrorMsg, "failed to write config");\r
1499 break;\r
1500 case MA_OPT_LOADCFG:\r
1501 ret = emu_ReadConfig(1, 1);\r
1502 if (!ret) ret = emu_ReadConfig(0, 1);\r
1503 if (ret) strcpy(menuErrorMsg, "config loaded");\r
1504 else strcpy(menuErrorMsg, "failed to load config");\r
1505 break;\r
1506 default:\r
1507 return 0;\r
1508 }\r
1509\r
1510 return 1;\r
1511}\r
1512\r
1513static const char *mgn_opt_renderer(menu_id id, int *offs)\r
1514{\r
1515 *offs = -6;\r
1516 if (PicoOpt & POPT_ALT_RENDERER)\r
1517 return " 8bit fast";\r
1518 else if (currentConfig.EmuOpt & 0x80)\r
1519 return "16bit accurate";\r
1520 else\r
1521 return " 8bit accurate";\r
1522}\r
1523\r
1524static const char *mgn_opt_fskip(menu_id id, int *offs)\r
1525{\r
1526 if (currentConfig.Frameskip < 0)\r
1527 return "Auto";\r
1528 sprintf(static_buff, "%d", currentConfig.Frameskip);\r
1529 return static_buff;\r
1530}\r
1531\r
1532static const char *mgn_opt_sound(menu_id id, int *offs)\r
1533{\r
1534 const char *str2;\r
1535 *offs = -8;\r
1536 str2 = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";\r
1537 sprintf(static_buff, "%5iHz %s", PsndRate, str2);\r
1538 return static_buff;\r
1539}\r
1540\r
1541static const char *mgn_opt_region(menu_id id, int *offs)\r
1542{\r
1543 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };\r
1544 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
1545 int code = PicoRegionOverride;\r
1546 int u, i = 0;\r
1547\r
1548 *offs = -6;\r
1549 if (code) {\r
1550 code <<= 1;\r
1551 while ((code >>= 1)) i++;\r
1552 if (i > 4)\r
1553 return "unknown";\r
1554 return names[i];\r
1555 } else {\r
1556 strcpy(static_buff, "Auto:");\r
1557 for (u = 0; u < 3; u++) {\r
1558 code = (PicoAutoRgnOrder >> u*4) & 0xf;\r
1559 for (i = 0; code; code >>= 1, i++)\r
1560 ;\r
1561 strcat(static_buff, names_short[i]);\r
1562 }\r
1563 return static_buff;\r
1564 }\r
1565}\r
1566\r
1567static const char *mgn_opt_c_saves(menu_id id, int *offs)\r
1568{\r
1569 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1570 default: return "OFF";\r
1571 case 1: return "writes";\r
1572 case 4: return "loads";\r
1573 case 5: return "both";\r
1574 }\r
1575}\r
1576\r
1577static const char *mgn_savecfg(menu_id id, int *offs)\r
1578{\r
1579 strcpy(static_buff, "Save global config");\r
1580 if (config_slot != 0)\r
1581 sprintf(static_buff + strlen(static_buff), " (profile: %i)", config_slot);\r
1582 return static_buff;\r
1583}\r
1584\r
1585static const char *mgn_loadcfg(menu_id id, int *offs)\r
1586{\r
1587 sprintf(static_buff, "Load cfg from profile %i", config_slot);\r
1588 return static_buff;\r
1589}\r
1590\r
1591static menu_entry e_menu_options[] =\r
1592{\r
1593 mee_range ("Save slot", MA_OPT_SAVE_SLOT, state_slot, 0, 9),\r
1594 mee_range_cust("Frameskip", MA_OPT_FRAMESKIP, currentConfig.Frameskip, -1, 16, mgn_opt_fskip),\r
1595 mee_cust ("Region", MA_OPT_REGION, mh_opt_misc, mgn_opt_region),\r
1596 mee_cust ("Renderer", MA_OPT_RENDERER, mh_opt_render, mgn_opt_renderer),\r
1597 mee_onoff ("Show FPS", MA_OPT_SHOW_FPS, currentConfig.EmuOpt, 0x002),\r
1598 mee_onoff ("Enable sound", MA_OPT_ENABLE_SOUND, currentConfig.EmuOpt, 0x004),\r
1599 mee_cust ("Sound Quality", MA_OPT_SOUND_QUALITY, mh_opt_misc, mgn_opt_sound),\r
1600 mee_cust ("Confirm savestate", MA_OPT_CONFIRM_STATES,mh_opt_misc, mgn_opt_c_saves),\r
1601#if defined(__GP2X__)\r
1602 mee_range ("GP2X CPU clocks", MA_OPT_CPU_CLOCKS, currentConfig.CPUclock, 20, 400),\r
1603#elif defined(PSP)\r
1604 mee_range ("PSP CPU clock", MA_OPT_CPU_CLOCKS, currentConfig.CPUclock, )\r
1605#endif\r
1606 mee_handler ("[Display options]", menu_loop_gfx_options),\r
1607 mee_handler ("[Advanced options]", menu_loop_adv_options),\r
1608 mee_handler ("[Sega/Mega CD options]", menu_loop_cd_options),\r
1609 mee_handler_mkname_id(MA_OPT_SAVECFG, mh_saveloadcfg, mgn_savecfg),\r
1610 mee_handler_id("Save cfg for current game only", MA_OPT_SAVECFG_GAME, mh_saveloadcfg),\r
1611 mee_handler_mkname_id(MA_OPT_LOADCFG, mh_saveloadcfg, mgn_loadcfg),\r
1612 mee_end,\r
1613};\r
1614\r
1615static int menu_loop_options(menu_id id, int keys)\r
1616{\r
1617 static int sel = 0;\r
1618\r
1619 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, rom_loaded);\r
1620 me_enable(e_menu_options, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1621\r
1622 me_loop(e_menu_options, &sel);\r
1623\r
1624 if (PicoRegionOverride)\r
1625 // force setting possibly changed..\r
1626 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1627\r
1628 return 0;\r
1629}\r
1630\r
1631// ------------ debug menu ------------\r
1632\r
1633#include <sys/stat.h>\r
1634#include <sys/types.h>\r
1635\r
1636#include <pico/debug.h>\r
1637\r
1638extern void SekStepM68k(void);\r
1639\r
1640static void mplayer_loop(void)\r
1641{\r
1642 emu_startSound();\r
1643\r
1644 while (1)\r
1645 {\r
1646 PDebugZ80Frame();\r
1647 if (in_menu_wait_any(0) & PBTN_NORTH) break;\r
1648 emu_waitSound();\r
1649 }\r
1650\r
1651 emu_endSound();\r
1652}\r
1653\r
1654static void draw_text_debug(const char *str, int skip, int from)\r
1655{\r
1656 const char *p;\r
1657 int line;\r
1658\r
1659 p = str;\r
1660 while (skip-- > 0)\r
1661 {\r
1662 while (*p && *p != '\n') p++;\r
1663 if (*p == 0 || p[1] == 0) return;\r
1664 p++;\r
1665 }\r
1666\r
1667 str = p;\r
1668 for (line = from; line < SCREEN_HEIGHT/10; line++)\r
1669 {\r
1670 while (*p && *p != '\n') p++;\r
1671 smalltext_out16(1, line*10, str, 0xffff);\r
1672 if (*p == 0) break;\r
1673 p++; str = p;\r
1674 }\r
1675}\r
1676\r
1677static void draw_frame_debug(void)\r
1678{\r
1679 char layer_str[48] = "layers: ";\r
1680 if (PicoDrawMask & PDRAW_LAYERB_ON) memcpy(layer_str + 8, "B", 1);\r
1681 if (PicoDrawMask & PDRAW_LAYERA_ON) memcpy(layer_str + 10, "A", 1);\r
1682 if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);\r
1683 if (PicoDrawMask & PDRAW_SPRITES_HI_ON) memcpy(layer_str + 19, "spr_hi", 6);\r
1684\r
1685 clear_screen();\r
1686 emu_forcedFrame(0);\r
1687 smalltext_out16(4, SCREEN_HEIGHT-8, layer_str, 0xffff);\r
1688}\r
1689\r
1690static void debug_menu_loop(void)\r
1691{\r
1692 int inp, mode = 0;\r
1693 int spr_offs = 0, dumped = 0;\r
1694 char *tmp;\r
1695\r
1696 while (1)\r
1697 {\r
1698 switch (mode)\r
1699 {\r
1700 case 0: plat_video_menu_begin();\r
1701 tmp = PDebugMain();\r
1702 emu_platformDebugCat(tmp);\r
1703 draw_text_debug(tmp, 0, 0);\r
1704 if (dumped) {\r
1705 smalltext_out16(SCREEN_WIDTH-6*10, SCREEN_HEIGHT-8, "dumped", 0xffff);\r
1706 dumped = 0;\r
1707 }\r
1708 break;\r
1709 case 1: draw_frame_debug(); break;\r
1710 case 2: clear_screen();\r
1711 emu_forcedFrame(0);\r
1712 darken_screen();\r
1713 PDebugShowSpriteStats((unsigned short *)SCREEN_BUFFER + (SCREEN_HEIGHT/2 - 240/2)*SCREEN_WIDTH +\r
1714 SCREEN_WIDTH/2 - 320/2, SCREEN_WIDTH); break;\r
1715 case 3: clear_screen();\r
1716 PDebugShowPalette(SCREEN_BUFFER, SCREEN_WIDTH);\r
1717 PDebugShowSprite((unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH*120+SCREEN_WIDTH/2+16,\r
1718 SCREEN_WIDTH, spr_offs);\r
1719 draw_text_debug(PDebugSpriteList(), spr_offs, 6);\r
1720 break;\r
1721 }\r
1722 plat_video_menu_end();\r
1723\r
1724 inp = in_menu_wait(PBTN_EAST|PBTN_MBACK|PBTN_WEST|PBTN_NORTH|PBTN_L|PBTN_R |\r
1725 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);\r
1726 if (inp & PBTN_MBACK) return;\r
1727 if (inp & PBTN_L) { mode--; if (mode < 0) mode = 3; }\r
1728 if (inp & PBTN_R) { mode++; if (mode > 3) mode = 0; }\r
1729 switch (mode)\r
1730 {\r
1731 case 0:\r
1732 if (inp & PBTN_EAST) SekStepM68k();\r
1733 if (inp & PBTN_NORTH) {\r
1734 while (inp & PBTN_NORTH) inp = in_menu_wait_any(-1);\r
1735 mplayer_loop();\r
1736 }\r
1737 if ((inp & (PBTN_WEST|PBTN_LEFT)) == (PBTN_WEST|PBTN_LEFT)) {\r
1738 mkdir("dumps", 0777);\r
1739 PDebugDumpMem();\r
1740 while (inp & PBTN_WEST) inp = in_menu_wait_any(-1);\r
1741 dumped = 1;\r
1742 }\r
1743 break;\r
1744 case 1:\r
1745 if (inp & PBTN_LEFT) PicoDrawMask ^= PDRAW_LAYERB_ON;\r
1746 if (inp & PBTN_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;\r
1747 if (inp & PBTN_DOWN) PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;\r
b3972d82 1748 if (inp & PBTN_UP) PicoDrawMask ^= PDRAW_SPRITES_HI_ON;\r
1749 if (inp & PBTN_EAST) {\r
8e6cbce1 1750 PsndOut = NULL; // just in case\r
1751 PicoSkipFrame = 1;\r
1752 PicoFrame();\r
1753 PicoSkipFrame = 0;\r
fce20e73 1754 while (inp & PBTN_EAST) inp = in_menu_wait_any(-1);\r
8e6cbce1 1755 }\r
b8464531 1756 break;\r
1757 case 3:\r
b3972d82 1758 if (inp & PBTN_DOWN) spr_offs++;\r
1759 if (inp & PBTN_UP) spr_offs--;\r
b8464531 1760 if (spr_offs < 0) spr_offs = 0;\r
1761 break;\r
1762 }\r
1763 }\r
1764}\r
1765\r
049a6b3e 1766// ------------ main menu ------------\r
36f6fd5a 1767\r
049a6b3e 1768static char *romsel_run(void)\r
1769{\r
1770 char *ret, *sel_name;\r
1771\r
1772 sel_name = malloc(sizeof(rom_fname_loaded));\r
1773 if (sel_name == NULL)\r
1774 return NULL;\r
1775 strcpy(sel_name, rom_fname_loaded);\r
1776\r
1777 ret = menu_loop_romsel(sel_name, sizeof(rom_fname_loaded));\r
1778 free(sel_name);\r
1779 return ret;\r
1780}\r
36f6fd5a 1781\r
049a6b3e 1782static int main_menu_handler(menu_id id, int keys)\r
36f6fd5a 1783{\r
049a6b3e 1784 char *ret_name;\r
1785\r
1786 switch (id)\r
1787 {\r
1788 case MA_MAIN_RESUME_GAME:\r
1789 if (rom_loaded)\r
1790 return 1;\r
1791 break;\r
1792 case MA_MAIN_SAVE_STATE:\r
1793 if (rom_loaded)\r
1794 return menu_loop_savestate(0);\r
1795 break;\r
1796 case MA_MAIN_LOAD_STATE:\r
1797 if (rom_loaded)\r
1798 return menu_loop_savestate(1);\r
1799 break;\r
1800 case MA_MAIN_RESET_GAME:\r
1801 if (rom_loaded) {\r
1802 emu_ResetGame();\r
1803 return 1;\r
1804 }\r
1805 break;\r
1806 case MA_MAIN_LOAD_ROM:\r
1807 ret_name = romsel_run();\r
1808 if (ret_name != NULL) {\r
1809 lprintf("selected file: %s\n", ret_name);\r
1810 engineState = PGS_ReloadRom;\r
1811 return 1;\r
1812 }\r
1813 break;\r
1814 case MA_MAIN_CREDITS:\r
1815 draw_menu_credits();\r
1816 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);\r
1817 break;\r
1818 case MA_MAIN_EXIT:\r
1819 engineState = PGS_Quit;\r
1820 return 1;\r
1821 case MA_MAIN_PATCHES:\r
1822 if (rom_loaded && PicoPatches) {\r
1823 menu_loop_patches();\r
1824 PicoPatchApply();\r
1825 strcpy(menuErrorMsg, "Patches applied");\r
36f6fd5a 1826 }\r
049a6b3e 1827 break;\r
1828 default:\r
1829 lprintf("%s: something unknown selected\n", __FUNCTION__);\r
1830 break;\r
1831 }\r
1832\r
1833 return 0;\r
1834}\r
1835\r
1836static menu_entry e_menu_main[] =\r
1837{\r
1838 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),\r
1839 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),\r
1840 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),\r
1841 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),\r
1842 mee_handler_id("Load new ROM/ISO", MA_MAIN_LOAD_ROM, main_menu_handler),\r
1843 mee_handler_id("Change options", MA_MAIN_OPTIONS, menu_loop_options),\r
1844 mee_handler_id("Configure controls", MA_MAIN_OPTIONS, menu_loop_keyconfig),\r
1845 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),\r
1846 mee_handler_id("Patches / GameGenie",MA_MAIN_PATCHES, main_menu_handler),\r
1847 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),\r
1848 mee_end,\r
1849};\r
1850\r
1851void menu_loop(void)\r
1852{\r
1853 static int sel = 0;\r
1854\r
1855 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, rom_loaded);\r
1856 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, rom_loaded);\r
1857 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, rom_loaded);\r
1858 me_enable(e_menu_main, MA_MAIN_RESET_GAME, rom_loaded);\r
1859 me_enable(e_menu_main, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1860\r
1861 plat_video_menu_enter(rom_loaded);\r
1862 in_set_blocking(1);\r
1863 me_loop(e_menu_main, &sel);\r
1864 in_set_blocking(0);\r
1865\r
1866 if (rom_loaded && engineState == PGS_Menu) {\r
1867 /* wait until menu, ok, back is released */\r
1868 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK));\r
1869 engineState = PGS_Running;\r
1870 }\r
1871}\r
1872\r
1873// --------- CD tray close menu ----------\r
1874\r
1875static int mh_tray_load_cd(menu_id id, int keys)\r
1876{\r
1877 cd_img_type cd_type;\r
1878 char *ret_name;\r
1879 int ret = -1;\r
1880\r
1881 ret_name = romsel_run();\r
1882 if (ret_name == NULL)\r
1883 return 0;\r
1884\r
1885 cd_type = emu_cdCheck(NULL, ret_name);\r
1886 if (cd_type != CIT_NOT_CD)\r
1887 ret = Insert_CD(ret_name, cd_type);\r
1888 if (ret != 0) {\r
1889 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1890 lprintf("%s\n", menuErrorMsg);\r
1891 return 0;\r
1892 }\r
1893\r
1894 engineState = PGS_RestartRun;\r
1895 return 1;\r
1896}\r
1897\r
1898static int mh_tray_nothing(menu_id id, int keys)\r
1899{\r
1900 return 1;\r
1901}\r
1902\r
1903static menu_entry e_menu_tray[] =\r
1904{\r
1905 mee_label ("The unit is about to"),\r
1906 mee_label ("close the CD tray."),\r
1907 mee_label (""),\r
1908 mee_label (""),\r
1909 mee_handler("Load CD image", mh_tray_load_cd),\r
1910 mee_handler("Insert nothing", mh_tray_nothing),\r
1911};\r
1912\r
1913int menu_loop_tray(void)\r
1914{\r
1915 int ret = 1, sel = 0;\r
1916\r
1917 plat_video_menu_enter(rom_loaded);\r
1918\r
1919 in_set_blocking(1);\r
1920 me_loop(e_menu_tray, &sel);\r
1921 in_set_blocking(0);\r
1922\r
1923 if (engineState != PGS_RestartRun) {\r
1924 engineState = PGS_RestartRun;\r
1925 ret = 0; /* no CD inserted */\r
36f6fd5a 1926 }\r
049a6b3e 1927\r
1928 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK));\r
1929\r
1930 return ret;\r
36f6fd5a 1931}\r
1932\r
049a6b3e 1933#endif // !UIQ3\r
1934\r
1935// ------------ util ------------\r
1936\r
24b24674 1937/* TODO: rename */\r
1938void menu_darken_bg(void *dst, int pixels, int darker)\r
1939{\r
1940 unsigned int *screen = dst;\r
1941 pixels /= 2;\r
1942 if (darker)\r
1943 {\r
1944 while (pixels--)\r
1945 {\r
1946 unsigned int p = *screen;\r
1947 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1948 }\r
1949 }\r
1950 else\r
1951 {\r
1952 while (pixels--)\r
1953 {\r
1954 unsigned int p = *screen;\r
1955 *screen++ = (p&0xf79ef79e)>>1;\r
1956 }\r
1957 }\r
1958}\r
b8464531 1959\r
049a6b3e 1960/* hidden options for config engine only */\r
1961static menu_entry e_menu_hidden[] =\r
1962{\r
1963 mee_onoff("Accurate sprites", MA_OPT_ACC_SPRITES, PicoOpt, 0x080),\r
1964 mee_end,\r
1965};\r
1966\r
1967static menu_entry *e_menu_table[] =\r
1968{\r
1969 e_menu_options,\r
1970 e_menu_gfx_options,\r
1971 e_menu_adv_options,\r
1972 e_menu_cd_options,\r
1973 e_menu_keyconfig,\r
1974 e_menu_hidden,\r
1975};\r
1976\r
1977static menu_entry *me_list_table = NULL;\r
1978static menu_entry *me_list_i = NULL;\r
1979\r
1980menu_entry *me_list_get_first(void)\r
1981{\r
1982 me_list_table = me_list_i = e_menu_table[0];\r
1983 return me_list_i;\r
1984}\r
1985\r
1986menu_entry *me_list_get_next(void)\r
1987{\r
1988 int i;\r
1989\r
1990 me_list_i++;\r
1991 if (me_list_i->name != NULL)\r
1992 return me_list_i;\r
1993\r
1994 for (i = 0; i < array_size(e_menu_table); i++)\r
1995 if (me_list_table == e_menu_table[i])\r
1996 break;\r
1997\r
1998 if (i + 1 < array_size(e_menu_table))\r
1999 me_list_table = me_list_i = e_menu_table[i + 1];\r
2000 else\r
2001 me_list_table = me_list_i = NULL;\r
2002\r
2003 return me_list_i;\r
2004}\r
2005\r