u8/pandora release
[gpsp.git] / gui.c
... / ...
CommitLineData
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
19#include "common.h"
20#include "font.h"
21
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
31#define MAX_PATH 1024
32
33// Blatantly stolen and trimmed from MZX (megazeux.sourceforge.net)
34
35#ifdef GP2X_BUILD
36
37#define FILE_LIST_ROWS 20
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
45#define DIR_LIST_POSITION (resolution_width * 3 / 4)
46
47#endif
48
49#ifdef PSP_BUILD
50
51#define COLOR_BG color16(2, 8, 10)
52
53#define color16(red, green, blue) \
54 (blue << 11) | (green << 5) | red \
55
56#else
57
58#define COLOR_BG color16(0, 0, 0)
59
60#define color16(red, green, blue) \
61 (red << 11) | (green << 5) | blue \
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
71#ifdef PSP_BUILD
72 static const char *clock_speed_options[] =
73 {
74 "33MHz", "66MHz", "100MHz", "133MHz", "166MHz", "200MHz", "233MHz",
75 "266MHz", "300MHz", "333MHz"
76 };
77 #define menu_get_clock_speed() \
78 clock_speed = (clock_speed_number + 1) * 33
79 #define get_clock_speed_number() \
80 clock_speed_number = (clock_speed / 33) - 1
81#elif defined(WIZ_BUILD)
82 static const char *clock_speed_options[] =
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() \
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)
94 static const char *clock_speed_options[] =
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() \
101 clock_speed = 150 + clock_speed_number * 10
102 #define get_clock_speed_number() \
103 clock_speed_number = (clock_speed - 150) / 10
104#else
105 static const char *clock_speed_options[] =
106 {
107 "0"
108 };
109 #define menu_get_clock_speed()
110 #define get_clock_speed_number()
111#endif
112
113
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
128s32 load_file(const char **wildcards, char *result)
129{
130 DIR *current_dir;
131 struct dirent *current_file;
132 struct stat file_info;
133 char current_dir_name[MAX_PATH];
134 char current_dir_short[81];
135 u32 current_dir_length;
136 u32 total_filenames_allocated;
137 u32 total_dirnames_allocated;
138 char **file_list;
139 char **dir_list;
140 u32 num_files;
141 u32 num_dirs;
142 char *file_name;
143 u32 file_name_length;
144 u32 ext_pos = -1;
145 u32 chosen_file, chosen_dir;
146 s32 return_value = 1;
147 s32 current_file_selection;
148 s32 current_file_scroll_value;
149 u32 current_dir_selection;
150 u32 current_dir_scroll_value;
151 s32 current_file_in_scroll;
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;
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);
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 {
201 dir_list[num_dirs] = malloc(file_name_length + 1);
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] =
228 malloc(file_name_length + 1);
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 {
242 file_list = (char **)realloc(file_list, sizeof(char *) *
243 total_filenames_allocated * 2);
244 memset(file_list + total_filenames_allocated, 0,
245 sizeof(char *) * total_filenames_allocated);
246 total_filenames_allocated *= 2;
247 }
248
249 if(num_dirs == total_dirnames_allocated)
250 {
251 dir_list = (char **)realloc(dir_list, sizeof(char *) *
252 total_dirnames_allocated * 2);
253 memset(dir_list + total_dirnames_allocated, 0,
254 sizeof(char *) * total_dirnames_allocated);
255 total_dirnames_allocated *= 2;
256 }
257 }
258 } while(current_file);
259
260 qsort((void *)file_list, num_files, sizeof(char *), sort_function);
261 qsort((void *)dir_list, num_dirs, sizeof(char *), sort_function);
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 {
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 }
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 }
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
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
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 }
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 }
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
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:
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;
525
526 default:
527 break;
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,
555 ACTION_OPTION = 0x08
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;
571 const char *display_string;
572 void *options;
573 u32 *current_option;
574 u32 num_options;
575 const char *help_string;
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[] =
738 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 14 };
739
740#endif
741
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
749static const char *scale_options[] =
750{
751#ifdef PSP_BUILD
752 "unscaled 3:2", "scaled 3:2", "fullscreen 16:9"
753#elif defined(WIZ_BUILD)
754 "unscaled 3:2", "scaled 3:2 (slower)",
755 "unscaled 3:2 (anti-tear)", "scaled 3:2 (anti-tear)"
756#elif defined(PND_BUILD)
757 "unscaled", "2x", "3x", "fullscreen"
758#elif defined(GP2X_BUILD)
759 "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)"
760#else
761 "unscaled 3:2"
762#endif
763};
764
765const char *filter2_options[] =
766{
767 "none", "scale2x", "scale3x", "eagle2x"
768};
769
770s32 load_game_config_file()
771{
772 char game_config_filename[512];
773 u32 file_loaded = 0;
774 u32 i;
775 make_rpath(game_config_filename, sizeof(game_config_filename), ".cfg");
776
777 file_open(game_config_file, game_config_filename, read);
778
779 if(file_check_valid(game_config_file))
780 {
781 u32 file_size = file_length(game_config_filename, game_config_file);
782
783 // Sanity check: File size must be the right size
784 if(file_size == 56)
785 {
786 u32 file_options[file_size / 4];
787
788 file_read_array(game_config_file, file_options);
789 current_frameskip_type = file_options[0] % 3;
790 frameskip_value = file_options[1];
791 random_skip = file_options[2] % 2;
792 clock_speed = file_options[3];
793
794#ifdef WIZ_BUILD
795 if(clock_speed > 900)
796 clock_speed = 533;
797#elif defined(GP2X_BUILD)
798 if(clock_speed >= 300)
799 clock_speed = 200;
800#else
801 if(clock_speed > 333)
802 clock_speed = 333;
803#endif
804
805 if(clock_speed < 33)
806 clock_speed = 33;
807
808 if(frameskip_value < 0)
809 frameskip_value = 0;
810
811 if(frameskip_value > 99)
812 frameskip_value = 99;
813
814 for(i = 0; i < 10; i++)
815 {
816 cheats[i].cheat_active = file_options[4 + i] % 2;
817 cheats[i].cheat_name[0] = 0;
818 }
819
820 file_close(game_config_file);
821 file_loaded = 1;
822 }
823 }
824
825 if(file_loaded)
826 return 0;
827
828 current_frameskip_type = auto_frameskip;
829 frameskip_value = 4;
830#ifdef WIZ_BUILD
831 frameskip_value = 1;
832#endif
833 random_skip = 0;
834 clock_speed = default_clock_speed;
835
836 for(i = 0; i < 10; i++)
837 {
838 cheats[i].cheat_active = 0;
839 cheats[i].cheat_name[0] = 0;
840 }
841
842 return -1;
843}
844
845#define FILE_OPTION_COUNT 24
846
847s32 load_config_file()
848{
849 char config_path[512];
850
851 sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, GPSP_CONFIG_FILENAME);
852
853 file_open(config_file, config_path, read);
854
855 if(file_check_valid(config_file))
856 {
857 u32 file_size = file_length(config_path, config_file);
858
859 // Sanity check: File size must be the right size
860 if(file_size == FILE_OPTION_COUNT * 4)
861 {
862 u32 file_options[file_size / 4];
863 file_read_array(config_file, file_options);
864
865 screen_scale = file_options[0] %
866 (sizeof(scale_options) / sizeof(scale_options[0]));
867 screen_filter = file_options[1] % 2;
868 global_enable_audio = file_options[2] % 2;
869 screen_filter2 = file_options[23] %
870 (sizeof(filter2_options) / sizeof(filter2_options[0]));
871
872#ifdef PSP_BUILD
873 audio_buffer_size_number = file_options[3] % 10;
874#else
875 audio_buffer_size_number = file_options[3] % 11;
876#endif
877
878 update_backup_flag = file_options[4] % 2;
879 global_enable_analog = file_options[5] % 2;
880 analog_sensitivity_level = file_options[6] % 8;
881
882#ifdef PSP_BUILD
883 scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
884#endif
885
886 // Sanity check: Make sure there's a MENU or FRAMESKIP
887 // key, if not assign to triangle
888
889#ifndef PC_BUILD
890 u32 i;
891 s32 menu_button = -1;
892 for(i = 0; i < 16; i++)
893 {
894 gamepad_config_map[i] = file_options[7 + i] %
895 (BUTTON_ID_NONE + 1);
896
897 if(gamepad_config_map[i] == BUTTON_ID_MENU)
898 {
899 menu_button = i;
900 }
901 }
902
903 if(menu_button == -1)
904 {
905 gamepad_config_map[0] = BUTTON_ID_MENU;
906 }
907#endif
908
909 file_close(config_file);
910 }
911
912 return 0;
913 }
914
915 return -1;
916}
917
918s32 save_game_config_file()
919{
920 char game_config_filename[512];
921 u32 i;
922
923 make_rpath(game_config_filename, sizeof(game_config_filename), ".cfg");
924
925 file_open(game_config_file, game_config_filename, write);
926
927 if(file_check_valid(game_config_file))
928 {
929 u32 file_options[14];
930
931 file_options[0] = current_frameskip_type;
932 file_options[1] = frameskip_value;
933 file_options[2] = random_skip;
934 file_options[3] = clock_speed;
935
936 for(i = 0; i < 10; i++)
937 {
938 file_options[4 + i] = cheats[i].cheat_active;
939 }
940
941 file_write_array(game_config_file, file_options);
942
943 file_close(game_config_file);
944
945 return 0;
946 }
947
948 return -1;
949}
950
951s32 save_config_file()
952{
953 char config_path[512];
954
955 sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, GPSP_CONFIG_FILENAME);
956
957 file_open(config_file, config_path, write);
958
959 save_game_config_file();
960
961 if(file_check_valid(config_file))
962 {
963 u32 file_options[FILE_OPTION_COUNT];
964
965 file_options[0] = screen_scale;
966 file_options[1] = screen_filter;
967 file_options[2] = global_enable_audio;
968 file_options[3] = audio_buffer_size_number;
969 file_options[4] = update_backup_flag;
970 file_options[5] = global_enable_analog;
971 file_options[6] = analog_sensitivity_level;
972 file_options[23] = screen_filter2;
973
974#ifndef PC_BUILD
975 u32 i;
976 for(i = 0; i < 16; i++)
977 {
978 file_options[7 + i] = gamepad_config_map[i];
979 }
980#endif
981
982 file_write_array(config_file, file_options);
983
984 file_close(config_file);
985
986 return 0;
987 }
988
989 return -1;
990}
991
992typedef enum
993{
994 MAIN_MENU,
995 GAMEPAD_MENU,
996 SAVESTATE_MENU,
997 FRAMESKIP_MENU,
998 CHEAT_MENU
999} menu_enum;
1000
1001u32 savestate_slot = 0;
1002
1003void get_savestate_snapshot(char *savestate_filename)
1004{
1005 u16 snapshot_buffer[240 * 160];
1006 char savestate_timestamp_string[80];
1007
1008 file_open(savestate_file, savestate_filename, read);
1009
1010 if(file_check_valid(savestate_file))
1011 {
1012 const char weekday_strings[7][11] =
1013 {
1014 "Sunday", "Monday", "Tuesday", "Wednesday",
1015 "Thursday", "Friday", "Saturday"
1016 };
1017 time_t savestate_time_flat;
1018 struct tm *current_time;
1019 file_read_array(savestate_file, snapshot_buffer);
1020 file_read_variable(savestate_file, savestate_time_flat);
1021
1022 file_close(savestate_file);
1023
1024 current_time = localtime(&savestate_time_flat);
1025 sprintf(savestate_timestamp_string,
1026 "%s %02d/%02d/%04d %02d:%02d:%02d ",
1027 weekday_strings[current_time->tm_wday], current_time->tm_mon + 1,
1028 current_time->tm_mday, current_time->tm_year + 1900,
1029 current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
1030
1031 savestate_timestamp_string[40] = 0;
1032 print_string(savestate_timestamp_string, COLOR_HELP_TEXT, COLOR_BG,
1033 10, 40);
1034 }
1035 else
1036 {
1037 memset(snapshot_buffer, 0, 240 * 160 * 2);
1038 print_string_ext("No savestate in this slot.",
1039 0xFFFF, 0x0000, 15, 75, snapshot_buffer, 240, 0, 0, FONT_HEIGHT);
1040 print_string("---------- --/--/---- --:--:-- ", COLOR_HELP_TEXT,
1041 COLOR_BG, 10, 40);
1042 }
1043
1044#ifndef GP2X_BUILD
1045 blit_to_screen(snapshot_buffer, 240, 160, 230, 40);
1046#endif
1047}
1048
1049void get_savestate_filename_noshot(u32 slot, char *name_buffer)
1050{
1051 char savestate_ext[16];
1052
1053 sprintf(savestate_ext, "%d.svs", slot);
1054 make_rpath(name_buffer, 512, savestate_ext);
1055}
1056
1057void get_savestate_filename(u32 slot, char *name_buffer)
1058{
1059 get_savestate_filename_noshot(slot, name_buffer);
1060 get_savestate_snapshot(name_buffer);
1061}
1062
1063#ifdef PSP_BUILD
1064 void _flush_cache()
1065 {
1066 invalidate_all_cache();
1067 }
1068#endif
1069
1070u32 menu(u16 *original_screen)
1071{
1072 char print_buffer[81];
1073 u32 clock_speed_number;
1074 gui_action_type gui_action;
1075 u32 i;
1076 u32 repeat = 1;
1077 u32 return_value = 0;
1078 u32 first_load = 0;
1079 char current_savestate_filename[512];
1080 char line_buffer[80];
1081 char cheat_format_str[10][41];
1082
1083 menu_type *current_menu;
1084 menu_option_type *current_option;
1085 menu_option_type *display_option;
1086 u32 current_option_num;
1087
1088 auto void choose_menu();
1089 auto void clear_help();
1090
1091#ifndef PC_BUILD
1092 static const char * const gamepad_help[] =
1093 {
1094 "Up button on GBA d-pad.",
1095 "Down button on GBA d-pad.",
1096 "Left button on GBA d-pad.",
1097 "Right button on GBA d-pad.",
1098 "A button on GBA.",
1099 "B button on GBA.",
1100 "Left shoulder button on GBA.",
1101 "Right shoulder button on GBA.",
1102 "Start button on GBA.",
1103 "Select button on GBA.",
1104 "Brings up the options menu.",
1105 "Toggles fastforward on/off.",
1106 "Loads the game state from the current slot.",
1107 "Saves the game state to the current slot.",
1108 "Rapidly press/release the A button on GBA.",
1109 "Rapidly press/release the B button on GBA.",
1110 "Rapidly press/release the L shoulder on GBA.",
1111 "Rapidly press/release the R shoulder on GBA.",
1112 "Increases the volume.",
1113 "Decreases the volume.",
1114 "Displays virtual/drawn frames per second.",
1115 "Does nothing."
1116 };
1117
1118 static const char *gamepad_config_buttons[] =
1119 {
1120 "UP",
1121 "DOWN",
1122 "LEFT",
1123 "RIGHT",
1124 "A",
1125 "B",
1126 "L",
1127 "R",
1128 "START",
1129 "SELECT",
1130 "MENU",
1131 "FASTFORWARD",
1132 "LOAD STATE",
1133 "SAVE STATE",
1134 "RAPIDFIRE A",
1135 "RAPIDFIRE B",
1136 "RAPIDFIRE L",
1137 "RAPIDFIRE R",
1138 "VOLUME UP",
1139 "VOLUME DOWN",
1140 "DISPLAY FPS",
1141 "NOTHING"
1142 };
1143#endif
1144
1145 void menu_update_clock()
1146 {
1147 get_clock_speed_number();
1148 if (clock_speed_number < 0 || clock_speed_number >=
1149 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]))
1150 {
1151 clock_speed = default_clock_speed;
1152 get_clock_speed_number();
1153 }
1154 }
1155
1156 void menu_exit()
1157 {
1158 if(!first_load)
1159 repeat = 0;
1160 }
1161
1162 void menu_quit()
1163 {
1164 menu_get_clock_speed();
1165 save_config_file();
1166 quit();
1167 }
1168
1169 void menu_load()
1170 {
1171 const char *file_ext[] = { ".gba", ".bin", ".zip", NULL };
1172 char load_filename[512];
1173 save_game_config_file();
1174 if(load_file(file_ext, load_filename) != -1)
1175 {
1176 if(load_gamepak(load_filename) == -1)
1177 {
1178 quit();
1179 }
1180 reset_gba();
1181 return_value = 1;
1182 repeat = 0;
1183 reg[CHANGED_PC_STATUS] = 1;
1184 menu_update_clock();
1185 }
1186 else
1187 {
1188 choose_menu(current_menu);
1189 }
1190 }
1191
1192 void menu_restart()
1193 {
1194 if(!first_load)
1195 {
1196 reset_gba();
1197 reg[CHANGED_PC_STATUS] = 1;
1198 return_value = 1;
1199 repeat = 0;
1200 }
1201 }
1202
1203 void menu_change_state()
1204 {
1205 get_savestate_filename(savestate_slot, current_savestate_filename);
1206 }
1207
1208 void menu_save_state()
1209 {
1210 if(!first_load)
1211 {
1212 get_savestate_filename_noshot(savestate_slot,
1213 current_savestate_filename);
1214 save_state(current_savestate_filename, original_screen);
1215 }
1216 menu_change_state();
1217 }
1218
1219 void menu_load_state()
1220 {
1221 if(!first_load)
1222 {
1223 load_state(current_savestate_filename);
1224 return_value = 1;
1225 repeat = 0;
1226 }
1227 }
1228
1229 void menu_load_state_file()
1230 {
1231 const char *file_ext[] = { ".svs", NULL };
1232 char load_filename[512];
1233 if(load_file(file_ext, load_filename) != -1)
1234 {
1235 load_state(load_filename);
1236 return_value = 1;
1237 repeat = 0;
1238 }
1239 else
1240 {
1241 choose_menu(current_menu);
1242 }
1243 }
1244
1245 void menu_fix_gamepad_help()
1246 {
1247#ifndef PC_BUILD
1248 clear_help();
1249 current_option->help_string =
1250 gamepad_help[gamepad_config_map[
1251 gamepad_config_line_to_button[current_option_num]]];
1252#endif
1253 }
1254
1255 void submenu_graphics_sound()
1256 {
1257
1258 }
1259
1260 void submenu_cheats_misc()
1261 {
1262
1263 }
1264
1265 void submenu_gamepad()
1266 {
1267
1268 }
1269
1270 void submenu_analog()
1271 {
1272
1273 }
1274
1275 void submenu_savestate()
1276 {
1277 print_string("Savestate options:", COLOR_ACTIVE_ITEM, COLOR_BG, 10, 70);
1278 menu_change_state();
1279 }
1280
1281 void submenu_main()
1282 {
1283 strncpy(print_buffer, gamepak_filename, 80);
1284 print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 10);
1285 sprintf(print_buffer, "%s %s %s", gamepak_title,
1286 gamepak_code, gamepak_maker);
1287 print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 20);
1288
1289 get_savestate_filename_noshot(savestate_slot,
1290 current_savestate_filename);
1291 }
1292
1293 const char *yes_no_options[] = { "no", "yes" };
1294 const char *enable_disable_options[] = { "disabled", "enabled" };
1295
1296 const char *frameskip_options[] = { "automatic", "manual", "off" };
1297 const char *frameskip_variation_options[] = { "uniform", "random" };
1298
1299#ifndef PSP_BUILD
1300 static const char *audio_buffer_options[] =
1301 {
1302 "16 bytes", "32 bytes", "64 bytes",
1303 "128 bytes", "256 bytes", "512 bytes", "1024 bytes", "2048 bytes",
1304 "4096 bytes", "8192 bytes", "16284 bytes"
1305 };
1306#else
1307 const char *audio_buffer_options[] =
1308 {
1309 "3072 bytes", "4096 bytes", "5120 bytes", "6144 bytes", "7168 bytes",
1310 "8192 bytes", "9216 bytes", "10240 bytes", "11264 bytes", "12288 bytes"
1311 };
1312#endif
1313
1314 static const char *update_backup_options[] = { "Exit only", "Automatic" };
1315
1316 // Marker for help information, don't go past this mark (except \n)------*
1317 menu_option_type graphics_sound_options[] =
1318 {
1319 string_selection_option(NULL, "Display scaling", scale_options,
1320 (u32 *)(&screen_scale),
1321 sizeof(scale_options) / sizeof(scale_options[0]),
1322#ifndef GP2X_BUILD
1323 "Determines how the GBA screen is resized in relation to the\n"
1324 "entire screen."
1325#ifdef PSP_BUILD
1326 " Select unscaled 3:2 for GBA resolution, scaled 3:2 for GBA\n"
1327 "aspect ratio scaled to fill the height of the PSP screen, and\n"
1328 "fullscreen to fill the entire PSP screen."
1329#endif
1330#endif
1331 "", 2),
1332#ifndef GP2X_BUILD
1333 string_selection_option(NULL, "Screen filtering", yes_no_options,
1334 (u32 *)(&screen_filter), 2,
1335 "Determines whether or not filtering should be used when\n"
1336 "scaling the screen. Selecting this will produce a more even and\n"
1337 "smooth image, at the cost of being blurry and having less vibrant\n"
1338 "colors.", 3),
1339#endif
1340#ifdef PND_BUILD
1341 string_selection_option(NULL, "Scaling filter", filter2_options,
1342 (u32 *)(&screen_filter2),
1343 sizeof(filter2_options) / sizeof(filter2_options[0]),
1344 "Optional pixel art scaling filter", 4),
1345#endif
1346 string_selection_option(NULL, "Frameskip type", frameskip_options,
1347 (u32 *)(&current_frameskip_type), 3,
1348#ifndef GP2X_BUILD
1349 "Determines what kind of frameskipping to use.\n"
1350 "Frameskipping may improve emulation speed of many games.\n"
1351#endif
1352 "Off: Do not skip any frames.\n"
1353 "Auto: Skip up to N frames (see next opt) as needed.\n"
1354 "Manual: Always render only 1 out of N + 1 frames."
1355 , 5),
1356 numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100,
1357#ifndef GP2X_BUILD
1358 "For auto frameskip, determines the maximum number of frames that\n"
1359 "are allowed to be skipped consecutively.\n"
1360 "For manual frameskip, determines the number of frames that will\n"
1361 "always be skipped."
1362#endif
1363 "", 6),
1364 string_selection_option(NULL, "Framskip variation",
1365 frameskip_variation_options, &random_skip, 2,
1366#ifndef GP2X_BUILD
1367 "If objects in the game flicker at a regular rate certain manual\n"
1368 "frameskip values may cause them to normally disappear. Change this\n"
1369 "value to 'random' to avoid this. Do not use otherwise, as it tends\n"
1370 "to make the image quality worse, especially in high motion games."
1371#endif
1372 "", 7),
1373 string_selection_option(NULL, "Audio output", yes_no_options,
1374 &global_enable_audio, 2,
1375 "Select 'no' to turn off all audio output. This will\n"
1376 "not result in a significant change in performance.", 9),
1377#ifndef PSP_BUILD
1378 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1379 &audio_buffer_size_number, 11,
1380#else
1381 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1382 &audio_buffer_size_number, 10,
1383#endif
1384
1385#ifdef PSP_BUILD
1386 "Set the size (in bytes) of the audio buffer. Larger values may result\n"
1387 "in slightly better performance at the cost of latency; the lowest\n"
1388 "value will give the most responsive audio.\n"
1389 "This option requires gpSP to be restarted before it will take effect.",
1390#else
1391 "Set the size (in bytes) of the audio buffer.\n"
1392 "This option requires gpSP restart to take effect.\n"
1393 "Settable values may be limited by SDL implementation.",
1394#endif
1395 10),
1396 submenu_option(NULL, "Back", "Return to the main menu.", 12)
1397 };
1398
1399 make_menu(graphics_sound, submenu_graphics_sound, NULL);
1400
1401 menu_option_type cheats_misc_options[] =
1402 {
1403 cheat_option(0),
1404 cheat_option(1),
1405 cheat_option(2),
1406 cheat_option(3),
1407 cheat_option(4),
1408 cheat_option(5),
1409 cheat_option(6),
1410 cheat_option(7),
1411 cheat_option(8),
1412 cheat_option(9),
1413#if defined(PSP_BUILD) || defined(GP2X_BUILD)
1414 string_selection_option(NULL, "Clock speed",
1415 clock_speed_options, &clock_speed_number,
1416 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]),
1417 "Change the clock speed of the device. Higher clock\n"
1418 "speed will yield better performance, but will drain\n"
1419 "battery life further.", 11),
1420#endif
1421 string_selection_option(NULL, "Update backup",
1422 update_backup_options, &update_backup_flag, 2,
1423#ifdef GP2X_BUILD
1424 "Determines when in-game save files should be\n"
1425 "written back to SD card."
1426#else
1427 "Determines when in-game save files should be written back to\n"
1428 "card. If set to 'automatic' writebacks will occur shortly after\n"
1429 "the game's backup is altered. On 'exit only' it will only be\n"
1430 "written back when you exit from this menu.\n"
1431#ifdef PSP
1432 "(NOT from using the home button), use the latter with extreme care."
1433#endif
1434#endif
1435 "", 12),
1436 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1437 };
1438
1439 make_menu(cheats_misc, submenu_cheats_misc, NULL);
1440
1441 menu_option_type savestate_options[] =
1442 {
1443 numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1444 "Load savestate from current slot", &savestate_slot, 10,
1445 "Select to load the game state from the current slot\n"
1446 "for this game.\n"
1447 "Press left + right to change the current slot.", 6),
1448 numeric_selection_action_hide_option(menu_save_state, menu_change_state,
1449 "Save savestate to current slot", &savestate_slot, 10,
1450 "Select to save the game state to the current slot\n"
1451 "for this game.\n"
1452 "Press left + right to change the current slot.", 7),
1453 numeric_selection_action_hide_option(menu_load_state_file,
1454 menu_change_state,
1455 "Load savestate from file", &savestate_slot, 10,
1456 "Restore gameplay from a savestate file.\n"
1457 "Note: The same file used to save the state must be\n"
1458 "present.\n", 9),
1459 numeric_selection_option(menu_change_state,
1460 "Current savestate slot", &savestate_slot, 10,
1461 "Change the current savestate slot.\n", 11),
1462 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1463 };
1464
1465 make_menu(savestate, submenu_savestate, NULL);
1466
1467#ifdef PSP_BUILD
1468
1469 menu_option_type gamepad_config_options[] =
1470 {
1471 gamepad_config_option("D-pad up ", 0),
1472 gamepad_config_option("D-pad down ", 1),
1473 gamepad_config_option("D-pad left ", 2),
1474 gamepad_config_option("D-pad right ", 3),
1475 gamepad_config_option("Circle ", 4),
1476 gamepad_config_option("Cross ", 5),
1477 gamepad_config_option("Square ", 6),
1478 gamepad_config_option("Triangle ", 7),
1479 gamepad_config_option("Left Trigger ", 8),
1480 gamepad_config_option("Right Trigger", 9),
1481 gamepad_config_option("Start ", 10),
1482 gamepad_config_option("Select ", 11),
1483 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1484 };
1485
1486
1487 menu_option_type analog_config_options[] =
1488 {
1489 analog_config_option("Analog up ", 0),
1490 analog_config_option("Analog down ", 1),
1491 analog_config_option("Analog left ", 2),
1492 analog_config_option("Analog right", 3),
1493 string_selection_option(NULL, "Enable analog", yes_no_options,
1494 &global_enable_analog, 2,
1495 "Select 'no' to block analog input entirely.", 7),
1496 numeric_selection_option(NULL, "Analog sensitivity",
1497 &analog_sensitivity_level, 10,
1498 "Determine sensitivity/responsiveness of the analog input.\n"
1499 "Lower numbers are less sensitive.", 8),
1500 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1501 };
1502
1503#endif
1504
1505#if defined(GP2X_BUILD) || defined(PND_BUILD)
1506
1507 menu_option_type gamepad_config_options[] =
1508 {
1509 gamepad_config_option("D-pad up ", 0),
1510 gamepad_config_option("D-pad down ", 1),
1511 gamepad_config_option("D-pad left ", 2),
1512 gamepad_config_option("D-pad right ", 3),
1513 gamepad_config_option("A ", 4),
1514 gamepad_config_option("B ", 5),
1515 gamepad_config_option("X ", 6),
1516 gamepad_config_option("Y ", 7),
1517 gamepad_config_option("Left Trigger ", 8),
1518 gamepad_config_option("Right Trigger", 9),
1519#ifdef WIZ_BUILD
1520 gamepad_config_option("Menu ", 10),
1521#else
1522 gamepad_config_option("Start ", 10),
1523#endif
1524 gamepad_config_option("Select ", 11),
1525#if !defined(WIZ_BUILD) && !defined(PND_BUILD)
1526 gamepad_config_option("Stick Push ", 12),
1527#endif
1528#ifdef PND_BUILD
1529 gamepad_config_option("1 ", 12),
1530 gamepad_config_option("2 ", 13),
1531 gamepad_config_option("3 ", 14),
1532 gamepad_config_option("4 ", 15),
1533 submenu_option(NULL, "Back", "Return to the main menu.", 16)
1534#else
1535 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1536#endif
1537 };
1538
1539
1540 menu_option_type analog_config_options[] =
1541 {
1542 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1543 };
1544
1545#endif
1546
1547#ifdef PC_BUILD
1548
1549 menu_option_type gamepad_config_options[] =
1550 {
1551 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1552 };
1553
1554 menu_option_type analog_config_options[] =
1555 {
1556 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1557 };
1558
1559#endif
1560
1561 make_menu(gamepad_config, submenu_gamepad, NULL);
1562 make_menu(analog_config, submenu_analog, NULL);
1563
1564 menu_option_type main_options[] =
1565 {
1566 submenu_option(&graphics_sound_menu, "Graphics and Sound options",
1567 "Select to set display parameters and frameskip\n"
1568 "behavior, audio on/off, buffer size, and filtering.", 0),
1569 numeric_selection_action_option(menu_load_state, NULL,
1570 "Load state from slot", &savestate_slot, 10,
1571 "Select to load the game state from the current slot\n"
1572 "for this game, if it exists.\n"
1573 "Press left + right to change the current slot.", 2),
1574 numeric_selection_action_option(menu_save_state, NULL,
1575 "Save state to slot", &savestate_slot, 10,
1576 "Select to save the game state to the current slot\n"
1577 "for this game. See the extended menu for more info.\n"
1578 "Press left + right to change the current slot.", 3),
1579 submenu_option(&savestate_menu, "Savestate options",
1580 "Select to enter a menu for loading, saving, and\n"
1581 "viewing the currently active savestate for this game\n"
1582 "(or to load a savestate file from another game)", 4),
1583 submenu_option(&gamepad_config_menu, "Configure gamepad input",
1584 "Select to change the in-game behavior of buttons\n"
1585 "and d-pad.", 6),
1586#ifndef GP2X_BUILD
1587 submenu_option(&analog_config_menu, "Configure analog input",
1588 "Select to change the in-game behavior of the PSP analog nub.", 7),
1589#endif
1590 submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
1591 "Select to manage cheats, set backup behavior,\n"
1592 "and set device clock speed.", 9),
1593 action_option(menu_load, NULL, "Load new game",
1594 "Select to load a new game\n"
1595 "(will exit a game if currently playing).", 11),
1596 action_option(menu_restart, NULL, "Restart game",
1597 "Select to reset the GBA with the current game\n"
1598 "loaded.", 12),
1599 action_option(menu_exit, NULL, "Return to game",
1600 "Select to exit this menu and resume gameplay.", 13),
1601 action_option(menu_quit, NULL, "Exit gpSP",
1602 "Select to exit gpSP and return to the menu.", 15)
1603 };
1604
1605 make_menu(main, submenu_main, NULL);
1606
1607 void choose_menu(menu_type *new_menu)
1608 {
1609 if(new_menu == NULL)
1610 new_menu = &main_menu;
1611
1612 clear_screen(COLOR_BG);
1613
1614#ifndef GP2X_BUILD
1615 blit_to_screen(original_screen, 240, 160, 230, 40);
1616#endif
1617
1618 current_menu = new_menu;
1619 current_option = new_menu->options;
1620 current_option_num = 0;
1621 if(current_menu->init_function)
1622 current_menu->init_function();
1623 }
1624
1625 void clear_help()
1626 {
1627 for(i = 0; i < 6; i++)
1628 {
1629 print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
1630 }
1631 }
1632
1633 menu_update_clock();
1634 video_resolution_large();
1635
1636#ifndef GP2X_BUILD
1637 SDL_LockMutex(sound_mutex);
1638#endif
1639 SDL_PauseAudio(1);
1640
1641#ifndef GP2X_BUILD
1642 SDL_UnlockMutex(sound_mutex);
1643#endif
1644
1645 if(gamepak_filename[0] == 0)
1646 {
1647 first_load = 1;
1648 memset(original_screen, 0x00, 240 * 160 * 2);
1649 print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
1650 60, 75,original_screen, 240, 0, 0, FONT_HEIGHT);
1651 }
1652
1653 choose_menu(&main_menu);
1654
1655 for(i = 0; i < 10; i++)
1656 {
1657 if(i >= num_cheats)
1658 {
1659 sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1660 }
1661 else
1662 {
1663 sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1664 cheats[i].cheat_name);
1665 }
1666 }
1667
1668 current_menu->init_function();
1669
1670 while(repeat)
1671 {
1672 display_option = current_menu->options;
1673
1674 for(i = 0; i < current_menu->num_options; i++, display_option++)
1675 {
1676 if(display_option->option_type & NUMBER_SELECTION_OPTION)
1677 {
1678 sprintf(line_buffer, display_option->display_string,
1679 *(display_option->current_option));
1680 }
1681 else
1682
1683 if(display_option->option_type & STRING_SELECTION_OPTION)
1684 {
1685 sprintf(line_buffer, display_option->display_string,
1686 ((u32 *)display_option->options)[*(display_option->current_option)]);
1687 }
1688 else
1689 {
1690 strcpy(line_buffer, display_option->display_string);
1691 }
1692
1693 if(display_option == current_option)
1694 {
1695 print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 6,
1696 (display_option->line_number * 10) + 40, 36);
1697 }
1698 else
1699 {
1700 print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 6,
1701 (display_option->line_number * 10) + 40, 36);
1702 }
1703 }
1704
1705 print_string(current_option->help_string, COLOR_HELP_TEXT,
1706 COLOR_BG, 8, 210);
1707
1708 flip_screen();
1709
1710 gui_action = get_gui_input();
1711
1712 switch(gui_action)
1713 {
1714 case CURSOR_DOWN:
1715 current_option_num = (current_option_num + 1) %
1716 current_menu->num_options;
1717
1718 current_option = current_menu->options + current_option_num;
1719 clear_help();
1720 break;
1721
1722 case CURSOR_UP:
1723 if(current_option_num)
1724 current_option_num--;
1725 else
1726 current_option_num = current_menu->num_options - 1;
1727
1728 current_option = current_menu->options + current_option_num;
1729 clear_help();
1730 break;
1731
1732 case CURSOR_RIGHT:
1733 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1734 STRING_SELECTION_OPTION))
1735 {
1736 *(current_option->current_option) =
1737 (*current_option->current_option + 1) %
1738 current_option->num_options;
1739
1740 if(current_option->passive_function)
1741 current_option->passive_function();
1742 }
1743 break;
1744
1745 case CURSOR_LEFT:
1746 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1747 STRING_SELECTION_OPTION))
1748 {
1749 u32 current_option_val = *(current_option->current_option);
1750
1751 if(current_option_val)
1752 current_option_val--;
1753 else
1754 current_option_val = current_option->num_options - 1;
1755
1756 *(current_option->current_option) = current_option_val;
1757
1758 if(current_option->passive_function)
1759 current_option->passive_function();
1760 }
1761 break;
1762
1763 case CURSOR_EXIT:
1764 if(current_menu == &main_menu)
1765 menu_exit();
1766
1767 choose_menu(&main_menu);
1768 break;
1769
1770 case CURSOR_SELECT:
1771 if(current_option->option_type & ACTION_OPTION)
1772 current_option->action_function();
1773
1774 if(current_option->option_type & SUBMENU_OPTION)
1775 choose_menu(current_option->sub_menu);
1776 break;
1777
1778 default:
1779 break;
1780 }
1781 }
1782
1783 set_gba_resolution(screen_scale);
1784 video_resolution_small();
1785 menu_get_clock_speed();
1786 set_clock_speed();
1787
1788 SDL_PauseAudio(0);
1789 num_skipped_frames = 100;
1790
1791 return return_value;
1792}