bugfixes, new config system and messed code for it
[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 \r
16 #if   defined(__GP2X__)\r
17  #include "../gp2x/gp2x.h"\r
18  #define SCREEN_WIDTH 320\r
19  #define SCREEN_BUFFER gp2x_screen\r
20 #elif defined(__GIZ__)\r
21  //#include "../gizmondo/giz.h"\r
22  #define SCREEN_WIDTH 321\r
23  #define SCREEN_BUFFER menu_screen\r
24  extern unsigned char *menu_screen;\r
25 #elif defined(PSP)\r
26  #include "../psp/psp.h"\r
27  #define SCREEN_WIDTH 512\r
28  #define SCREEN_BUFFER psp_screen\r
29 #endif\r
30 \r
31 char menuErrorMsg[64] = { 0, };\r
32 \r
33 // PicoPad[] format: MXYZ SACB RLDU\r
34 me_bind_action me_ctrl_actions[12] =\r
35 {\r
36         { "UP     ", 0x001 },\r
37         { "DOWN   ", 0x002 },\r
38         { "LEFT   ", 0x004 },\r
39         { "RIGHT  ", 0x008 },\r
40         { "A      ", 0x040 },\r
41         { "B      ", 0x010 },\r
42         { "C      ", 0x020 },\r
43         { "START  ", 0x080 },\r
44         { "MODE   ", 0x800 },\r
45         { "X      ", 0x400 },\r
46         { "Y      ", 0x200 },\r
47         { "Z      ", 0x100 }\r
48 };\r
49 \r
50 \r
51 static unsigned char menu_font_data[10240];\r
52 static int menu_text_color = 0xffff; // default to white\r
53 static int menu_sel_color = -1; // disabled\r
54 \r
55 // draws text to current bbp16 screen\r
56 static void text_out16_(int x, int y, const char *text, int color)\r
57 {\r
58         int i, l, u, tr, tg, tb, len;\r
59         unsigned short *dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
60         tr = (color & 0xf800) >> 8;\r
61         tg = (color & 0x07e0) >> 3;\r
62         tb = (color & 0x001f) << 3;\r
63 \r
64         if (text == (void *)1)\r
65         {\r
66                 // selector symbol\r
67                 text = "";\r
68                 len = 1;\r
69         }\r
70         else\r
71                 len = strlen(text);\r
72 \r
73         for (i = 0; i < len; i++)\r
74         {\r
75                 unsigned char  *src = menu_font_data + (unsigned int)text[i]*4*10;\r
76                 unsigned short *dst = dest;\r
77                 for (l = 0; l < 10; l++, dst += SCREEN_WIDTH-8)\r
78                 {\r
79                         for (u = 8/2; u > 0; u--, src++)\r
80                         {\r
81                                 int c, r, g, b;\r
82                                 c = *src >> 4;\r
83                                 r = (*dst & 0xf800) >> 8;\r
84                                 g = (*dst & 0x07e0) >> 3;\r
85                                 b = (*dst & 0x001f) << 3;\r
86                                 r = (c^0xf)*r/15 + c*tr/15;\r
87                                 g = (c^0xf)*g/15 + c*tg/15;\r
88                                 b = (c^0xf)*b/15 + c*tb/15;\r
89                                 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
90                                 c = *src & 0xf;\r
91                                 r = (*dst & 0xf800) >> 8;\r
92                                 g = (*dst & 0x07e0) >> 3;\r
93                                 b = (*dst & 0x001f) << 3;\r
94                                 r = (c^0xf)*r/15 + c*tr/15;\r
95                                 g = (c^0xf)*g/15 + c*tg/15;\r
96                                 b = (c^0xf)*b/15 + c*tb/15;\r
97                                 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
98                         }\r
99                 }\r
100                 dest += 8;\r
101         }\r
102 }\r
103 \r
104 void text_out16(int x, int y, const char *texto, ...)\r
105 {\r
106         va_list args;\r
107         char    buffer[512];\r
108 \r
109         va_start(args,texto);\r
110         vsprintf(buffer,texto,args);\r
111         va_end(args);\r
112 \r
113         text_out16_(x,y,buffer,menu_text_color);\r
114 }\r
115 \r
116 \r
117 void smalltext_out16(int x, int y, const char *texto, int color)\r
118 {\r
119         int i;\r
120         unsigned char  *src;\r
121         unsigned short *dst;\r
122 \r
123         for (i = 0;; i++, x += 6)\r
124         {\r
125                 unsigned char c = (unsigned char) texto[i];\r
126                 int h = 8;\r
127 \r
128                 if (!c) break;\r
129 \r
130                 src = fontdata6x8[c];\r
131                 dst = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;\r
132 \r
133                 while (h--)\r
134                 {\r
135                         int w = 0x20;\r
136                         while (w)\r
137                         {\r
138                                 if( *src & w ) *dst = color;\r
139                                 dst++;\r
140                                 w>>=1;\r
141                         }\r
142                         src++;\r
143 \r
144                         dst += SCREEN_WIDTH-6;\r
145                 }\r
146         }\r
147 }\r
148 \r
149 void smalltext_out16_lim(int x, int y, const char *texto, int color, int max)\r
150 {\r
151         char    buffer[SCREEN_WIDTH/6+1];\r
152 \r
153         strncpy(buffer, texto, SCREEN_WIDTH/6);\r
154         if (max > SCREEN_WIDTH/6) max = SCREEN_WIDTH/6;\r
155         if (max < 0) max = 0;\r
156         buffer[max] = 0;\r
157 \r
158         smalltext_out16(x, y, buffer, color);\r
159 }\r
160 \r
161 void menu_draw_selection(int x, int y, int w)\r
162 {\r
163         int i, h;\r
164         unsigned short *dst, *dest;\r
165 \r
166         text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);\r
167 \r
168         if (menu_sel_color < 0) return; // no selection hilight\r
169 \r
170         if (y > 0) y--;\r
171         dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH + 14;\r
172         for (h = 11; h > 0; h--)\r
173         {\r
174                 dst = dest;\r
175                 for (i = w; i > 0; i--)\r
176                         *dst++ = menu_sel_color;\r
177                 dest += SCREEN_WIDTH;\r
178         }\r
179 }\r
180 \r
181 static int parse_hex_color(char *buff)\r
182 {\r
183         char *endp = buff;\r
184         int t = (int) strtoul(buff, &endp, 16);\r
185         if (endp != buff)\r
186 #ifdef PSP\r
187                 return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);\r
188 #else\r
189                 return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);\r
190 #endif\r
191         return -1;\r
192 }\r
193 \r
194 void menu_init(void)\r
195 {\r
196         int c, l;\r
197         unsigned char *fd = menu_font_data;\r
198         char buff[256];\r
199         FILE *f;\r
200 \r
201         // generate default font from fontdata8x8\r
202         memset(menu_font_data, 0, sizeof(menu_font_data));\r
203         for (c = 0; c < 256; c++)\r
204         {\r
205                 for (l = 0; l < 8; l++)\r
206                 {\r
207                         unsigned char fd8x8 = fontdata8x8[c*8+l];\r
208                         if (fd8x8&0x80) *fd |= 0xf0;\r
209                         if (fd8x8&0x40) *fd |= 0x0f; fd++;\r
210                         if (fd8x8&0x20) *fd |= 0xf0;\r
211                         if (fd8x8&0x10) *fd |= 0x0f; fd++;\r
212                         if (fd8x8&0x08) *fd |= 0xf0;\r
213                         if (fd8x8&0x04) *fd |= 0x0f; fd++;\r
214                         if (fd8x8&0x02) *fd |= 0xf0;\r
215                         if (fd8x8&0x01) *fd |= 0x0f; fd++;\r
216                 }\r
217                 fd += 8*2/2; // 2 empty lines\r
218         }\r
219 \r
220         // load custom font and selector (stored as 1st symbol in font table)\r
221         readpng(menu_font_data, "skin/font.png", READPNG_FONT);\r
222         memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>'\r
223         readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR);\r
224 \r
225         // load custom colors\r
226         f = fopen("skin/skin.txt", "r");\r
227         if (f != NULL)\r
228         {\r
229                 lprintf("found skin.txt\n");\r
230                 while (!feof(f))\r
231                 {\r
232                         fgets(buff, sizeof(buff), f);\r
233                         if (buff[0] == '#'  || buff[0] == '/')  continue; // comment\r
234                         if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line\r
235                         if (strncmp(buff, "text_color=", 11) == 0)\r
236                         {\r
237                                 int tmp = parse_hex_color(buff+11);\r
238                                 if (tmp >= 0) menu_text_color = tmp;\r
239                                 else lprintf("skin.txt: parse error for text_color\n");\r
240                         }\r
241                         else if (strncmp(buff, "selection_color=", 16) == 0)\r
242                         {\r
243                                 int tmp = parse_hex_color(buff+16);\r
244                                 if (tmp >= 0) menu_sel_color = tmp;\r
245                                 else lprintf("skin.txt: parse error for selection_color\n");\r
246                         }\r
247                         else\r
248                                 lprintf("skin.txt: parse error: %s\n", buff);\r
249                 }\r
250                 fclose(f);\r
251         }\r
252 }\r
253 \r
254 \r
255 int me_id2offset(const menu_entry *entries, int count, menu_id id)\r
256 {\r
257         int i;\r
258         for (i = 0; i < count; i++)\r
259         {\r
260                 if (entries[i].id == id) return i;\r
261         }\r
262 \r
263         lprintf("%s: id %i not found\n", __FUNCTION__, id);\r
264         return 0;\r
265 }\r
266 \r
267 void me_enable(menu_entry *entries, int count, menu_id id, int enable)\r
268 {\r
269         int i = me_id2offset(entries, count, id);\r
270         entries[i].enabled = enable;\r
271 }\r
272 \r
273 int me_count_enabled(const menu_entry *entries, int count)\r
274 {\r
275         int i, ret = 0;\r
276 \r
277         for (i = 0; i < count; i++)\r
278         {\r
279                 if (entries[i].enabled) ret++;\r
280         }\r
281 \r
282         return ret;\r
283 }\r
284 \r
285 menu_id me_index2id(const menu_entry *entries, int count, int index)\r
286 {\r
287         int i;\r
288 \r
289         for (i = 0; i < count; i++)\r
290         {\r
291                 if (entries[i].enabled)\r
292                 {\r
293                         if (index == 0) break;\r
294                         index--;\r
295                 }\r
296         }\r
297         if (i >= count) i = count - 1;\r
298         return entries[i].id;\r
299 }\r
300 \r
301 void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param)\r
302 {\r
303         int i, y1 = y;\r
304 \r
305         for (i = 0; i < count; i++)\r
306         {\r
307                 if (!entries[i].enabled) continue;\r
308                 if (entries[i].name == NULL)\r
309                 {\r
310                         if (cust_draw != NULL)\r
311                                 cust_draw(&entries[i], x, y1, param);\r
312                         y1 += 10;\r
313                         continue;\r
314                 }\r
315                 text_out16(x, y1, entries[i].name);\r
316                 if (entries[i].beh == MB_ONOFF)\r
317                         text_out16(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF");\r
318                 else if (entries[i].beh == MB_RANGE)\r
319                         text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var);\r
320                 y1 += 10;\r
321         }\r
322 }\r
323 \r
324 int me_process(menu_entry *entries, int count, menu_id id, int is_next)\r
325 {\r
326         int i = me_id2offset(entries, count, id);\r
327         menu_entry *entry = &entries[i];\r
328         switch (entry->beh)\r
329         {\r
330                 case MB_ONOFF:\r
331                         *(int *)entry->var ^= entry->mask;\r
332                         return 1;\r
333                 case MB_RANGE:\r
334                         *(int *)entry->var += is_next ? 1 : -1;\r
335                         if (*(int *)entry->var < (int)entry->min) *(int *)entry->var = (int)entry->min;\r
336                         if (*(int *)entry->var > (int)entry->max) *(int *)entry->var = (int)entry->max;\r
337                         return 1;\r
338                 default:\r
339                         return 0;\r
340         }\r
341 }\r
342 \r
343 const char *me_region_name(unsigned int code, int auto_order)\r
344 {\r
345         static const char *names[] = { "Auto", "      Japan NTSC", "      Japan PAL", "      USA", "      Europe" };\r
346         static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
347         int u, i = 0;\r
348         if (code) {\r
349                 code <<= 1;\r
350                 while((code >>= 1)) i++;\r
351                 if (i > 4) return "unknown";\r
352                 return names[i];\r
353         } else {\r
354                 static char name[24];\r
355                 strcpy(name, "Auto:");\r
356                 for (u = 0; u < 3; u++) {\r
357                         i = 0; code = ((auto_order >> u*4) & 0xf) << 1;\r
358                         while((code >>= 1)) i++;\r
359                         strcat(name, names_short[i]);\r
360                 }\r
361                 return name;\r
362         }\r
363 }\r
364 \r