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