c3ef87f56f06d3c13ab5a9de511f13a4a2feadf6
[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 "input.h"\r
17 #include "emu.h"\r
18 \r
19 \r
20 char menuErrorMsg[64] = { 0, };\r
21 \r
22 // PicoPad[] format: MXYZ SACB RLDU\r
23 me_bind_action me_ctrl_actions[15] =\r
24 {\r
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
40 };\r
41 \r
42 \r
43 #ifndef UIQ3\r
44 \r
45 static unsigned char menu_font_data[10240];\r
46 static int menu_text_color = 0xffff; // default to white\r
47 static int menu_sel_color = -1; // disabled\r
48 \r
49 // draws text to current bbp16 screen\r
50 static 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
98 void 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
111 void 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
143 void 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
155 void 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
175 static int parse_hex_color(char *buff)\r
176 {\r
177         char *endp = buff;\r
178         int t = (int) strtoul(buff, &endp, 16);\r
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
185         return -1;\r
186 }\r
187 \r
188 void 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
249 int 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
261 void 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
267 int 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
279 menu_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
295 void 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
318 int 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
337 // ------------ debug menu ------------\r
338 \r
339 #include <sys/stat.h>\r
340 #include <sys/types.h>\r
341 \r
342 #include <pico/pico.h>\r
343 #include <pico/debug.h>\r
344 \r
345 void SekStepM68k(void);\r
346 \r
347 static void mplayer_loop(void)\r
348 {\r
349         emu_startSound();\r
350 \r
351         while (1)\r
352         {\r
353                 PDebugZ80Frame();\r
354                 if (in_menu_wait_any(0) & PBTN_NORTH) break;\r
355                 emu_waitSound();\r
356         }\r
357 \r
358         emu_endSound();\r
359 }\r
360 \r
361 static 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
386 static 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
399 void debug_menu_loop(void)\r
400 {\r
401         int inp, mode = 0;\r
402         int spr_offs = 0, dumped = 0;\r
403         char *tmp;\r
404 \r
405         while (1)\r
406         {\r
407                 switch (mode)\r
408                 {\r
409                         case 0: menu_draw_begin();\r
410                                 tmp = PDebugMain();\r
411                                 emu_platformDebugCat(tmp);\r
412                                 draw_text_debug(tmp, 0, 0);\r
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
422                                 PDebugShowSpriteStats((unsigned short *)SCREEN_BUFFER + (SCREEN_HEIGHT/2 - 240/2)*SCREEN_WIDTH +\r
423                                         SCREEN_WIDTH/2 - 320/2, SCREEN_WIDTH); break;\r
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
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
435                 if (inp & PBTN_L) { mode--; if (mode < 0) mode = 3; }\r
436                 if (inp & PBTN_R) { mode++; if (mode > 3) mode = 0; }\r
437                 switch (mode)\r
438                 {\r
439                         case 0:\r
440                                 if (inp & PBTN_EAST) SekStepM68k();\r
441                                 if (inp & PBTN_NORTH) {\r
442                                         while (inp & PBTN_NORTH) inp = in_menu_wait_any(-1);\r
443                                         mplayer_loop();\r
444                                 }\r
445                                 if ((inp & (PBTN_WEST|PBTN_LEFT)) == (PBTN_WEST|PBTN_LEFT)) {\r
446                                         mkdir("dumps", 0777);\r
447                                         PDebugDumpMem();\r
448                                         while (inp & PBTN_WEST) inp = in_menu_wait_any(-1);\r
449                                         dumped = 1;\r
450                                 }\r
451                                 break;\r
452                         case 1:\r
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
458                                         PsndOut = NULL; // just in case\r
459                                         PicoSkipFrame = 1;\r
460                                         PicoFrame();\r
461                                         PicoSkipFrame = 0;\r
462                                         while (inp & PBTN_EAST) inp = in_menu_wait_any(-1);\r
463                                 }\r
464                                 break;\r
465                         case 3:\r
466                                 if (inp & PBTN_DOWN)  spr_offs++;\r
467                                 if (inp & PBTN_UP)    spr_offs--;\r
468                                 if (spr_offs < 0) spr_offs = 0;\r
469                                 break;\r
470                 }\r
471         }\r
472 }\r
473 \r
474 #endif // !UIQ3\r
475 \r
476 // ------------ util ------------\r
477 \r
478 const 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
500 \r