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