arm_linux.S for cache ops and random fixes
[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[15] =\r
23 {\r
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
39 };\r
40 \r
41 \r
42 #ifndef UIQ3\r
43 \r
44 static unsigned char menu_font_data[10240];\r
45 static int menu_text_color = 0xffff; // default to white\r
46 static int menu_sel_color = -1; // disabled\r
47 \r
48 // draws text to current bbp16 screen\r
49 static 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
97 void 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
110 void 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
142 void 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
154 void 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
174 static int parse_hex_color(char *buff)\r
175 {\r
176         char *endp = buff;\r
177         int t = (int) strtoul(buff, &endp, 16);\r
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
184         return -1;\r
185 }\r
186 \r
187 void 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
248 int 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
260 void 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
266 int 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
278 menu_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
294 void 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
317 int 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
336 // ------------ debug menu ------------\r
337 \r
338 #include <sys/stat.h>\r
339 #include <sys/types.h>\r
340 \r
341 #include <pico/pico.h>\r
342 #include <pico/debug.h>\r
343 \r
344 void SekStepM68k(void);\r
345 \r
346 static void draw_text_debug(const char *str, int skip, int from)\r
347 {\r
348         const char *p;\r
349         int len, line;\r
350 \r
351         p = str;\r
352         while (skip-- > 0)\r
353         {\r
354                 while (*p && *p != '\n') p++;\r
355                 if (*p == 0 || p[1] == 0) return;\r
356                 p++;\r
357         }\r
358 \r
359         str = p;\r
360         for (line = from; line < SCREEN_HEIGHT/10; line++)\r
361         {\r
362                 while (*p && *p != '\n') p++;\r
363                 len = p - str;\r
364                 if (len > 55) len = 55;\r
365                 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
366                 if (*p == 0) break;\r
367                 p++; str = p;\r
368         }\r
369 }\r
370 \r
371 static void draw_frame_debug(void)\r
372 {\r
373         char layer_str[48] = "layers:             ";\r
374         if (PicoDrawMask & PDRAW_LAYERB_ON)      memcpy(layer_str +  8, "B", 1);\r
375         if (PicoDrawMask & PDRAW_LAYERA_ON)      memcpy(layer_str + 10, "A", 1);\r
376         if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);\r
377         if (PicoDrawMask & PDRAW_SPRITES_HI_ON)  memcpy(layer_str + 19, "spr_hi", 6);\r
378 \r
379         clear_screen();\r
380         emu_forcedFrame(0);\r
381         smalltext_out16(4, SCREEN_HEIGHT-8, layer_str, 0xffff);\r
382 }\r
383 \r
384 void debug_menu_loop(void)\r
385 {\r
386         int inp, mode = 0;\r
387         int spr_offs = 0, dumped = 0;\r
388         char *tmp;\r
389 \r
390         while (1)\r
391         {\r
392                 switch (mode)\r
393                 {\r
394                         case 0: menu_draw_begin();\r
395                                 tmp = PDebugMain();\r
396                                 emu_platformDebugCat(tmp);\r
397                                 draw_text_debug(tmp, 0, 0);\r
398                                 if (dumped) {\r
399                                         smalltext_out16(SCREEN_WIDTH-6*10, SCREEN_HEIGHT-8, "dumped", 0xffff);\r
400                                         dumped = 0;\r
401                                 }\r
402                                 break;\r
403                         case 1: draw_frame_debug(); break;\r
404                         case 2: clear_screen();\r
405                                 emu_forcedFrame(0);\r
406                                 darken_screen();\r
407                                 PDebugShowSpriteStats((unsigned short *)SCREEN_BUFFER + (SCREEN_HEIGHT/2 - 240/2)*SCREEN_WIDTH +\r
408                                         SCREEN_WIDTH/2 - 320/2, SCREEN_WIDTH); break;\r
409                         case 3: clear_screen();\r
410                                 PDebugShowPalette(SCREEN_BUFFER, SCREEN_WIDTH);\r
411                                 PDebugShowSprite((unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH*120+SCREEN_WIDTH/2+16,\r
412                                         SCREEN_WIDTH, spr_offs);\r
413                                 draw_text_debug(PDebugSpriteList(), spr_offs, 6);\r
414                                 break;\r
415                 }\r
416                 menu_draw_end();\r
417 \r
418                 inp = read_buttons(BTN_EAST|BTN_SOUTH|BTN_WEST|BTN_L|BTN_R|BTN_UP|BTN_DOWN|BTN_LEFT|BTN_RIGHT);\r
419                 if (inp & BTN_SOUTH) return;\r
420                 if (inp & BTN_L) { mode--; if (mode < 0) mode = 3; }\r
421                 if (inp & BTN_R) { mode++; if (mode > 3) mode = 0; }\r
422                 switch (mode)\r
423                 {\r
424                         case 0:\r
425                                 if (inp & BTN_EAST) SekStepM68k();\r
426                                 if ((inp & (BTN_WEST|BTN_LEFT)) == (BTN_WEST|BTN_LEFT)) {\r
427                                         mkdir("dumps", 0777);\r
428                                         PDebugDumpMem();\r
429                                         while (inp & BTN_WEST) inp = read_buttons_async(BTN_WEST);\r
430                                         dumped = 1;\r
431                                 }\r
432                                 break;\r
433                         case 1:\r
434                                 if (inp & BTN_LEFT)  PicoDrawMask ^= PDRAW_LAYERB_ON;\r
435                                 if (inp & BTN_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;\r
436                                 if (inp & BTN_DOWN)  PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;\r
437                                 if (inp & BTN_UP)    PicoDrawMask ^= PDRAW_SPRITES_HI_ON;\r
438                                 if (inp & BTN_EAST) {\r
439                                         PsndOut = NULL; // just in case\r
440                                         PicoSkipFrame = 1;\r
441                                         PicoFrame();\r
442                                         PicoSkipFrame = 0;\r
443                                         while (inp & BTN_EAST) inp = read_buttons_async(BTN_EAST);\r
444                                 }\r
445                                 break;\r
446                         case 3:\r
447                                 if (inp & BTN_DOWN)  spr_offs++;\r
448                                 if (inp & BTN_UP)    spr_offs--;\r
449                                 if (spr_offs < 0) spr_offs = 0;\r
450                                 break;\r
451                 }\r
452         }\r
453 }\r
454 \r
455 #endif // !UIQ3\r
456 \r
457 // ------------ util ------------\r
458 \r
459 const char *me_region_name(unsigned int code, int auto_order)\r
460 {\r
461         static const char *names[] = { "Auto", "      Japan NTSC", "      Japan PAL", "      USA", "      Europe" };\r
462         static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
463         int u, i = 0;\r
464         if (code) {\r
465                 code <<= 1;\r
466                 while((code >>= 1)) i++;\r
467                 if (i > 4) return "unknown";\r
468                 return names[i];\r
469         } else {\r
470                 static char name[24];\r
471                 strcpy(name, "Auto:");\r
472                 for (u = 0; u < 3; u++) {\r
473                         i = 0; code = ((auto_order >> u*4) & 0xf) << 1;\r
474                         while((code >>= 1)) i++;\r
475                         strcat(name, names_short[i]);\r
476                 }\r
477                 return name;\r
478         }\r
479 }\r
480 \r
481 \r