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