asyn-only dev support + in_gp2x driver
[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
c7a4ff64 18\r
c7a4ff64 19\r
f013066e 20char menuErrorMsg[64] = { 0, };\r
21\r
367b6f1f 22// PicoPad[] format: MXYZ SACB RLDU\r
6589c840 23me_bind_action me_ctrl_actions[15] =\r
367b6f1f 24{\r
6589c840 25 { "UP ", 0x0001 },\r
26 { "DOWN ", 0x0002 },\r
27 { "LEFT ", 0x0004 },\r
28 { "RIGHT ", 0x0008 },\r
29 { "A ", 0x0040 },\r
30 { "B ", 0x0010 },\r
31 { "C ", 0x0020 },\r
32 { "A turbo", 0x4000 },\r
33 { "B turbo", 0x1000 },\r
34 { "C turbo", 0x2000 },\r
35 { "START ", 0x0080 },\r
36 { "MODE ", 0x0800 },\r
37 { "X ", 0x0400 },\r
38 { "Y ", 0x0200 },\r
39 { "Z ", 0x0100 }\r
367b6f1f 40};\r
41\r
42\r
36f6fd5a 43#ifndef UIQ3\r
44\r
c7a4ff64 45static unsigned char menu_font_data[10240];\r
46static int menu_text_color = 0xffff; // default to white\r
47static int menu_sel_color = -1; // disabled\r
48\r
49// draws text to current bbp16 screen\r
50static void text_out16_(int x, int y, const char *text, int color)\r
51{\r
52 int i, l, u, tr, tg, tb, len;\r
53 unsigned short *dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
54 tr = (color & 0xf800) >> 8;\r
55 tg = (color & 0x07e0) >> 3;\r
56 tb = (color & 0x001f) << 3;\r
57\r
58 if (text == (void *)1)\r
59 {\r
60 // selector symbol\r
61 text = "";\r
62 len = 1;\r
63 }\r
64 else\r
65 len = strlen(text);\r
66\r
67 for (i = 0; i < len; i++)\r
68 {\r
69 unsigned char *src = menu_font_data + (unsigned int)text[i]*4*10;\r
70 unsigned short *dst = dest;\r
71 for (l = 0; l < 10; l++, dst += SCREEN_WIDTH-8)\r
72 {\r
73 for (u = 8/2; u > 0; u--, src++)\r
74 {\r
75 int c, r, g, b;\r
76 c = *src >> 4;\r
77 r = (*dst & 0xf800) >> 8;\r
78 g = (*dst & 0x07e0) >> 3;\r
79 b = (*dst & 0x001f) << 3;\r
80 r = (c^0xf)*r/15 + c*tr/15;\r
81 g = (c^0xf)*g/15 + c*tg/15;\r
82 b = (c^0xf)*b/15 + c*tb/15;\r
83 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
84 c = *src & 0xf;\r
85 r = (*dst & 0xf800) >> 8;\r
86 g = (*dst & 0x07e0) >> 3;\r
87 b = (*dst & 0x001f) << 3;\r
88 r = (c^0xf)*r/15 + c*tr/15;\r
89 g = (c^0xf)*g/15 + c*tg/15;\r
90 b = (c^0xf)*b/15 + c*tb/15;\r
91 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
92 }\r
93 }\r
94 dest += 8;\r
95 }\r
96}\r
97\r
98void text_out16(int x, int y, const char *texto, ...)\r
99{\r
100 va_list args;\r
101 char buffer[512];\r
102\r
103 va_start(args,texto);\r
104 vsprintf(buffer,texto,args);\r
105 va_end(args);\r
106\r
107 text_out16_(x,y,buffer,menu_text_color);\r
108}\r
109\r
110\r
111void smalltext_out16(int x, int y, const char *texto, int color)\r
112{\r
113 int i;\r
114 unsigned char *src;\r
115 unsigned short *dst;\r
116\r
117 for (i = 0;; i++, x += 6)\r
118 {\r
119 unsigned char c = (unsigned char) texto[i];\r
120 int h = 8;\r
121\r
122 if (!c) break;\r
123\r
124 src = fontdata6x8[c];\r
125 dst = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
126\r
127 while (h--)\r
128 {\r
129 int w = 0x20;\r
130 while (w)\r
131 {\r
132 if( *src & w ) *dst = color;\r
133 dst++;\r
134 w>>=1;\r
135 }\r
136 src++;\r
137\r
138 dst += SCREEN_WIDTH-6;\r
139 }\r
140 }\r
141}\r
142\r
143void smalltext_out16_lim(int x, int y, const char *texto, int color, int max)\r
144{\r
145 char buffer[SCREEN_WIDTH/6+1];\r
146\r
147 strncpy(buffer, texto, SCREEN_WIDTH/6);\r
148 if (max > SCREEN_WIDTH/6) max = SCREEN_WIDTH/6;\r
149 if (max < 0) max = 0;\r
150 buffer[max] = 0;\r
151\r
152 smalltext_out16(x, y, buffer, color);\r
153}\r
154\r
155void menu_draw_selection(int x, int y, int w)\r
156{\r
157 int i, h;\r
158 unsigned short *dst, *dest;\r
159\r
160 text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);\r
161\r
162 if (menu_sel_color < 0) return; // no selection hilight\r
163\r
164 if (y > 0) y--;\r
165 dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH + 14;\r
166 for (h = 11; h > 0; h--)\r
167 {\r
168 dst = dest;\r
169 for (i = w; i > 0; i--)\r
170 *dst++ = menu_sel_color;\r
171 dest += SCREEN_WIDTH;\r
172 }\r
173}\r
174\r
175static int parse_hex_color(char *buff)\r
176{\r
177 char *endp = buff;\r
178 int t = (int) strtoul(buff, &endp, 16);\r
703e4c7b 179 if (endp != buff)\r
180#ifdef PSP\r
181 return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);\r
182#else\r
183 return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);\r
184#endif\r
c7a4ff64 185 return -1;\r
186}\r
187\r
188void menu_init(void)\r
189{\r
190 int c, l;\r
191 unsigned char *fd = menu_font_data;\r
192 char buff[256];\r
193 FILE *f;\r
194\r
195 // generate default font from fontdata8x8\r
196 memset(menu_font_data, 0, sizeof(menu_font_data));\r
197 for (c = 0; c < 256; c++)\r
198 {\r
199 for (l = 0; l < 8; l++)\r
200 {\r
201 unsigned char fd8x8 = fontdata8x8[c*8+l];\r
202 if (fd8x8&0x80) *fd |= 0xf0;\r
203 if (fd8x8&0x40) *fd |= 0x0f; fd++;\r
204 if (fd8x8&0x20) *fd |= 0xf0;\r
205 if (fd8x8&0x10) *fd |= 0x0f; fd++;\r
206 if (fd8x8&0x08) *fd |= 0xf0;\r
207 if (fd8x8&0x04) *fd |= 0x0f; fd++;\r
208 if (fd8x8&0x02) *fd |= 0xf0;\r
209 if (fd8x8&0x01) *fd |= 0x0f; fd++;\r
210 }\r
211 fd += 8*2/2; // 2 empty lines\r
212 }\r
213\r
214 // load custom font and selector (stored as 1st symbol in font table)\r
215 readpng(menu_font_data, "skin/font.png", READPNG_FONT);\r
216 memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>'\r
217 readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR);\r
218\r
219 // load custom colors\r
220 f = fopen("skin/skin.txt", "r");\r
221 if (f != NULL)\r
222 {\r
223 lprintf("found skin.txt\n");\r
224 while (!feof(f))\r
225 {\r
226 fgets(buff, sizeof(buff), f);\r
227 if (buff[0] == '#' || buff[0] == '/') continue; // comment\r
228 if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line\r
229 if (strncmp(buff, "text_color=", 11) == 0)\r
230 {\r
231 int tmp = parse_hex_color(buff+11);\r
232 if (tmp >= 0) menu_text_color = tmp;\r
233 else lprintf("skin.txt: parse error for text_color\n");\r
234 }\r
235 else if (strncmp(buff, "selection_color=", 16) == 0)\r
236 {\r
237 int tmp = parse_hex_color(buff+16);\r
238 if (tmp >= 0) menu_sel_color = tmp;\r
239 else lprintf("skin.txt: parse error for selection_color\n");\r
240 }\r
241 else\r
242 lprintf("skin.txt: parse error: %s\n", buff);\r
243 }\r
244 fclose(f);\r
245 }\r
246}\r
247\r
248\r
249int me_id2offset(const menu_entry *entries, int count, menu_id id)\r
250{\r
251 int i;\r
252 for (i = 0; i < count; i++)\r
253 {\r
254 if (entries[i].id == id) return i;\r
255 }\r
256\r
257 lprintf("%s: id %i not found\n", __FUNCTION__, id);\r
258 return 0;\r
259}\r
260\r
261void me_enable(menu_entry *entries, int count, menu_id id, int enable)\r
262{\r
263 int i = me_id2offset(entries, count, id);\r
264 entries[i].enabled = enable;\r
265}\r
266\r
267int me_count_enabled(const menu_entry *entries, int count)\r
268{\r
269 int i, ret = 0;\r
270\r
271 for (i = 0; i < count; i++)\r
272 {\r
273 if (entries[i].enabled) ret++;\r
274 }\r
275\r
276 return ret;\r
277}\r
278\r
279menu_id me_index2id(const menu_entry *entries, int count, int index)\r
280{\r
281 int i;\r
282\r
283 for (i = 0; i < count; i++)\r
284 {\r
285 if (entries[i].enabled)\r
286 {\r
287 if (index == 0) break;\r
288 index--;\r
289 }\r
290 }\r
291 if (i >= count) i = count - 1;\r
292 return entries[i].id;\r
293}\r
294\r
295void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param)\r
296{\r
297 int i, y1 = y;\r
298\r
299 for (i = 0; i < count; i++)\r
300 {\r
301 if (!entries[i].enabled) continue;\r
302 if (entries[i].name == NULL)\r
303 {\r
304 if (cust_draw != NULL)\r
305 cust_draw(&entries[i], x, y1, param);\r
306 y1 += 10;\r
307 continue;\r
308 }\r
309 text_out16(x, y1, entries[i].name);\r
310 if (entries[i].beh == MB_ONOFF)\r
311 text_out16(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF");\r
312 else if (entries[i].beh == MB_RANGE)\r
313 text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var);\r
314 y1 += 10;\r
315 }\r
316}\r
317\r
318int me_process(menu_entry *entries, int count, menu_id id, int is_next)\r
319{\r
320 int i = me_id2offset(entries, count, id);\r
321 menu_entry *entry = &entries[i];\r
322 switch (entry->beh)\r
323 {\r
324 case MB_ONOFF:\r
325 *(int *)entry->var ^= entry->mask;\r
326 return 1;\r
327 case MB_RANGE:\r
328 *(int *)entry->var += is_next ? 1 : -1;\r
329 if (*(int *)entry->var < (int)entry->min) *(int *)entry->var = (int)entry->min;\r
330 if (*(int *)entry->var > (int)entry->max) *(int *)entry->var = (int)entry->max;\r
331 return 1;\r
332 default:\r
333 return 0;\r
334 }\r
335}\r
336\r
b8464531 337// ------------ debug menu ------------\r
338\r
339#include <sys/stat.h>\r
340#include <sys/types.h>\r
341\r
f11bad75 342#include <pico/pico.h>\r
343#include <pico/debug.h>\r
b8464531 344\r
345void SekStepM68k(void);\r
346\r
725d7f6c 347static void mplayer_loop(void)\r
348{\r
349 emu_startSound();\r
350\r
351 while (1)\r
352 {\r
353 PDebugZ80Frame();\r
fce20e73 354 if (in_menu_wait_any(0) & PBTN_NORTH) break;\r
725d7f6c 355 emu_waitSound();\r
356 }\r
357\r
358 emu_endSound();\r
359}\r
360\r
b8464531 361static void draw_text_debug(const char *str, int skip, int from)\r
362{\r
363 const char *p;\r
364 int len, line;\r
365\r
366 p = str;\r
367 while (skip-- > 0)\r
368 {\r
369 while (*p && *p != '\n') p++;\r
370 if (*p == 0 || p[1] == 0) return;\r
371 p++;\r
372 }\r
373\r
374 str = p;\r
375 for (line = from; line < SCREEN_HEIGHT/10; line++)\r
376 {\r
377 while (*p && *p != '\n') p++;\r
378 len = p - str;\r
379 if (len > 55) len = 55;\r
380 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
381 if (*p == 0) break;\r
382 p++; str = p;\r
383 }\r
384}\r
385\r
386static void draw_frame_debug(void)\r
387{\r
388 char layer_str[48] = "layers: ";\r
389 if (PicoDrawMask & PDRAW_LAYERB_ON) memcpy(layer_str + 8, "B", 1);\r
390 if (PicoDrawMask & PDRAW_LAYERA_ON) memcpy(layer_str + 10, "A", 1);\r
391 if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);\r
392 if (PicoDrawMask & PDRAW_SPRITES_HI_ON) memcpy(layer_str + 19, "spr_hi", 6);\r
393\r
394 clear_screen();\r
395 emu_forcedFrame(0);\r
396 smalltext_out16(4, SCREEN_HEIGHT-8, layer_str, 0xffff);\r
397}\r
398\r
399void debug_menu_loop(void)\r
400{\r
401 int inp, mode = 0;\r
402 int spr_offs = 0, dumped = 0;\r
7443ecd9 403 char *tmp;\r
b8464531 404\r
405 while (1)\r
406 {\r
407 switch (mode)\r
408 {\r
409 case 0: menu_draw_begin();\r
7443ecd9 410 tmp = PDebugMain();\r
411 emu_platformDebugCat(tmp);\r
412 draw_text_debug(tmp, 0, 0);\r
b8464531 413 if (dumped) {\r
414 smalltext_out16(SCREEN_WIDTH-6*10, SCREEN_HEIGHT-8, "dumped", 0xffff);\r
415 dumped = 0;\r
416 }\r
417 break;\r
418 case 1: draw_frame_debug(); break;\r
419 case 2: clear_screen();\r
420 emu_forcedFrame(0);\r
421 darken_screen();\r
dfa8d77a 422 PDebugShowSpriteStats((unsigned short *)SCREEN_BUFFER + (SCREEN_HEIGHT/2 - 240/2)*SCREEN_WIDTH +\r
423 SCREEN_WIDTH/2 - 320/2, SCREEN_WIDTH); break;\r
b8464531 424 case 3: clear_screen();\r
425 PDebugShowPalette(SCREEN_BUFFER, SCREEN_WIDTH);\r
426 PDebugShowSprite((unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH*120+SCREEN_WIDTH/2+16,\r
427 SCREEN_WIDTH, spr_offs);\r
428 draw_text_debug(PDebugSpriteList(), spr_offs, 6);\r
429 break;\r
430 }\r
431 menu_draw_end();\r
432\r
fce20e73 433 inp = in_menu_wait(PBTN_EAST|PBTN_MBACK|PBTN_WEST|PBTN_NORTH|PBTN_L|PBTN_R|PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT);\r
434 if (inp & PBTN_MBACK) return;\r
b3972d82 435 if (inp & PBTN_L) { mode--; if (mode < 0) mode = 3; }\r
436 if (inp & PBTN_R) { mode++; if (mode > 3) mode = 0; }\r
b8464531 437 switch (mode)\r
438 {\r
439 case 0:\r
b3972d82 440 if (inp & PBTN_EAST) SekStepM68k();\r
441 if (inp & PBTN_NORTH) {\r
fce20e73 442 while (inp & PBTN_NORTH) inp = in_menu_wait_any(-1);\r
725d7f6c 443 mplayer_loop();\r
444 }\r
b3972d82 445 if ((inp & (PBTN_WEST|PBTN_LEFT)) == (PBTN_WEST|PBTN_LEFT)) {\r
b8464531 446 mkdir("dumps", 0777);\r
447 PDebugDumpMem();\r
fce20e73 448 while (inp & PBTN_WEST) inp = in_menu_wait_any(-1);\r
b8464531 449 dumped = 1;\r
450 }\r
451 break;\r
452 case 1:\r
b3972d82 453 if (inp & PBTN_LEFT) PicoDrawMask ^= PDRAW_LAYERB_ON;\r
454 if (inp & PBTN_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;\r
455 if (inp & PBTN_DOWN) PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;\r
456 if (inp & PBTN_UP) PicoDrawMask ^= PDRAW_SPRITES_HI_ON;\r
457 if (inp & PBTN_EAST) {\r
8e6cbce1 458 PsndOut = NULL; // just in case\r
459 PicoSkipFrame = 1;\r
460 PicoFrame();\r
461 PicoSkipFrame = 0;\r
fce20e73 462 while (inp & PBTN_EAST) inp = in_menu_wait_any(-1);\r
8e6cbce1 463 }\r
b8464531 464 break;\r
465 case 3:\r
b3972d82 466 if (inp & PBTN_DOWN) spr_offs++;\r
467 if (inp & PBTN_UP) spr_offs--;\r
b8464531 468 if (spr_offs < 0) spr_offs = 0;\r
469 break;\r
470 }\r
471 }\r
472}\r
473\r
36f6fd5a 474#endif // !UIQ3\r
475\r
476// ------------ util ------------\r
477\r
478const char *me_region_name(unsigned int code, int auto_order)\r
479{\r
480 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };\r
481 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
482 int u, i = 0;\r
483 if (code) {\r
484 code <<= 1;\r
485 while((code >>= 1)) i++;\r
486 if (i > 4) return "unknown";\r
487 return names[i];\r
488 } else {\r
489 static char name[24];\r
490 strcpy(name, "Auto:");\r
491 for (u = 0; u < 3; u++) {\r
492 i = 0; code = ((auto_order >> u*4) & 0xf) << 1;\r
493 while((code >>= 1)) i++;\r
494 strcat(name, names_short[i]);\r
495 }\r
496 return name;\r
497 }\r
498}\r
499\r
b8464531 500\r