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