debug menu unified, more debug tools
[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
22me_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
c7a4ff64 39static unsigned char menu_font_data[10240];\r
40static int menu_text_color = 0xffff; // default to white\r
41static int menu_sel_color = -1; // disabled\r
42\r
43// draws text to current bbp16 screen\r
44static 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
92void 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
105void 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
137void 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
149void 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
169static int parse_hex_color(char *buff)\r
170{\r
171 char *endp = buff;\r
172 int t = (int) strtoul(buff, &endp, 16);\r
703e4c7b 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
c7a4ff64 179 return -1;\r
180}\r
181\r
182void 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
243int 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
255void 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
261int 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
273menu_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
289void 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
312int 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
0ae25549 331const 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
c7a4ff64 352\r
b8464531 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
361void SekStepM68k(void);\r
362\r
363static 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
388static 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
401void 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