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