revive PC build, support Linux
[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 #include "common.h"
29 #include "font.h"
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   char *display_string;
573   void *options;
574   u32 *current_option;
575   u32 num_options;
576   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 u8 *scale_options[] =
744 {
745 #ifdef WIZ_BUILD
746   "unscaled 3:2", "scaled 3:2 (slower)",
747   "unscaled 3:2 (anti-tear)", "scaled 3:2 (anti-tear)"
748 #else
749   "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)"
750 #ifdef PSP_BUILD
751   " 16:9"
752 #endif
753 #endif
754 };
755
756 s32 load_game_config_file()
757 {
758   u8 game_config_filename[512];
759   u32 file_loaded = 0;
760   u32 i;
761   change_ext(gamepak_filename, game_config_filename, ".cfg");
762
763   file_open(game_config_file, game_config_filename, read);
764
765   if(file_check_valid(game_config_file))
766   {
767     u32 file_size = file_length(game_config_filename, game_config_file);
768
769     // Sanity check: File size must be the right size
770     if(file_size == 56)
771     {
772       u32 file_options[file_size / 4];
773
774       file_read_array(game_config_file, file_options);
775       current_frameskip_type = file_options[0] % 3;
776       frameskip_value = file_options[1];
777       random_skip = file_options[2] % 2;
778       clock_speed = file_options[3];
779
780 #ifdef WIZ_BUILD
781       if(clock_speed > 900)
782         clock_speed = 533;
783 #elif defined(GP2X_BUILD)
784       if(clock_speed >= 300)
785         clock_speed = 200;
786 #else
787       if(clock_speed > 333)
788         clock_speed = 333;
789 #endif
790
791       if(clock_speed < 33)
792         clock_speed = 33;
793
794       if(frameskip_value < 0)
795         frameskip_value = 0;
796
797       if(frameskip_value > 99)
798         frameskip_value = 99;
799
800       for(i = 0; i < 10; i++)
801       {
802         cheats[i].cheat_active = file_options[3 + i] % 2;
803         cheats[i].cheat_name[0] = 0;
804       }
805
806       file_close(game_config_file);
807       file_loaded = 1;
808     }
809   }
810
811   if(file_loaded)
812     return 0;
813
814   current_frameskip_type = auto_frameskip;
815   frameskip_value = 4;
816 #ifdef WIZ_BUILD
817   frameskip_value = 1;
818 #endif
819   random_skip = 0;
820   clock_speed = default_clock_speed;
821
822   for(i = 0; i < 10; i++)
823   {
824     cheats[i].cheat_active = 0;
825     cheats[i].cheat_name[0] = 0;
826   }
827
828   return -1;
829 }
830
831 s32 load_config_file()
832 {
833   u8 config_path[512];
834
835   #if defined(_WIN32) || defined(_WIN32_WCE)
836     sprintf(config_path, "%s\\%s", main_path, GPSP_CONFIG_FILENAME);
837   #else
838     sprintf(config_path, "%s/%s", main_path, GPSP_CONFIG_FILENAME);
839   #endif
840
841   file_open(config_file, config_path, read);
842
843   if(file_check_valid(config_file))
844   {
845     u32 file_size = file_length(config_path, config_file);
846
847     // Sanity check: File size must be the right size
848     if(file_size == 92)
849     {
850       u32 file_options[file_size / 4];
851       u32 i;
852       s32 menu_button = -1;
853       file_read_array(config_file, file_options);
854
855       screen_scale = file_options[0] %
856         (sizeof(scale_options) / sizeof(scale_options[0]));
857       screen_filter = file_options[1] % 2;
858       global_enable_audio = file_options[2] % 2;
859
860 #ifdef PSP_BUILD
861       audio_buffer_size_number = file_options[3] % 10;
862 #else
863       audio_buffer_size_number = file_options[3] % 11;
864 #endif
865
866       update_backup_flag = file_options[4] % 2;
867       global_enable_analog = file_options[5] % 2;
868       analog_sensitivity_level = file_options[6] % 8;
869
870 #ifdef PSP_BUILD
871     scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
872 #endif
873
874       // Sanity check: Make sure there's a MENU or FRAMESKIP
875       // key, if not assign to triangle
876
877 #ifndef PC_BUILD
878       for(i = 0; i < 16; i++)
879       {
880         gamepad_config_map[i] = file_options[7 + i] %
881          (BUTTON_ID_NONE + 1);
882
883         if(gamepad_config_map[i] == BUTTON_ID_MENU)
884         {
885           menu_button = i;
886         }
887       }
888
889       if(menu_button == -1)
890       {
891         gamepad_config_map[0] = BUTTON_ID_MENU;
892       }
893 #endif
894
895       file_close(config_file);
896     }
897
898     return 0;
899   }
900
901   return -1;
902 }
903
904 s32 save_game_config_file()
905 {
906   u8 game_config_filename[512];
907   u32 i;
908
909   change_ext(gamepak_filename, game_config_filename, ".cfg");
910
911   file_open(game_config_file, game_config_filename, write);
912
913   if(file_check_valid(game_config_file))
914   {
915     u32 file_options[14];
916
917     file_options[0] = current_frameskip_type;
918     file_options[1] = frameskip_value;
919     file_options[2] = random_skip;
920     file_options[3] = clock_speed;
921
922     for(i = 0; i < 10; i++)
923     {
924       file_options[4 + i] = cheats[i].cheat_active;
925     }
926
927     file_write_array(game_config_file, file_options);
928
929     file_close(game_config_file);
930
931     return 0;
932   }
933
934   return -1;
935 }
936
937 s32 save_config_file()
938 {
939   u8 config_path[512];
940
941   #if (defined(PSP_BUILD) || defined(ARM_ARCH)) && !defined(_WIN32_WCE)
942     sprintf(config_path, "%s/%s", main_path, GPSP_CONFIG_FILENAME);
943   #else
944     sprintf(config_path, "%s\\%s", main_path, GPSP_CONFIG_FILENAME);
945   #endif
946
947   file_open(config_file, config_path, write);
948
949   save_game_config_file();
950
951   if(file_check_valid(config_file))
952   {
953     u32 file_options[23];
954     u32 i;
955
956     file_options[0] = screen_scale;
957     file_options[1] = screen_filter;
958     file_options[2] = global_enable_audio;
959     file_options[3] = audio_buffer_size_number;
960     file_options[4] = update_backup_flag;
961     file_options[5] = global_enable_analog;
962     file_options[6] = analog_sensitivity_level;
963
964 #ifndef PC_BUILD
965     for(i = 0; i < 16; i++)
966     {
967       file_options[7 + i] = gamepad_config_map[i];
968     }
969 #endif
970
971     file_write_array(config_file, file_options);
972
973     file_close(config_file);
974
975     return 0;
976   }
977
978   return -1;
979 }
980
981 typedef enum
982 {
983   MAIN_MENU,
984   GAMEPAD_MENU,
985   SAVESTATE_MENU,
986   FRAMESKIP_MENU,
987   CHEAT_MENU
988 } menu_enum;
989
990 u32 savestate_slot = 0;
991
992 void get_savestate_snapshot(u8 *savestate_filename)
993 {
994   u16 snapshot_buffer[240 * 160];
995   u8 savestate_timestamp_string[80];
996
997   file_open(savestate_file, savestate_filename, read);
998
999   if(file_check_valid(savestate_file))
1000   {
1001     u8 weekday_strings[7][11] =
1002     {
1003       "Sunday", "Monday", "Tuesday", "Wednesday",
1004       "Thursday", "Friday", "Saturday"
1005     };
1006     time_t savestate_time_flat;
1007     struct tm *current_time;
1008     file_read_array(savestate_file, snapshot_buffer);
1009     file_read_variable(savestate_file, savestate_time_flat);
1010
1011     file_close(savestate_file);
1012
1013     current_time = localtime(&savestate_time_flat);
1014     sprintf(savestate_timestamp_string,
1015      "%s  %02d/%02d/%04d  %02d:%02d:%02d                ",
1016      weekday_strings[current_time->tm_wday], current_time->tm_mon + 1,
1017      current_time->tm_mday, current_time->tm_year + 1900,
1018      current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
1019
1020     savestate_timestamp_string[40] = 0;
1021     print_string(savestate_timestamp_string, COLOR_HELP_TEXT, COLOR_BG,
1022      10, 40);
1023   }
1024   else
1025   {
1026     memset(snapshot_buffer, 0, 240 * 160 * 2);
1027     print_string_ext("No savestate exists for this slot.",
1028      0xFFFF, 0x0000, 15, 75, snapshot_buffer, 240, 0, 0, FONT_HEIGHT);
1029     print_string("---------- --/--/---- --:--:--          ", COLOR_HELP_TEXT,
1030      COLOR_BG, 10, 40);
1031   }
1032
1033 #ifndef GP2X_BUILD
1034   blit_to_screen(snapshot_buffer, 240, 160, 230, 40);
1035 #endif
1036 }
1037
1038 void get_savestate_filename(u32 slot, u8 *name_buffer)
1039 {
1040   u8 savestate_ext[16];
1041
1042   sprintf(savestate_ext, "%d.svs", slot);
1043   change_ext(gamepak_filename, name_buffer, savestate_ext);
1044
1045   get_savestate_snapshot(name_buffer);
1046 }
1047
1048 void get_savestate_filename_noshot(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
1056 #ifdef PSP_BUILD
1057   void _flush_cache()
1058   {
1059     invalidate_all_cache();
1060   }
1061 #endif
1062
1063 u32 menu(u16 *original_screen)
1064 {
1065   u8 print_buffer[81];
1066   u32 clock_speed_number;
1067   u32 _current_option = 0;
1068   gui_action_type gui_action;
1069   menu_enum _current_menu = MAIN_MENU;
1070   u32 i;
1071   u32 repeat = 1;
1072   u32 return_value = 0;
1073   u32 first_load = 0;
1074   u8 savestate_ext[16];
1075   u8 current_savestate_filename[512];
1076   u8 line_buffer[80];
1077   u8 cheat_format_str[10][41];
1078
1079   menu_type *current_menu;
1080   menu_option_type *current_option;
1081   menu_option_type *display_option;
1082   u32 current_option_num;
1083
1084   auto void choose_menu();
1085   auto void clear_help();
1086
1087   u8 *gamepad_help[] =
1088   {
1089     "Up button on GBA d-pad.",
1090     "Down button on GBA d-pad.",
1091     "Left button on GBA d-pad.",
1092     "Right button on GBA d-pad.",
1093     "A button on GBA.",
1094     "B button on GBA.",
1095     "Left shoulder button on GBA.",
1096     "Right shoulder button on GBA.",
1097     "Start button on GBA.",
1098     "Select button on GBA.",
1099     "Brings up the options menu.",
1100     "Toggles fastforward on/off.",
1101     "Loads the game state from the current slot.",
1102     "Saves the game state to the current slot.",
1103     "Rapidly press/release the A button on GBA.",
1104     "Rapidly press/release the B button on GBA.",
1105     "Rapidly press/release the L shoulder on GBA.",
1106     "Rapidly press/release the R shoulder on GBA.",
1107     "Increases the volume.",
1108     "Decreases the volume.",
1109     "Displays virtual/drawn frames per second.",
1110     "Does nothing."
1111   };
1112
1113   void menu_update_clock()
1114   {
1115     get_clock_speed_number();
1116     if (clock_speed_number < 0 || clock_speed_number >=
1117      sizeof(clock_speed_options) / sizeof(clock_speed_options[0]))
1118     {
1119       clock_speed = default_clock_speed;
1120       get_clock_speed_number();
1121     }
1122   }
1123
1124   void menu_exit()
1125   {
1126     if(!first_load)
1127      repeat = 0;
1128   }
1129
1130   void menu_quit()
1131   {
1132     menu_get_clock_speed();
1133     save_config_file();
1134     quit();
1135   }
1136
1137   void menu_load()
1138   {
1139     u8 *file_ext[] = { ".gba", ".bin", ".zip", NULL };
1140     u8 load_filename[512];
1141     save_game_config_file();
1142     if(load_file(file_ext, load_filename) != -1)
1143     {
1144        if(load_gamepak(load_filename) == -1)
1145        {
1146          quit();
1147        }
1148        reset_gba();
1149        return_value = 1;
1150        repeat = 0;
1151        reg[CHANGED_PC_STATUS] = 1;
1152        menu_update_clock();
1153     }
1154     else
1155     {
1156       choose_menu(current_menu);
1157     }
1158   }
1159
1160   void menu_restart()
1161   {
1162     if(!first_load)
1163     {
1164       reset_gba();
1165       reg[CHANGED_PC_STATUS] = 1;
1166       return_value = 1;
1167       repeat = 0;
1168     }
1169   }
1170
1171   void menu_change_state()
1172   {
1173     get_savestate_filename(savestate_slot, current_savestate_filename);
1174   }
1175
1176   void menu_save_state()
1177   {
1178     if(!first_load)
1179     {
1180       get_savestate_filename_noshot(savestate_slot,
1181        current_savestate_filename);
1182       save_state(current_savestate_filename, original_screen);
1183     }
1184     menu_change_state();
1185   }
1186
1187   void menu_load_state()
1188   {
1189     if(!first_load)
1190     {
1191       load_state(current_savestate_filename);
1192       return_value = 1;
1193       repeat = 0;
1194     }
1195   }
1196
1197   void menu_load_state_file()
1198   {
1199     u8 *file_ext[] = { ".svs", NULL };
1200     u8 load_filename[512];
1201     if(load_file(file_ext, load_filename) != -1)
1202     {
1203       load_state(load_filename);
1204       return_value = 1;
1205       repeat = 0;
1206     }
1207     else
1208     {
1209       choose_menu(current_menu);
1210     }
1211   }
1212
1213   void menu_fix_gamepad_help()
1214   {
1215 #ifndef PC_BUILD
1216     clear_help();
1217     current_option->help_string =
1218      gamepad_help[gamepad_config_map[
1219      gamepad_config_line_to_button[current_option_num]]];
1220 #endif
1221   }
1222
1223   void submenu_graphics_sound()
1224   {
1225
1226   }
1227
1228   void submenu_cheats_misc()
1229   {
1230
1231   }
1232
1233   void submenu_gamepad()
1234   {
1235
1236   }
1237
1238   void submenu_analog()
1239   {
1240
1241   }
1242
1243   void submenu_savestate()
1244   {
1245     print_string("Savestate options:", COLOR_ACTIVE_ITEM, COLOR_BG, 10, 70);
1246     menu_change_state();
1247   }
1248
1249   void submenu_main()
1250   {
1251     strncpy(print_buffer, gamepak_filename, 80);
1252     print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 10);
1253     sprintf(print_buffer, "%s  %s  %s", gamepak_title,
1254      gamepak_code, gamepak_maker);
1255     print_string(print_buffer, COLOR_ROM_INFO, COLOR_BG, 10, 20);
1256
1257     get_savestate_filename_noshot(savestate_slot,
1258      current_savestate_filename);
1259   }
1260
1261   u8 *yes_no_options[] = { "no", "yes" };
1262   u8 *enable_disable_options[] = { "disabled", "enabled" };
1263
1264   u8 *frameskip_options[] = { "automatic", "manual", "off" };
1265   u8 *frameskip_variation_options[] = { "uniform", "random" };
1266
1267 #ifndef PSP_BUILD
1268   u8 *audio_buffer_options[] =
1269   {
1270     "16 bytes", "32 bytes", "64 bytes",
1271     "128 bytes", "256 bytes", "512 bytes", "1024 bytes", "2048 bytes",
1272     "4096 bytes", "8192 bytes", "16284 bytes"
1273   };
1274 #else
1275   u8 *audio_buffer_options[] =
1276   {
1277     "3072 bytes", "4096 bytes", "5120 bytes", "6144 bytes", "7168 bytes",
1278     "8192 bytes", "9216 bytes", "10240 bytes", "11264 bytes", "12288 bytes"
1279   };
1280
1281 #endif
1282
1283   u8 *update_backup_options[] = { "Exit only", "Automatic" };
1284
1285   u8 *gamepad_config_buttons[] =
1286   {
1287     "UP",
1288     "DOWN",
1289     "LEFT",
1290     "RIGHT",
1291     "A",
1292     "B",
1293     "L",
1294     "R",
1295     "START",
1296     "SELECT",
1297     "MENU",
1298     "FASTFORWARD",
1299     "LOAD STATE",
1300     "SAVE STATE",
1301     "RAPIDFIRE A",
1302     "RAPIDFIRE B",
1303     "RAPIDFIRE L",
1304     "RAPIDFIRE R",
1305     "VOLUME UP",
1306     "VOLUME DOWN",
1307     "DISPLAY FPS",
1308     "NOTHING"
1309   };
1310
1311   // Marker for help information, don't go past this mark (except \n)------*
1312   menu_option_type graphics_sound_options[] =
1313   {
1314     string_selection_option(NULL, "Display scaling", scale_options,
1315      (u32 *)(&screen_scale),
1316      sizeof(scale_options) / sizeof(scale_options[0]),
1317 #ifndef GP2X_BUILD
1318      "Determines how the GBA screen is resized in relation to the entire\n"
1319      "screen. Select unscaled 3:2 for GBA resolution, scaled 3:2 for GBA\n"
1320      "aspect ratio scaled to fill the height of the PSP screen, and\n"
1321      "fullscreen to fill the entire PSP screen."
1322 #endif
1323      "", 2),
1324 #ifndef GP2X_BUILD
1325     string_selection_option(NULL, "Screen filtering", yes_no_options,
1326      (u32 *)(&screen_filter), 2,
1327      "Determines whether or not bilinear filtering should be used when\n"
1328      "scaling the screen. Selecting this will produce a more even and\n"
1329      "smooth image, at the cost of being blurry and having less vibrant\n"
1330      "colors.", 3),
1331 #endif
1332     string_selection_option(NULL, "Frameskip type", frameskip_options,
1333      (u32 *)(&current_frameskip_type), 3,
1334 #ifndef GP2X_BUILD
1335      "Determines what kind of frameskipping to use.\n"
1336      "Frameskipping may improve emulation speed of many games.\n"
1337 #endif
1338      "Off: Do not skip any frames.\n"
1339      "Auto: Skip up to N frames (see next opt) as needed.\n"
1340      "Manual: Always render only 1 out of N + 1 frames."
1341      , 5),
1342     numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100,
1343 #ifndef GP2X_BUILD
1344      "For auto frameskip, determines the maximum number of frames that\n"
1345      "are allowed to be skipped consecutively.\n"
1346      "For manual frameskip, determines the number of frames that will\n"
1347      "always be skipped."
1348 #endif
1349      "", 6),
1350     string_selection_option(NULL, "Framskip variation",
1351      frameskip_variation_options, &random_skip, 2,
1352 #ifndef GP2X_BUILD
1353      "If objects in the game flicker at a regular rate certain manual\n"
1354      "frameskip values may cause them to normally disappear. Change this\n"
1355      "value to 'random' to avoid this. Do not use otherwise, as it tends to\n"
1356      "make the image quality worse, especially in high motion games."
1357 #endif
1358      "", 7),
1359     string_selection_option(NULL, "Audio output", yes_no_options,
1360      &global_enable_audio, 2,
1361      "Select 'no' to turn off all audio output. This will\n"
1362      "not result in a significant change in performance.", 9),
1363 #ifndef PSP_BUILD
1364     string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1365              &audio_buffer_size_number, 11,
1366 #else
1367     string_selection_option(NULL, "Audio buffer", audio_buffer_options,
1368              &audio_buffer_size_number, 10,
1369 #endif
1370
1371 #ifdef PSP_BUILD
1372      "Set the size (in bytes) of the audio buffer. Larger values may result\n"
1373      "in slightly better performance at the cost of latency; the lowest\n"
1374      "value will give the most responsive audio.\n"
1375      "This option requires gpSP to be restarted before it will take effect.",
1376 #else
1377      "Set the size (in bytes) of the audio buffer.\n"
1378      "This option requires gpSP restart to take effect.",
1379 #endif
1380      10),
1381     submenu_option(NULL, "Back", "Return to the main menu.", 12)
1382   };
1383
1384   make_menu(graphics_sound, submenu_graphics_sound, NULL);
1385
1386   menu_option_type cheats_misc_options[] =
1387   {
1388     cheat_option(0),
1389     cheat_option(1),
1390     cheat_option(2),
1391     cheat_option(3),
1392     cheat_option(4),
1393     cheat_option(5),
1394     cheat_option(6),
1395     cheat_option(7),
1396     cheat_option(8),
1397     cheat_option(9),
1398     string_selection_option(NULL, "Clock speed",
1399      clock_speed_options, &clock_speed_number,
1400      sizeof(clock_speed_options) / sizeof(clock_speed_options[0]),
1401      "Change the clock speed of the device. Higher clock\n"
1402      "speed will yield better performance, but will drain\n"
1403      "battery life further.", 11),
1404     string_selection_option(NULL, "Update backup",
1405      update_backup_options, &update_backup_flag, 2,
1406 #ifdef GP2X_BUILD
1407      "Determines when in-game save files should be\n"
1408      "written back to SD card.",
1409 #else
1410      "Determines when in-game save files should be written back to\n"
1411      "memstick. If set to 'automatic' writebacks will occur shortly after\n"
1412      "the game's backup is altered. On 'exit only' it will only be written\n"
1413      "back when you exit from this menu (NOT from using the home button).\n"
1414      "Use the latter with extreme care.",
1415 #endif
1416      12),
1417     submenu_option(NULL, "Back", "Return to the main menu.", 14)
1418   };
1419
1420   make_menu(cheats_misc, submenu_cheats_misc, NULL);
1421
1422   menu_option_type savestate_options[] =
1423   {
1424     numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1425      "Load savestate from current slot", &savestate_slot, 10,
1426      "Select to load the game state from the current slot\n"
1427      "for this game.\n"
1428      "Press left + right to change the current slot.", 6),
1429     numeric_selection_action_hide_option(menu_save_state, menu_change_state,
1430      "Save savestate to current slot", &savestate_slot, 10,
1431      "Select to save the game state to the current slot\n"
1432      "for this game.\n"
1433      "Press left + right to change the current slot.", 7),
1434     numeric_selection_action_hide_option(menu_load_state_file,
1435       menu_change_state,
1436      "Load savestate from file", &savestate_slot, 10,
1437      "Restore gameplay from a savestate file.\n"
1438      "Note: The same file used to save the state must be\n"
1439      "present.\n", 9),
1440     numeric_selection_option(menu_change_state,
1441      "Current savestate slot", &savestate_slot, 10,
1442      "Change the current savestate slot.\n", 11),
1443     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1444   };
1445
1446   make_menu(savestate, submenu_savestate, NULL);
1447
1448 #ifdef PSP_BUILD
1449
1450   menu_option_type gamepad_config_options[] =
1451   {
1452     gamepad_config_option("D-pad up     ", 0),
1453     gamepad_config_option("D-pad down   ", 1),
1454     gamepad_config_option("D-pad left   ", 2),
1455     gamepad_config_option("D-pad right  ", 3),
1456     gamepad_config_option("Circle       ", 4),
1457     gamepad_config_option("Cross        ", 5),
1458     gamepad_config_option("Square       ", 6),
1459     gamepad_config_option("Triangle     ", 7),
1460     gamepad_config_option("Left Trigger ", 8),
1461     gamepad_config_option("Right Trigger", 9),
1462     gamepad_config_option("Start        ", 10),
1463     gamepad_config_option("Select       ", 11),
1464     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1465   };
1466
1467
1468   menu_option_type analog_config_options[] =
1469   {
1470     analog_config_option("Analog up   ", 0),
1471     analog_config_option("Analog down ", 1),
1472     analog_config_option("Analog left ", 2),
1473     analog_config_option("Analog right", 3),
1474     string_selection_option(NULL, "Enable analog", yes_no_options,
1475      &global_enable_analog, 2,
1476      "Select 'no' to block analog input entirely.", 7),
1477     numeric_selection_option(NULL, "Analog sensitivity",
1478      &analog_sensitivity_level, 10,
1479      "Determine sensitivity/responsiveness of the analog input.\n"
1480      "Lower numbers are less sensitive.", 8),
1481     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1482   };
1483
1484 #endif
1485
1486 #ifdef GP2X_BUILD
1487
1488   menu_option_type gamepad_config_options[] =
1489   {
1490     gamepad_config_option("D-pad up     ", 0),
1491     gamepad_config_option("D-pad down   ", 1),
1492     gamepad_config_option("D-pad left   ", 2),
1493     gamepad_config_option("D-pad right  ", 3),
1494     gamepad_config_option("A            ", 4),
1495     gamepad_config_option("B            ", 5),
1496     gamepad_config_option("X            ", 6),
1497     gamepad_config_option("Y            ", 7),
1498     gamepad_config_option("Left Trigger ", 8),
1499     gamepad_config_option("Right Trigger", 9),
1500 #ifdef WIZ_BUILD
1501     gamepad_config_option("Menu         ", 10),
1502 #else
1503     gamepad_config_option("Start        ", 10),
1504 #endif
1505     gamepad_config_option("Select       ", 11),
1506 #ifndef WIZ_BUILD
1507     gamepad_config_option("Stick Push   ", 12),
1508 #endif
1509     submenu_option(NULL, "Back", "Return to the main menu.", 14)
1510   };
1511
1512
1513   menu_option_type analog_config_options[] =
1514   {
1515     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1516   };
1517
1518 #endif
1519
1520 #ifdef PC_BUILD
1521
1522   menu_option_type gamepad_config_options[] =
1523   {
1524     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1525   };
1526
1527   menu_option_type analog_config_options[] =
1528   {
1529     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1530   };
1531
1532 #endif
1533
1534   make_menu(gamepad_config, submenu_gamepad, NULL);
1535   make_menu(analog_config, submenu_analog, NULL);
1536
1537   menu_option_type main_options[] =
1538   {
1539     submenu_option(&graphics_sound_menu, "Graphics and Sound options",
1540      "Select to set display parameters and frameskip\n"
1541      "behavior, audio on/off, buffer size, and filtering.", 0),
1542     numeric_selection_action_option(menu_load_state, NULL,
1543      "Load state from slot", &savestate_slot, 10,
1544      "Select to load the game state from the current slot\n"
1545      "for this game, if it exists.\n"
1546      "Press left + right to change the current slot.", 2),
1547     numeric_selection_action_option(menu_save_state, NULL,
1548      "Save state to slot", &savestate_slot, 10,
1549      "Select to save the game state to the current slot\n"
1550      "for this game. See the extended menu for more info.\n"
1551      "Press left + right to change the current slot.", 3),
1552     submenu_option(&savestate_menu, "Savestate options",
1553      "Select to enter a menu for loading, saving, and\n"
1554      "viewing the currently active savestate for this game\n"
1555      "(or to load a savestate file from another game)", 4),
1556     submenu_option(&gamepad_config_menu, "Configure gamepad input",
1557      "Select to change the in-game behavior of buttons\n"
1558      "and d-pad.", 6),
1559 #ifndef GP2X_BUILD
1560     submenu_option(&analog_config_menu, "Configure analog input",
1561      "Select to change the in-game behavior of the PSP analog nub.", 7),
1562 #endif
1563     submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
1564      "Select to manage cheats, set backup behavior,\n"
1565      "and set device clock speed.", 9),
1566     action_option(menu_load, NULL, "Load new game",
1567      "Select to load a new game\n"
1568      "(will exit a game if currently playing).", 11),
1569     action_option(menu_restart, NULL, "Restart game",
1570      "Select to reset the GBA with the current game\n"
1571      "loaded.", 12),
1572     action_option(menu_exit, NULL, "Return to game",
1573      "Select to exit this menu and resume gameplay.", 13),
1574     action_option(menu_quit, NULL, "Exit gpSP",
1575      "Select to exit gpSP and return to the menu.", 15)
1576   };
1577
1578   make_menu(main, submenu_main, NULL);
1579
1580   void choose_menu(menu_type *new_menu)
1581   {
1582     if(new_menu == NULL)
1583       new_menu = &main_menu;
1584
1585     clear_screen(COLOR_BG);
1586
1587 #ifndef GP2X_BUILD
1588     blit_to_screen(original_screen, 240, 160, 230, 40);
1589 #endif
1590
1591     current_menu = new_menu;
1592     current_option = new_menu->options;
1593     current_option_num = 0;
1594     if(current_menu->init_function)
1595      current_menu->init_function();
1596   }
1597
1598   void clear_help()
1599   {
1600     for(i = 0; i < 6; i++)
1601     {
1602       print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
1603     }
1604   }
1605
1606   menu_update_clock();
1607   video_resolution_large();
1608
1609 #ifndef GP2X_BUILD
1610   SDL_LockMutex(sound_mutex);
1611 #endif
1612   SDL_PauseAudio(1);
1613
1614 #ifndef GP2X_BUILD
1615   SDL_UnlockMutex(sound_mutex);
1616 #endif
1617
1618   if(gamepak_filename[0] == 0)
1619   {
1620     first_load = 1;
1621     memset(original_screen, 0x00, 240 * 160 * 2);
1622     print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
1623      60, 75,original_screen, 240, 0, 0, FONT_HEIGHT);
1624   }
1625
1626   choose_menu(&main_menu);
1627
1628   for(i = 0; i < 10; i++)
1629   {
1630     if(i >= num_cheats)
1631     {
1632       sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1633     }
1634     else
1635     {
1636       sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1637        cheats[i].cheat_name);
1638     }
1639   }
1640
1641   current_menu->init_function();
1642
1643   while(repeat)
1644   {
1645     display_option = current_menu->options;
1646
1647     for(i = 0; i < current_menu->num_options; i++, display_option++)
1648     {
1649       if(display_option->option_type & NUMBER_SELECTION_OPTION)
1650       {
1651         sprintf(line_buffer, display_option->display_string,
1652          *(display_option->current_option));
1653       }
1654       else
1655
1656       if(display_option->option_type & STRING_SELECTION_OPTION)
1657       {
1658         sprintf(line_buffer, display_option->display_string,
1659          ((u32 *)display_option->options)[*(display_option->current_option)]);
1660       }
1661       else
1662       {
1663         strcpy(line_buffer, display_option->display_string);
1664       }
1665
1666       if(display_option == current_option)
1667       {
1668         print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 10,
1669          (display_option->line_number * 10) + 40, 41);
1670       }
1671       else
1672       {
1673         print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 10,
1674          (display_option->line_number * 10) + 40, 41);
1675       }
1676     }
1677
1678     print_string(current_option->help_string, COLOR_HELP_TEXT,
1679      COLOR_BG, 8, 210);
1680
1681     flip_screen();
1682
1683     gui_action = get_gui_input();
1684
1685     switch(gui_action)
1686     {
1687       case CURSOR_DOWN:
1688         current_option_num = (current_option_num + 1) %
1689           current_menu->num_options;
1690
1691         current_option = current_menu->options + current_option_num;
1692         clear_help();
1693         break;
1694
1695       case CURSOR_UP:
1696         if(current_option_num)
1697           current_option_num--;
1698         else
1699           current_option_num = current_menu->num_options - 1;
1700
1701         current_option = current_menu->options + current_option_num;
1702         clear_help();
1703         break;
1704
1705       case CURSOR_RIGHT:
1706         if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1707          STRING_SELECTION_OPTION))
1708         {
1709           *(current_option->current_option) =
1710            (*current_option->current_option + 1) %
1711            current_option->num_options;
1712
1713           if(current_option->passive_function)
1714             current_option->passive_function();
1715         }
1716         break;
1717
1718       case CURSOR_LEFT:
1719         if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1720          STRING_SELECTION_OPTION))
1721         {
1722           u32 current_option_val = *(current_option->current_option);
1723
1724           if(current_option_val)
1725             current_option_val--;
1726           else
1727             current_option_val = current_option->num_options - 1;
1728
1729           *(current_option->current_option) = current_option_val;
1730
1731           if(current_option->passive_function)
1732             current_option->passive_function();
1733         }
1734         break;
1735
1736       case CURSOR_EXIT:
1737         if(current_menu == &main_menu)
1738           menu_exit();
1739
1740         choose_menu(&main_menu);
1741         break;
1742
1743       case CURSOR_SELECT:
1744         if(current_option->option_type & ACTION_OPTION)
1745           current_option->action_function();
1746
1747         if(current_option->option_type & SUBMENU_OPTION)
1748           choose_menu(current_option->sub_menu);
1749         break;
1750     }
1751   }
1752
1753   set_gba_resolution(screen_scale);
1754   video_resolution_small();
1755   menu_get_clock_speed();
1756   set_clock_speed();
1757
1758   SDL_PauseAudio(0);
1759
1760   return return_value;
1761 }