minor bugfixes
[fceu.git] / drivers / common / menu.c
CommitLineData
4a2a617a 1/*
2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
3 *
4 * This work is licensed under the terms of any of these licenses
5 * (at your option):
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.
9 */
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <errno.h>
15#include <sys/stat.h>
16
17#include "main.h"
18#include "menu.h"
19#include "input.h"
20#include "settings.h"
21#include "config.h"
22#include "args.h"
23#include "dface.h"
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"
30#include "revision.h"
31
32#define array_size(x) (sizeof(x) / sizeof(x[0]))
33
34#define state_slot CurrentState
35extern uint8 Exit;
36
37typedef enum
38{
39 MA_NONE = 1,
40 MA_CTRL_PLAYER1,
41 MA_CTRL_PLAYER2,
42 MA_CTRL_EMU,
43 MA_CTRL_DEADZONE,
44 MA_OPT_SAVECFG,
45 MA_OPT_SAVECFG_GAME,
46 MA_CTRL_DEV_FIRST,
47 MA_CTRL_DEV_NEXT,
48 MA_OPT_NTSC_COLOR,
49 MA_OPT_NTSC_TINT,
50 MA_OPT_NTSC_HUE,
51 MA_OPT_SREND_N,
52 MA_OPT_EREND_N,
53 MA_OPT_SREND_P,
54 MA_OPT_EREND_P,
55 MA_OPT_CLIP,
56 MA_OPT_NO8LIM,
57 MA_OPT_GG,
58 MA_OPT_SHOWFPS,
59 MA_OPT_FSKIP,
60 MA_OPT_RENDERER,
61 MA_OPT_SOUNDON,
62 MA_OPT_SOUNDRATE,
63 MA_OPT_REGION,
64 MA_OPT_TURBO,
65 MA_OPT_SSTATE,
66 MA_OPT_SSLOT,
67 MA_OPT_GAMMA,
68 MA_MAIN_RESUME_GAME,
69 MA_MAIN_SAVE_STATE,
70 MA_MAIN_LOAD_STATE,
71 MA_MAIN_RESET_GAME,
72 MA_MAIN_LOAD_ROM,
73 MA_MAIN_CHEATS,
74 MA_MAIN_CREDITS,
75 MA_MAIN_EXIT,
76} menu_id;
77
78void emu_make_path(char *buff, const char *end, int size)
79{
80 int pos, end_len;
81
82 end_len = strlen(end);
83 pos = plat_get_root_dir(buff, size);
84 strncpy(buff + pos, end, size - pos);
85 buff[size - 1] = 0;
86 if (pos + end_len > size - 1)
87 printf("Warning: path truncated: %s\n", buff);
88}
89
90static int emu_check_save_file(int slot, int *time)
91{
92 struct stat status;
93 char *fname;
94 FILE *st;
95 int retval = 0;
96 int ret;
97
7a938870 98 fname = FCEU_MakeFName(FCEUMKF_STATE, slot, 0);
4a2a617a 99 st = fopen(fname,"rb");
100 if (st == NULL)
101 goto out;
102 fclose(st);
103
104 retval = 1;
105 if (time == NULL)
106 goto out;
107
108 ret = stat(fname, &status);
109 if (ret != 0)
110 goto out;
111
112 if (status.st_mtime < 1347000000)
113 goto out; // probably bad rtc like on some Caanoos
114
115 *time = status.st_mtime;
116
117out:
118 free(fname);
119 return retval;
120}
121
122static int emu_save_load_game(int load, int unused)
123{
124 if (load)
125 FCEUI_LoadState();
126 else
127 FCEUI_SaveState();
128
129 return 0;
130}
131
132// rrrr rggg gggb bbbb
133static unsigned short fname2color(const char *fname)
134{
135 static const char *rom_exts[] = { ".zip", ".nes", ".fds", ".unf",
136 ".nez", ".unif" };
137 static const char *other_exts[] = { ".nsf", ".ips", ".fcm" };
138 const char *ext = strrchr(fname, '.');
139 int i;
140
141 if (ext == NULL)
142 return 0xffff;
143 for (i = 0; i < array_size(rom_exts); i++)
144 if (strcasecmp(ext, rom_exts[i]) == 0)
145 return 0xbdff;
146 for (i = 0; i < array_size(other_exts); i++)
147 if (strcasecmp(ext, other_exts[i]) == 0)
148 return 0xaff5;
149 return 0xffff;
150}
151
152static const char *filter_exts[] = {
153 ".txt", ".srm", ".pnd"
154};
155
156#define MENU_ALIGN_LEFT
157#ifdef __ARM_ARCH_7A__ // assume hires device
158#define MENU_X2 1
159#else
160#define MENU_X2 0
161#endif
162
163#include "../libpicofe/menu.c"
164
165static void draw_savestate_bg(int slot)
166{
167}
168
169static void debug_menu_loop(void)
170{
171}
172
173// ------------ patch/gg menu ------------
174
175extern void *cheats;
176static int cheat_count, cheat_start, cheat_pos;
177
178static int countcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data)
179{
180 cheat_count++;
181 return 1;
182}
183
184static int clistcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data)
185{
186 int pos;
187
188 pos = cheat_start + cheat_pos;
189 cheat_pos++;
190 if (pos < 0) return 1;
191 if (pos >= g_menuscreen_h / me_sfont_h) return 0;
192
193 smalltext_out16(14, pos * me_sfont_h, s ? "ON " : "OFF", 0xffff);
194 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h, type ? "S" : "R", 0xffff);
195 smalltext_out16(14 + me_sfont_w*6, pos * me_sfont_h, name, 0xffff);
196
197 return 1;
198}
199
200static void draw_patchlist(int sel)
201{
202 int pos, max_cnt;
203
204 menu_draw_begin(1, 1);
205
206 max_cnt = g_menuscreen_h / me_sfont_h;
207 cheat_start = max_cnt / 2 - sel;
208 cheat_pos = 0;
209 FCEUI_ListCheats(clistcallb, 0);
210
211 pos = cheat_start + cheat_pos;
212 if (pos < max_cnt)
213 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
214
215 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
216 menu_draw_end();
217}
218
219void patches_menu_loop(void)
220{
221 static int menu_sel = 0;
222 int inp;
223
224 cheat_count = 0;
225 FCEUI_ListCheats(countcallb, 0);
226
227 for (;;)
228 {
229 draw_patchlist(menu_sel);
230 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
231 |PBTN_MOK|PBTN_MBACK, NULL, 33);
232 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = cheat_count; }
233 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > cheat_count) menu_sel = 0; }
234 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
235 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > cheat_count) menu_sel = cheat_count; }
236 if (inp & PBTN_MOK) { // action
237 if (menu_sel < cheat_count)
238 FCEUI_ToggleCheat(menu_sel);
239 else break;
240 }
241 if (inp & PBTN_MBACK)
242 break;
243 }
244}
245
246// -------------- key config --------------
247
248// b_turbo,a_turbo RLDU SEBA
249me_bind_action me_ctrl_actions[] =
250{
251 { "UP ", NKEY_UP },
252 { "DOWN ", NKEY_DOWN },
253 { "LEFT ", NKEY_LEFT },
254 { "RIGHT ", NKEY_RIGHT },
255 { "A ", NKEY_A },
256 { "B ", NKEY_B },
257 { "A TURBO", NKEY_A_TURBO },
258 { "B TURBO", NKEY_B_TURBO },
259 { "START ", NKEY_START },
260 { "SELECT ", NKEY_SELECT },
261 { NULL, 0 }
262};
263
264me_bind_action emuctrl_actions[] =
265{
266 { "Save State ", EACT_SAVE_STATE },
267 { "Load State ", EACT_LOAD_STATE },
268 { "Next State Slot ", EACT_NEXT_SLOT },
269 { "Prev State Slot ", EACT_PREV_SLOT },
270 { "FDS Insert/Eject ", EACT_FDS_INSERT },
271 { "FDS Select Disk ", EACT_FDS_SELECT },
272 { "VSUni Insert Coin", EACT_INSERT_COIN },
273 { "Enter Menu ", EACT_ENTER_MENU },
274 { NULL, 0 }
275};
276
277static int key_config_loop_wrap(int id, int keys)
278{
279 switch (id) {
280 case MA_CTRL_PLAYER1:
281 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
282 break;
283 case MA_CTRL_PLAYER2:
284 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
285 break;
286 case MA_CTRL_EMU:
287 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
288 break;
289 default:
290 break;
291 }
292 return 0;
293}
294
295static const char *mgn_dev_name(int id, int *offs)
296{
297 const char *name = NULL;
298 static int it = 0;
299
300 if (id == MA_CTRL_DEV_FIRST)
301 it = 0;
302
303 for (; it < IN_MAX_DEVS; it++) {
304 name = in_get_dev_name(it, 1, 1);
305 if (name != NULL)
306 break;
307 }
308
309 it++;
310 return name;
311}
312
313static const char *mgn_saveloadcfg(int id, int *offs)
314{
315 return "";
316}
317
318static void config_commit(void);
319
320static int mh_savecfg(int id, int keys)
321{
322 const char *fname = NULL;
323 if (id == MA_OPT_SAVECFG_GAME)
324 fname = lastLoadedGameName;
325
326 config_commit();
327 if (SaveConfig(fname) == 0)
328 menu_update_msg("config saved");
329 else
330 menu_update_msg("failed to write config");
331
332 return 1;
333}
334
335static int mh_input_rescan(int id, int keys)
336{
337 //menu_sync_config();
338 in_probe();
339 menu_update_msg("rescan complete.");
340
341 return 0;
342}
343
344static menu_entry e_menu_keyconfig[] =
345{
346 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
347 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
348 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
349 mee_label (""),
350// mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
351 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
352 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
353 mee_handler ("Rescan devices:", mh_input_rescan),
354 mee_label (""),
355 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
356 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
357 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
358 mee_label_mk (MA_CTRL_DEV_NEXT, 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_end,
363};
364
365static int menu_loop_keyconfig(int id, int keys)
366{
367 static int sel = 0;
368
369 me_loop(e_menu_keyconfig, &sel);
370 return 0;
371}
372
373// --------- FCEU options ----------
374
375extern int ntsccol,ntschue,ntsctint;
376extern int srendlinev[2];
377extern int erendlinev[2];
378extern int eoptions;
379extern char *cpalette;
380extern void LoadCPalette(void);
381
382static menu_entry e_menu_fceu_options[] =
383{
384 //gp2x_text_out15(tl_x, y, "Custom palette: %s", cpal);
385 mee_onoff ("NTSC Color Emulation", MA_OPT_NTSC_COLOR, ntsccol, 1),
386 mee_range (" Tint (default: 56)", MA_OPT_NTSC_TINT, ntsctint, 0, 128),
387 mee_range (" Hue (default: 72)", MA_OPT_NTSC_HUE, ntschue, 0, 128),
388 mee_range ("First visible line (NTSC)", MA_OPT_SREND_N, srendlinev[0], 0, 239),
389 mee_range ("Last visible line (NTSC)", MA_OPT_EREND_N, erendlinev[0], 0, 239),
390 mee_range ("First visible line (PAL)", MA_OPT_SREND_P, srendlinev[1], 0, 239),
391 mee_range ("Last visible line (PAL)", MA_OPT_EREND_P, erendlinev[1], 0, 239),
392 mee_onoff ("Clip 8 left/right columns", MA_OPT_CLIP, eoptions, EO_CLIPSIDES),
393 mee_onoff ("Disable 8 sprite limit", MA_OPT_NO8LIM, eoptions, EO_NO8LIM),
394 mee_onoff ("Enable authentic GameGenie",MA_OPT_GG, eoptions, EO_GG),
395 mee_end,
396};
397
398static int menu_loop_fceu_options(int id, int keys)
399{
400 static int sel = 0;
401 int i;
402
403 FCEUI_GetNTSCTH(&ntsctint, &ntschue);
404
405 me_loop(e_menu_fceu_options, &sel);
406
407 for(i = 0; i < 2; i++)
408 {
409 if (srendlinev[i] < 0 || srendlinev[i] > 239)
410 srendlinev[i] = 0;
411 if (erendlinev[i] < srendlinev[i] || erendlinev[i] > 239)
412 erendlinev[i] = 239;
413 }
414 FCEUI_SetNTSCTH(ntsccol, ntsctint, ntschue);
415 FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);
416 FCEUI_DisableSpriteLimitation(eoptions&EO_NO8LIM);
417 FCEUI_SetGameGenie(eoptions&EO_GG);
418 //if (cpalette) LoadCPalette();
419 //else
420 FCEUI_SetPaletteArray(0); // set to default
421 FCEU_ResetPalette();
422
423 return 0;
424}
425
426// -------------- options --------------
427
428static const char *men_frameskip[] = { "Auto", "0", "1", "2", "3", "4", NULL };
429static const char *men_rates[] = { "8000", "11025", "16000", "22050", "44100", NULL };
430static const int men_rates_i[] = { 8000 , 11025 , 16000 , 22050 , 44100 };
431static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
432static const char *men_sstate[] = { "OFF", "writes", "loads", "both", NULL };
433static const char h_renderer[] = "ROM reload required for this\n"
434 "setting to take effect";
435
436static int sndrate_i;
437static int sndon;
438static int turbo_i;
7a938870 439static int frameskip_i;
4a2a617a 440
441static void config_commit(void)
442{
443 Settings.sound_rate = men_rates_i[sndrate_i];
444 soundvol = sndon ? 0 : 50;
445 Settings.turbo_rate_add = (turbo_i * 2 << 24) / 60 + 1;
7a938870 446 Settings.frameskip = frameskip_i - 1;
4a2a617a 447
448 if (Settings.region_force)
449 FCEUI_SetVidSystem(Settings.region_force - 1);
450}
451
452static menu_entry e_menu_options[] =
453{
454 mee_onoff ("Show FPS", MA_OPT_SHOWFPS, Settings.showfps, 1),
7a938870 455 mee_enum ("Frameskip", MA_OPT_FSKIP, frameskip_i, men_frameskip),
4a2a617a 456 mee_onoff_h ("Accurate renderer (slow)",MA_OPT_RENDERER, Settings.accurate_mode, 1, h_renderer),
457 mee_onoff ("Enable sound", MA_OPT_SOUNDON, sndon, 1),
458 mee_enum ("Sound Rate", MA_OPT_SOUNDRATE, sndrate_i, men_rates),
459 mee_enum ("Region", MA_OPT_REGION, Settings.region_force, men_region),
460 mee_range ("Turbo rate (Hz)", MA_OPT_TURBO, turbo_i, 1, 30),
461 mee_enum ("Confirm savestate", MA_OPT_SSTATE, Settings.sstate_confirm, men_sstate),
462 mee_range ("Save slot", MA_OPT_SSLOT, CurrentState, 0, 9),
463 mee_range ("Gamma correction", MA_OPT_GAMMA, Settings.gamma, 0, 300),
464 mee_handler ("[FCE Ultra options]", menu_loop_fceu_options),
465 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
466 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
467 mee_end,
468};
469
470static int menu_loop_options(int id, int keys)
471{
472 static int sel = 0;
473 int oldrate;
474 int i;
475
476 oldrate = Settings.sound_rate;
477 for (i = 0; i < array_size(men_rates_i); i++) {
478 if (Settings.sound_rate == men_rates_i[i]) {
479 sndrate_i = i;
480 break;
481 }
482 }
483 sndon = soundvol != 0;
484 turbo_i = (Settings.turbo_rate_add * 60 / 2) >> 24;
7a938870 485 frameskip_i = Settings.frameskip + 1;
4a2a617a 486
487 me_loop(e_menu_options, &sel);
488
489 config_commit();
490 if (oldrate != Settings.sound_rate)
491 InitSound();
492
493 return 0;
494}
495
496// -------------- root menu --------------
497
498static const char credits_text[] =
499 "GPFCE " REV "\n"
500 "(c) notaz, 2007,2012\n\n"
501 "Based on FCE Ultra versions\n"
502 "0.81 and 0.98.1x\n\n"
503 " - Credits -\n"
504 "Bero: FCE\n"
505 "Xodnizel: FCE Ultra\n"
506 "FCA author: 6502 core\n";
507
508static void draw_frame_credits(void)
509{
510 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
511}
512
513static int romsel_run(void)
514{
515 const char *fname;
516
517 fname = menu_loop_romsel(lastLoadedGameName, sizeof(lastLoadedGameName));
518 if (fname == NULL)
519 return -1;
520
521 printf("selected file: %s\n", fname);
522 //keys_load_all(cfg);
523
524 strcpy(lastLoadedGameName, rom_fname_reload);
525 return 0;
526}
527
528static int menu_loop_ret;
529
530static int main_menu_handler(int id, int keys)
531{
532 switch (id)
533 {
534 case MA_MAIN_RESUME_GAME:
535 if (fceugi)
536 return 1;
537 break;
538 case MA_MAIN_SAVE_STATE:
539 if (fceugi) {
4a2a617a 540 return menu_loop_savestate(0);
541 }
542 break;
543 case MA_MAIN_LOAD_STATE:
544 if (fceugi) {
4a2a617a 545 return menu_loop_savestate(1);
546 }
547 break;
548 case MA_MAIN_RESET_GAME:
549 if (fceugi) {
550 FCEU_DoSimpleCommand(FCEUNPCMD_RESET);
7a938870 551 return 1;
4a2a617a 552 }
553 break;
554 case MA_MAIN_LOAD_ROM:
555 if (romsel_run() == 0) {
556 menu_loop_ret = 2;
557 return 1;
558 }
559 break;
560 case MA_MAIN_CREDITS:
561 draw_menu_message(credits_text, draw_frame_credits);
562 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
563 break;
564 case MA_MAIN_EXIT:
565 menu_loop_ret = 1;
566 return 1;
567 default:
568 lprintf("%s: something unknown selected\n", __FUNCTION__);
569 break;
570 }
571
572 return 0;
573}
574
575static menu_entry e_menu_main[] =
576{
577 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
578 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
579 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
580 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
581 mee_handler_id("Load ROM", MA_MAIN_LOAD_ROM, main_menu_handler),
582 mee_handler ("Options", menu_loop_options),
583 mee_handler ("Controls", menu_loop_keyconfig),
584 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
585 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
586 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
587 mee_end,
588};
589
590// ----------------------------
591
592int menu_loop(void)
593{
594 static int sel = 0;
595
596 menu_loop_ret = 0;
597
598 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, fceugi != NULL);
599 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, fceugi != NULL);
600 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, fceugi != NULL);
601 me_enable(e_menu_main, MA_MAIN_RESET_GAME, fceugi != NULL);
602 me_enable(e_menu_main, MA_MAIN_CHEATS, fceugi && cheats);
603
604 plat_video_menu_enter(fceugi != NULL);
605 in_set_config_int(0, IN_CFG_BLOCKING, 1);
606
607 do {
608 me_loop_d(e_menu_main, &sel, NULL, NULL);
7a938870 609 } while (!fceugi);
4a2a617a 610
611 /* wait until menu, ok, back is released */
612 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
613 ;
614
615 in_set_config_int(0, IN_CFG_BLOCKING, 0);
616 plat_video_menu_leave();
617
7a938870 618 Exit = 0;
4a2a617a 619 return menu_loop_ret;
620}
621
622void menu_init(void)
623{
624 char buff[256];
625
626 g_border_style = 1;
627 menu_init_base();
628
629 //menu_load_config(0);
630
631 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
632 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
633 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
634 fprintf(stderr, "OOM\n");
635 exit(1);
636 }
637
638 emu_make_path(buff, "skin/background.png", sizeof(buff));
639 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
640}
641
642void menu_update_msg(const char *msg)
643{
644 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
645 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
646
647 menu_error_time = plat_get_ticks_ms();
648 lprintf("msg: %s\n", menu_error_msg);
649}
650