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