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