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.
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.
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
33 // Blatantly stolen and trimmed from MZX (megazeux.sourceforge.net)
37 #define FILE_LIST_ROWS 20
38 #define FILE_LIST_POSITION 5
39 #define DIR_LIST_POSITION 260
43 #define FILE_LIST_ROWS 25
44 #define FILE_LIST_POSITION 5
45 #define DIR_LIST_POSITION 360
51 #define COLOR_BG color16(2, 8, 10)
53 #define color16(red, green, blue) \
54 (blue << 11) | (green << 5) | red \
58 #define COLOR_BG color16(0, 0, 0)
60 #define color16(red, green, blue) \
61 (red << 11) | (green << 5) | blue \
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)
72 u8 *clock_speed_options[] =
74 "33MHz", "66MHz", "100MHz", "133MHz", "166MHz", "200MHz", "233MHz",
75 "266MHz", "300MHz", "333MHz"
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 u8 *clock_speed_options[] =
84 "300MHz", "333MHz", "366MHz", "400MHz", "433MHz",
85 "466MHz", "500MHz", "533MHz", "566MHz", "600MHz",
86 "633MHz", "666MHz", "700MHz", "733MHz", "766MHz",
87 "800MHz", "833MHz", "866MHz", "900MHz"
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 u8 *clock_speed_options[] =
96 "150MHz", "160MHz", "170MHz", "180MHz", "190MHz",
97 "200MHz", "210MHz", "220MHz", "230MHz", "240MHz",
98 "250MHz", "260MHz", "270MHz", "280MHz", "290MHz"
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
105 u8 *clock_speed_options[] =
109 #define menu_get_clock_speed() 0
110 #define get_clock_speed_number() 0
114 int sort_function(const void *dest_str_ptr, const void *src_str_ptr)
116 char *dest_str = *((char **)dest_str_ptr);
117 char *src_str = *((char **)src_str_ptr);
119 if(src_str[0] == '.')
122 if(dest_str[0] == '.')
125 return strcasecmp(dest_str, src_str);
128 s32 load_file(u8 **wildcards, u8 *result)
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;
143 u32 file_name_length;
145 u32 chosen_file, chosen_dir;
146 u32 dialog_result = 1;
147 s32 return_value = 1;
148 s32 current_file_selection;
149 s32 current_file_scroll_value;
150 u32 current_dir_selection;
151 u32 current_dir_scroll_value;
152 s32 current_file_in_scroll;
153 u32 current_dir_in_scroll;
154 u32 current_file_number, current_dir_number;
155 u32 current_column = 0;
158 gui_action_type gui_action;
160 while(return_value == 1)
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;
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);
181 getcwd(current_dir_name, MAX_PATH);
183 current_dir = opendir(current_dir_name);
188 current_file = readdir(current_dir);
194 file_name = current_file->d_name;
195 file_name_length = strlen(file_name);
197 if((stat(file_name, &file_info) >= 0) &&
198 ((file_name[0] != '.') || (file_name[1] == '.')))
200 if(S_ISDIR(file_info.st_mode))
203 (u8 *)malloc(file_name_length + 1);
205 sprintf(dir_list[num_dirs], "%s", file_name);
211 // Must match one of the wildcards, also ignore the .
212 if(file_name_length >= 4)
214 if(file_name[file_name_length - 4] == '.')
215 ext_pos = file_name_length - 4;
218 if(file_name[file_name_length - 3] == '.')
219 ext_pos = file_name_length - 3;
224 for(i = 0; wildcards[i] != NULL; i++)
226 if(!strcasecmp((file_name + ext_pos),
229 file_list[num_files] =
230 (u8 *)malloc(file_name_length + 1);
232 sprintf(file_list[num_files], "%s", file_name);
242 if(num_files == total_filenames_allocated)
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;
251 if(num_dirs == total_dirnames_allocated)
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;
260 } while(current_file);
262 qsort((void *)file_list, num_files, sizeof(u8 *), sort_function);
263 qsort((void *)dir_list, num_dirs, sizeof(u8 *), sort_function);
265 closedir(current_dir);
267 current_dir_length = strlen(current_dir_name);
269 if(current_dir_length > 80)
273 snprintf(current_dir_short, 80,
274 "...%s", current_dir_name + current_dir_length - 77);
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;
285 snprintf(current_dir_short, 80, "%s", current_dir_name);
287 memcpy(current_dir_short, current_dir_name,
288 current_dir_length + 1);
297 clear_screen(COLOR_BG);
305 print_string(current_dir_short, COLOR_ACTIVE_ITEM, COLOR_BG, 0, 0);
307 print_string("Press X to return to the main menu.",
308 COLOR_HELP_TEXT, COLOR_BG, 20, 220);
310 print_string("Press X to return to the main menu.",
311 COLOR_HELP_TEXT, COLOR_BG, 20, 260);
314 for(i = 0, current_file_number = i + current_file_scroll_value;
315 i < FILE_LIST_ROWS; i++, current_file_number++)
317 if(current_file_number < num_files)
319 if((current_file_number == current_file_selection) &&
320 (current_column == 0))
322 print_string(file_list[current_file_number], COLOR_ACTIVE_ITEM,
323 COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
327 print_string(file_list[current_file_number], COLOR_INACTIVE_ITEM,
328 COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
333 for(i = 0, current_dir_number = i + current_dir_scroll_value;
334 i < FILE_LIST_ROWS; i++, current_dir_number++)
336 if(current_dir_number < num_dirs)
338 if((current_dir_number == current_dir_selection) &&
339 (current_column == 1))
341 print_string(dir_list[current_dir_number], COLOR_ACTIVE_ITEM,
342 COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
346 print_string(dir_list[current_dir_number], COLOR_INACTIVE_ITEM,
347 COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
352 gui_action = get_gui_input();
357 if(current_column == 0)
359 if(current_file_selection < (num_files - 1))
361 current_file_selection++;
362 if(current_file_in_scroll == (FILE_LIST_ROWS - 1))
364 clear_screen(COLOR_BG);
365 current_file_scroll_value++;
369 current_file_in_scroll++;
374 clear_screen(COLOR_BG);
375 current_file_selection = 0;
376 current_file_scroll_value = 0;
377 current_file_in_scroll = 0;
382 if(current_dir_selection < (num_dirs - 1))
384 current_dir_selection++;
385 if(current_dir_in_scroll == (FILE_LIST_ROWS - 1))
387 clear_screen(COLOR_BG);
388 current_dir_scroll_value++;
392 current_dir_in_scroll++;
400 if (current_column != 0)
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)
409 current_file_scroll_value = 0;
410 current_file_in_scroll = current_file_selection;
414 current_file_in_scroll = FILE_LIST_ROWS / 2;
419 if(current_column == 0)
421 if(current_file_selection)
423 current_file_selection--;
424 if(current_file_in_scroll == 0)
426 clear_screen(COLOR_BG);
427 current_file_scroll_value--;
431 current_file_in_scroll--;
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;
448 if(current_dir_selection)
450 current_dir_selection--;
451 if(current_dir_in_scroll == 0)
453 clear_screen(COLOR_BG);
454 current_dir_scroll_value--;
458 current_dir_in_scroll--;
465 if (current_column != 0)
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)
474 current_file_scroll_value = 0;
475 current_file_in_scroll = current_file_selection;
479 current_file_in_scroll = FILE_LIST_ROWS / 2;
484 if(current_column == 0)
492 if(current_column == 1)
500 if(current_column == 1)
503 chdir(dir_list[current_dir_selection]);
511 strcpy(result, file_list[current_file_selection]);
518 if(!strcmp(current_dir_name, "ms0:/PSP"))
533 for(i = 0; i < num_files; i++)
539 for(i = 0; i < num_dirs; i++)
546 clear_screen(COLOR_BG);
553 NUMBER_SELECTION_OPTION = 0x01,
554 STRING_SELECTION_OPTION = 0x02,
555 SUBMENU_OPTION = 0x04,
557 } menu_option_type_enum;
561 void (* init_function)();
562 void (* passive_function)();
563 struct _menu_option_type *options;
567 struct _menu_option_type
569 void (* action_function)();
570 void (* passive_function)();
571 struct _menu_type *sub_menu;
572 char *display_string;
578 menu_option_type_enum option_type;
581 typedef struct _menu_option_type menu_option_type;
582 typedef struct _menu_type menu_type;
584 #define make_menu(name, init_function, passive_function) \
585 menu_type name##_menu = \
590 sizeof(name##_options) / sizeof(menu_option_type) \
593 #define gamepad_config_option(display_string, number) \
596 menu_fix_gamepad_help, \
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]]], \
605 STRING_SELECTION_OPTION \
608 #define analog_config_option(display_string, number) \
611 menu_fix_gamepad_help, \
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]], \
619 STRING_SELECTION_OPTION \
622 #define cheat_option(number) \
627 cheat_format_str[number], \
628 enable_disable_options, \
629 &(cheats[number].cheat_active), \
631 "Activate/deactivate this cheat code.", \
633 STRING_SELECTION_OPTION \
636 #define action_option(action_function, passive_function, display_string, \
637 help_string, line_number) \
651 #define submenu_option(sub_menu, display_string, help_string, line_number) \
659 sizeof(sub_menu) / sizeof(menu_option_type), \
665 #define selection_option(passive_function, display_string, options, \
666 option_ptr, num_options, help_string, line_number, type) \
680 #define action_selection_option(action_function, passive_function, \
681 display_string, options, option_ptr, num_options, help_string, line_number, \
693 type | ACTION_OPTION \
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)\
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) \
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) \
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) \
719 #define numeric_selection_action_hide_option(action_function, \
720 passive_function, display_string, option_ptr, num_options, help_string, \
722 action_selection_option(action_function, passive_function, \
723 display_string, NULL, option_ptr, num_options, help_string, \
724 line_number, NUMBER_SELECTION_OPTION) \
727 #define GAMEPAD_MENU_WIDTH 15
731 u32 gamepad_config_line_to_button[] =
732 { 8, 6, 7, 9, 1, 2, 3, 0, 4, 5, 11, 10 };
738 u32 gamepad_config_line_to_button[] =
739 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 14 };
743 u8 *scale_options[] =
746 "unscaled 3:2", "scaled 3:2 (slower)",
747 "unscaled 3:2 (anti-tear)", "scaled 3:2 (anti-tear)"
749 "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)"
756 s32 load_game_config_file()
758 u8 game_config_filename[512];
761 change_ext(gamepak_filename, game_config_filename, ".cfg");
763 file_open(game_config_file, game_config_filename, read);
765 if(file_check_valid(game_config_file))
767 u32 file_size = file_length(game_config_filename, game_config_file);
769 // Sanity check: File size must be the right size
772 u32 file_options[file_size / 4];
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];
781 if(clock_speed > 900)
783 #elif defined(GP2X_BUILD)
784 if(clock_speed >= 300)
787 if(clock_speed > 333)
794 if(frameskip_value < 0)
797 if(frameskip_value > 99)
798 frameskip_value = 99;
800 for(i = 0; i < 10; i++)
802 cheats[i].cheat_active = file_options[3 + i] % 2;
803 cheats[i].cheat_name[0] = 0;
806 file_close(game_config_file);
814 current_frameskip_type = auto_frameskip;
820 clock_speed = default_clock_speed;
822 for(i = 0; i < 10; i++)
824 cheats[i].cheat_active = 0;
825 cheats[i].cheat_name[0] = 0;
831 s32 load_config_file()
835 #if (defined(PSP_BUILD) || defined(ARM_ARCH)) && !defined(_WIN32_WCE)
836 sprintf(config_path, "%s/%s", main_path, GPSP_CONFIG_FILENAME);
838 sprintf(config_path, "%s\\%s", main_path, GPSP_CONFIG_FILENAME);
841 file_open(config_file, config_path, read);
843 if(file_check_valid(config_file))
845 u32 file_size = file_length(config_path, config_file);
847 // Sanity check: File size must be the right size
850 u32 file_options[file_size / 4];
852 s32 menu_button = -1;
853 file_read_array(config_file, file_options);
855 screen_scale = file_options[0] %
856 (sizeof(scale_options) / sizeof(scale_options[0]));
857 screen_filter = file_options[1] % 2;
858 global_enable_audio = file_options[2] % 2;
861 audio_buffer_size_number = file_options[3] % 10;
863 audio_buffer_size_number = file_options[3] % 11;
866 update_backup_flag = file_options[4] % 2;
867 global_enable_analog = file_options[5] % 2;
868 analog_sensitivity_level = file_options[6] % 8;
871 scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
874 // Sanity check: Make sure there's a MENU or FRAMESKIP
875 // key, if not assign to triangle
878 for(i = 0; i < 16; i++)
880 gamepad_config_map[i] = file_options[7 + i] %
881 (BUTTON_ID_NONE + 1);
883 if(gamepad_config_map[i] == BUTTON_ID_MENU)
889 if(menu_button == -1)
891 gamepad_config_map[0] = BUTTON_ID_MENU;
895 file_close(config_file);
904 s32 save_game_config_file()
906 u8 game_config_filename[512];
909 change_ext(gamepak_filename, game_config_filename, ".cfg");
911 file_open(game_config_file, game_config_filename, write);
913 if(file_check_valid(game_config_file))
915 u32 file_options[14];
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;
922 for(i = 0; i < 10; i++)
924 file_options[4 + i] = cheats[i].cheat_active;
927 file_write_array(game_config_file, file_options);
929 file_close(game_config_file);
937 s32 save_config_file()
941 #if (defined(PSP_BUILD) || defined(ARM_ARCH)) && !defined(_WIN32_WCE)
942 sprintf(config_path, "%s/%s", main_path, GPSP_CONFIG_FILENAME);
944 sprintf(config_path, "%s\\%s", main_path, GPSP_CONFIG_FILENAME);
947 file_open(config_file, config_path, write);
949 save_game_config_file();
951 if(file_check_valid(config_file))
953 u32 file_options[23];
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;
965 for(i = 0; i < 16; i++)
967 file_options[7 + i] = gamepad_config_map[i];
971 file_write_array(config_file, file_options);
973 file_close(config_file);
990 u32 savestate_slot = 0;
992 void get_savestate_snapshot(u8 *savestate_filename)
994 u16 snapshot_buffer[240 * 160];
995 u8 savestate_timestamp_string[80];
997 file_open(savestate_file, savestate_filename, read);
999 if(file_check_valid(savestate_file))
1001 u8 weekday_strings[7][11] =
1003 "Sunday", "Monday", "Tuesday", "Wednesday",
1004 "Thursday", "Friday", "Saturday"
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);
1011 file_close(savestate_file);
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);
1020 savestate_timestamp_string[40] = 0;
1021 print_string(savestate_timestamp_string, COLOR_HELP_TEXT, COLOR_BG,
1026 memset(snapshot_buffer, 0, 240 * 160 * 2);
1027 print_string_ext("No savestate exists for this slot.",
1028 0xFFFF, 0x0000, 15, 75, snapshot_buffer, 240, 0, 0, FONT_HEIGHT);
1029 print_string("---------- --/--/---- --:--:-- ", COLOR_HELP_TEXT,
1034 blit_to_screen(snapshot_buffer, 240, 160, 230, 40);
1038 void get_savestate_filename(u32 slot, u8 *name_buffer)
1040 u8 savestate_ext[16];
1042 sprintf(savestate_ext, "%d.svs", slot);
1043 change_ext(gamepak_filename, name_buffer, savestate_ext);
1045 get_savestate_snapshot(name_buffer);
1048 void get_savestate_filename_noshot(u32 slot, u8 *name_buffer)
1050 u8 savestate_ext[16];
1052 sprintf(savestate_ext, "%d.svs", slot);
1053 change_ext(gamepak_filename, name_buffer, savestate_ext);
1059 invalidate_all_cache();
1063 u32 menu(u16 *original_screen)
1065 u8 print_buffer[81];
1066 u32 clock_speed_number;
1067 u32 _current_option = 0;
1068 gui_action_type gui_action;
1069 menu_enum _current_menu = MAIN_MENU;
1072 u32 return_value = 0;
1074 u8 savestate_ext[16];
1075 u8 current_savestate_filename[512];
1077 u8 cheat_format_str[10][41];
1079 menu_type *current_menu;
1080 menu_option_type *current_option;
1081 menu_option_type *display_option;
1082 u32 current_option_num;
1084 auto void choose_menu();
1085 auto void clear_help();
1087 u8 *gamepad_help[] =
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.",
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.",
1113 void menu_update_clock()
1115 get_clock_speed_number();
1116 if (clock_speed_number < 0 || clock_speed_number >=
1117 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]))
1119 clock_speed = default_clock_speed;
1120 get_clock_speed_number();
1132 menu_get_clock_speed();
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)
1144 if(load_gamepak(load_filename) == -1)
1151 reg[CHANGED_PC_STATUS] = 1;
1152 menu_update_clock();
1156 choose_menu(current_menu);
1165 reg[CHANGED_PC_STATUS] = 1;
1171 void menu_change_state()
1173 get_savestate_filename(savestate_slot, current_savestate_filename);
1176 void menu_save_state()
1180 get_savestate_filename_noshot(savestate_slot,
1181 current_savestate_filename);
1182 save_state(current_savestate_filename, original_screen);
1184 menu_change_state();
1187 void menu_load_state()
1191 load_state(current_savestate_filename);
1197 void menu_load_state_file()
1199 u8 *file_ext[] = { ".svs", NULL };
1200 u8 load_filename[512];
1201 if(load_file(file_ext, load_filename) != -1)
1203 load_state(load_filename);
1209 choose_menu(current_menu);
1213 void menu_fix_gamepad_help()
1217 current_option->help_string =
1218 gamepad_help[gamepad_config_map[
1219 gamepad_config_line_to_button[current_option_num]]];
1223 void submenu_graphics_sound()
1228 void submenu_cheats_misc()
1233 void submenu_gamepad()
1238 void submenu_analog()
1243 void submenu_savestate()
1245 print_string("Savestate options:", COLOR_ACTIVE_ITEM, COLOR_BG, 10, 70);
1246 menu_change_state();
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);
1257 get_savestate_filename_noshot(savestate_slot,
1258 current_savestate_filename);
1261 u8 *yes_no_options[] = { "no", "yes" };
1262 u8 *enable_disable_options[] = { "disabled", "enabled" };
1264 u8 *frameskip_options[] = { "automatic", "manual", "off" };
1265 u8 *frameskip_variation_options[] = { "uniform", "random" };
1268 u8 *audio_buffer_options[] =
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"
1275 u8 *audio_buffer_options[] =
1277 "3072 bytes", "4096 bytes", "5120 bytes", "6144 bytes", "7168 bytes",
1278 "8192 bytes", "9216 bytes", "10240 bytes", "11264 bytes", "12288 bytes"
1283 u8 *update_backup_options[] = { "Exit only", "Automatic" };
1285 u8 *gamepad_config_buttons[] =
1311 // Marker for help information, don't go past this mark (except \n)------*
1312 menu_option_type graphics_sound_options[] =
1314 string_selection_option(NULL, "Display scaling", scale_options,
1315 (u32 *)(&screen_scale),
1316 sizeof(scale_options) / sizeof(scale_options[0]),
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"
1321 "fullscreen to fill the entire PSP screen."
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"
1332 string_selection_option(NULL, "Frameskip type", frameskip_options,
1333 (u32 *)(¤t_frameskip_type), 3,
1335 "Determines what kind of frameskipping to use.\n"
1336 "Frameskipping may improve emulation speed of many games.\n"
1338 "Off: Do not skip any frames.\n"
1339 "Auto: Skip up to N frames (see next opt) as needed.\n"
1340 "Manual: Always render only 1 out of N + 1 frames."
1342 numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100,
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"
1347 "always be skipped."
1350 string_selection_option(NULL, "Framskip variation",
1351 frameskip_variation_options, &random_skip, 2,
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"
1356 "make the image quality worse, especially in high motion games."
1359 string_selection_option(NULL, "Audio output", yes_no_options,
1360 &global_enable_audio, 2,
1361 "Select 'no' to turn off all audio output. This will\n"
1362 "not result in a significant change in performance.", 9),
1364 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1365 &audio_buffer_size_number, 11,
1367 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1368 &audio_buffer_size_number, 10,
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.",
1377 "Set the size (in bytes) of the audio buffer.\n"
1378 "This option requires gpSP restart to take effect.",
1381 submenu_option(NULL, "Back", "Return to the main menu.", 12)
1384 make_menu(graphics_sound, submenu_graphics_sound, NULL);
1386 menu_option_type cheats_misc_options[] =
1398 string_selection_option(NULL, "Clock speed",
1399 clock_speed_options, &clock_speed_number,
1400 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]),
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),
1404 string_selection_option(NULL, "Update backup",
1405 update_backup_options, &update_backup_flag, 2,
1407 "Determines when in-game save files should be\n"
1408 "written back to SD card.",
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"
1414 "Use the latter with extreme care.",
1417 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1420 make_menu(cheats_misc, submenu_cheats_misc, NULL);
1422 menu_option_type savestate_options[] =
1424 numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1425 "Load savestate from current slot", &savestate_slot, 10,
1426 "Select to load the game state from the current slot\n"
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,
1431 "Select to save the game state to the current slot\n"
1433 "Press left + right to change the current slot.", 7),
1434 numeric_selection_action_hide_option(menu_load_state_file,
1436 "Load savestate from file", &savestate_slot, 10,
1437 "Restore gameplay from a savestate file.\n"
1438 "Note: The same file used to save the state must be\n"
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)
1446 make_menu(savestate, submenu_savestate, NULL);
1450 menu_option_type gamepad_config_options[] =
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)
1468 menu_option_type analog_config_options[] =
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)
1488 menu_option_type gamepad_config_options[] =
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),
1501 gamepad_config_option("Menu ", 10),
1503 gamepad_config_option("Start ", 10),
1505 gamepad_config_option("Select ", 11),
1507 gamepad_config_option("Stick Push ", 12),
1509 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1513 menu_option_type analog_config_options[] =
1515 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1522 menu_option_type gamepad_config_options[] =
1524 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1527 menu_option_type analog_config_options[] =
1529 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1534 make_menu(gamepad_config, submenu_gamepad, NULL);
1535 make_menu(analog_config, submenu_analog, NULL);
1537 menu_option_type main_options[] =
1539 submenu_option(&graphics_sound_menu, "Graphics and Sound options",
1540 "Select to set display parameters and frameskip\n"
1541 "behavior, audio on/off, buffer size, and filtering.", 0),
1542 numeric_selection_action_option(menu_load_state, NULL,
1543 "Load state from slot", &savestate_slot, 10,
1544 "Select to load the game state from the current slot\n"
1545 "for this game, if it exists.\n"
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,
1549 "Select to save the game state to the current slot\n"
1550 "for this game. See the extended menu for more info.\n"
1551 "Press left + right to change the current slot.", 3),
1552 submenu_option(&savestate_menu, "Savestate options",
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),
1556 submenu_option(&gamepad_config_menu, "Configure gamepad input",
1557 "Select to change the in-game behavior of buttons\n"
1560 submenu_option(&analog_config_menu, "Configure analog input",
1561 "Select to change the in-game behavior of the PSP analog nub.", 7),
1563 submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
1564 "Select to manage cheats, set backup behavior,\n"
1565 "and set device clock speed.", 9),
1566 action_option(menu_load, NULL, "Load new game",
1567 "Select to load a new game\n"
1568 "(will exit a game if currently playing).", 11),
1569 action_option(menu_restart, NULL, "Restart game",
1570 "Select to reset the GBA with the current game\n"
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",
1575 "Select to exit gpSP and return to the menu.", 15)
1578 make_menu(main, submenu_main, NULL);
1580 void choose_menu(menu_type *new_menu)
1582 if(new_menu == NULL)
1583 new_menu = &main_menu;
1585 clear_screen(COLOR_BG);
1588 blit_to_screen(original_screen, 240, 160, 230, 40);
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();
1600 for(i = 0; i < 6; i++)
1602 print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
1606 menu_update_clock();
1607 video_resolution_large();
1610 SDL_LockMutex(sound_mutex);
1615 SDL_UnlockMutex(sound_mutex);
1618 if(gamepak_filename[0] == 0)
1621 memset(original_screen, 0x00, 240 * 160 * 2);
1622 print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
1623 60, 75,original_screen, 240, 0, 0, FONT_HEIGHT);
1626 choose_menu(&main_menu);
1628 for(i = 0; i < 10; i++)
1632 sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1636 sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1637 cheats[i].cheat_name);
1641 current_menu->init_function();
1645 display_option = current_menu->options;
1647 for(i = 0; i < current_menu->num_options; i++, display_option++)
1649 if(display_option->option_type & NUMBER_SELECTION_OPTION)
1651 sprintf(line_buffer, display_option->display_string,
1652 *(display_option->current_option));
1656 if(display_option->option_type & STRING_SELECTION_OPTION)
1658 sprintf(line_buffer, display_option->display_string,
1659 ((u32 *)display_option->options)[*(display_option->current_option)]);
1663 strcpy(line_buffer, display_option->display_string);
1666 if(display_option == current_option)
1668 print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 10,
1669 (display_option->line_number * 10) + 40, 41);
1673 print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 10,
1674 (display_option->line_number * 10) + 40, 41);
1678 print_string(current_option->help_string, COLOR_HELP_TEXT,
1683 gui_action = get_gui_input();
1688 current_option_num = (current_option_num + 1) %
1689 current_menu->num_options;
1691 current_option = current_menu->options + current_option_num;
1696 if(current_option_num)
1697 current_option_num--;
1699 current_option_num = current_menu->num_options - 1;
1701 current_option = current_menu->options + current_option_num;
1706 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1707 STRING_SELECTION_OPTION))
1709 *(current_option->current_option) =
1710 (*current_option->current_option + 1) %
1711 current_option->num_options;
1713 if(current_option->passive_function)
1714 current_option->passive_function();
1719 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1720 STRING_SELECTION_OPTION))
1722 u32 current_option_val = *(current_option->current_option);
1724 if(current_option_val)
1725 current_option_val--;
1727 current_option_val = current_option->num_options - 1;
1729 *(current_option->current_option) = current_option_val;
1731 if(current_option->passive_function)
1732 current_option->passive_function();
1737 if(current_menu == &main_menu)
1740 choose_menu(&main_menu);
1744 if(current_option->option_type & ACTION_OPTION)
1745 current_option->action_function();
1747 if(current_option->option_type & SUBMENU_OPTION)
1748 choose_menu(current_option->sub_menu);
1753 set_gba_resolution(screen_scale);
1754 video_resolution_small();
1755 menu_get_clock_speed();
1760 return return_value;