1 // (c) Copyright 2006,2007 notaz, All rights reserved.
\r
2 // Free for non-commercial use.
\r
4 // For commercial use, separate licencing terms must be obtained.
\r
13 #include "readpng.h"
\r
14 #include "lprintf.h"
\r
20 #include <pico/patch.h>
\r
22 char menuErrorMsg[64] = { 0, };
\r
24 // PicoPad[] format: MXYZ SACB RLDU
\r
25 me_bind_action me_ctrl_actions[15] =
\r
28 { "DOWN ", 0x0002 },
\r
29 { "LEFT ", 0x0004 },
\r
30 { "RIGHT ", 0x0008 },
\r
34 { "A turbo", 0x4000 },
\r
35 { "B turbo", 0x1000 },
\r
36 { "C turbo", 0x2000 },
\r
37 { "START ", 0x0080 },
\r
38 { "MODE ", 0x0800 },
\r
47 static unsigned char menu_font_data[10240];
\r
48 static int menu_text_color = 0xffff; // default to white
\r
49 static int menu_sel_color = -1; // disabled
\r
51 // draws text to current bbp16 screen
\r
52 static void text_out16_(int x, int y, const char *text, int color)
\r
54 int i, l, u, tr, tg, tb, len;
\r
55 unsigned short *dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;
\r
56 tr = (color & 0xf800) >> 8;
\r
57 tg = (color & 0x07e0) >> 3;
\r
58 tb = (color & 0x001f) << 3;
\r
60 if (text == (void *)1)
\r
69 for (i = 0; i < len; i++)
\r
71 unsigned char *src = menu_font_data + (unsigned int)text[i]*4*10;
\r
72 unsigned short *dst = dest;
\r
73 for (l = 0; l < 10; l++, dst += SCREEN_WIDTH-8)
\r
75 for (u = 8/2; u > 0; u--, src++)
\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
87 r = (*dst & 0xf800) >> 8;
\r
88 g = (*dst & 0x07e0) >> 3;
\r
89 b = (*dst & 0x001f) << 3;
\r
90 r = (c^0xf)*r/15 + c*tr/15;
\r
91 g = (c^0xf)*g/15 + c*tg/15;
\r
92 b = (c^0xf)*b/15 + c*tb/15;
\r
93 *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);
\r
100 void text_out16(int x, int y, const char *texto, ...)
\r
104 int maxw = (SCREEN_WIDTH - x) / 8;
\r
106 va_start(args, texto);
\r
107 vsnprintf(buffer, sizeof(buffer), texto, args);
\r
114 text_out16_(x,y,buffer,menu_text_color);
\r
118 void smalltext_out16(int x, int y, const char *texto, int color)
\r
121 unsigned char *src;
\r
122 unsigned short *dst;
\r
124 for (i = 0;; i++, x += 6)
\r
126 unsigned char c = (unsigned char) texto[i];
\r
131 src = fontdata6x8[c];
\r
132 dst = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH;
\r
139 if( *src & w ) *dst = color;
\r
145 dst += SCREEN_WIDTH-6;
\r
150 void smalltext_out16_lim(int x, int y, const char *texto, int color, int max)
\r
152 char buffer[SCREEN_WIDTH/6+1];
\r
154 strncpy(buffer, texto, SCREEN_WIDTH/6);
\r
155 if (max > SCREEN_WIDTH/6) max = SCREEN_WIDTH/6;
\r
156 if (max < 0) max = 0;
\r
159 smalltext_out16(x, y, buffer, color);
\r
162 void menu_draw_selection(int x, int y, int w)
\r
165 unsigned short *dst, *dest;
\r
167 text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);
\r
169 if (menu_sel_color < 0) return; // no selection hilight
\r
172 dest = (unsigned short *)SCREEN_BUFFER + x + y*SCREEN_WIDTH + 14;
\r
173 for (h = 11; h > 0; h--)
\r
176 for (i = w; i > 0; i--)
\r
177 *dst++ = menu_sel_color;
\r
178 dest += SCREEN_WIDTH;
\r
182 static int parse_hex_color(char *buff)
\r
185 int t = (int) strtoul(buff, &endp, 16);
\r
188 return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);
\r
190 return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);
\r
195 void menu_init(void)
\r
198 unsigned char *fd = menu_font_data;
\r
202 // generate default font from fontdata8x8
\r
203 memset(menu_font_data, 0, sizeof(menu_font_data));
\r
204 for (c = 0; c < 256; c++)
\r
206 for (l = 0; l < 8; l++)
\r
208 unsigned char fd8x8 = fontdata8x8[c*8+l];
\r
209 if (fd8x8&0x80) *fd |= 0xf0;
\r
210 if (fd8x8&0x40) *fd |= 0x0f; fd++;
\r
211 if (fd8x8&0x20) *fd |= 0xf0;
\r
212 if (fd8x8&0x10) *fd |= 0x0f; fd++;
\r
213 if (fd8x8&0x08) *fd |= 0xf0;
\r
214 if (fd8x8&0x04) *fd |= 0x0f; fd++;
\r
215 if (fd8x8&0x02) *fd |= 0xf0;
\r
216 if (fd8x8&0x01) *fd |= 0x0f; fd++;
\r
218 fd += 8*2/2; // 2 empty lines
\r
221 // load custom font and selector (stored as 1st symbol in font table)
\r
222 readpng(menu_font_data, "skin/font.png", READPNG_FONT);
\r
223 memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>'
\r
224 readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR);
\r
226 // load custom colors
\r
227 f = fopen("skin/skin.txt", "r");
\r
230 lprintf("found skin.txt\n");
\r
233 fgets(buff, sizeof(buff), f);
\r
234 if (buff[0] == '#' || buff[0] == '/') continue; // comment
\r
235 if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line
\r
236 if (strncmp(buff, "text_color=", 11) == 0)
\r
238 int tmp = parse_hex_color(buff+11);
\r
239 if (tmp >= 0) menu_text_color = tmp;
\r
240 else lprintf("skin.txt: parse error for text_color\n");
\r
242 else if (strncmp(buff, "selection_color=", 16) == 0)
\r
244 int tmp = parse_hex_color(buff+16);
\r
245 if (tmp >= 0) menu_sel_color = tmp;
\r
246 else lprintf("skin.txt: parse error for selection_color\n");
\r
249 lprintf("skin.txt: parse error: %s\n", buff);
\r
256 int me_id2offset(const menu_entry *ent, menu_id id)
\r
259 for (i = 0; ent->name; ent++, i++)
\r
260 if (ent->id == id) return i;
\r
262 lprintf("%s: id %i not found\n", __FUNCTION__, id);
\r
266 void me_enable(menu_entry *entries, menu_id id, int enable)
\r
268 int i = me_id2offset(entries, id);
\r
269 entries[i].enabled = enable;
\r
272 int me_count(const menu_entry *ent)
\r
276 for (ret = 0; ent->name; ent++, ret++)
\r
282 menu_id me_index2id(const menu_entry *ent, int index)
\r
284 const menu_entry *last;
\r
286 for (; ent->name; ent++)
\r
290 if (index == 0) break;
\r
295 if (ent->name == NULL)
\r
301 void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param)
\r
305 for (i = 0; i < count; i++)
\r
307 if (!entries[i].enabled) continue;
\r
308 if (entries[i].name == NULL)
\r
310 if (cust_draw != NULL)
\r
311 cust_draw(&entries[i], x, y1, param);
\r
315 text_out16(x, y1, entries[i].name);
\r
316 if (entries[i].beh == MB_OPT_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_OPT_RANGE)
\r
319 text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var);
\r
325 static void me_draw2(const menu_entry *entries, int sel)
\r
327 const menu_entry *ent;
\r
328 int x, y, w = 0, h = 0;
\r
329 int opt_offs = 27*8;
\r
334 /* calculate size of menu rect */
\r
335 for (ent = entries, i = n = 0; ent->name; ent++, i++)
\r
346 wt = strlen(ent->name) * 8; /* FIXME: unhardcode font width */
\r
347 if (wt == 0 && ent->generate_name)
\r
348 name = ent->generate_name(1);
\r
350 wt = strlen(name) * 8;
\r
352 if (ent->beh != MB_NONE)
\r
358 switch (ent->beh) {
\r
359 case MB_NONE: break;
\r
361 case MB_OPT_RANGE: wt += 8*3; break;
\r
362 case MB_OPT_CUSTOM:
\r
364 if (ent->generate_name != NULL)
\r
365 name = ent->generate_name(0);
\r
367 wt += strlen(name) * 8;
\r
377 w += 16; /* selector */
\r
379 if (w > SCREEN_WIDTH) {
\r
380 lprintf("width %d > %d\n", w, SCREEN_WIDTH);
\r
383 if (h > SCREEN_HEIGHT) {
\r
384 lprintf("height %d > %d\n", w, SCREEN_HEIGHT);
\r
388 x = SCREEN_WIDTH / 2 - w / 2;
\r
389 y = SCREEN_HEIGHT / 2 - h / 2;
\r
392 plat_video_menu_begin();
\r
393 menu_draw_selection(x, y + asel * 10, w);
\r
395 for (ent = entries; ent->name; ent++)
\r
401 if (strlen(name) == 0) {
\r
402 if (ent->generate_name)
\r
403 name = ent->generate_name(1);
\r
406 text_out16(x + 16, y, name);
\r
408 switch (ent->beh) {
\r
412 text_out16(x + 16 + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");
\r
415 text_out16(x + 16 + opt_offs, y, "%i", *(int *)ent->var);
\r
417 case MB_OPT_CUSTOM:
\r
419 if (ent->generate_name)
\r
420 name = ent->generate_name(0);
\r
422 text_out16(x + 16 + opt_offs, y, "%s", name);
\r
429 plat_video_menu_end();
\r
432 int me_process(menu_entry *entries, menu_id id, int is_next)
\r
434 int i = me_id2offset(entries, id);
\r
435 menu_entry *entry = &entries[i];
\r
436 switch (entry->beh)
\r
439 *(int *)entry->var ^= entry->mask;
\r
442 *(int *)entry->var += is_next ? 1 : -1;
\r
443 if (*(int *)entry->var < (int)entry->min) *(int *)entry->var = (int)entry->min;
\r
444 if (*(int *)entry->var > (int)entry->max) *(int *)entry->var = (int)entry->max;
\r
451 static void me_loop(menu_entry *menu, int *menu_sel)
\r
453 int ret, inp, sel = *menu_sel, menu_sel_max;
\r
455 menu_sel_max = me_count(menu) - 1;
\r
456 if (menu_sel_max < 1) {
\r
457 lprintf("no enabled menu entries\n");
\r
461 while (!menu[sel].enabled && sel < menu_sel_max)
\r
464 /* make sure action buttons are not pressed on entering menu */
\r
465 me_draw2(menu, sel);
\r
466 while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU));
\r
470 me_draw2(menu, sel);
\r
471 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R);
\r
472 if (inp & PBTN_UP ) {
\r
476 sel = menu_sel_max;
\r
478 while (!menu[sel].enabled);
\r
480 if (inp & PBTN_DOWN) {
\r
483 if (sel > menu_sel_max)
\r
486 while (!menu[sel].enabled);
\r
488 // if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R)) debug_menu_loop(); // TODO
\r
489 if (inp & (PBTN_MENU|PBTN_MBACK))
\r
492 if (inp & PBTN_MOK)
\r
494 if (menu[sel].submenu_handler != NULL) {
\r
495 ret = menu[sel].submenu_handler(menu[sel].id);
\r
499 // menuErrorMsg[0] = 0; // TODO: clear error msg
\r
504 /* ***************************************** */
\r
507 int menu_loop_tray(void) { return 0; }
\r
508 void menu_romload_prepare(const char *rom_name) {}
\r
509 void menu_romload_end(void) {}
\r
510 me_bind_action emuctrl_actions[1];
\r
511 menu_entry opt_entries[1];
\r
512 menu_entry opt2_entries[1];
\r
513 menu_entry cdopt_entries[1];
\r
514 menu_entry ctrlopt_entries[1];
\r
515 const int opt_entry_count = 0;
\r
516 const int opt2_entry_count = 0;
\r
517 const int cdopt_entry_count = 0;
\r
518 const int ctrlopt_entry_count = 0;
\r
520 extern int engineState;
\r
522 int savestate_menu_loop(int a) { return 1; }
\r
523 int menu_loop_options() { return 1; }
\r
524 void kc_sel_loop() {}
\r
525 void draw_menu_credits() {}
\r
526 void patches_menu_loop() {}
\r
528 // ------------ main menu ------------
\r
530 static int main_menu_handler(menu_id id)
\r
536 case MA_MAIN_RESUME_GAME:
\r
538 while (in_menu_wait_any(50) & PBTN_MOK);
\r
539 engineState = PGS_Running;
\r
543 case MA_MAIN_SAVE_STATE:
\r
545 if (savestate_menu_loop(0))
\r
547 engineState = PGS_Running;
\r
551 case MA_MAIN_LOAD_STATE:
\r
553 if (savestate_menu_loop(1))
\r
555 while (in_menu_wait_any(50) & PBTN_MOK);
\r
556 engineState = PGS_Running;
\r
560 case MA_MAIN_RESET_GAME:
\r
563 while (in_menu_wait_any(50) & PBTN_MOK);
\r
564 engineState = PGS_Running;
\r
568 case MA_MAIN_LOAD_ROM:
\r
570 /* char curr_path[PATH_MAX], *selfname;
\r
572 if ( (tstf = fopen(loadedRomFName, "rb")) )
\r
575 strcpy(curr_path, loadedRomFName);
\r
578 getcwd(curr_path, PATH_MAX);
\r
579 selfname = romsel_loop(curr_path);
\r
581 printf("selected file: %s\n", selfname);
\r
582 engineState = PGS_ReloadRom;
\r
587 case MA_MAIN_OPTIONS:
\r
588 ret = menu_loop_options();
\r
589 if (ret == 1) break; // status update
\r
590 if (engineState == PGS_ReloadRom)
\r
591 return 1; // BIOS test
\r
593 case MA_MAIN_CONTROLS:
\r
596 case MA_MAIN_CREDITS:
\r
597 draw_menu_credits();
\r
598 usleep(500*1000); /* FIXME */
\r
599 in_menu_wait(PBTN_MOK|PBTN_MBACK);
\r
602 engineState = PGS_Quit;
\r
604 case MA_MAIN_PATCHES:
\r
605 if (rom_loaded && PicoPatches) {
\r
606 patches_menu_loop();
\r
608 strcpy(menuErrorMsg, "Patches applied");
\r
612 lprintf("%s: something unknown selected\n", __FUNCTION__);
\r
619 menu_entry e_main_menu[] =
\r
621 mee_submenu_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
\r
622 mee_submenu_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
\r
623 mee_submenu_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
\r
624 mee_submenu_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
\r
625 mee_submenu_id("Load new ROM/ISO", MA_MAIN_LOAD_ROM, main_menu_handler),
\r
626 mee_submenu_id("Change options", MA_MAIN_OPTIONS, main_menu_handler),
\r
627 mee_submenu_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
\r
628 mee_submenu_id("Patches / GameGenie",MA_MAIN_PATCHES, main_menu_handler),
\r
629 mee_submenu_id("Exit", MA_MAIN_EXIT, main_menu_handler),
\r
633 void menu_loop(void)
\r
635 static int sel = 0;
\r
637 me_enable(e_main_menu, MA_MAIN_RESUME_GAME, rom_loaded);
\r
638 me_enable(e_main_menu, MA_MAIN_SAVE_STATE, rom_loaded);
\r
639 me_enable(e_main_menu, MA_MAIN_LOAD_STATE, rom_loaded);
\r
640 me_enable(e_main_menu, MA_MAIN_RESET_GAME, rom_loaded);
\r
641 me_enable(e_main_menu, MA_MAIN_PATCHES, PicoPatches != NULL);
\r
643 plat_video_menu_enter(rom_loaded);
\r
644 in_set_blocking(1);
\r
645 me_loop(e_main_menu, &sel);
\r
646 in_set_blocking(0);
\r
649 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MBACK)); // wait until select is released
\r
650 engineState = PGS_Running;
\r
655 // ------------ debug menu ------------
\r
657 #include <sys/stat.h>
\r
658 #include <sys/types.h>
\r
660 #include <pico/pico.h>
\r
661 #include <pico/debug.h>
\r
663 void SekStepM68k(void);
\r
665 static void mplayer_loop(void)
\r
672 if (in_menu_wait_any(0) & PBTN_NORTH) break;
\r
679 static void draw_text_debug(const char *str, int skip, int from)
\r
687 while (*p && *p != '\n') p++;
\r
688 if (*p == 0 || p[1] == 0) return;
\r
693 for (line = from; line < SCREEN_HEIGHT/10; line++)
\r
695 while (*p && *p != '\n') p++;
\r
697 if (len > 55) len = 55;
\r
698 smalltext_out16_lim(1, line*10, str, 0xffff, len);
\r
699 if (*p == 0) break;
\r
704 static void draw_frame_debug(void)
\r
706 char layer_str[48] = "layers: ";
\r
707 if (PicoDrawMask & PDRAW_LAYERB_ON) memcpy(layer_str + 8, "B", 1);
\r
708 if (PicoDrawMask & PDRAW_LAYERA_ON) memcpy(layer_str + 10, "A", 1);
\r
709 if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);
\r
710 if (PicoDrawMask & PDRAW_SPRITES_HI_ON) memcpy(layer_str + 19, "spr_hi", 6);
\r
713 emu_forcedFrame(0);
\r
714 smalltext_out16(4, SCREEN_HEIGHT-8, layer_str, 0xffff);
\r
717 void debug_menu_loop(void)
\r
720 int spr_offs = 0, dumped = 0;
\r
727 case 0: plat_video_menu_begin();
\r
728 tmp = PDebugMain();
\r
729 emu_platformDebugCat(tmp);
\r
730 draw_text_debug(tmp, 0, 0);
\r
732 smalltext_out16(SCREEN_WIDTH-6*10, SCREEN_HEIGHT-8, "dumped", 0xffff);
\r
736 case 1: draw_frame_debug(); break;
\r
737 case 2: clear_screen();
\r
738 emu_forcedFrame(0);
\r
740 PDebugShowSpriteStats((unsigned short *)SCREEN_BUFFER + (SCREEN_HEIGHT/2 - 240/2)*SCREEN_WIDTH +
\r
741 SCREEN_WIDTH/2 - 320/2, SCREEN_WIDTH); break;
\r
742 case 3: clear_screen();
\r
743 PDebugShowPalette(SCREEN_BUFFER, SCREEN_WIDTH);
\r
744 PDebugShowSprite((unsigned short *)SCREEN_BUFFER + SCREEN_WIDTH*120+SCREEN_WIDTH/2+16,
\r
745 SCREEN_WIDTH, spr_offs);
\r
746 draw_text_debug(PDebugSpriteList(), spr_offs, 6);
\r
749 plat_video_menu_end();
\r
751 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
752 if (inp & PBTN_MBACK) return;
\r
753 if (inp & PBTN_L) { mode--; if (mode < 0) mode = 3; }
\r
754 if (inp & PBTN_R) { mode++; if (mode > 3) mode = 0; }
\r
758 if (inp & PBTN_EAST) SekStepM68k();
\r
759 if (inp & PBTN_NORTH) {
\r
760 while (inp & PBTN_NORTH) inp = in_menu_wait_any(-1);
\r
763 if ((inp & (PBTN_WEST|PBTN_LEFT)) == (PBTN_WEST|PBTN_LEFT)) {
\r
764 mkdir("dumps", 0777);
\r
766 while (inp & PBTN_WEST) inp = in_menu_wait_any(-1);
\r
771 if (inp & PBTN_LEFT) PicoDrawMask ^= PDRAW_LAYERB_ON;
\r
772 if (inp & PBTN_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;
\r
773 if (inp & PBTN_DOWN) PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;
\r
774 if (inp & PBTN_UP) PicoDrawMask ^= PDRAW_SPRITES_HI_ON;
\r
775 if (inp & PBTN_EAST) {
\r
776 PsndOut = NULL; // just in case
\r
780 while (inp & PBTN_EAST) inp = in_menu_wait_any(-1);
\r
784 if (inp & PBTN_DOWN) spr_offs++;
\r
785 if (inp & PBTN_UP) spr_offs--;
\r
786 if (spr_offs < 0) spr_offs = 0;
\r
794 // ------------ util ------------
\r
796 const char *me_region_name(unsigned int code, int auto_order)
\r
798 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };
\r
799 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };
\r
803 while((code >>= 1)) i++;
\r
804 if (i > 4) return "unknown";
\r
807 static char name[24];
\r
808 strcpy(name, "Auto:");
\r
809 for (u = 0; u < 3; u++) {
\r
810 i = 0; code = ((auto_order >> u*4) & 0xf) << 1;
\r
811 while((code >>= 1)) i++;
\r
812 strcat(name, names_short[i]);
\r
819 void menu_darken_bg(void *dst, int pixels, int darker)
\r
821 unsigned int *screen = dst;
\r
827 unsigned int p = *screen;
\r
828 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);
\r
835 unsigned int p = *screen;
\r
836 *screen++ = (p&0xf79ef79e)>>1;
\r