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