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