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