2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
4 * This work is licensed under the terms of any of these licenses
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
8 * See the COPYING file in the top-level directory.
24 #include "../libpicofe/plat.h"
25 #include "../libpicofe/input.h"
26 #include "../../state.h"
27 #include "../../general.h"
28 #include "../../input.h"
29 #include "../../palette.h"
32 #define array_size(x) (sizeof(x) / sizeof(x[0]))
34 #define state_slot CurrentState
81 void emu_make_path(char *buff, const char *end, int size)
85 end_len = strlen(end);
86 pos = plat_get_root_dir(buff, size);
87 strncpy(buff + pos, end, size - pos);
89 if (pos + end_len > size - 1)
90 printf("Warning: path truncated: %s\n", buff);
93 static int emu_check_save_file(int slot, int *time)
101 fname = FCEU_MakeFName(FCEUMKF_STATE, slot, 0);
102 st = fopen(fname,"rb");
111 ret = stat(fname, &status);
115 if (status.st_mtime < 1347000000)
116 goto out; // probably bad rtc like on some Caanoos
118 *time = status.st_mtime;
125 static int emu_save_load_game(int load, int unused)
135 // rrrr rggg gggb bbbb
136 static unsigned short fname2color(const char *fname)
138 static const char *rom_exts[] = { ".zip", ".nes", ".fds", ".unf",
140 static const char *other_exts[] = { ".nsf", ".ips", ".fcm" };
141 const char *ext = strrchr(fname, '.');
146 for (i = 0; i < array_size(rom_exts); i++)
147 if (strcasecmp(ext, rom_exts[i]) == 0)
149 for (i = 0; i < array_size(other_exts); i++)
150 if (strcasecmp(ext, other_exts[i]) == 0)
155 static const char *filter_exts[] = {
156 ".txt", ".srm", ".pnd"
159 #define MENU_ALIGN_LEFT
160 #ifdef __ARM_ARCH_7A__ // assume hires device
166 #include "../libpicofe/menu.c"
168 static void draw_savestate_bg(int slot)
172 static void debug_menu_loop(void)
176 // ------------ patch/gg menu ------------
179 static int cheat_count, cheat_start, cheat_pos;
181 static int countcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data)
187 static int clistcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data)
191 pos = cheat_start + cheat_pos;
193 if (pos < 0) return 1;
194 if (pos >= g_menuscreen_h / me_sfont_h) return 0;
196 smalltext_out16(14, pos * me_sfont_h, s ? "ON " : "OFF", 0xffff);
197 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h, type ? "S" : "R", 0xffff);
198 smalltext_out16(14 + me_sfont_w*6, pos * me_sfont_h, name, 0xffff);
203 static void draw_patchlist(int sel)
207 menu_draw_begin(1, 1);
209 max_cnt = g_menuscreen_h / me_sfont_h;
210 cheat_start = max_cnt / 2 - sel;
212 FCEUI_ListCheats(clistcallb, 0);
214 pos = cheat_start + cheat_pos;
216 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
218 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
222 void patches_menu_loop(void)
224 static int menu_sel = 0;
228 FCEUI_ListCheats(countcallb, 0);
232 draw_patchlist(menu_sel);
233 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
234 |PBTN_MOK|PBTN_MBACK, NULL, 33);
235 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = cheat_count; }
236 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > cheat_count) menu_sel = 0; }
237 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
238 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > cheat_count) menu_sel = cheat_count; }
239 if (inp & PBTN_MOK) { // action
240 if (menu_sel < cheat_count)
241 FCEUI_ToggleCheat(menu_sel);
244 if (inp & PBTN_MBACK)
249 // -------------- key config --------------
251 // b_turbo,a_turbo RLDU SEBA
252 me_bind_action me_ctrl_actions[] =
255 { "DOWN ", NKEY_DOWN },
256 { "LEFT ", NKEY_LEFT },
257 { "RIGHT ", NKEY_RIGHT },
260 { "A TURBO", NKEY_A_TURBO },
261 { "B TURBO", NKEY_B_TURBO },
262 { "START ", NKEY_START },
263 { "SELECT ", NKEY_SELECT },
267 me_bind_action emuctrl_actions[] =
269 { "Save State ", EACT_SAVE_STATE },
270 { "Load State ", EACT_LOAD_STATE },
271 { "Next State Slot ", EACT_NEXT_SLOT },
272 { "Prev State Slot ", EACT_PREV_SLOT },
273 { "FDS Insert/Eject ", EACT_FDS_INSERT },
274 { "FDS Select Disk ", EACT_FDS_SELECT },
275 { "VSUni Insert Coin", EACT_INSERT_COIN },
276 { "Enter Menu ", EACT_ENTER_MENU },
280 static int key_config_loop_wrap(int id, int keys)
283 case MA_CTRL_PLAYER1:
284 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
286 case MA_CTRL_PLAYER2:
287 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
290 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
298 static const char *mgn_dev_name(int id, int *offs)
300 const char *name = NULL;
303 if (id == MA_CTRL_DEV_FIRST)
306 for (; it < IN_MAX_DEVS; it++) {
307 name = in_get_dev_name(it, 1, 1);
316 static const char *mgn_saveloadcfg(int id, int *offs)
321 static void config_commit(void);
323 static int mh_savecfg(int id, int keys)
325 const char *fname = NULL;
326 if (id == MA_OPT_SAVECFG_GAME)
327 fname = lastLoadedGameName;
330 if (SaveConfig(fname) == 0)
331 menu_update_msg("config saved");
333 menu_update_msg("failed to write config");
338 static int mh_input_rescan(int id, int keys)
340 //menu_sync_config();
342 menu_update_msg("rescan complete.");
347 static menu_entry e_menu_keyconfig[] =
349 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
350 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
351 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
353 // mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
354 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
355 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
356 mee_handler ("Rescan devices:", mh_input_rescan),
358 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
359 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
360 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
361 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
362 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
363 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
364 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
368 static int menu_loop_keyconfig(int id, int keys)
372 me_loop(e_menu_keyconfig, &sel);
376 // --------- FCEU options ----------
378 extern int ntsccol,ntschue,ntsctint;
379 extern int srendlinev[2];
380 extern int erendlinev[2];
382 extern char *cpalette;
383 extern void LoadCPalette(void);
385 static menu_entry e_menu_fceu_options[] =
387 //gp2x_text_out15(tl_x, y, "Custom palette: %s", cpal);
388 mee_onoff ("NTSC Color Emulation", MA_OPT_NTSC_COLOR, ntsccol, 1),
389 mee_range (" Tint (default: 56)", MA_OPT_NTSC_TINT, ntsctint, 0, 128),
390 mee_range (" Hue (default: 72)", MA_OPT_NTSC_HUE, ntschue, 0, 128),
391 mee_range ("First visible line (NTSC)", MA_OPT_SREND_N, srendlinev[0], 0, 239),
392 mee_range ("Last visible line (NTSC)", MA_OPT_EREND_N, erendlinev[0], 0, 239),
393 mee_range ("First visible line (PAL)", MA_OPT_SREND_P, srendlinev[1], 0, 239),
394 mee_range ("Last visible line (PAL)", MA_OPT_EREND_P, erendlinev[1], 0, 239),
395 mee_onoff ("Clip 8 left/right columns", MA_OPT_CLIP, eoptions, EO_CLIPSIDES),
396 mee_onoff ("Disable 8 sprite limit", MA_OPT_NO8LIM, eoptions, EO_NO8LIM),
397 mee_onoff ("Enable authentic GameGenie",MA_OPT_GG, eoptions, EO_GG),
401 static int menu_loop_fceu_options(int id, int keys)
406 FCEUI_GetNTSCTH(&ntsctint, &ntschue);
408 me_loop(e_menu_fceu_options, &sel);
410 for(i = 0; i < 2; i++)
412 if (srendlinev[i] < 0 || srendlinev[i] > 239)
414 if (erendlinev[i] < srendlinev[i] || erendlinev[i] > 239)
417 FCEUI_SetNTSCTH(ntsccol, ntsctint, ntschue);
418 FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);
419 FCEUI_DisableSpriteLimitation(eoptions&EO_NO8LIM);
420 FCEUI_SetGameGenie(eoptions&EO_GG);
421 //if (cpalette) LoadCPalette();
423 FCEUI_SetPaletteArray(0); // set to default
429 // -------------- options --------------
431 static const char *men_frameskip[] = { "Auto", "0", "1", "2", "3", "4", NULL };
432 static const char *men_swfilter[] = { "none", "Scale2x", "Eagle2x", NULL };
433 static const char *men_scaling[] = { "1x", "proportional", "4:3 scaled", "fullscreen", NULL };
434 static const char *men_rates[] = { "8000", "11025", "16000", "22050", "44100", NULL };
435 static const int men_rates_i[] = { 8000 , 11025 , 16000 , 22050 , 44100 };
436 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
437 static const char *men_sstate[] = { "OFF", "writes", "loads", "both", NULL };
438 static const char h_renderer[] = "ROM reload required for this\n"
439 "setting to take effect";
441 static int sndrate_i;
444 static int frameskip_i;
446 static void config_commit(void)
448 Settings.sound_rate = men_rates_i[sndrate_i];
449 soundvol = sndon ? 50 : 0;
450 Settings.turbo_rate_add = (turbo_i * 2 << 24) / 60 + 1;
451 Settings.frameskip = frameskip_i - 1;
453 if (Settings.region_force)
454 FCEUI_SetVidSystem(Settings.region_force - 1);
457 static menu_entry e_menu_options[] =
459 // mee_onoff ("Show FPS", MA_OPT_SHOWFPS, Settings.showfps, 1),
460 mee_enum ("Frameskip", MA_OPT_FSKIP, frameskip_i, men_frameskip),
461 mee_enum ("Softwere filter", MA_OPT_SWFILTER, Settings.sw_filter, men_swfilter),
462 mee_enum ("Hardware filter", MA_OPT_HWFILTER, Settings.hw_filter, NULL),
463 mee_enum ("Scaling", MA_OPT_SCALING, Settings.scaling, men_scaling),
464 mee_onoff_h ("Accurate renderer (slow)",MA_OPT_RENDERER, Settings.accurate_mode, 1, h_renderer),
465 mee_onoff ("Enable sound", MA_OPT_SOUNDON, sndon, 1),
466 mee_enum ("Sound Rate", MA_OPT_SOUNDRATE, sndrate_i, men_rates),
467 mee_enum ("Region", MA_OPT_REGION, Settings.region_force, men_region),
468 mee_range ("Turbo rate (Hz)", MA_OPT_TURBO, turbo_i, 1, 30),
469 mee_enum ("Confirm savestate", MA_OPT_SSTATE, Settings.sstate_confirm, men_sstate),
470 mee_range ("Save slot", MA_OPT_SSLOT, CurrentState, 0, 9),
471 // mee_range ("Gamma correction", MA_OPT_GAMMA, Settings.gamma, 0, 300),
472 mee_handler ("[FCE Ultra options]", menu_loop_fceu_options),
473 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
474 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
478 static int menu_loop_options(int id, int keys)
484 i = me_id2offset(e_menu_options, MA_OPT_HWFILTER);
485 e_menu_options[i].data = plat_target.filters;
486 me_enable(e_menu_options, MA_OPT_HWFILTER, plat_target.filters != NULL);
488 oldrate = Settings.sound_rate;
489 for (i = 0; i < array_size(men_rates_i); i++) {
490 if (Settings.sound_rate == men_rates_i[i]) {
495 sndon = soundvol != 0;
496 turbo_i = (Settings.turbo_rate_add * 60 / 2) >> 24;
497 frameskip_i = Settings.frameskip + 1;
499 me_loop(e_menu_options, &sel);
502 if (oldrate != Settings.sound_rate)
508 // -------------- root menu --------------
510 static const char credits_text[] =
512 "(c) notaz, 2007,2012\n\n"
513 "Based on FCE Ultra versions\n"
514 "0.81 and 0.98.1x\n\n"
517 "Xodnizel: FCE Ultra\n"
518 "FCA author: 6502 core\n"
519 "M-HT: NEON scalers\n";
521 static void draw_frame_credits(void)
523 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
526 static int romsel_run(void)
530 fname = menu_loop_romsel(lastLoadedGameName, sizeof(lastLoadedGameName));
534 printf("selected file: %s\n", fname);
535 //keys_load_all(cfg);
537 strcpy(lastLoadedGameName, rom_fname_reload);
541 static int menu_loop_ret;
543 static int main_menu_handler(int id, int keys)
547 case MA_MAIN_RESUME_GAME:
551 case MA_MAIN_SAVE_STATE:
553 return menu_loop_savestate(0);
556 case MA_MAIN_LOAD_STATE:
558 return menu_loop_savestate(1);
561 case MA_MAIN_RESET_GAME:
563 FCEU_DoSimpleCommand(FCEUNPCMD_RESET);
567 case MA_MAIN_LOAD_ROM:
568 if (romsel_run() == 0) {
573 case MA_MAIN_CREDITS:
574 draw_menu_message(credits_text, draw_frame_credits);
575 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
581 lprintf("%s: something unknown selected\n", __FUNCTION__);
588 static menu_entry e_menu_main[] =
590 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
591 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
592 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
593 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
594 mee_handler_id("Load ROM", MA_MAIN_LOAD_ROM, main_menu_handler),
595 mee_handler ("Options", menu_loop_options),
596 mee_handler ("Controls", menu_loop_keyconfig),
597 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
598 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
599 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
603 // ----------------------------
611 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, fceugi != NULL);
612 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, fceugi != NULL);
613 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, fceugi != NULL);
614 me_enable(e_menu_main, MA_MAIN_RESET_GAME, fceugi != NULL);
615 me_enable(e_menu_main, MA_MAIN_CHEATS, fceugi && cheats);
617 plat_video_menu_enter(fceugi != NULL);
618 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
619 in_set_config_int(0, IN_CFG_BLOCKING, 1);
622 me_loop_d(e_menu_main, &sel, NULL, NULL);
623 } while (!fceugi && menu_loop_ret == 0);
625 /* wait until menu, ok, back is released */
626 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
629 in_set_config_int(0, IN_CFG_BLOCKING, 0);
630 plat_video_menu_leave();
633 return menu_loop_ret;
643 //menu_load_config(0);
645 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
646 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
647 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
648 fprintf(stderr, "OOM\n");
652 emu_make_path(buff, "skin/background.png", sizeof(buff));
653 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
656 void menu_update_msg(const char *msg)
658 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
659 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
661 menu_error_time = plat_get_ticks_ms();
662 lprintf("msg: %s\n", menu_error_msg);