move platform-independent stuff out of /gp2x.
[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   u8 *clock_speed_options[] =
83   {
84     "33MHz", "66MHz", "100MHz", "133MHz", "166MHz", "200MHz", "233MHz",
85     "266MHz", "300MHz", "333MHz"
86   };
87   #define menu_get_clock_speed() \
88     clock_speed = (clock_speed_number + 1) * 33
89   #define get_clock_speed_number() \
90     clock_speed_number = (clock_speed / 33) - 1
91 #elif defined(WIZ_BUILD)
92   u8 *clock_speed_options[] =
93   {
94     "300MHz", "333MHz", "366MHz", "400MHz", "433MHz",
95     "466MHz", "500MHz", "533MHz", "566MHz", "600MHz",
96     "633MHz", "666MHz", "700MHz", "733MHz", "766MHz",
97     "800MHz", "833MHz", "866MHz", "900MHz"
98   };
99   #define menu_get_clock_speed() \
100     clock_speed = 300 + (clock_speed_number * 3333) / 100
101   #define get_clock_speed_number() \
102     clock_speed_number = (clock_speed - 300) / 33
103 #elif defined(GP2X_BUILD)
104   u8 *clock_speed_options[] =
105   {
106     "150MHz", "160MHz", "170MHz", "180MHz", "190MHz",
107     "200MHz", "210MHz", "220MHz", "230MHz", "240MHz",
108     "250MHz", "260MHz", "270MHz", "280MHz", "290MHz"
109   };
110   #define menu_get_clock_speed() \
111     clock_speed = 150 + clock_speed_number * 10
112   #define get_clock_speed_number() \
113     clock_speed_number = (clock_speed - 150) / 10
114 #else
115   u8 *clock_speed_options[] =
116   {
117     "0"
118   };
119   #define menu_get_clock_speed() 0
120   #define get_clock_speed_number() 0
121 #endif
122
123
124 int sort_function(const void *dest_str_ptr, const void *src_str_ptr)
125 {
126   char *dest_str = *((char **)dest_str_ptr);
127   char *src_str = *((char **)src_str_ptr);
128
129   if(src_str[0] == '.')
130     return 1;
131
132   if(dest_str[0] == '.')
133     return -1;
134
135   return strcasecmp(dest_str, src_str);
136 }
137
138 s32 load_file(u8 **wildcards, u8 *result)
139 {
140   DIR *current_dir;
141   struct dirent *current_file;
142   struct stat file_info;
143   u8 current_dir_name[MAX_PATH];
144   u8 current_dir_short[81];
145   u32 current_dir_length;
146   u32 total_filenames_allocated;
147   u32 total_dirnames_allocated;
148   u8 **file_list;
149   u8 **dir_list;
150   u32 num_files;
151   u32 num_dirs;
152   u8 *file_name;
153   u32 file_name_length;
154   u32 ext_pos = -1;
155   u32 chosen_file, chosen_dir;
156   u32 dialog_result = 1;
157   s32 return_value = 1;
158   s32 current_file_selection;
159   s32 current_file_scroll_value;
160   u32 current_dir_selection;
161   u32 current_dir_scroll_value;
162   s32 current_file_in_scroll;
163   u32 current_dir_in_scroll;
164   u32 current_file_number, current_dir_number;
165   u32 current_column = 0;
166   u32 repeat;
167   u32 i;
168   gui_action_type gui_action;
169
170   while(return_value == 1)
171   {
172     current_file_selection = 0;
173     current_file_scroll_value = 0;
174     current_dir_selection = 0;
175     current_dir_scroll_value = 0;
176     current_file_in_scroll = 0;
177     current_dir_in_scroll = 0;
178
179     total_filenames_allocated = 32;
180     total_dirnames_allocated = 32;
181     file_list = (u8 **)malloc(sizeof(u8 *) * 32);
182     dir_list = (u8 **)malloc(sizeof(u8 *) * 32);
183     memset(file_list, 0, sizeof(u8 *) * 32);
184     memset(dir_list, 0, sizeof(u8 *) * 32);
185
186     num_files = 0;
187     num_dirs = 0;
188     chosen_file = 0;
189     chosen_dir = 0;
190
191     getcwd(current_dir_name, MAX_PATH);
192
193     current_dir = opendir(current_dir_name);
194
195     do
196     {
197       if(current_dir)
198         current_file = readdir(current_dir);
199       else
200         current_file = NULL;
201
202       if(current_file)
203       {
204         file_name = current_file->d_name;
205         file_name_length = strlen(file_name);
206
207         if((stat(file_name, &file_info) >= 0) &&
208          ((file_name[0] != '.') || (file_name[1] == '.')))
209         {
210           if(S_ISDIR(file_info.st_mode))
211           {
212             dir_list[num_dirs] =
213              (u8 *)malloc(file_name_length + 1);
214
215             sprintf(dir_list[num_dirs], "%s", file_name);
216
217             num_dirs++;
218           }
219           else
220           {
221             // Must match one of the wildcards, also ignore the .
222             if(file_name_length >= 4)
223             {
224               if(file_name[file_name_length - 4] == '.')
225                 ext_pos = file_name_length - 4;
226               else
227
228               if(file_name[file_name_length - 3] == '.')
229                 ext_pos = file_name_length - 3;
230
231               else
232                 ext_pos = 0;
233
234               for(i = 0; wildcards[i] != NULL; i++)
235               {
236                 if(!strcasecmp((file_name + ext_pos),
237                  wildcards[i]))
238                 {
239                   file_list[num_files] =
240                    (u8 *)malloc(file_name_length + 1);
241
242                   sprintf(file_list[num_files], "%s", file_name);
243
244                   num_files++;
245                   break;
246                 }
247               }
248             }
249           }
250         }
251
252         if(num_files == total_filenames_allocated)
253         {
254           file_list = (u8 **)realloc(file_list, sizeof(u8 *) *
255            total_filenames_allocated * 2);
256           memset(file_list + total_filenames_allocated, 0,
257            sizeof(u8 *) * total_filenames_allocated);
258           total_filenames_allocated *= 2;
259         }
260
261         if(num_dirs == total_dirnames_allocated)
262         {
263           dir_list = (u8 **)realloc(dir_list, sizeof(u8 *) *
264            total_dirnames_allocated * 2);
265           memset(dir_list + total_dirnames_allocated, 0,
266            sizeof(u8 *) * total_dirnames_allocated);
267           total_dirnames_allocated *= 2;
268         }
269       }
270     } while(current_file);
271
272     qsort((void *)file_list, num_files, sizeof(u8 *), sort_function);
273     qsort((void *)dir_list, num_dirs, sizeof(u8 *), sort_function);
274
275     closedir(current_dir);
276
277     current_dir_length = strlen(current_dir_name);
278
279     if(current_dir_length > 80)
280     {
281
282 #ifdef GP2X_BUILD
283     snprintf(current_dir_short, 80,
284      "...%s", current_dir_name + current_dir_length - 77);
285 #else
286     memcpy(current_dir_short, "...", 3);
287       memcpy(current_dir_short + 3,
288        current_dir_name + current_dir_length - 77, 77);
289       current_dir_short[80] = 0;
290 #endif
291     }
292     else
293     {
294 #ifdef GP2X_BUILD
295       snprintf(current_dir_short, 80, "%s", current_dir_name);
296 #else
297       memcpy(current_dir_short, current_dir_name,
298        current_dir_length + 1);
299 #endif
300     }
301
302     repeat = 1;
303
304     if(num_files == 0)
305       current_column = 1;
306
307     clear_screen(COLOR_BG);
308   {
309     u8 print_buffer[81];
310
311     while(repeat)
312     {
313       flip_screen();
314
315       print_string(current_dir_short, COLOR_ACTIVE_ITEM, COLOR_BG, 0, 0);
316 #ifdef GP2X_BUILD
317       print_string("Press X to return to the main menu.",
318        COLOR_HELP_TEXT, COLOR_BG, 20, 220);
319 #else
320       print_string("Press X to return to the main menu.",
321        COLOR_HELP_TEXT, COLOR_BG, 20, 260);
322 #endif
323
324       for(i = 0, current_file_number = i + current_file_scroll_value;
325        i < FILE_LIST_ROWS; i++, current_file_number++)
326       {
327         if(current_file_number < num_files)
328         {
329           if((current_file_number == current_file_selection) &&
330            (current_column == 0))
331           {
332             print_string(file_list[current_file_number], COLOR_ACTIVE_ITEM,
333              COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
334           }
335           else
336           {
337             print_string(file_list[current_file_number], COLOR_INACTIVE_ITEM,
338              COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10));
339           }
340         }
341       }
342
343       for(i = 0, current_dir_number = i + current_dir_scroll_value;
344        i < FILE_LIST_ROWS; i++, current_dir_number++)
345       {
346         if(current_dir_number < num_dirs)
347         {
348           if((current_dir_number == current_dir_selection) &&
349            (current_column == 1))
350           {
351             print_string(dir_list[current_dir_number], COLOR_ACTIVE_ITEM,
352              COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
353           }
354           else
355           {
356             print_string(dir_list[current_dir_number], COLOR_INACTIVE_ITEM,
357              COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10));
358           }
359         }
360       }
361
362       gui_action = get_gui_input();
363
364       switch(gui_action)
365       {
366         case CURSOR_DOWN:
367           if(current_column == 0)
368           {
369             if(current_file_selection < (num_files - 1))
370             {
371               current_file_selection++;
372               if(current_file_in_scroll == (FILE_LIST_ROWS - 1))
373               {
374                 clear_screen(COLOR_BG);
375                 current_file_scroll_value++;
376               }
377               else
378               {
379                 current_file_in_scroll++;
380               }
381             }
382             else
383             {
384               clear_screen(COLOR_BG);
385               current_file_selection = 0;
386               current_file_scroll_value = 0;
387               current_file_in_scroll = 0;
388             }
389           }
390           else
391           {
392             if(current_dir_selection < (num_dirs - 1))
393             {
394               current_dir_selection++;
395               if(current_dir_in_scroll == (FILE_LIST_ROWS - 1))
396               {
397                 clear_screen(COLOR_BG);
398                 current_dir_scroll_value++;
399               }
400               else
401               {
402                 current_dir_in_scroll++;
403               }
404             }
405           }
406
407           break;
408
409         case CURSOR_R:
410           if (current_column != 0)
411             break;
412           clear_screen(COLOR_BG);
413           current_file_selection += FILE_LIST_ROWS;
414           if (current_file_selection > num_files - 1)
415             current_file_selection = num_files - 1;
416           current_file_scroll_value = current_file_selection - FILE_LIST_ROWS / 2;
417           if (current_file_scroll_value < 0)
418           {
419             current_file_scroll_value = 0;
420             current_file_in_scroll = current_file_selection;
421           }
422           else
423           {
424             current_file_in_scroll = FILE_LIST_ROWS / 2;
425           }
426           break;
427
428         case CURSOR_UP:
429           if(current_column == 0)
430           {
431             if(current_file_selection)
432             {
433               current_file_selection--;
434               if(current_file_in_scroll == 0)
435               {
436                 clear_screen(COLOR_BG);
437                 current_file_scroll_value--;
438               }
439               else
440               {
441                 current_file_in_scroll--;
442               }
443             }
444             else
445             {
446               clear_screen(COLOR_BG);
447               current_file_selection = num_files - 1;
448               current_file_in_scroll = FILE_LIST_ROWS - 1;
449               if (current_file_in_scroll > num_files - 1)
450                 current_file_in_scroll = num_files - 1;
451               current_file_scroll_value = num_files - FILE_LIST_ROWS;
452               if (current_file_scroll_value < 0)
453                 current_file_scroll_value = 0;
454             }
455           }
456           else
457           {
458             if(current_dir_selection)
459             {
460               current_dir_selection--;
461               if(current_dir_in_scroll == 0)
462               {
463                 clear_screen(COLOR_BG);
464                 current_dir_scroll_value--;
465               }
466               else
467               {
468                 current_dir_in_scroll--;
469               }
470             }
471           }
472           break;
473
474         case CURSOR_L:
475           if (current_column != 0)
476             break;
477           clear_screen(COLOR_BG);
478           current_file_selection -= FILE_LIST_ROWS;
479           if (current_file_selection < 0)
480             current_file_selection = 0;
481           current_file_scroll_value = current_file_selection - FILE_LIST_ROWS / 2;
482           if (current_file_scroll_value < 0)
483           {
484             current_file_scroll_value = 0;
485             current_file_in_scroll = current_file_selection;
486           }
487           else
488           {
489             current_file_in_scroll = FILE_LIST_ROWS / 2;
490           }
491           break;
492
493          case CURSOR_RIGHT:
494           if(current_column == 0)
495           {
496             if(num_dirs != 0)
497               current_column = 1;
498           }
499           break;
500
501         case CURSOR_LEFT:
502           if(current_column == 1)
503           {
504             if(num_files != 0)
505               current_column = 0;
506           }
507           break;
508
509         case CURSOR_SELECT:
510           if(current_column == 1)
511           {
512             repeat = 0;
513             chdir(dir_list[current_dir_selection]);
514           }
515           else
516           {
517             if(num_files != 0)
518             {
519               repeat = 0;
520               return_value = 0;
521               strcpy(result, file_list[current_file_selection]);
522             }
523           }
524           break;
525
526         case CURSOR_BACK:
527 #ifdef PSP_BUILD
528           if(!strcmp(current_dir_name, "ms0:/PSP"))
529             break;
530 #endif
531           repeat = 0;
532           chdir("..");
533           break;
534
535         case CURSOR_EXIT:
536           return_value = -1;
537           repeat = 0;
538           break;
539       }
540     }
541   }
542
543     for(i = 0; i < num_files; i++)
544     {
545       free(file_list[i]);
546     }
547     free(file_list);
548
549     for(i = 0; i < num_dirs; i++)
550     {
551       free(dir_list[i]);
552     }
553     free(dir_list);
554   }
555
556   clear_screen(COLOR_BG);
557
558   return return_value;
559 }
560
561 typedef enum
562 {
563   NUMBER_SELECTION_OPTION = 0x01,
564   STRING_SELECTION_OPTION = 0x02,
565   SUBMENU_OPTION          = 0x04,
566   ACTION_OPTION           = 0x08
567 } menu_option_type_enum;
568
569 struct _menu_type
570 {
571   void (* init_function)();
572   void (* passive_function)();
573   struct _menu_option_type *options;
574   u32 num_options;
575 };
576
577 struct _menu_option_type
578 {
579   void (* action_function)();
580   void (* passive_function)();
581   struct _menu_type *sub_menu;
582   char *display_string;
583   void *options;
584   u32 *current_option;
585   u32 num_options;
586   char *help_string;
587   u32 line_number;
588   menu_option_type_enum option_type;
589 };
590
591 typedef struct _menu_option_type menu_option_type;
592 typedef struct _menu_type menu_type;
593
594 #define make_menu(name, init_function, passive_function)                      \
595   menu_type name##_menu =                                                     \
596   {                                                                           \
597     init_function,                                                            \
598     passive_function,                                                         \
599     name##_options,                                                           \
600     sizeof(name##_options) / sizeof(menu_option_type)                         \
601   }                                                                           \
602
603 #define gamepad_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 + gamepad_config_line_to_button[number],                 \
611   sizeof(gamepad_config_buttons) / sizeof(gamepad_config_buttons[0]),         \
612   gamepad_help[gamepad_config_map[                                            \
613    gamepad_config_line_to_button[number]]],                                   \
614   number,                                                                     \
615   STRING_SELECTION_OPTION                                                     \
616 }                                                                             \
617
618 #define analog_config_option(display_string, number)                          \
619 {                                                                             \
620   NULL,                                                                       \
621   menu_fix_gamepad_help,                                                      \
622   NULL,                                                                       \
623   display_string ": %s",                                                      \
624   gamepad_config_buttons,                                                     \
625   gamepad_config_map + number + 12,                                           \
626   sizeof(gamepad_config_buttons) / sizeof(gamepad_config_buttons[0]),         \
627   gamepad_help[gamepad_config_map[number + 12]],                              \
628   number + 2,                                                                 \
629   STRING_SELECTION_OPTION                                                     \
630 }                                                                             \
631
632 #define cheat_option(number)                                                  \
633 {                                                                             \
634   NULL,                                                                       \
635   NULL,                                                                       \
636   NULL,                                                                       \
637   cheat_format_str[number],                                                   \
638   enable_disable_options,                                                     \
639   &(cheats[number].cheat_active),                                             \
640   2,                                                                          \
641   "Activate/deactivate this cheat code.",                                     \
642   number,                                                                     \
643   STRING_SELECTION_OPTION                                                     \
644 }                                                                             \
645
646 #define action_option(action_function, passive_function, display_string,      \
647  help_string, line_number)                                                    \
648 {                                                                             \
649   action_function,                                                            \
650   passive_function,                                                           \
651   NULL,                                                                       \
652   display_string,                                                             \
653   NULL,                                                                       \
654   NULL,                                                                       \
655   0,                                                                          \
656   help_string,                                                                \
657   line_number,                                                                \
658   ACTION_OPTION                                                               \
659 }                                                                             \
660
661 #define submenu_option(sub_menu, display_string, help_string, line_number)    \
662 {                                                                             \
663   NULL,                                                                       \
664   NULL,                                                                       \
665   sub_menu,                                                                   \
666   display_string,                                                             \
667   NULL,                                                                       \
668   NULL,                                                                       \
669   sizeof(sub_menu) / sizeof(menu_option_type),                                \
670   help_string,                                                                \
671   line_number,                                                                \
672   SUBMENU_OPTION                                                              \
673 }                                                                             \
674
675 #define selection_option(passive_function, display_string, options,           \
676  option_ptr, num_options, help_string, line_number, type)                     \
677 {                                                                             \
678   NULL,                                                                       \
679   passive_function,                                                           \
680   NULL,                                                                       \
681   display_string,                                                             \
682   options,                                                                    \
683   option_ptr,                                                                 \
684   num_options,                                                                \
685   help_string,                                                                \
686   line_number,                                                                \
687   type                                                                        \
688 }                                                                             \
689
690 #define action_selection_option(action_function, passive_function,            \
691  display_string, options, option_ptr, num_options, help_string, line_number,  \
692  type)                                                                        \
693 {                                                                             \
694   action_function,                                                            \
695   passive_function,                                                           \
696   NULL,                                                                       \
697   display_string,                                                             \
698   options,                                                                    \
699   option_ptr,                                                                 \
700   num_options,                                                                \
701   help_string,                                                                \
702   line_number,                                                                \
703   type | ACTION_OPTION                                                        \
704 }                                                                             \
705
706
707 #define string_selection_option(passive_function, display_string, options,    \
708  option_ptr, num_options, help_string, line_number)                           \
709   selection_option(passive_function, display_string ": %s", options,          \
710    option_ptr, num_options, help_string, line_number, STRING_SELECTION_OPTION)\
711
712 #define numeric_selection_option(passive_function, display_string,            \
713  option_ptr, num_options, help_string, line_number)                           \
714   selection_option(passive_function, display_string ": %d", NULL, option_ptr, \
715    num_options, help_string, line_number, NUMBER_SELECTION_OPTION)            \
716
717 #define string_selection_action_option(action_function, passive_function,     \
718  display_string, options, option_ptr, num_options, help_string, line_number)  \
719   action_selection_option(action_function, passive_function,                  \
720    display_string ": %s",  options, option_ptr, num_options, help_string,     \
721    line_number, STRING_SELECTION_OPTION)                                      \
722
723 #define numeric_selection_action_option(action_function, passive_function,    \
724  display_string, option_ptr, num_options, help_string, line_number)           \
725   action_selection_option(action_function, passive_function,                  \
726    display_string ": %d",  NULL, option_ptr, num_options, help_string,        \
727    line_number, NUMBER_SELECTION_OPTION)                                      \
728
729 #define numeric_selection_action_hide_option(action_function,                 \
730  passive_function, display_string, option_ptr, num_options, help_string,      \
731  line_number)                                                                 \
732   action_selection_option(action_function, passive_function,                  \
733    display_string, NULL, option_ptr, num_options, help_string,                \
734    line_number, NUMBER_SELECTION_OPTION)                                      \
735
736
737 #define GAMEPAD_MENU_WIDTH 15
738
739 #ifdef PSP_BUILD
740
741 u32 gamepad_config_line_to_button[] =
742  { 8, 6, 7, 9, 1, 2, 3, 0, 4, 5, 11, 10 };
743
744 #endif
745
746 #ifdef GP2X_BUILD
747
748 u32 gamepad_config_line_to_button[] =
749  { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 14 };
750
751 #endif
752
753 u8 *scale_options[] =
754 {
755 #ifdef WIZ_BUILD
756   "unscaled 3:2", "scaled 3:2 (slower)",
757   "unscaled 3:2 (anti-tear)", "scaled 3:2 (anti-tear)"
758 #else
759   "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)"
760 #ifdef PSP_BUILD
761   " 16:9"
762 #endif
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(PSP_BUILD) || defined(ARM_ARCH)) && !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 exists for 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   u8 *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 entire\n"
1329      "screen. Select unscaled 3:2 for GBA resolution, scaled 3:2 for GBA\n"
1330      "aspect ratio scaled to fill the height of the PSP screen, and\n"
1331      "fullscreen to fill the entire PSP screen."
1332 #endif
1333      "", 2),
1334 #ifndef GP2X_BUILD
1335     string_selection_option(NULL, "Screen filtering", yes_no_options,
1336      (u32 *)(&screen_filter), 2,
1337      "Determines whether or not bilinear filtering should be used when\n"
1338      "scaling the screen. Selecting this will produce a more even and\n"
1339      "smooth image, at the cost of being blurry and having less vibrant\n"
1340      "colors.", 3),
1341 #endif
1342     string_selection_option(NULL, "Frameskip type", frameskip_options,
1343      (u32 *)(&current_frameskip_type), 3,
1344 #ifndef GP2X_BUILD
1345      "Determines what kind of frameskipping to use.\n"
1346      "Frameskipping may improve emulation speed of many games.\n"
1347 #endif
1348      "Off: Do not skip any frames.\n"
1349      "Auto: Skip up to N frames (see next opt) as needed.\n"
1350      "Manual: Always render only 1 out of N + 1 frames."
1351      , 5),
1352     numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100,
1353 #ifndef GP2X_BUILD
1354      "For auto frameskip, determines the maximum number of frames that\n"
1355      "are allowed to be skipped consecutively.\n"
1356      "For manual frameskip, determines the number of frames that will\n"
1357      "always be skipped."
1358 #endif
1359      "", 6),
1360     string_selection_option(NULL, "Framskip variation",
1361      frameskip_variation_options, &random_skip, 2,
1362 #ifndef GP2X_BUILD
1363      "If objects in the game flicker at a regular rate certain manual\n"
1364      "frameskip values may cause them to normally disappear. Change this\n"
1365      "value to 'random' to avoid this. Do not use otherwise, as it tends to\n"
1366      "make the image quality worse, especially in high motion games."
1367 #endif
1368      "", 7),
1369     string_selection_option(NULL, "Audio output", yes_no_options,
1370      &global_enable_audio, 2,
1371      "Select 'no' to turn off all audio output. This will\n"
1372      "not result in a significant change in performance.", 9),
1373 #ifndef PSP_BUILD
1374     string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1375              &audio_buffer_size_number, 11,
1376 #else
1377     string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1378              &audio_buffer_size_number, 10,
1379 #endif
1380
1381 #ifdef PSP_BUILD
1382      "Set the size (in bytes) of the audio buffer. Larger values may result\n"
1383      "in slightly better performance at the cost of latency; the lowest\n"
1384      "value will give the most responsive audio.\n"
1385      "This option requires gpSP to be restarted before it will take effect.",
1386 #else
1387      "Set the size (in bytes) of the audio buffer.\n"
1388      "This option requires gpSP restart to take effect.",
1389 #endif
1390      10),
1391     submenu_option(NULL, "Back", "Return to the main menu.", 12)
1392   };
1393
1394   make_menu(graphics_sound, submenu_graphics_sound, NULL);
1395
1396   menu_option_type cheats_misc_options[] =
1397   {
1398     cheat_option(0),
1399     cheat_option(1),
1400     cheat_option(2),
1401     cheat_option(3),
1402     cheat_option(4),
1403     cheat_option(5),
1404     cheat_option(6),
1405     cheat_option(7),
1406     cheat_option(8),
1407     cheat_option(9),
1408     string_selection_option(NULL, "Clock speed",
1409      clock_speed_options, &clock_speed_number,
1410      sizeof(clock_speed_options) / sizeof(clock_speed_options[0]),
1411      "Change the clock speed of the device. Higher clock\n"
1412      "speed will yield better performance, but will drain\n"
1413      "battery life further.", 11),
1414     string_selection_option(NULL, "Update backup",
1415      update_backup_options, &update_backup_flag, 2,
1416 #ifdef GP2X_BUILD
1417      "Determines when in-game save files should be\n"
1418      "written back to SD card.",
1419 #else
1420      "Determines when in-game save files should be written back to\n"
1421      "memstick. If set to 'automatic' writebacks will occur shortly after\n"
1422      "the game's backup is altered. On 'exit only' it will only be written\n"
1423      "back when you exit from this menu (NOT from using the home button).\n"
1424      "Use the latter with extreme care.",
1425 #endif
1426      12),
1427     submenu_option(NULL, "Back", "Return to the main menu.", 14)
1428   };
1429
1430   make_menu(cheats_misc, submenu_cheats_misc, NULL);
1431
1432   menu_option_type savestate_options[] =
1433   {
1434     numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1435      "Load savestate from current slot", &savestate_slot, 10,
1436      "Select to load the game state from the current slot\n"
1437      "for this game.\n"
1438      "Press left + right to change the current slot.", 6),
1439     numeric_selection_action_hide_option(menu_save_state, menu_change_state,
1440      "Save savestate to current slot", &savestate_slot, 10,
1441      "Select to save the game state to the current slot\n"
1442      "for this game.\n"
1443      "Press left + right to change the current slot.", 7),
1444     numeric_selection_action_hide_option(menu_load_state_file,
1445       menu_change_state,
1446      "Load savestate from file", &savestate_slot, 10,
1447      "Restore gameplay from a savestate file.\n"
1448      "Note: The same file used to save the state must be\n"
1449      "present.\n", 9),
1450     numeric_selection_option(menu_change_state,
1451      "Current savestate slot", &savestate_slot, 10,
1452      "Change the current savestate slot.\n", 11),
1453     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1454   };
1455
1456   make_menu(savestate, submenu_savestate, NULL);
1457
1458 #ifdef PSP_BUILD
1459
1460   menu_option_type gamepad_config_options[] =
1461   {
1462     gamepad_config_option("D-pad up     ", 0),
1463     gamepad_config_option("D-pad down   ", 1),
1464     gamepad_config_option("D-pad left   ", 2),
1465     gamepad_config_option("D-pad right  ", 3),
1466     gamepad_config_option("Circle       ", 4),
1467     gamepad_config_option("Cross        ", 5),
1468     gamepad_config_option("Square       ", 6),
1469     gamepad_config_option("Triangle     ", 7),
1470     gamepad_config_option("Left Trigger ", 8),
1471     gamepad_config_option("Right Trigger", 9),
1472     gamepad_config_option("Start        ", 10),
1473     gamepad_config_option("Select       ", 11),
1474     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1475   };
1476
1477
1478   menu_option_type analog_config_options[] =
1479   {
1480     analog_config_option("Analog up   ", 0),
1481     analog_config_option("Analog down ", 1),
1482     analog_config_option("Analog left ", 2),
1483     analog_config_option("Analog right", 3),
1484     string_selection_option(NULL, "Enable analog", yes_no_options,
1485      &global_enable_analog, 2,
1486      "Select 'no' to block analog input entirely.", 7),
1487     numeric_selection_option(NULL, "Analog sensitivity",
1488      &analog_sensitivity_level, 10,
1489      "Determine sensitivity/responsiveness of the analog input.\n"
1490      "Lower numbers are less sensitive.", 8),
1491     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1492   };
1493
1494 #endif
1495
1496 #ifdef GP2X_BUILD
1497
1498   menu_option_type gamepad_config_options[] =
1499   {
1500     gamepad_config_option("D-pad up     ", 0),
1501     gamepad_config_option("D-pad down   ", 1),
1502     gamepad_config_option("D-pad left   ", 2),
1503     gamepad_config_option("D-pad right  ", 3),
1504     gamepad_config_option("A            ", 4),
1505     gamepad_config_option("B            ", 5),
1506     gamepad_config_option("X            ", 6),
1507     gamepad_config_option("Y            ", 7),
1508     gamepad_config_option("Left Trigger ", 8),
1509     gamepad_config_option("Right Trigger", 9),
1510 #ifdef WIZ_BUILD
1511     gamepad_config_option("Menu         ", 10),
1512 #else
1513     gamepad_config_option("Start        ", 10),
1514 #endif
1515     gamepad_config_option("Select       ", 11),
1516 #ifndef WIZ_BUILD
1517     gamepad_config_option("Stick Push   ", 12),
1518 #endif
1519     submenu_option(NULL, "Back", "Return to the main menu.", 14)
1520   };
1521
1522
1523   menu_option_type analog_config_options[] =
1524   {
1525     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1526   };
1527
1528 #endif
1529
1530 #ifdef PC_BUILD
1531
1532   menu_option_type gamepad_config_options[] =
1533   {
1534     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1535   };
1536
1537   menu_option_type analog_config_options[] =
1538   {
1539     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1540   };
1541
1542 #endif
1543
1544   make_menu(gamepad_config, submenu_gamepad, NULL);
1545   make_menu(analog_config, submenu_analog, NULL);
1546
1547   menu_option_type main_options[] =
1548   {
1549     submenu_option(&graphics_sound_menu, "Graphics and Sound options",
1550      "Select to set display parameters and frameskip\n"
1551      "behavior, audio on/off, buffer size, and filtering.", 0),
1552     numeric_selection_action_option(menu_load_state, NULL,
1553      "Load state from slot", &savestate_slot, 10,
1554      "Select to load the game state from the current slot\n"
1555      "for this game, if it exists.\n"
1556      "Press left + right to change the current slot.", 2),
1557     numeric_selection_action_option(menu_save_state, NULL,
1558      "Save state to slot", &savestate_slot, 10,
1559      "Select to save the game state to the current slot\n"
1560      "for this game. See the extended menu for more info.\n"
1561      "Press left + right to change the current slot.", 3),
1562     submenu_option(&savestate_menu, "Savestate options",
1563      "Select to enter a menu for loading, saving, and\n"
1564      "viewing the currently active savestate for this game\n"
1565      "(or to load a savestate file from another game)", 4),
1566     submenu_option(&gamepad_config_menu, "Configure gamepad input",
1567      "Select to change the in-game behavior of buttons\n"
1568      "and d-pad.", 6),
1569 #ifndef GP2X_BUILD
1570     submenu_option(&analog_config_menu, "Configure analog input",
1571      "Select to change the in-game behavior of the PSP analog nub.", 7),
1572 #endif
1573     submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
1574      "Select to manage cheats, set backup behavior,\n"
1575      "and set device clock speed.", 9),
1576     action_option(menu_load, NULL, "Load new game",
1577      "Select to load a new game\n"
1578      "(will exit a game if currently playing).", 11),
1579     action_option(menu_restart, NULL, "Restart game",
1580      "Select to reset the GBA with the current game\n"
1581      "loaded.", 12),
1582     action_option(menu_exit, NULL, "Return to game",
1583      "Select to exit this menu and resume gameplay.", 13),
1584     action_option(menu_quit, NULL, "Exit gpSP",
1585      "Select to exit gpSP and return to the menu.", 15)
1586   };
1587
1588   make_menu(main, submenu_main, NULL);
1589
1590   void choose_menu(menu_type *new_menu)
1591   {
1592     if(new_menu == NULL)
1593       new_menu = &main_menu;
1594
1595     clear_screen(COLOR_BG);
1596
1597 #ifndef GP2X_BUILD
1598     blit_to_screen(original_screen, 240, 160, 230, 40);
1599 #endif
1600
1601     current_menu = new_menu;
1602     current_option = new_menu->options;
1603     current_option_num = 0;
1604     if(current_menu->init_function)
1605      current_menu->init_function();
1606   }
1607
1608   void clear_help()
1609   {
1610     for(i = 0; i < 6; i++)
1611     {
1612       print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
1613     }
1614   }
1615
1616   menu_update_clock();
1617   video_resolution_large();
1618
1619 #ifndef GP2X_BUILD
1620   SDL_LockMutex(sound_mutex);
1621 #endif
1622   SDL_PauseAudio(1);
1623
1624 #ifndef GP2X_BUILD
1625   SDL_UnlockMutex(sound_mutex);
1626 #endif
1627
1628   if(gamepak_filename[0] == 0)
1629   {
1630     first_load = 1;
1631     memset(original_screen, 0x00, 240 * 160 * 2);
1632     print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
1633      60, 75,original_screen, 240, 0, 0, FONT_HEIGHT);
1634   }
1635
1636   choose_menu(&main_menu);
1637
1638   for(i = 0; i < 10; i++)
1639   {
1640     if(i >= num_cheats)
1641     {
1642       sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1643     }
1644     else
1645     {
1646       sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1647        cheats[i].cheat_name);
1648     }
1649   }
1650
1651   current_menu->init_function();
1652
1653   while(repeat)
1654   {
1655     display_option = current_menu->options;
1656
1657     for(i = 0; i < current_menu->num_options; i++, display_option++)
1658     {
1659       if(display_option->option_type & NUMBER_SELECTION_OPTION)
1660       {
1661         sprintf(line_buffer, display_option->display_string,
1662          *(display_option->current_option));
1663       }
1664       else
1665
1666       if(display_option->option_type & STRING_SELECTION_OPTION)
1667       {
1668         sprintf(line_buffer, display_option->display_string,
1669          ((u32 *)display_option->options)[*(display_option->current_option)]);
1670       }
1671       else
1672       {
1673         strcpy(line_buffer, display_option->display_string);
1674       }
1675
1676       if(display_option == current_option)
1677       {
1678         print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 10,
1679          (display_option->line_number * 10) + 40, 41);
1680       }
1681       else
1682       {
1683         print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 10,
1684          (display_option->line_number * 10) + 40, 41);
1685       }
1686     }
1687
1688     print_string(current_option->help_string, COLOR_HELP_TEXT,
1689      COLOR_BG, 8, 210);
1690
1691     flip_screen();
1692
1693     gui_action = get_gui_input();
1694
1695     switch(gui_action)
1696     {
1697       case CURSOR_DOWN:
1698         current_option_num = (current_option_num + 1) %
1699           current_menu->num_options;
1700
1701         current_option = current_menu->options + current_option_num;
1702         clear_help();
1703         break;
1704
1705       case CURSOR_UP:
1706         if(current_option_num)
1707           current_option_num--;
1708         else
1709           current_option_num = current_menu->num_options - 1;
1710
1711         current_option = current_menu->options + current_option_num;
1712         clear_help();
1713         break;
1714
1715       case CURSOR_RIGHT:
1716         if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1717          STRING_SELECTION_OPTION))
1718         {
1719           *(current_option->current_option) =
1720            (*current_option->current_option + 1) %
1721            current_option->num_options;
1722
1723           if(current_option->passive_function)
1724             current_option->passive_function();
1725         }
1726         break;
1727
1728       case CURSOR_LEFT:
1729         if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1730          STRING_SELECTION_OPTION))
1731         {
1732           u32 current_option_val = *(current_option->current_option);
1733
1734           if(current_option_val)
1735             current_option_val--;
1736           else
1737             current_option_val = current_option->num_options - 1;
1738
1739           *(current_option->current_option) = current_option_val;
1740
1741           if(current_option->passive_function)
1742             current_option->passive_function();
1743         }
1744         break;
1745
1746       case CURSOR_EXIT:
1747         if(current_menu == &main_menu)
1748           menu_exit();
1749
1750         choose_menu(&main_menu);
1751         break;
1752
1753       case CURSOR_SELECT:
1754         if(current_option->option_type & ACTION_OPTION)
1755           current_option->action_function();
1756
1757         if(current_option->option_type & SUBMENU_OPTION)
1758           choose_menu(current_option->sub_menu);
1759         break;
1760     }
1761   }
1762
1763   set_gba_resolution(screen_scale);
1764   video_resolution_small();
1765   menu_get_clock_speed();
1766   set_clock_speed();
1767
1768   SDL_PauseAudio(0);
1769
1770   return return_value;
1771 }