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