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 (resolution_width * 3 / 4)
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 static const char *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(POLLUX_BUILD)
82 static const char *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 static const char *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 static const char *clock_speed_options[] =
109 #define menu_get_clock_speed()
110 #define get_clock_speed_number()
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(const char **wildcards, char *result)
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;
143 u32 file_name_length;
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;
157 gui_action_type gui_action;
159 while(return_value == 1)
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;
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);
180 getcwd(current_dir_name, MAX_PATH);
182 current_dir = opendir(current_dir_name);
187 current_file = readdir(current_dir);
193 file_name = current_file->d_name;
194 file_name_length = strlen(file_name);
196 if((stat(file_name, &file_info) >= 0) &&
197 ((file_name[0] != '.') || (file_name[1] == '.')))
199 if(S_ISDIR(file_info.st_mode))
201 dir_list[num_dirs] = malloc(file_name_length + 1);
203 sprintf(dir_list[num_dirs], "%s", file_name);
209 // Must match one of the wildcards, also ignore the .
210 if(file_name_length >= 4)
212 if(file_name[file_name_length - 4] == '.')
213 ext_pos = file_name_length - 4;
216 if(file_name[file_name_length - 3] == '.')
217 ext_pos = file_name_length - 3;
222 for(i = 0; wildcards[i] != NULL; i++)
224 if(!strcasecmp((file_name + ext_pos),
227 file_list[num_files] =
228 malloc(file_name_length + 1);
230 sprintf(file_list[num_files], "%s", file_name);
240 if(num_files == total_filenames_allocated)
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;
249 if(num_dirs == total_dirnames_allocated)
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;
258 } while(current_file);
260 qsort((void *)file_list, num_files, sizeof(char *), sort_function);
261 qsort((void *)dir_list, num_dirs, sizeof(char *), sort_function);
263 closedir(current_dir);
265 current_dir_length = strlen(current_dir_name);
267 if(current_dir_length > 80)
271 snprintf(current_dir_short, 80,
272 "...%s", current_dir_name + current_dir_length - 77);
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;
283 snprintf(current_dir_short, 80, "%s", current_dir_name);
285 memcpy(current_dir_short, current_dir_name,
286 current_dir_length + 1);
295 clear_screen(COLOR_BG);
301 print_string(current_dir_short, COLOR_ACTIVE_ITEM, COLOR_BG, 0, 0);
303 print_string("Press X to return to the main menu.",
304 COLOR_HELP_TEXT, COLOR_BG, 20, 220);
306 print_string("Press X to return to the main menu.",
307 COLOR_HELP_TEXT, COLOR_BG, 20, 260);
310 for(i = 0, current_file_number = i + current_file_scroll_value;
311 i < FILE_LIST_ROWS; i++, current_file_number++)
313 if(current_file_number < num_files)
315 if((current_file_number == current_file_selection) &&
316 (current_column == 0))
318 print_string(file_list[current_file_number], COLOR_ACTIVE_ITEM,
319 COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
323 print_string(file_list[current_file_number], COLOR_INACTIVE_ITEM,
324 COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
329 for(i = 0, current_dir_number = i + current_dir_scroll_value;
330 i < FILE_LIST_ROWS; i++, current_dir_number++)
332 if(current_dir_number < num_dirs)
334 if((current_dir_number == current_dir_selection) &&
335 (current_column == 1))
337 print_string(dir_list[current_dir_number], COLOR_ACTIVE_ITEM,
338 COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
342 print_string(dir_list[current_dir_number], COLOR_INACTIVE_ITEM,
343 COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
348 gui_action = get_gui_input();
353 if(current_column == 0)
355 if(current_file_selection < (num_files - 1))
357 current_file_selection++;
358 if(current_file_in_scroll == (FILE_LIST_ROWS - 1))
360 clear_screen(COLOR_BG);
361 current_file_scroll_value++;
365 current_file_in_scroll++;
370 clear_screen(COLOR_BG);
371 current_file_selection = 0;
372 current_file_scroll_value = 0;
373 current_file_in_scroll = 0;
378 if(current_dir_selection < (num_dirs - 1))
380 current_dir_selection++;
381 if(current_dir_in_scroll == (FILE_LIST_ROWS - 1))
383 clear_screen(COLOR_BG);
384 current_dir_scroll_value++;
388 current_dir_in_scroll++;
396 if (current_column != 0)
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)
405 current_file_scroll_value = 0;
406 current_file_in_scroll = current_file_selection;
410 current_file_in_scroll = FILE_LIST_ROWS / 2;
415 if(current_column == 0)
417 if(current_file_selection)
419 current_file_selection--;
420 if(current_file_in_scroll == 0)
422 clear_screen(COLOR_BG);
423 current_file_scroll_value--;
427 current_file_in_scroll--;
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;
444 if(current_dir_selection)
446 current_dir_selection--;
447 if(current_dir_in_scroll == 0)
449 clear_screen(COLOR_BG);
450 current_dir_scroll_value--;
454 current_dir_in_scroll--;
461 if (current_column != 0)
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)
470 current_file_scroll_value = 0;
471 current_file_in_scroll = current_file_selection;
475 current_file_in_scroll = FILE_LIST_ROWS / 2;
480 if(current_column == 0)
488 if(current_column == 1)
496 if(current_column == 1)
499 chdir(dir_list[current_dir_selection]);
507 strcpy(result, file_list[current_file_selection]);
514 if(!strcmp(current_dir_name, "ms0:/PSP"))
532 for(i = 0; i < num_files; i++)
538 for(i = 0; i < num_dirs; i++)
545 clear_screen(COLOR_BG);
552 NUMBER_SELECTION_OPTION = 0x01,
553 STRING_SELECTION_OPTION = 0x02,
554 SUBMENU_OPTION = 0x04,
555 ACTION_OPTION = 0x08,
556 } menu_option_type_enum;
560 void (* init_function)();
561 void (* passive_function)();
562 struct _menu_option_type *options;
566 struct _menu_option_type
568 void (* action_function)();
569 void (* passive_function)();
570 struct _menu_type *sub_menu;
571 const char *display_string;
575 const char *help_string;
577 menu_option_type_enum option_type;
580 typedef struct _menu_option_type menu_option_type;
581 typedef struct _menu_type menu_type;
583 #define make_menu(name, init_function, passive_function) \
584 menu_type name##_menu = \
589 sizeof(name##_options) / sizeof(menu_option_type) \
592 #define gamepad_config_option(display_string, number) \
595 menu_fix_gamepad_help, \
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]]], \
604 STRING_SELECTION_OPTION \
607 #define analog_config_option(display_string, number) \
610 menu_fix_gamepad_help, \
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]], \
618 STRING_SELECTION_OPTION \
621 #define cheat_option(number) \
626 cheat_format_str[number], \
627 enable_disable_options, \
628 &(cheats[number].cheat_active), \
630 "Activate/deactivate this cheat code.", \
632 STRING_SELECTION_OPTION \
635 #define action_option(action_function, passive_function, display_string, \
636 help_string, line_number) \
650 #define submenu_option(sub_menu, display_string, help_string, line_number) \
658 sizeof(sub_menu) / sizeof(menu_option_type), \
664 #define selection_option(passive_function, display_string, options, \
665 option_ptr, num_options, help_string, line_number, type) \
679 #define action_selection_option(action_function, passive_function, \
680 display_string, options, option_ptr, num_options, help_string, line_number, \
692 type | ACTION_OPTION \
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)\
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) \
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) \
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) \
718 #define numeric_selection_action_hide_option(action_function, \
719 passive_function, display_string, option_ptr, num_options, help_string, \
721 action_selection_option(action_function, passive_function, \
722 display_string, NULL, option_ptr, num_options, help_string, \
723 line_number, NUMBER_SELECTION_OPTION) \
726 #define GAMEPAD_MENU_WIDTH 15
730 u32 gamepad_config_line_to_button[] =
731 { 8, 6, 7, 9, 1, 2, 3, 0, 4, 5, 11, 10 };
737 u32 gamepad_config_line_to_button[] =
738 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 14, 15 };
744 u32 gamepad_config_line_to_button[] =
745 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 12, 13, 14, 15 };
751 u32 gamepad_config_line_to_button[] =
752 { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 12, 13, 14, 15 };
756 static const char *scale_options[] =
759 "unscaled 3:2", "scaled 3:2", "fullscreen 16:9"
760 #elif defined(WIZ_BUILD)
761 "unscaled 3:2", "scaled 3:2 (slower)",
762 "unscaled 3:2 (anti-tear)", "scaled 3:2 (anti-tear)"
763 #elif defined(POLLUX_BUILD)
764 "unscaled 3:2", "scaled 3:2 (slower)"
765 #elif defined(PND_BUILD)
766 "unscaled", "2x", "3x", "fullscreen"
767 #elif defined(GP2X_BUILD)
768 "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)"
769 #elif defined(RPI_BUILD)
776 const char *filter2_options[] =
778 "none", "scale2x", "scale3x", "eagle2x"
782 static const char *audio_buffer_options[] =
784 "16 bytes", "32 bytes", "64 bytes",
785 "128 bytes", "256 bytes", "512 bytes", "1024 bytes", "2048 bytes",
786 "4096 bytes", "8192 bytes", "16284 bytes"
789 const char *audio_buffer_options[] =
791 "3072 bytes", "4096 bytes", "5120 bytes", "6144 bytes", "7168 bytes",
792 "8192 bytes", "9216 bytes", "10240 bytes", "11264 bytes", "12288 bytes"
797 s32 load_game_config_file()
799 char game_config_filename[512];
802 make_rpath(game_config_filename, sizeof(game_config_filename), ".cfg");
804 file_open(game_config_file, game_config_filename, read);
806 if(file_check_valid(game_config_file))
808 u32 file_size = file_length(game_config_filename, game_config_file);
810 // Sanity check: File size must be the right size
813 u32 file_options[file_size / 4];
815 file_read_array(game_config_file, file_options);
816 current_frameskip_type = file_options[0] % 3;
817 frameskip_value = file_options[1];
818 random_skip = file_options[2] % 2;
819 clock_speed = file_options[3];
822 if(clock_speed > 900)
824 #elif defined(GP2X_BUILD)
825 if(clock_speed >= 300)
828 if(clock_speed > 333)
835 if(frameskip_value < 0)
838 if(frameskip_value > 99)
839 frameskip_value = 99;
841 for(i = 0; i < 10; i++)
843 cheats[i].cheat_active = file_options[4 + i] % 2;
844 cheats[i].cheat_name[0] = 0;
847 file_close(game_config_file);
856 current_frameskip_type = manual_frameskip;
859 current_frameskip_type = auto_frameskip;
866 clock_speed = default_clock_speed;
868 for(i = 0; i < 10; i++)
870 cheats[i].cheat_active = 0;
871 cheats[i].cheat_name[0] = 0;
880 fo_global_enable_audio,
881 fo_audio_buffer_size,
882 fo_update_backup_flag,
883 fo_global_enable_analog,
884 fo_analog_sensitivity_level,
886 fo_main_option_count,
890 #define PLAT_BUTTON_COUNT 0
892 #define FILE_OPTION_COUNT (fo_main_option_count + PLAT_BUTTON_COUNT)
894 s32 load_config_file()
896 char config_path[512];
898 sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, GPSP_CONFIG_FILENAME);
900 file_open(config_file, config_path, read);
902 if(file_check_valid(config_file))
904 u32 file_size = file_length(config_path, config_file);
906 // Sanity check: File size must be the right size
907 if(file_size == FILE_OPTION_COUNT * 4)
909 u32 file_options[file_size / 4];
910 file_read_array(config_file, file_options);
912 screen_scale = file_options[fo_screen_scale] %
913 (sizeof(scale_options) / sizeof(scale_options[0]));
914 screen_filter = file_options[fo_screen_filter] % 2;
915 global_enable_audio = file_options[fo_global_enable_audio] % 2;
916 screen_filter2 = file_options[fo_screen_filter2] %
917 (sizeof(filter2_options) / sizeof(filter2_options[0]));
919 audio_buffer_size_number = file_options[fo_audio_buffer_size] %
920 (sizeof(audio_buffer_options) / sizeof(audio_buffer_options[0]));
922 update_backup_flag = file_options[fo_update_backup_flag] % 2;
923 global_enable_analog = file_options[fo_global_enable_analog] % 2;
924 analog_sensitivity_level = file_options[fo_analog_sensitivity_level] % 8;
927 scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
930 // Sanity check: Make sure there's a MENU or FRAMESKIP
931 // key, if not assign to triangle
935 s32 menu_button = -1;
936 for(i = 0; i < PLAT_BUTTON_COUNT; i++)
938 gamepad_config_map[i] = file_options[fo_main_option_count + i] %
939 (BUTTON_ID_NONE + 1);
941 if(gamepad_config_map[i] == BUTTON_ID_MENU)
947 if(menu_button == -1 && PLAT_MENU_BUTTON >= 0)
949 gamepad_config_map[PLAT_MENU_BUTTON] = BUTTON_ID_MENU;
953 file_close(config_file);
962 s32 save_game_config_file()
964 char game_config_filename[512];
967 make_rpath(game_config_filename, sizeof(game_config_filename), ".cfg");
969 file_open(game_config_file, game_config_filename, write);
971 if(file_check_valid(game_config_file))
973 u32 file_options[14];
975 file_options[0] = current_frameskip_type;
976 file_options[1] = frameskip_value;
977 file_options[2] = random_skip;
978 file_options[3] = clock_speed;
980 for(i = 0; i < 10; i++)
982 file_options[4 + i] = cheats[i].cheat_active;
985 file_write_array(game_config_file, file_options);
987 file_close(game_config_file);
995 s32 save_config_file()
997 char config_path[512];
999 sprintf(config_path, "%s" PATH_SEPARATOR "%s", main_path, GPSP_CONFIG_FILENAME);
1001 file_open(config_file, config_path, write);
1003 save_game_config_file();
1005 if(file_check_valid(config_file))
1007 u32 file_options[FILE_OPTION_COUNT];
1009 file_options[fo_screen_scale] = screen_scale;
1010 file_options[fo_screen_filter] = screen_filter;
1011 file_options[fo_global_enable_audio] = global_enable_audio;
1012 file_options[fo_audio_buffer_size] = audio_buffer_size_number;
1013 file_options[fo_update_backup_flag] = update_backup_flag;
1014 file_options[fo_global_enable_analog] = global_enable_analog;
1015 file_options[fo_analog_sensitivity_level] = analog_sensitivity_level;
1016 file_options[fo_screen_filter2] = screen_filter2;
1020 for(i = 0; i < PLAT_BUTTON_COUNT; i++)
1022 file_options[fo_main_option_count + i] = gamepad_config_map[i];
1026 file_write_array(config_file, file_options);
1028 file_close(config_file);
1045 u32 savestate_slot = 0;
1047 void get_savestate_snapshot(char *savestate_filename)
1049 u16 snapshot_buffer[240 * 160];
1050 char savestate_timestamp_string[80];
1052 file_open(savestate_file, savestate_filename, read);
1054 if(file_check_valid(savestate_file))
1056 const char weekday_strings[7][11] =
1058 "Sunday", "Monday", "Tuesday", "Wednesday",
1059 "Thursday", "Friday", "Saturday"
1061 time_t savestate_time_flat;
1062 struct tm *current_time;
1063 file_read_array(savestate_file, snapshot_buffer);
1064 file_read_variable(savestate_file, savestate_time_flat);
1066 file_close(savestate_file);
1068 current_time = localtime(&savestate_time_flat);
1069 sprintf(savestate_timestamp_string,
1070 "%s %02d/%02d/%04d %02d:%02d:%02d ",
1071 weekday_strings[current_time->tm_wday], current_time->tm_mon + 1,
1072 current_time->tm_mday, current_time->tm_year + 1900,
1073 current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
1075 savestate_timestamp_string[40] = 0;
1076 print_string(savestate_timestamp_string, COLOR_HELP_TEXT, COLOR_BG,
1081 memset(snapshot_buffer, 0, 240 * 160 * 2);
1082 print_string_ext("No savestate in this slot.",
1083 0xFFFF, 0x0000, 15, 75, snapshot_buffer, 240, 0, 0, FONT_HEIGHT);
1084 print_string("---------- --/--/---- --:--:-- ", COLOR_HELP_TEXT,
1089 blit_to_screen(snapshot_buffer, 240, 160, 230, 40);
1093 void get_savestate_filename_noshot(u32 slot, char *name_buffer)
1095 char savestate_ext[16];
1097 sprintf(savestate_ext, "%d.svs", slot);
1098 make_rpath(name_buffer, 512, savestate_ext);
1101 void get_savestate_filename(u32 slot, char *name_buffer)
1103 get_savestate_filename_noshot(slot, name_buffer);
1104 get_savestate_snapshot(name_buffer);
1110 invalidate_all_cache();
1114 u32 menu(u16 *original_screen)
1116 char print_buffer[81];
1117 u32 clock_speed_number;
1118 gui_action_type gui_action;
1121 u32 return_value = 0;
1123 char current_savestate_filename[512];
1124 char line_buffer[80];
1125 char cheat_format_str[10][41];
1127 menu_type *current_menu;
1128 menu_option_type *current_option;
1129 menu_option_type *display_option;
1130 u32 current_option_num;
1132 auto void choose_menu();
1133 auto void clear_help();
1136 static const char * const gamepad_help[] =
1138 "Up button on GBA d-pad.",
1139 "Down button on GBA d-pad.",
1140 "Left button on GBA d-pad.",
1141 "Right button on GBA d-pad.",
1144 "Left shoulder button on GBA.",
1145 "Right shoulder button on GBA.",
1146 "Start button on GBA.",
1147 "Select button on GBA.",
1148 "Brings up the options menu.",
1149 "Toggles fastforward on/off.",
1150 "Loads the game state from the current slot.",
1151 "Saves the game state to the current slot.",
1152 "Rapidly press/release the A button on GBA.",
1153 "Rapidly press/release the B button on GBA.",
1154 "Rapidly press/release the L shoulder on GBA.",
1155 "Rapidly press/release the R shoulder on GBA.",
1156 "Increases the volume.",
1157 "Decreases the volume.",
1158 "Displays virtual/drawn frames per second.",
1162 static const char *gamepad_config_buttons[] =
1189 void menu_update_clock()
1191 get_clock_speed_number();
1192 if (clock_speed_number < 0 || clock_speed_number >=
1193 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]))
1195 clock_speed = default_clock_speed;
1196 get_clock_speed_number();
1208 menu_get_clock_speed();
1215 const char *file_ext[] = { ".gba", ".bin", ".zip", NULL };
1216 char load_filename[512];
1217 save_game_config_file();
1218 if(load_file(file_ext, load_filename) != -1)
1220 if(load_gamepak(load_filename) == -1)
1227 reg[CHANGED_PC_STATUS] = 1;
1228 menu_update_clock();
1237 reg[CHANGED_PC_STATUS] = 1;
1243 void menu_change_state()
1245 get_savestate_filename(savestate_slot, current_savestate_filename);
1248 void menu_save_state()
1252 get_savestate_filename_noshot(savestate_slot,
1253 current_savestate_filename);
1254 save_state(current_savestate_filename, original_screen);
1256 menu_change_state();
1259 void menu_load_state()
1263 load_state(current_savestate_filename);
1269 void menu_load_state_file()
1271 const char *file_ext[] = { ".svs", NULL };
1272 char load_filename[512];
1273 if(load_file(file_ext, load_filename) != -1)
1275 load_state(load_filename);
1281 choose_menu(current_menu);
1285 void menu_fix_gamepad_help()
1289 current_option->help_string =
1290 gamepad_help[gamepad_config_map[
1291 gamepad_config_line_to_button[current_option_num]]];
1295 void submenu_graphics_sound()
1300 void submenu_cheats_misc()
1305 void submenu_gamepad()
1310 void submenu_analog()
1315 void submenu_savestate()
1317 print_string("Savestate options:", COLOR_ACTIVE_ITEM, COLOR_BG, 10, 70);
1318 menu_change_state();
1323 strncpy(print_buffer, gamepak_filename, 80);
1324 print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 10);
1325 sprintf(print_buffer, "%s %s %s", gamepak_title,
1326 gamepak_code, gamepak_maker);
1327 print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 20);
1329 get_savestate_filename_noshot(savestate_slot,
1330 current_savestate_filename);
1333 const char *yes_no_options[] = { "no", "yes" };
1334 const char *enable_disable_options[] = { "disabled", "enabled" };
1336 const char *frameskip_options[] = { "automatic", "manual", "off" };
1337 const char *frameskip_variation_options[] = { "uniform", "random" };
1339 static const char *update_backup_options[] = { "Exit only", "Automatic" };
1341 // Marker for help information, don't go past this mark (except \n)------*
1342 menu_option_type graphics_sound_options[] =
1345 string_selection_option(NULL, "Display scaling", scale_options,
1346 (u32 *)(&screen_scale),
1347 sizeof(scale_options) / sizeof(scale_options[0]),
1349 "Determines how the GBA screen is resized in relation to the\n"
1352 " Select unscaled 3:2 for GBA resolution, scaled 3:2 for GBA\n"
1353 "aspect ratio scaled to fill the height of the PSP screen, and\n"
1354 "fullscreen to fill the entire PSP screen."
1361 string_selection_option(NULL, "Screen filtering", yes_no_options,
1362 (u32 *)(&screen_filter), 2,
1363 "Determines whether or not filtering should be used when\n"
1364 "scaling the screen. Selecting this will produce a more even and\n"
1365 "smooth image, at the cost of being blurry and having less vibrant\n"
1368 #if defined (PND_BUILD)
1369 string_selection_option(NULL, "Scaling filter", filter2_options,
1370 (u32 *)(&screen_filter2),
1371 sizeof(filter2_options) / sizeof(filter2_options[0]),
1372 "Optional pixel art scaling filter", 4),
1374 string_selection_option(NULL, "Frameskip type", frameskip_options,
1375 (u32 *)(¤t_frameskip_type), 3,
1377 "Determines what kind of frameskipping to use.\n"
1378 "Frameskipping may improve emulation speed of many games.\n"
1380 "Off: Do not skip any frames.\n"
1381 "Auto: Skip up to N frames (see next opt) as needed.\n"
1382 "Manual: Always render only 1 out of N + 1 frames."
1384 numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100,
1386 "For auto frameskip, determines the maximum number of frames that\n"
1387 "are allowed to be skipped consecutively.\n"
1388 "For manual frameskip, determines the number of frames that will\n"
1389 "always be skipped."
1392 string_selection_option(NULL, "Framskip variation",
1393 frameskip_variation_options, &random_skip, 2,
1395 "If objects in the game flicker at a regular rate certain manual\n"
1396 "frameskip values may cause them to normally disappear. Change this\n"
1397 "value to 'random' to avoid this. Do not use otherwise, as it tends\n"
1398 "to make the image quality worse, especially in high motion games."
1401 string_selection_option(NULL, "Audio output", yes_no_options,
1402 &global_enable_audio, 2,
1403 "Select 'no' to turn off all audio output. This will\n"
1404 "not result in a significant change in performance.", 9),
1406 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1407 &audio_buffer_size_number, 11,
1409 string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1410 &audio_buffer_size_number, 10,
1414 "Set the size (in bytes) of the audio buffer. Larger values may result\n"
1415 "in slightly better performance at the cost of latency; the lowest\n"
1416 "value will give the most responsive audio.\n"
1417 "This option requires gpSP to be restarted before it will take effect.",
1419 "Set the size (in bytes) of the audio buffer.\n"
1420 "This option requires gpSP restart to take effect.\n"
1421 "Settable values may be limited by SDL implementation.",
1424 submenu_option(NULL, "Back", "Return to the main menu.", 12)
1427 make_menu(graphics_sound, submenu_graphics_sound, NULL);
1429 menu_option_type cheats_misc_options[] =
1441 #if defined(PSP_BUILD) || defined(GP2X_BUILD)
1442 string_selection_option(NULL, "Clock speed",
1443 clock_speed_options, &clock_speed_number,
1444 sizeof(clock_speed_options) / sizeof(clock_speed_options[0]),
1445 "Change the clock speed of the device. Higher clock\n"
1446 "speed will yield better performance, but will drain\n"
1447 "battery life further.", 11),
1449 string_selection_option(NULL, "Update backup",
1450 update_backup_options, &update_backup_flag, 2,
1452 "Determines when in-game save files should be\n"
1453 "written back to SD card."
1455 "Determines when in-game save files should be written back to\n"
1456 "card. If set to 'automatic' writebacks will occur shortly after\n"
1457 "the game's backup is altered. On 'exit only' it will only be\n"
1458 "written back when you exit from this menu.\n"
1460 "(NOT from using the home button), use the latter with extreme care."
1464 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1467 make_menu(cheats_misc, submenu_cheats_misc, NULL);
1469 menu_option_type savestate_options[] =
1471 numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1472 "Load savestate from current slot", &savestate_slot, 10,
1473 "Select to load the game state from the current slot\n"
1475 "Press left + right to change the current slot.", 6),
1476 numeric_selection_action_hide_option(menu_save_state, menu_change_state,
1477 "Save savestate to current slot", &savestate_slot, 10,
1478 "Select to save the game state to the current slot\n"
1480 "Press left + right to change the current slot.", 7),
1481 numeric_selection_action_hide_option(menu_load_state_file,
1483 "Load savestate from file", &savestate_slot, 10,
1484 "Restore gameplay from a savestate file.\n"
1485 "Note: The same file used to save the state must be\n"
1487 numeric_selection_option(menu_change_state,
1488 "Current savestate slot", &savestate_slot, 10,
1489 "Change the current savestate slot.\n", 11),
1490 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1493 make_menu(savestate, submenu_savestate, NULL);
1497 menu_option_type gamepad_config_options[] =
1499 gamepad_config_option("D-pad up ", 0),
1500 gamepad_config_option("D-pad down ", 1),
1501 gamepad_config_option("D-pad left ", 2),
1502 gamepad_config_option("D-pad right ", 3),
1503 gamepad_config_option("Circle ", 4),
1504 gamepad_config_option("Cross ", 5),
1505 gamepad_config_option("Square ", 6),
1506 gamepad_config_option("Triangle ", 7),
1507 gamepad_config_option("Left Trigger ", 8),
1508 gamepad_config_option("Right Trigger", 9),
1509 gamepad_config_option("Start ", 10),
1510 gamepad_config_option("Select ", 11),
1511 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1515 menu_option_type analog_config_options[] =
1517 analog_config_option("Analog up ", 0),
1518 analog_config_option("Analog down ", 1),
1519 analog_config_option("Analog left ", 2),
1520 analog_config_option("Analog right", 3),
1521 string_selection_option(NULL, "Enable analog", yes_no_options,
1522 &global_enable_analog, 2,
1523 "Select 'no' to block analog input entirely.", 7),
1524 numeric_selection_option(NULL, "Analog sensitivity",
1525 &analog_sensitivity_level, 10,
1526 "Determine sensitivity/responsiveness of the analog input.\n"
1527 "Lower numbers are less sensitive.", 8),
1528 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1533 #if defined(GP2X_BUILD) || defined(PND_BUILD)
1535 menu_option_type gamepad_config_options[] =
1537 gamepad_config_option("D-pad up ", 0),
1538 gamepad_config_option("D-pad down ", 1),
1539 gamepad_config_option("D-pad left ", 2),
1540 gamepad_config_option("D-pad right ", 3),
1541 gamepad_config_option("A ", 4),
1542 gamepad_config_option("B ", 5),
1543 gamepad_config_option("X ", 6),
1544 gamepad_config_option("Y ", 7),
1545 gamepad_config_option("Left Trigger ", 8),
1546 gamepad_config_option("Right Trigger", 9),
1548 gamepad_config_option("Menu ", 10),
1549 gamepad_config_option("Select ", 11),
1550 #elif defined(POLLUX_BUILD)
1551 gamepad_config_option("I ", 10),
1552 gamepad_config_option("II ", 11),
1553 gamepad_config_option("Push ", 12),
1554 gamepad_config_option("Home ", 13),
1555 #elif defined(PND_BUILD)
1556 gamepad_config_option("Start ", 10),
1557 gamepad_config_option("Select ", 11),
1558 gamepad_config_option("1 ", 12),
1559 gamepad_config_option("2 ", 13),
1560 gamepad_config_option("3 ", 14),
1561 gamepad_config_option("4 ", 15),
1563 gamepad_config_option("Start ", 10),
1564 gamepad_config_option("Select ", 11),
1565 gamepad_config_option("Stick Push ", 12),
1568 submenu_option(NULL, "Back", "Return to the main menu.", 16)
1570 submenu_option(NULL, "Back", "Return to the main menu.", 14)
1575 menu_option_type analog_config_options[] =
1577 #if defined(POLLUX_BUILD)
1578 numeric_selection_option(NULL, "Analog sensitivity",
1579 &analog_sensitivity_level, 10,
1580 "Determine sensitivity/responsiveness of the analog input.\n"
1581 "Lower numbers are less sensitive.", 8),
1583 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1588 #if defined(PC_BUILD) || defined(RPI_BUILD)
1590 menu_option_type gamepad_config_options[] =
1592 submenu_option(NULL, "Back", "Return to the main menu.", 13)
1595 menu_option_type analog_config_options[] =
1597 submenu_option(NULL, "Back", "Return to the main menu.", 11)
1602 make_menu(gamepad_config, submenu_gamepad, NULL);
1603 make_menu(analog_config, submenu_analog, NULL);
1605 menu_option_type main_options[] =
1607 submenu_option(&graphics_sound_menu, "Graphics and Sound options",
1608 "Select to set display parameters and frameskip\n"
1609 "behavior, audio on/off, buffer size, and filtering.", 0),
1610 numeric_selection_action_option(menu_load_state, NULL,
1611 "Load state from slot", &savestate_slot, 10,
1612 "Select to load the game state from the current slot\n"
1613 "for this game, if it exists.\n"
1614 "Press left + right to change the current slot.", 2),
1615 numeric_selection_action_option(menu_save_state, NULL,
1616 "Save state to slot", &savestate_slot, 10,
1617 "Select to save the game state to the current slot\n"
1618 "for this game. See the extended menu for more info.\n"
1619 "Press left + right to change the current slot.", 3),
1620 submenu_option(&savestate_menu, "Savestate options",
1621 "Select to enter a menu for loading, saving, and\n"
1622 "viewing the currently active savestate for this game\n"
1623 "(or to load a savestate file from another game)", 4),
1624 submenu_option(&gamepad_config_menu, "Configure gamepad input",
1625 "Select to change the in-game behavior of buttons\n"
1628 submenu_option(&analog_config_menu, "Configure analog input",
1629 "Select to change the in-game behavior of the analog nub.", 7),
1631 submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
1632 "Select to manage cheats, set backup behavior,\n"
1633 "and set device clock speed.", 9),
1634 action_option(menu_load, NULL, "Load new game",
1635 "Select to load a new game\n"
1636 "(will exit a game if currently playing).", 11),
1637 action_option(menu_restart, NULL, "Restart game",
1638 "Select to reset the GBA with the current game\n"
1640 action_option(menu_exit, NULL, "Return to game",
1641 "Select to exit this menu and resume gameplay.", 13),
1642 action_option(menu_quit, NULL, "Exit gpSP",
1643 "Select to exit gpSP and return to the menu.", 15)
1646 make_menu(main, submenu_main, NULL);
1648 void choose_menu(menu_type *new_menu)
1650 if(new_menu == NULL)
1651 new_menu = &main_menu;
1653 clear_screen(COLOR_BG);
1656 blit_to_screen(original_screen, 240, 160, 230, 40);
1659 current_menu = new_menu;
1660 current_option = new_menu->options;
1661 current_option_num = 0;
1662 if(current_menu->init_function)
1663 current_menu->init_function();
1668 for(i = 0; i < 6; i++)
1670 print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
1674 menu_update_clock();
1675 video_resolution_large();
1678 SDL_LockMutex(sound_mutex);
1683 SDL_UnlockMutex(sound_mutex);
1686 if(gamepak_filename[0] == 0)
1689 memset(original_screen, 0x00, 240 * 160 * 2);
1690 print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
1691 60, 75,original_screen, 240, 0, 0, FONT_HEIGHT);
1694 choose_menu(&main_menu);
1696 for(i = 0; i < 10; i++)
1700 sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1704 sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1705 cheats[i].cheat_name);
1709 current_menu->init_function();
1713 display_option = current_menu->options;
1715 for(i = 0; i < current_menu->num_options; i++, display_option++)
1717 if(display_option->option_type & NUMBER_SELECTION_OPTION)
1719 sprintf(line_buffer, display_option->display_string,
1720 *(display_option->current_option));
1724 if(display_option->option_type & STRING_SELECTION_OPTION)
1726 sprintf(line_buffer, display_option->display_string,
1727 ((u32 *)display_option->options)[*(display_option->current_option)]);
1731 strcpy(line_buffer, display_option->display_string);
1734 if(display_option == current_option)
1736 print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 6,
1737 (display_option->line_number * 10) + 40, 36);
1741 print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 6,
1742 (display_option->line_number * 10) + 40, 36);
1746 print_string(current_option->help_string, COLOR_HELP_TEXT,
1751 gui_action = get_gui_input();
1756 current_option_num = (current_option_num + 1) %
1757 current_menu->num_options;
1759 current_option = current_menu->options + current_option_num;
1764 if(current_option_num)
1765 current_option_num--;
1767 current_option_num = current_menu->num_options - 1;
1769 current_option = current_menu->options + current_option_num;
1774 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1775 STRING_SELECTION_OPTION))
1777 *(current_option->current_option) =
1778 (*current_option->current_option + 1) %
1779 current_option->num_options;
1781 if(current_option->passive_function)
1782 current_option->passive_function();
1787 if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1788 STRING_SELECTION_OPTION))
1790 u32 current_option_val = *(current_option->current_option);
1792 if(current_option_val)
1793 current_option_val--;
1795 current_option_val = current_option->num_options - 1;
1797 *(current_option->current_option) = current_option_val;
1799 if(current_option->passive_function)
1800 current_option->passive_function();
1805 if(current_menu == &main_menu)
1808 choose_menu(&main_menu);
1812 if(current_option->option_type & ACTION_OPTION)
1813 current_option->action_function();
1815 if(current_option->option_type & SUBMENU_OPTION)
1816 choose_menu(current_option->sub_menu);
1818 if(current_menu == &main_menu)
1819 choose_menu(&main_menu);
1828 set_gba_resolution(screen_scale);
1829 video_resolution_small();
1830 menu_get_clock_speed();
1834 num_skipped_frames = 100;
1836 return return_value;