Add copyright message to gles_video
[gpsp.git] / gui.c
CommitLineData
2823a4c8 1/* gameplaySP
2 *
3 * Copyright (C) 2006 Exophase <exophase@gmail.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public Licens e as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
8b6232a6 19#include "common.h"
20#include "font.h"
21
2823a4c8 22#ifndef _WIN32_WCE
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <ctype.h>
27#include <dirent.h>
28
29#endif
30
2823a4c8 31#define MAX_PATH 1024
32
33// Blatantly stolen and trimmed from MZX (megazeux.sourceforge.net)
34
35#ifdef GP2X_BUILD
36
ee0a3871 37#define FILE_LIST_ROWS 20
2823a4c8 38#define FILE_LIST_POSITION 5
39#define DIR_LIST_POSITION 260
40
41#else
42
43#define FILE_LIST_ROWS 25
44#define FILE_LIST_POSITION 5
ffc30d25 45#define DIR_LIST_POSITION (resolution_width * 3 / 4)
2823a4c8 46
47#endif
48
49#ifdef PSP_BUILD
50
ee0a3871 51#define COLOR_BG color16(2, 8, 10)
52
2823a4c8 53#define color16(red, green, blue) \
54 (blue << 11) | (green << 5) | red \
55
56#else
57
2823a4c8 58#define COLOR_BG color16(0, 0, 0)
59
ee0a3871 60#define color16(red, green, blue) \
61 (red << 11) | (green << 5) | blue \
2823a4c8 62
63#endif
64
65#define COLOR_ROM_INFO color16(22, 36, 26)
66#define COLOR_ACTIVE_ITEM color16(31, 63, 31)
67#define COLOR_INACTIVE_ITEM color16(13, 40, 18)
68#define COLOR_FRAMESKIP_BAR color16(15, 31, 31)
69#define COLOR_HELP_TEXT color16(16, 40, 24)
70
638cc626 71#ifdef PSP_BUILD
bbba3209 72 static const char *clock_speed_options[] =
1d02ca75 73 {
74 "33MHz", "66MHz", "100MHz", "133MHz", "166MHz", "200MHz", "233MHz",
75 "266MHz", "300MHz", "333MHz"
76 };
77 #define menu_get_clock_speed() \
638cc626 78 clock_speed = (clock_speed_number + 1) * 33
79 #define get_clock_speed_number() \
80 clock_speed_number = (clock_speed / 33) - 1
108c704a 81#elif defined(POLLUX_BUILD)
bbba3209 82 static const char *clock_speed_options[] =
1d02ca75 83 {
84 "300MHz", "333MHz", "366MHz", "400MHz", "433MHz",
85 "466MHz", "500MHz", "533MHz", "566MHz", "600MHz",
86 "633MHz", "666MHz", "700MHz", "733MHz", "766MHz",
87 "800MHz", "833MHz", "866MHz", "900MHz"
88 };
89 #define menu_get_clock_speed() \
638cc626 90 clock_speed = 300 + (clock_speed_number * 3333) / 100
91 #define get_clock_speed_number() \
92 clock_speed_number = (clock_speed - 300) / 33
93#elif defined(GP2X_BUILD)
bbba3209 94 static const char *clock_speed_options[] =
1d02ca75 95 {
96 "150MHz", "160MHz", "170MHz", "180MHz", "190MHz",
97 "200MHz", "210MHz", "220MHz", "230MHz", "240MHz",
98 "250MHz", "260MHz", "270MHz", "280MHz", "290MHz"
99 };
100 #define menu_get_clock_speed() \
638cc626 101 clock_speed = 150 + clock_speed_number * 10
102 #define get_clock_speed_number() \
103 clock_speed_number = (clock_speed - 150) / 10
104#else
bbba3209 105 static const char *clock_speed_options[] =
1d02ca75 106 {
107 "0"
108 };
bbba3209 109 #define menu_get_clock_speed()
110 #define get_clock_speed_number()
638cc626 111#endif
112
1d02ca75 113
2823a4c8 114int sort_function(const void *dest_str_ptr, const void *src_str_ptr)
115{
116 char *dest_str = *((char **)dest_str_ptr);
117 char *src_str = *((char **)src_str_ptr);
118
119 if(src_str[0] == '.')
120 return 1;
121
122 if(dest_str[0] == '.')
123 return -1;
124
125 return strcasecmp(dest_str, src_str);
126}
127
bbba3209 128s32 load_file(const char **wildcards, char *result)
2823a4c8 129{
130 DIR *current_dir;
131 struct dirent *current_file;
132 struct stat file_info;
bbba3209 133 char current_dir_name[MAX_PATH];
134 char current_dir_short[81];
2823a4c8 135 u32 current_dir_length;
136 u32 total_filenames_allocated;
137 u32 total_dirnames_allocated;
bbba3209 138 char **file_list;
139 char **dir_list;
2823a4c8 140 u32 num_files;
141 u32 num_dirs;
bbba3209 142 char *file_name;
2823a4c8 143 u32 file_name_length;
144 u32 ext_pos = -1;
145 u32 chosen_file, chosen_dir;
2823a4c8 146 s32 return_value = 1;
42c81190 147 s32 current_file_selection;
148 s32 current_file_scroll_value;
2823a4c8 149 u32 current_dir_selection;
150 u32 current_dir_scroll_value;
42c81190 151 s32 current_file_in_scroll;
2823a4c8 152 u32 current_dir_in_scroll;
153 u32 current_file_number, current_dir_number;
154 u32 current_column = 0;
155 u32 repeat;
156 u32 i;
157 gui_action_type gui_action;
158
159 while(return_value == 1)
160 {
161 current_file_selection = 0;
162 current_file_scroll_value = 0;
163 current_dir_selection = 0;
164 current_dir_scroll_value = 0;
165 current_file_in_scroll = 0;
166 current_dir_in_scroll = 0;
167
168 total_filenames_allocated = 32;
169 total_dirnames_allocated = 32;
bbba3209 170 file_list = (char **)malloc(sizeof(char *) * 32);
171 dir_list = (char **)malloc(sizeof(char *) * 32);
172 memset(file_list, 0, sizeof(char *) * 32);
173 memset(dir_list, 0, sizeof(char *) * 32);
2823a4c8 174
175 num_files = 0;
176 num_dirs = 0;
177 chosen_file = 0;
178 chosen_dir = 0;
179
180 getcwd(current_dir_name, MAX_PATH);
181
182 current_dir = opendir(current_dir_name);
183
184 do
185 {
186 if(current_dir)
187 current_file = readdir(current_dir);
188 else
189 current_file = NULL;
190
191 if(current_file)
192 {
193 file_name = current_file->d_name;
194 file_name_length = strlen(file_name);
195
196 if((stat(file_name, &file_info) >= 0) &&
197 ((file_name[0] != '.') || (file_name[1] == '.')))
198 {
199 if(S_ISDIR(file_info.st_mode))
200 {
bbba3209 201 dir_list[num_dirs] = malloc(file_name_length + 1);
2823a4c8 202
203 sprintf(dir_list[num_dirs], "%s", file_name);
204
205 num_dirs++;
206 }
207 else
208 {
209 // Must match one of the wildcards, also ignore the .
210 if(file_name_length >= 4)
211 {
212 if(file_name[file_name_length - 4] == '.')
213 ext_pos = file_name_length - 4;
214 else
215
216 if(file_name[file_name_length - 3] == '.')
217 ext_pos = file_name_length - 3;
218
219 else
220 ext_pos = 0;
221
222 for(i = 0; wildcards[i] != NULL; i++)
223 {
224 if(!strcasecmp((file_name + ext_pos),
225 wildcards[i]))
226 {
227 file_list[num_files] =
bbba3209 228 malloc(file_name_length + 1);
2823a4c8 229
230 sprintf(file_list[num_files], "%s", file_name);
231
232 num_files++;
233 break;
234 }
235 }
236 }
237 }
238 }
239
240 if(num_files == total_filenames_allocated)
241 {
bbba3209 242 file_list = (char **)realloc(file_list, sizeof(char *) *
2823a4c8 243 total_filenames_allocated * 2);
244 memset(file_list + total_filenames_allocated, 0,
bbba3209 245 sizeof(char *) * total_filenames_allocated);
2823a4c8 246 total_filenames_allocated *= 2;
247 }
248
249 if(num_dirs == total_dirnames_allocated)
250 {
bbba3209 251 dir_list = (char **)realloc(dir_list, sizeof(char *) *
2823a4c8 252 total_dirnames_allocated * 2);
253 memset(dir_list + total_dirnames_allocated, 0,
bbba3209 254 sizeof(char *) * total_dirnames_allocated);
2823a4c8 255 total_dirnames_allocated *= 2;
256 }
257 }
258 } while(current_file);
259
bbba3209 260 qsort((void *)file_list, num_files, sizeof(char *), sort_function);
261 qsort((void *)dir_list, num_dirs, sizeof(char *), sort_function);
2823a4c8 262
263 closedir(current_dir);
264
265 current_dir_length = strlen(current_dir_name);
266
267 if(current_dir_length > 80)
268 {
269
270#ifdef GP2X_BUILD
271 snprintf(current_dir_short, 80,
272 "...%s", current_dir_name + current_dir_length - 77);
273#else
274 memcpy(current_dir_short, "...", 3);
275 memcpy(current_dir_short + 3,
276 current_dir_name + current_dir_length - 77, 77);
277 current_dir_short[80] = 0;
278#endif
279 }
280 else
281 {
282#ifdef GP2X_BUILD
283 snprintf(current_dir_short, 80, "%s", current_dir_name);
284#else
285 memcpy(current_dir_short, current_dir_name,
286 current_dir_length + 1);
287#endif
288 }
289
290 repeat = 1;
291
292 if(num_files == 0)
293 current_column = 1;
294
295 clear_screen(COLOR_BG);
296 {
2823a4c8 297 while(repeat)
298 {
299 flip_screen();
300
301 print_string(current_dir_short, COLOR_ACTIVE_ITEM, COLOR_BG, 0, 0);
302#ifdef GP2X_BUILD
303 print_string("Press X to return to the main menu.",
304 COLOR_HELP_TEXT, COLOR_BG, 20, 220);
305#else
306 print_string("Press X to return to the main menu.",
307 COLOR_HELP_TEXT, COLOR_BG, 20, 260);
308#endif
309
310 for(i = 0, current_file_number = i + current_file_scroll_value;
311 i < FILE_LIST_ROWS; i++, current_file_number++)
312 {
313 if(current_file_number < num_files)
314 {
315 if((current_file_number == current_file_selection) &&
316 (current_column == 0))
317 {
318 print_string(file_list[current_file_number], COLOR_ACTIVE_ITEM,
319 COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
320 }
321 else
322 {
323 print_string(file_list[current_file_number], COLOR_INACTIVE_ITEM,
324 COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
325 }
326 }
327 }
328
329 for(i = 0, current_dir_number = i + current_dir_scroll_value;
330 i < FILE_LIST_ROWS; i++, current_dir_number++)
331 {
332 if(current_dir_number < num_dirs)
333 {
334 if((current_dir_number == current_dir_selection) &&
335 (current_column == 1))
336 {
337 print_string(dir_list[current_dir_number], COLOR_ACTIVE_ITEM,
338 COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
339 }
340 else
341 {
342 print_string(dir_list[current_dir_number], COLOR_INACTIVE_ITEM,
343 COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
344 }
345 }
346 }
347
348 gui_action = get_gui_input();
349
350 switch(gui_action)
351 {
352 case CURSOR_DOWN:
353 if(current_column == 0)
354 {
355 if(current_file_selection < (num_files - 1))
356 {
357 current_file_selection++;
358 if(current_file_in_scroll == (FILE_LIST_ROWS - 1))
359 {
360 clear_screen(COLOR_BG);
361 current_file_scroll_value++;
362 }
363 else
364 {
365 current_file_in_scroll++;
366 }
367 }
42c81190 368 else
369 {
370 clear_screen(COLOR_BG);
371 current_file_selection = 0;
372 current_file_scroll_value = 0;
373 current_file_in_scroll = 0;
374 }
2823a4c8 375 }
376 else
377 {
378 if(current_dir_selection < (num_dirs - 1))
379 {
380 current_dir_selection++;
381 if(current_dir_in_scroll == (FILE_LIST_ROWS - 1))
382 {
383 clear_screen(COLOR_BG);
384 current_dir_scroll_value++;
385 }
386 else
387 {
388 current_dir_in_scroll++;
389 }
390 }
391 }
392
393 break;
394
42c81190 395 case CURSOR_R:
396 if (current_column != 0)
397 break;
398 clear_screen(COLOR_BG);
399 current_file_selection += FILE_LIST_ROWS;
400 if (current_file_selection > num_files - 1)
401 current_file_selection = num_files - 1;
402 current_file_scroll_value = current_file_selection - FILE_LIST_ROWS / 2;
403 if (current_file_scroll_value < 0)
404 {
405 current_file_scroll_value = 0;
406 current_file_in_scroll = current_file_selection;
407 }
408 else
409 {
410 current_file_in_scroll = FILE_LIST_ROWS / 2;
411 }
412 break;
413
2823a4c8 414 case CURSOR_UP:
415 if(current_column == 0)
416 {
417 if(current_file_selection)
418 {
419 current_file_selection--;
420 if(current_file_in_scroll == 0)
421 {
422 clear_screen(COLOR_BG);
423 current_file_scroll_value--;
424 }
425 else
426 {
427 current_file_in_scroll--;
428 }
429 }
42c81190 430 else
431 {
432 clear_screen(COLOR_BG);
433 current_file_selection = num_files - 1;
434 current_file_in_scroll = FILE_LIST_ROWS - 1;
435 if (current_file_in_scroll > num_files - 1)
436 current_file_in_scroll = num_files - 1;
437 current_file_scroll_value = num_files - FILE_LIST_ROWS;
438 if (current_file_scroll_value < 0)
439 current_file_scroll_value = 0;
440 }
2823a4c8 441 }
442 else
443 {
444 if(current_dir_selection)
445 {
446 current_dir_selection--;
447 if(current_dir_in_scroll == 0)
448 {
449 clear_screen(COLOR_BG);
450 current_dir_scroll_value--;
451 }
452 else
453 {
454 current_dir_in_scroll--;
455 }
456 }
457 }
458 break;
459
42c81190 460 case CURSOR_L:
461 if (current_column != 0)
462 break;
463 clear_screen(COLOR_BG);
464 current_file_selection -= FILE_LIST_ROWS;
465 if (current_file_selection < 0)
466 current_file_selection = 0;
467 current_file_scroll_value = current_file_selection - FILE_LIST_ROWS / 2;
468 if (current_file_scroll_value < 0)
469 {
470 current_file_scroll_value = 0;
471 current_file_in_scroll = current_file_selection;
472 }
473 else
474 {
475 current_file_in_scroll = FILE_LIST_ROWS / 2;
476 }
477 break;
478
479 case CURSOR_RIGHT:
2823a4c8 480 if(current_column == 0)
481 {
482 if(num_dirs != 0)
483 current_column = 1;
484 }
485 break;
486
487 case CURSOR_LEFT:
488 if(current_column == 1)
489 {
490 if(num_files != 0)
491 current_column = 0;
492 }
493 break;
494
495 case CURSOR_SELECT:
496 if(current_column == 1)
497 {
498 repeat = 0;
499 chdir(dir_list[current_dir_selection]);
500 }
501 else
502 {
503 if(num_files != 0)
504 {
505 repeat = 0;
506 return_value = 0;
507 strcpy(result, file_list[current_file_selection]);
508 }
509 }
510 break;
511
512 case CURSOR_BACK:
513#ifdef PSP_BUILD
514 if(!strcmp(current_dir_name, "ms0:/PSP"))
515 break;
516#endif
517 repeat = 0;
518 chdir("..");
519 break;
520
521 case CURSOR_EXIT:
522 return_value = -1;
523 repeat = 0;
524 break;
bbba3209 525
526 default:
527 break;
2823a4c8 528 }
529 }
530 }
531
532 for(i = 0; i < num_files; i++)
533 {
534 free(file_list[i]);
535 }
536 free(file_list);
537
538 for(i = 0; i < num_dirs; i++)
539 {
540 free(dir_list[i]);
541 }
542 free(dir_list);
543 }
544
545 clear_screen(COLOR_BG);
546
547 return return_value;
548}
549
550typedef enum
551{
552 NUMBER_SELECTION_OPTION = 0x01,
553 STRING_SELECTION_OPTION = 0x02,
554 SUBMENU_OPTION = 0x04,
ffa573f8 555 ACTION_OPTION = 0x08,
2823a4c8 556} menu_option_type_enum;
557
558struct _menu_type
559{
560 void (* init_function)();
561 void (* passive_function)();
562 struct _menu_option_type *options;
563 u32 num_options;
564};
565
566struct _menu_option_type
567{
568 void (* action_function)();
569 void (* passive_function)();
570 struct _menu_type *sub_menu;
eb3668fc 571 const char *display_string;
2823a4c8 572 void *options;
573 u32 *current_option;
574 u32 num_options;
eb3668fc 575 const char *help_string;
2823a4c8 576 u32 line_number;
577 menu_option_type_enum option_type;
578};
579
580typedef struct _menu_option_type menu_option_type;
581typedef struct _menu_type menu_type;
582
583#define make_menu(name, init_function, passive_function) \
584 menu_type name##_menu = \
585 { \
586 init_function, \
587 passive_function, \
588 name##_options, \
589 sizeof(name##_options) / sizeof(menu_option_type) \
590 } \
591
592#define gamepad_config_option(display_string, number) \
593{ \
594 NULL, \
595 menu_fix_gamepad_help, \
596 NULL, \
597 display_string ": %s", \
598 gamepad_config_buttons, \
599 gamepad_config_map + gamepad_config_line_to_button[number], \
600 sizeof(gamepad_config_buttons) / sizeof(gamepad_config_buttons[0]), \
601 gamepad_help[gamepad_config_map[ \
602 gamepad_config_line_to_button[number]]], \
603 number, \
604 STRING_SELECTION_OPTION \
605} \
606
607#define analog_config_option(display_string, number) \
608{ \
609 NULL, \
610 menu_fix_gamepad_help, \
611 NULL, \
612 display_string ": %s", \
613 gamepad_config_buttons, \
614 gamepad_config_map + number + 12, \
615 sizeof(gamepad_config_buttons) / sizeof(gamepad_config_buttons[0]), \
616 gamepad_help[gamepad_config_map[number + 12]], \
617 number + 2, \
618 STRING_SELECTION_OPTION \
619} \
620
621#define cheat_option(number) \
622{ \
623 NULL, \
624 NULL, \
625 NULL, \
626 cheat_format_str[number], \
627 enable_disable_options, \
628 &(cheats[number].cheat_active), \
629 2, \
630 "Activate/deactivate this cheat code.", \
631 number, \
632 STRING_SELECTION_OPTION \
633} \
634
635#define action_option(action_function, passive_function, display_string, \
636 help_string, line_number) \
637{ \
638 action_function, \
639 passive_function, \
640 NULL, \
641 display_string, \
642 NULL, \
643 NULL, \
644 0, \
645 help_string, \
646 line_number, \
647 ACTION_OPTION \
648} \
649
650#define submenu_option(sub_menu, display_string, help_string, line_number) \
651{ \
652 NULL, \
653 NULL, \
654 sub_menu, \
655 display_string, \
656 NULL, \
657 NULL, \
658 sizeof(sub_menu) / sizeof(menu_option_type), \
659 help_string, \
660 line_number, \
661 SUBMENU_OPTION \
662} \
663
664#define selection_option(passive_function, display_string, options, \
665 option_ptr, num_options, help_string, line_number, type) \
666{ \
667 NULL, \
668 passive_function, \
669 NULL, \
670 display_string, \
671 options, \
672 option_ptr, \
673 num_options, \
674 help_string, \
675 line_number, \
676 type \
677} \
678
679#define action_selection_option(action_function, passive_function, \
680 display_string, options, option_ptr, num_options, help_string, line_number, \
681 type) \
682{ \
683 action_function, \
684 passive_function, \
685 NULL, \
686 display_string, \
687 options, \
688 option_ptr, \
689 num_options, \
690 help_string, \
691 line_number, \
692 type | ACTION_OPTION \
693} \
694
695
696#define string_selection_option(passive_function, display_string, options, \
697 option_ptr, num_options, help_string, line_number) \
698 selection_option(passive_function, display_string ": %s", options, \
699 option_ptr, num_options, help_string, line_number, STRING_SELECTION_OPTION)\
700
701#define numeric_selection_option(passive_function, display_string, \
702 option_ptr, num_options, help_string, line_number) \
703 selection_option(passive_function, display_string ": %d", NULL, option_ptr, \
704 num_options, help_string, line_number, NUMBER_SELECTION_OPTION) \
705
706#define string_selection_action_option(action_function, passive_function, \
707 display_string, options, option_ptr, num_options, help_string, line_number) \
708 action_selection_option(action_function, passive_function, \
709 display_string ": %s", options, option_ptr, num_options, help_string, \
710 line_number, STRING_SELECTION_OPTION) \
711
712#define numeric_selection_action_option(action_function, passive_function, \
713 display_string, option_ptr, num_options, help_string, line_number) \
714 action_selection_option(action_function, passive_function, \
715 display_string ": %d", NULL, option_ptr, num_options, help_string, \
716 line_number, NUMBER_SELECTION_OPTION) \
717
718#define numeric_selection_action_hide_option(action_function, \
719 passive_function, display_string, option_ptr, num_options, help_string, \
720 line_number) \
721 action_selection_option(action_function, passive_function, \
722 display_string, NULL, option_ptr, num_options, help_string, \
723 line_number, NUMBER_SELECTION_OPTION) \
724
725
726#define GAMEPAD_MENU_WIDTH 15
727
728#ifdef PSP_BUILD
729
730u32 gamepad_config_line_to_button[] =
731 { 8, 6, 7, 9, 1, 2, 3, 0, 4, 5, 11, 10 };
732
733#endif
734
735#ifdef GP2X_BUILD
736
737u32 gamepad_config_line_to_button[] =
108c704a 738 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 14, 15 };
2823a4c8 739
740#endif
741
eb3668fc 742#ifdef PND_BUILD
743
744u32 gamepad_config_line_to_button[] =
745 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 12, 13, 14, 15 };
746
747#endif
748
ffa573f8
D
749#ifdef RPI_BUILD
750
751u32 gamepad_config_line_to_button[] =
752 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 12, 13, 14, 15 };
753
754#endif
755
bbba3209 756static const char *scale_options[] =
1d02ca75 757{
eb3668fc 758#ifdef PSP_BUILD
759 "unscaled 3:2", "scaled 3:2", "fullscreen 16:9"
760#elif defined(WIZ_BUILD)
1d02ca75 761 "unscaled 3:2", "scaled 3:2 (slower)",
762 "unscaled 3:2 (anti-tear)", "scaled 3:2 (anti-tear)"
108c704a 763#elif defined(POLLUX_BUILD)
764 "unscaled 3:2", "scaled 3:2 (slower)"
eb3668fc 765#elif defined(PND_BUILD)
766 "unscaled", "2x", "3x", "fullscreen"
767#elif defined(GP2X_BUILD)
4cadce97 768 "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)"
ffa573f8
D
769#elif defined(RPI_BUILD)
770 "fullscreen"
eb3668fc 771#else
772 "unscaled 3:2"
1d02ca75 773#endif
774};
2823a4c8 775
e38fee1b 776const char *filter2_options[] =
777{
778 "none", "scale2x", "scale3x", "eagle2x"
779};
780
0dfe793b 781#ifndef PSP_BUILD
782static const char *audio_buffer_options[] =
783{
784 "16 bytes", "32 bytes", "64 bytes",
785 "128 bytes", "256 bytes", "512 bytes", "1024 bytes", "2048 bytes",
786 "4096 bytes", "8192 bytes", "16284 bytes"
787};
788#else
789const char *audio_buffer_options[] =
790{
791 "3072 bytes", "4096 bytes", "5120 bytes", "6144 bytes", "7168 bytes",
792 "8192 bytes", "9216 bytes", "10240 bytes", "11264 bytes", "12288 bytes"
793};
794#endif
795
796
2823a4c8 797s32 load_game_config_file()
798{
bbba3209 799 char game_config_filename[512];
2823a4c8 800 u32 file_loaded = 0;
801 u32 i;
d0944fc9 802 make_rpath(game_config_filename, sizeof(game_config_filename), ".cfg");
2823a4c8 803
804 file_open(game_config_file, game_config_filename, read);
805
806 if(file_check_valid(game_config_file))
807 {
808 u32 file_size = file_length(game_config_filename, game_config_file);
809
810 // Sanity check: File size must be the right size
811 if(file_size == 56)
812 {
813 u32 file_options[file_size / 4];
814
815 file_read_array(game_config_file, file_options);
816 current_frameskip_type = file_options[0] % 3;
817 frameskip_value = file_options[1];
818 random_skip = file_options[2] % 2;
819 clock_speed = file_options[3];
820
108c704a 821#ifdef POLLUX_BUILD
638cc626 822 if(clock_speed > 900)
823 clock_speed = 533;
824#elif defined(GP2X_BUILD)
90206450 825 if(clock_speed >= 300)
826 clock_speed = 200;
827#else
2823a4c8 828 if(clock_speed > 333)
829 clock_speed = 333;
90206450 830#endif
2823a4c8 831
832 if(clock_speed < 33)
833 clock_speed = 33;
834
835 if(frameskip_value < 0)
836 frameskip_value = 0;
837
838 if(frameskip_value > 99)
839 frameskip_value = 99;
840
841 for(i = 0; i < 10; i++)
842 {
c95affa7 843 cheats[i].cheat_active = file_options[4 + i] % 2;
2823a4c8 844 cheats[i].cheat_name[0] = 0;
845 }
846
847 file_close(game_config_file);
848 file_loaded = 1;
849 }
850 }
851
852 if(file_loaded)
853 return 0;
854
ffa573f8
D
855#ifdef RPI_BUILD
856 current_frameskip_type = manual_frameskip;
857 frameskip_value = 1;
858#else
2823a4c8 859 current_frameskip_type = auto_frameskip;
860 frameskip_value = 4;
108c704a 861#ifdef POLLUX_BUILD
8ff85457 862 frameskip_value = 1;
ffa573f8 863#endif
8ff85457 864#endif
2823a4c8 865 random_skip = 0;
638cc626 866 clock_speed = default_clock_speed;
2823a4c8 867
868 for(i = 0; i < 10; i++)
869 {
870 cheats[i].cheat_active = 0;
871 cheats[i].cheat_name[0] = 0;
872 }
873
874 return -1;
875}
876
0dfe793b 877enum file_options {
878 fo_screen_scale = 0,
879 fo_screen_filter,
880 fo_global_enable_audio,
881 fo_audio_buffer_size,
882 fo_update_backup_flag,
883 fo_global_enable_analog,
884 fo_analog_sensitivity_level,
885 fo_screen_filter2,
886 fo_main_option_count,
887};
888
889#ifdef PC_BUILD
890#define PLAT_BUTTON_COUNT 0
891#endif
892#define FILE_OPTION_COUNT (fo_main_option_count + PLAT_BUTTON_COUNT)
e38fee1b 893
2823a4c8 894s32 load_config_file()
895{
bbba3209 896 char config_path[512];
2823a4c8 897
d0944fc9 898 sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, GPSP_CONFIG_FILENAME);
2823a4c8 899
900 file_open(config_file, config_path, read);
901
902 if(file_check_valid(config_file))
903 {
904 u32 file_size = file_length(config_path, config_file);
905
906 // Sanity check: File size must be the right size
e38fee1b 907 if(file_size == FILE_OPTION_COUNT * 4)
2823a4c8 908 {
909 u32 file_options[file_size / 4];
2823a4c8 910 file_read_array(config_file, file_options);
911
0dfe793b 912 screen_scale = file_options[fo_screen_scale] %
1d02ca75 913 (sizeof(scale_options) / sizeof(scale_options[0]));
0dfe793b 914 screen_filter = file_options[fo_screen_filter] % 2;
915 global_enable_audio = file_options[fo_global_enable_audio] % 2;
916 screen_filter2 = file_options[fo_screen_filter2] %
e38fee1b 917 (sizeof(filter2_options) / sizeof(filter2_options[0]));
2823a4c8 918
0dfe793b 919 audio_buffer_size_number = file_options[fo_audio_buffer_size] %
920 (sizeof(audio_buffer_options) / sizeof(audio_buffer_options[0]));
2823a4c8 921
0dfe793b 922 update_backup_flag = file_options[fo_update_backup_flag] % 2;
923 global_enable_analog = file_options[fo_global_enable_analog] % 2;
924 analog_sensitivity_level = file_options[fo_analog_sensitivity_level] % 8;
2823a4c8 925
926#ifdef PSP_BUILD
927 scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
928#endif
929
930 // Sanity check: Make sure there's a MENU or FRAMESKIP
931 // key, if not assign to triangle
932
933#ifndef PC_BUILD
bbba3209 934 u32 i;
935 s32 menu_button = -1;
0dfe793b 936 for(i = 0; i < PLAT_BUTTON_COUNT; i++)
2823a4c8 937 {
0dfe793b 938 gamepad_config_map[i] = file_options[fo_main_option_count + i] %
2823a4c8 939 (BUTTON_ID_NONE + 1);
940
941 if(gamepad_config_map[i] == BUTTON_ID_MENU)
942 {
943 menu_button = i;
944 }
945 }
946
0dfe793b 947 if(menu_button == -1 && PLAT_MENU_BUTTON >= 0)
2823a4c8 948 {
0dfe793b 949 gamepad_config_map[PLAT_MENU_BUTTON] = BUTTON_ID_MENU;
2823a4c8 950 }
951#endif
952
953 file_close(config_file);
954 }
955
956 return 0;
957 }
958
959 return -1;
960}
961
962s32 save_game_config_file()
963{
bbba3209 964 char game_config_filename[512];
2823a4c8 965 u32 i;
966
d0944fc9 967 make_rpath(game_config_filename, sizeof(game_config_filename), ".cfg");
2823a4c8 968
969 file_open(game_config_file, game_config_filename, write);
970
971 if(file_check_valid(game_config_file))
972 {
973 u32 file_options[14];
974
975 file_options[0] = current_frameskip_type;
976 file_options[1] = frameskip_value;
977 file_options[2] = random_skip;
978 file_options[3] = clock_speed;
979
980 for(i = 0; i < 10; i++)
981 {
982 file_options[4 + i] = cheats[i].cheat_active;
983 }
984
985 file_write_array(game_config_file, file_options);
986
987 file_close(game_config_file);
988
989 return 0;
990 }
991
992 return -1;
993}
994
995s32 save_config_file()
996{
bbba3209 997 char config_path[512];
2823a4c8 998
d0944fc9 999 sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, GPSP_CONFIG_FILENAME);
2823a4c8 1000
1001 file_open(config_file, config_path, write);
1002
1003 save_game_config_file();
1004
1005 if(file_check_valid(config_file))
1006 {
e38fee1b 1007 u32 file_options[FILE_OPTION_COUNT];
2823a4c8 1008
0dfe793b 1009 file_options[fo_screen_scale] = screen_scale;
1010 file_options[fo_screen_filter] = screen_filter;
1011 file_options[fo_global_enable_audio] = global_enable_audio;
1012 file_options[fo_audio_buffer_size] = audio_buffer_size_number;
1013 file_options[fo_update_backup_flag] = update_backup_flag;
1014 file_options[fo_global_enable_analog] = global_enable_analog;
1015 file_options[fo_analog_sensitivity_level] = analog_sensitivity_level;
1016 file_options[fo_screen_filter2] = screen_filter2;
2823a4c8 1017
1018#ifndef PC_BUILD
bbba3209 1019 u32 i;
0dfe793b 1020 for(i = 0; i < PLAT_BUTTON_COUNT; i++)
2823a4c8 1021 {
0dfe793b 1022 file_options[fo_main_option_count + i] = gamepad_config_map[i];
2823a4c8 1023 }
1024#endif
1025
1026 file_write_array(config_file, file_options);
1027
1028 file_close(config_file);
1029
1030 return 0;
1031 }
1032
1033 return -1;
1034}
1035
1036typedef enum
1037{
1038 MAIN_MENU,
1039 GAMEPAD_MENU,
1040 SAVESTATE_MENU,
1041 FRAMESKIP_MENU,
1042 CHEAT_MENU
1043} menu_enum;
1044
1045u32 savestate_slot = 0;
1046
bbba3209 1047void get_savestate_snapshot(char *savestate_filename)
2823a4c8 1048{
1049 u16 snapshot_buffer[240 * 160];
bbba3209 1050 char savestate_timestamp_string[80];
2823a4c8 1051
1052 file_open(savestate_file, savestate_filename, read);
1053
1054 if(file_check_valid(savestate_file))
1055 {
bbba3209 1056 const char weekday_strings[7][11] =
2823a4c8 1057 {
1058 "Sunday", "Monday", "Tuesday", "Wednesday",
1059 "Thursday", "Friday", "Saturday"
1060 };
1061 time_t savestate_time_flat;
1062 struct tm *current_time;
1063 file_read_array(savestate_file, snapshot_buffer);
1064 file_read_variable(savestate_file, savestate_time_flat);
1065
1066 file_close(savestate_file);
1067
1068 current_time = localtime(&savestate_time_flat);
1069 sprintf(savestate_timestamp_string,
1070 "%s %02d/%02d/%04d %02d:%02d:%02d ",
1071 weekday_strings[current_time->tm_wday], current_time->tm_mon + 1,
1072 current_time->tm_mday, current_time->tm_year + 1900,
1073 current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
1074
1075 savestate_timestamp_string[40] = 0;
1076 print_string(savestate_timestamp_string, COLOR_HELP_TEXT, COLOR_BG,
1077 10, 40);
1078 }
1079 else
1080 {
1081 memset(snapshot_buffer, 0, 240 * 160 * 2);
eb3668fc 1082 print_string_ext("No savestate in this slot.",
42c81190 1083 0xFFFF, 0x0000, 15, 75, snapshot_buffer, 240, 0, 0, FONT_HEIGHT);
2823a4c8 1084 print_string("---------- --/--/---- --:--:-- ", COLOR_HELP_TEXT,
1085 COLOR_BG, 10, 40);
1086 }
1087
1088#ifndef GP2X_BUILD
1089 blit_to_screen(snapshot_buffer, 240, 160, 230, 40);
1090#endif
1091}
1092
d0944fc9 1093void get_savestate_filename_noshot(u32 slot, char *name_buffer)
2823a4c8 1094{
bbba3209 1095 char savestate_ext[16];
2823a4c8 1096
1097 sprintf(savestate_ext, "%d.svs", slot);
d0944fc9 1098 make_rpath(name_buffer, 512, savestate_ext);
2823a4c8 1099}
1100
d0944fc9 1101void get_savestate_filename(u32 slot, char *name_buffer)
2823a4c8 1102{
d0944fc9 1103 get_savestate_filename_noshot(slot, name_buffer);
1104 get_savestate_snapshot(name_buffer);
2823a4c8 1105}
1106
1107#ifdef PSP_BUILD
1108 void _flush_cache()
1109 {
1110 invalidate_all_cache();
1111 }
1112#endif
1113
1114u32 menu(u16 *original_screen)
1115{
bbba3209 1116 char print_buffer[81];
1d02ca75 1117 u32 clock_speed_number;
2823a4c8 1118 gui_action_type gui_action;
2823a4c8 1119 u32 i;
1120 u32 repeat = 1;
1121 u32 return_value = 0;
1122 u32 first_load = 0;
bbba3209 1123 char current_savestate_filename[512];
1124 char line_buffer[80];
1125 char cheat_format_str[10][41];
2823a4c8 1126
1127 menu_type *current_menu;
1128 menu_option_type *current_option;
1129 menu_option_type *display_option;
1130 u32 current_option_num;
1131
1132 auto void choose_menu();
1133 auto void clear_help();
1134
bbba3209 1135#ifndef PC_BUILD
1136 static const char * const gamepad_help[] =
2823a4c8 1137 {
1138 "Up button on GBA d-pad.",
1139 "Down button on GBA d-pad.",
1140 "Left button on GBA d-pad.",
1141 "Right button on GBA d-pad.",
1142 "A button on GBA.",
1143 "B button on GBA.",
1144 "Left shoulder button on GBA.",
1145 "Right shoulder button on GBA.",
1146 "Start button on GBA.",
1147 "Select button on GBA.",
1148 "Brings up the options menu.",
1149 "Toggles fastforward on/off.",
1150 "Loads the game state from the current slot.",
1151 "Saves the game state to the current slot.",
1152 "Rapidly press/release the A button on GBA.",
1153 "Rapidly press/release the B button on GBA.",
1154 "Rapidly press/release the L shoulder on GBA.",
1155 "Rapidly press/release the R shoulder on GBA.",
1156 "Increases the volume.",
1157 "Decreases the volume.",
1158 "Displays virtual/drawn frames per second.",
1159 "Does nothing."
1160 };
1161
bbba3209 1162 static const char *gamepad_config_buttons[] =
1163 {
1164 "UP",
1165 "DOWN",
1166 "LEFT",
1167 "RIGHT",
1168 "A",
1169 "B",
1170 "L",
1171 "R",
1172 "START",
1173 "SELECT",
1174 "MENU",
1175 "FASTFORWARD",
1176 "LOAD STATE",
1177 "SAVE STATE",
1178 "RAPIDFIRE A",
1179 "RAPIDFIRE B",
1180 "RAPIDFIRE L",
1181 "RAPIDFIRE R",
1182 "VOLUME UP",
1183 "VOLUME DOWN",
1184 "DISPLAY FPS",
1185 "NOTHING"
1186 };
1187#endif
1188
1d02ca75 1189 void menu_update_clock()
1190 {
1191 get_clock_speed_number();
1192 if (clock_speed_number < 0 || clock_speed_number >=
1193 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]))
1194 {
1195 clock_speed = default_clock_speed;
1196 get_clock_speed_number();
1197 }
1198 }
1199
2823a4c8 1200 void menu_exit()
1201 {
1202 if(!first_load)
1203 repeat = 0;
1204 }
1205
1206 void menu_quit()
1207 {
1d02ca75 1208 menu_get_clock_speed();
2823a4c8 1209 save_config_file();
1210 quit();
1211 }
1212
1213 void menu_load()
1214 {
bbba3209 1215 const char *file_ext[] = { ".gba", ".bin", ".zip", NULL };
1216 char load_filename[512];
2823a4c8 1217 save_game_config_file();
1218 if(load_file(file_ext, load_filename) != -1)
1219 {
1220 if(load_gamepak(load_filename) == -1)
1221 {
1222 quit();
1223 }
1224 reset_gba();
1225 return_value = 1;
1226 repeat = 0;
1227 reg[CHANGED_PC_STATUS] = 1;
1d02ca75 1228 menu_update_clock();
2823a4c8 1229 }
2823a4c8 1230 }
1231
1232 void menu_restart()
1233 {
1234 if(!first_load)
1235 {
1236 reset_gba();
1237 reg[CHANGED_PC_STATUS] = 1;
1238 return_value = 1;
1239 repeat = 0;
1240 }
1241 }
1242
1243 void menu_change_state()
1244 {
1245 get_savestate_filename(savestate_slot, current_savestate_filename);
1246 }
1247
1248 void menu_save_state()
1249 {
1250 if(!first_load)
1251 {
1252 get_savestate_filename_noshot(savestate_slot,
1253 current_savestate_filename);
1254 save_state(current_savestate_filename, original_screen);
1255 }
1256 menu_change_state();
1257 }
1258
1259 void menu_load_state()
1260 {
1261 if(!first_load)
1262 {
1263 load_state(current_savestate_filename);
1264 return_value = 1;
1265 repeat = 0;
1266 }
1267 }
1268
1269 void menu_load_state_file()
1270 {
bbba3209 1271 const char *file_ext[] = { ".svs", NULL };
1272 char load_filename[512];
2823a4c8 1273 if(load_file(file_ext, load_filename) != -1)
1274 {
1275 load_state(load_filename);
1276 return_value = 1;
1277 repeat = 0;
1278 }
1279 else
1280 {
1281 choose_menu(current_menu);
1282 }
1283 }
1284
1285 void menu_fix_gamepad_help()
1286 {
1287#ifndef PC_BUILD
1288 clear_help();
1289 current_option->help_string =
1290 gamepad_help[gamepad_config_map[
1291 gamepad_config_line_to_button[current_option_num]]];
1292#endif
1293 }
1294
1295 void submenu_graphics_sound()
1296 {
1297
1298 }
1299
1300 void submenu_cheats_misc()
1301 {
1302
1303 }
1304
1305 void submenu_gamepad()
1306 {
1307
1308 }
1309
1310 void submenu_analog()
1311 {
1312
1313 }
1314
1315 void submenu_savestate()
1316 {
1317 print_string("Savestate options:", COLOR_ACTIVE_ITEM, COLOR_BG, 10, 70);
1318 menu_change_state();
1319 }
1320
1321 void submenu_main()
1322 {
1323 strncpy(print_buffer, gamepak_filename, 80);
1324 print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 10);
1325 sprintf(print_buffer, "%s %s %s", gamepak_title,
1326 gamepak_code, gamepak_maker);
1327 print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 20);
1328
1329 get_savestate_filename_noshot(savestate_slot,
1330 current_savestate_filename);
1331 }
1332
bbba3209 1333 const char *yes_no_options[] = { "no", "yes" };
1334 const char *enable_disable_options[] = { "disabled", "enabled" };
2823a4c8 1335
bbba3209 1336 const char *frameskip_options[] = { "automatic", "manual", "off" };
1337 const char *frameskip_variation_options[] = { "uniform", "random" };
2823a4c8 1338
bbba3209 1339 static const char *update_backup_options[] = { "Exit only", "Automatic" };
2823a4c8 1340
1341 // Marker for help information, don't go past this mark (except \n)------*
ffa573f8
D
1342 menu_option_type graphics_sound_options[] =
1343 {
1344#ifndef RPI_BUILD
2823a4c8 1345 string_selection_option(NULL, "Display scaling", scale_options,
4cdfc0bc 1346 (u32 *)(&screen_scale),
42c81190 1347 sizeof(scale_options) / sizeof(scale_options[0]),
90206450 1348#ifndef GP2X_BUILD
eb3668fc 1349 "Determines how the GBA screen is resized in relation to the\n"
1350 "entire screen."
1351#ifdef PSP_BUILD
1352 " Select unscaled 3:2 for GBA resolution, scaled 3:2 for GBA\n"
2823a4c8 1353 "aspect ratio scaled to fill the height of the PSP screen, and\n"
90206450 1354 "fullscreen to fill the entire PSP screen."
eb3668fc 1355#endif
90206450 1356#endif
1357 "", 2),
ffa573f8
D
1358#endif
1359
90206450 1360#ifndef GP2X_BUILD
2823a4c8 1361 string_selection_option(NULL, "Screen filtering", yes_no_options,
1362 (u32 *)(&screen_filter), 2,
eb3668fc 1363 "Determines whether or not filtering should be used when\n"
2823a4c8 1364 "scaling the screen. Selecting this will produce a more even and\n"
1365 "smooth image, at the cost of being blurry and having less vibrant\n"
1366 "colors.", 3),
e38fee1b 1367#endif
ffa573f8 1368#if defined (PND_BUILD)
e38fee1b 1369 string_selection_option(NULL, "Scaling filter", filter2_options,
1370 (u32 *)(&screen_filter2),
1371 sizeof(filter2_options) / sizeof(filter2_options[0]),
1372 "Optional pixel art scaling filter", 4),
90206450 1373#endif
2823a4c8 1374 string_selection_option(NULL, "Frameskip type", frameskip_options,
1375 (u32 *)(&current_frameskip_type), 3,
90206450 1376#ifndef GP2X_BUILD
4cdfc0bc 1377 "Determines what kind of frameskipping to use.\n"
2823a4c8 1378 "Frameskipping may improve emulation speed of many games.\n"
4cdfc0bc 1379#endif
2823a4c8 1380 "Off: Do not skip any frames.\n"
4cdfc0bc 1381 "Auto: Skip up to N frames (see next opt) as needed.\n"
90206450 1382 "Manual: Always render only 1 out of N + 1 frames."
4cdfc0bc 1383 , 5),
2823a4c8 1384 numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100,
90206450 1385#ifndef GP2X_BUILD
2823a4c8 1386 "For auto frameskip, determines the maximum number of frames that\n"
1387 "are allowed to be skipped consecutively.\n"
1388 "For manual frameskip, determines the number of frames that will\n"
90206450 1389 "always be skipped."
1390#endif
1391 "", 6),
2823a4c8 1392 string_selection_option(NULL, "Framskip variation",
1393 frameskip_variation_options, &random_skip, 2,
90206450 1394#ifndef GP2X_BUILD
2823a4c8 1395 "If objects in the game flicker at a regular rate certain manual\n"
1396 "frameskip values may cause them to normally disappear. Change this\n"
eb3668fc 1397 "value to 'random' to avoid this. Do not use otherwise, as it tends\n"
1398 "to make the image quality worse, especially in high motion games."
90206450 1399#endif
1400 "", 7),
2823a4c8 1401 string_selection_option(NULL, "Audio output", yes_no_options,
1402 &global_enable_audio, 2,
90206450 1403 "Select 'no' to turn off all audio output. This will\n"
1404 "not result in a significant change in performance.", 9),
2823a4c8 1405#ifndef PSP_BUILD
1406 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1407 &audio_buffer_size_number, 11,
1408#else
1409 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1410 &audio_buffer_size_number, 10,
1411#endif
1412
4742480d 1413#ifdef PSP_BUILD
2823a4c8 1414 "Set the size (in bytes) of the audio buffer. Larger values may result\n"
1415 "in slightly better performance at the cost of latency; the lowest\n"
1416 "value will give the most responsive audio.\n"
1417 "This option requires gpSP to be restarted before it will take effect.",
4742480d 1418#else
1419 "Set the size (in bytes) of the audio buffer.\n"
e38fee1b 1420 "This option requires gpSP restart to take effect.\n"
1421 "Settable values may be limited by SDL implementation.",
4742480d 1422#endif
2823a4c8 1423 10),
1424 submenu_option(NULL, "Back", "Return to the main menu.", 12)
1425 };
1426
1427 make_menu(graphics_sound, submenu_graphics_sound, NULL);
1428
1429 menu_option_type cheats_misc_options[] =
1430 {
1431 cheat_option(0),
1432 cheat_option(1),
1433 cheat_option(2),
1434 cheat_option(3),
1435 cheat_option(4),
1436 cheat_option(5),
1437 cheat_option(6),
1438 cheat_option(7),
1439 cheat_option(8),
1440 cheat_option(9),
eb3668fc 1441#if defined(PSP_BUILD) || defined(GP2X_BUILD)
2823a4c8 1442 string_selection_option(NULL, "Clock speed",
90206450 1443 clock_speed_options, &clock_speed_number,
638cc626 1444 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]),
90206450 1445 "Change the clock speed of the device. Higher clock\n"
1446 "speed will yield better performance, but will drain\n"
1447 "battery life further.", 11),
eb3668fc 1448#endif
2823a4c8 1449 string_selection_option(NULL, "Update backup",
1450 update_backup_options, &update_backup_flag, 2,
90206450 1451#ifdef GP2X_BUILD
1452 "Determines when in-game save files should be\n"
eb3668fc 1453 "written back to SD card."
90206450 1454#else
2823a4c8 1455 "Determines when in-game save files should be written back to\n"
eb3668fc 1456 "card. If set to 'automatic' writebacks will occur shortly after\n"
1457 "the game's backup is altered. On 'exit only' it will only be\n"
1458 "written back when you exit from this menu.\n"
1459#ifdef PSP
1460 "(NOT from using the home button), use the latter with extreme care."
1461#endif
90206450 1462#endif
eb3668fc 1463 "", 12),
2823a4c8 1464 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1465 };
1466
1467 make_menu(cheats_misc, submenu_cheats_misc, NULL);
1468
1469 menu_option_type savestate_options[] =
1470 {
1471 numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1472 "Load savestate from current slot", &savestate_slot, 10,
90206450 1473 "Select to load the game state from the current slot\n"
1474 "for this game.\n"
2823a4c8 1475 "Press left + right to change the current slot.", 6),
1476 numeric_selection_action_hide_option(menu_save_state, menu_change_state,
1477 "Save savestate to current slot", &savestate_slot, 10,
90206450 1478 "Select to save the game state to the current slot\n"
1479 "for this game.\n"
2823a4c8 1480 "Press left + right to change the current slot.", 7),
1481 numeric_selection_action_hide_option(menu_load_state_file,
1482 menu_change_state,
1483 "Load savestate from file", &savestate_slot, 10,
1484 "Restore gameplay from a savestate file.\n"
90206450 1485 "Note: The same file used to save the state must be\n"
1486 "present.\n", 9),
2823a4c8 1487 numeric_selection_option(menu_change_state,
1488 "Current savestate slot", &savestate_slot, 10,
1489 "Change the current savestate slot.\n", 11),
1490 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1491 };
1492
1493 make_menu(savestate, submenu_savestate, NULL);
1494
1495#ifdef PSP_BUILD
1496
1497 menu_option_type gamepad_config_options[] =
1498 {
1499 gamepad_config_option("D-pad up ", 0),
1500 gamepad_config_option("D-pad down ", 1),
1501 gamepad_config_option("D-pad left ", 2),
1502 gamepad_config_option("D-pad right ", 3),
1503 gamepad_config_option("Circle ", 4),
1504 gamepad_config_option("Cross ", 5),
1505 gamepad_config_option("Square ", 6),
1506 gamepad_config_option("Triangle ", 7),
1507 gamepad_config_option("Left Trigger ", 8),
1508 gamepad_config_option("Right Trigger", 9),
1509 gamepad_config_option("Start ", 10),
1510 gamepad_config_option("Select ", 11),
1511 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1512 };
1513
1514
1515 menu_option_type analog_config_options[] =
1516 {
1517 analog_config_option("Analog up ", 0),
1518 analog_config_option("Analog down ", 1),
1519 analog_config_option("Analog left ", 2),
1520 analog_config_option("Analog right", 3),
1521 string_selection_option(NULL, "Enable analog", yes_no_options,
1522 &global_enable_analog, 2,
1523 "Select 'no' to block analog input entirely.", 7),
1524 numeric_selection_option(NULL, "Analog sensitivity",
1525 &analog_sensitivity_level, 10,
1526 "Determine sensitivity/responsiveness of the analog input.\n"
1527 "Lower numbers are less sensitive.", 8),
1528 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1529 };
1530
1531#endif
1532
eb3668fc 1533#if defined(GP2X_BUILD) || defined(PND_BUILD)
2823a4c8 1534
1535 menu_option_type gamepad_config_options[] =
1536 {
1537 gamepad_config_option("D-pad up ", 0),
1538 gamepad_config_option("D-pad down ", 1),
1539 gamepad_config_option("D-pad left ", 2),
1540 gamepad_config_option("D-pad right ", 3),
1541 gamepad_config_option("A ", 4),
1542 gamepad_config_option("B ", 5),
1543 gamepad_config_option("X ", 6),
1544 gamepad_config_option("Y ", 7),
1545 gamepad_config_option("Left Trigger ", 8),
1546 gamepad_config_option("Right Trigger", 9),
4cdfc0bc 1547#ifdef WIZ_BUILD
1548 gamepad_config_option("Menu ", 10),
108c704a 1549 gamepad_config_option("Select ", 11),
1550#elif defined(POLLUX_BUILD)
1551 gamepad_config_option("I ", 10),
1552 gamepad_config_option("II ", 11),
1553 gamepad_config_option("Push ", 12),
1554 gamepad_config_option("Home ", 13),
1555#elif defined(PND_BUILD)
2823a4c8 1556 gamepad_config_option("Start ", 10),
1557 gamepad_config_option("Select ", 11),
eb3668fc 1558 gamepad_config_option("1 ", 12),
1559 gamepad_config_option("2 ", 13),
1560 gamepad_config_option("3 ", 14),
1561 gamepad_config_option("4 ", 15),
108c704a 1562#else // GP2X
1563 gamepad_config_option("Start ", 10),
1564 gamepad_config_option("Select ", 11),
1565 gamepad_config_option("Stick Push ", 12),
1566#endif
1567#ifdef PND_BUILD
eb3668fc 1568 submenu_option(NULL, "Back", "Return to the main menu.", 16)
1569#else
90206450 1570 submenu_option(NULL, "Back", "Return to the main menu.", 14)
eb3668fc 1571#endif
2823a4c8 1572 };
1573
1574
1575 menu_option_type analog_config_options[] =
1576 {
108c704a 1577#if defined(POLLUX_BUILD)
1578 numeric_selection_option(NULL, "Analog sensitivity",
1579 &analog_sensitivity_level, 10,
1580 "Determine sensitivity/responsiveness of the analog input.\n"
1581 "Lower numbers are less sensitive.", 8),
1582#endif
2823a4c8 1583 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1584 };
1585
1586#endif
1587
ffa573f8 1588#if defined(PC_BUILD) || defined(RPI_BUILD)
2823a4c8 1589
1590 menu_option_type gamepad_config_options[] =
1591 {
1592 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1593 };
1594
1595 menu_option_type analog_config_options[] =
1596 {
1597 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1598 };
1599
1600#endif
1601
1602 make_menu(gamepad_config, submenu_gamepad, NULL);
1603 make_menu(analog_config, submenu_analog, NULL);
1604
1605 menu_option_type main_options[] =
1606 {
1607 submenu_option(&graphics_sound_menu, "Graphics and Sound options",
90206450 1608 "Select to set display parameters and frameskip\n"
1609 "behavior, audio on/off, buffer size, and filtering.", 0),
2823a4c8 1610 numeric_selection_action_option(menu_load_state, NULL,
1611 "Load state from slot", &savestate_slot, 10,
90206450 1612 "Select to load the game state from the current slot\n"
1613 "for this game, if it exists.\n"
2823a4c8 1614 "Press left + right to change the current slot.", 2),
1615 numeric_selection_action_option(menu_save_state, NULL,
1616 "Save state to slot", &savestate_slot, 10,
90206450 1617 "Select to save the game state to the current slot\n"
1618 "for this game. See the extended menu for more info.\n"
2823a4c8 1619 "Press left + right to change the current slot.", 3),
1620 submenu_option(&savestate_menu, "Savestate options",
90206450 1621 "Select to enter a menu for loading, saving, and\n"
1622 "viewing the currently active savestate for this game\n"
1623 "(or to load a savestate file from another game)", 4),
2823a4c8 1624 submenu_option(&gamepad_config_menu, "Configure gamepad input",
90206450 1625 "Select to change the in-game behavior of buttons\n"
1626 "and d-pad.", 6),
108c704a 1627#ifndef WIZ_BUILD
2823a4c8 1628 submenu_option(&analog_config_menu, "Configure analog input",
108c704a 1629 "Select to change the in-game behavior of the analog nub.", 7),
90206450 1630#endif
2823a4c8 1631 submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
90206450 1632 "Select to manage cheats, set backup behavior,\n"
1633 "and set device clock speed.", 9),
2823a4c8 1634 action_option(menu_load, NULL, "Load new game",
90206450 1635 "Select to load a new game\n"
1636 "(will exit a game if currently playing).", 11),
2823a4c8 1637 action_option(menu_restart, NULL, "Restart game",
90206450 1638 "Select to reset the GBA with the current game\n"
1639 "loaded.", 12),
2823a4c8 1640 action_option(menu_exit, NULL, "Return to game",
1641 "Select to exit this menu and resume gameplay.", 13),
1642 action_option(menu_quit, NULL, "Exit gpSP",
90206450 1643 "Select to exit gpSP and return to the menu.", 15)
2823a4c8 1644 };
1645
1646 make_menu(main, submenu_main, NULL);
1647
1648 void choose_menu(menu_type *new_menu)
1649 {
1650 if(new_menu == NULL)
1651 new_menu = &main_menu;
1652
1653 clear_screen(COLOR_BG);
1654
1655#ifndef GP2X_BUILD
1656 blit_to_screen(original_screen, 240, 160, 230, 40);
1657#endif
1658
1659 current_menu = new_menu;
1660 current_option = new_menu->options;
1661 current_option_num = 0;
1662 if(current_menu->init_function)
1663 current_menu->init_function();
1664 }
1665
1666 void clear_help()
1667 {
1668 for(i = 0; i < 6; i++)
1669 {
90206450 1670 print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
2823a4c8 1671 }
1672 }
1673
1d02ca75 1674 menu_update_clock();
2823a4c8 1675 video_resolution_large();
1676
1677#ifndef GP2X_BUILD
1678 SDL_LockMutex(sound_mutex);
1679#endif
1680 SDL_PauseAudio(1);
1681
1682#ifndef GP2X_BUILD
1683 SDL_UnlockMutex(sound_mutex);
1684#endif
1685
1686 if(gamepak_filename[0] == 0)
1687 {
1688 first_load = 1;
1689 memset(original_screen, 0x00, 240 * 160 * 2);
1690 print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
42c81190 1691 60, 75,original_screen, 240, 0, 0, FONT_HEIGHT);
2823a4c8 1692 }
1693
1694 choose_menu(&main_menu);
1695
1696 for(i = 0; i < 10; i++)
1697 {
1698 if(i >= num_cheats)
1699 {
1700 sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1701 }
1702 else
1703 {
1704 sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1705 cheats[i].cheat_name);
1706 }
1707 }
1708
1709 current_menu->init_function();
1710
1711 while(repeat)
1712 {
1713 display_option = current_menu->options;
1714
1715 for(i = 0; i < current_menu->num_options; i++, display_option++)
1716 {
1717 if(display_option->option_type & NUMBER_SELECTION_OPTION)
1718 {
1719 sprintf(line_buffer, display_option->display_string,
1720 *(display_option->current_option));
1721 }
1722 else
1723
1724 if(display_option->option_type & STRING_SELECTION_OPTION)
1725 {
1726 sprintf(line_buffer, display_option->display_string,
1727 ((u32 *)display_option->options)[*(display_option->current_option)]);
1728 }
1729 else
1730 {
1731 strcpy(line_buffer, display_option->display_string);
1732 }
1733
1734 if(display_option == current_option)
1735 {
eb3668fc 1736 print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 6,
1737 (display_option->line_number * 10) + 40, 36);
2823a4c8 1738 }
1739 else
1740 {
eb3668fc 1741 print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 6,
1742 (display_option->line_number * 10) + 40, 36);
2823a4c8 1743 }
1744 }
1745
1746 print_string(current_option->help_string, COLOR_HELP_TEXT,
90206450 1747 COLOR_BG, 8, 210);
2823a4c8 1748
1749 flip_screen();
1750
1751 gui_action = get_gui_input();
1752
1753 switch(gui_action)
1754 {
1755 case CURSOR_DOWN:
1756 current_option_num = (current_option_num + 1) %
1757 current_menu->num_options;
1758
1759 current_option = current_menu->options + current_option_num;
1760 clear_help();
1761 break;
1762
1763 case CURSOR_UP:
1764 if(current_option_num)
1765 current_option_num--;
1766 else
1767 current_option_num = current_menu->num_options - 1;
1768
1769 current_option = current_menu->options + current_option_num;
1770 clear_help();
1771 break;
1772
1773 case CURSOR_RIGHT:
1774 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1775 STRING_SELECTION_OPTION))
1776 {
1777 *(current_option->current_option) =
1778 (*current_option->current_option + 1) %
1779 current_option->num_options;
1780
1781 if(current_option->passive_function)
1782 current_option->passive_function();
1783 }
1784 break;
1785
1786 case CURSOR_LEFT:
1787 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1788 STRING_SELECTION_OPTION))
1789 {
1790 u32 current_option_val = *(current_option->current_option);
1791
1792 if(current_option_val)
1793 current_option_val--;
1794 else
1795 current_option_val = current_option->num_options - 1;
1796
1797 *(current_option->current_option) = current_option_val;
1798
1799 if(current_option->passive_function)
1800 current_option->passive_function();
1801 }
1802 break;
1803
1804 case CURSOR_EXIT:
1805 if(current_menu == &main_menu)
1806 menu_exit();
1807
1808 choose_menu(&main_menu);
1809 break;
1810
1811 case CURSOR_SELECT:
1812 if(current_option->option_type & ACTION_OPTION)
1813 current_option->action_function();
1814
1815 if(current_option->option_type & SUBMENU_OPTION)
1816 choose_menu(current_option->sub_menu);
ffa573f8
D
1817
1818 if(current_menu == &main_menu)
1819 choose_menu(&main_menu);
1820
2823a4c8 1821 break;
bbba3209 1822
1823 default:
1824 break;
2823a4c8 1825 }
1826 }
1827
1828 set_gba_resolution(screen_scale);
1829 video_resolution_small();
1d02ca75 1830 menu_get_clock_speed();
1831 set_clock_speed();
2823a4c8 1832
1833 SDL_PauseAudio(0);
2455b6a3 1834 num_skipped_frames = 100;
2823a4c8 1835
1836 return return_value;
1837}