wiz port wip
[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 #ifdef PSP_BUILD
1285      "Set the size (in bytes) of the audio buffer. Larger values may result\n"
1286      "in slightly better performance at the cost of latency; the lowest\n"
1287      "value will give the most responsive audio.\n"
1288      "This option requires gpSP to be restarted before it will take effect.",
1289 #else
1290      "Set the size (in bytes) of the audio buffer.\n"
1291      "This option requires gpSP restart to take effect.",
1292 #endif
1293      10),
1294     submenu_option(NULL, "Back", "Return to the main menu.", 12)
1295   };
1296
1297   make_menu(graphics_sound, submenu_graphics_sound, NULL);
1298
1299   menu_option_type cheats_misc_options[] =
1300   {
1301     cheat_option(0),
1302     cheat_option(1),
1303     cheat_option(2),
1304     cheat_option(3),
1305     cheat_option(4),
1306     cheat_option(5),
1307     cheat_option(6),
1308     cheat_option(7),
1309     cheat_option(8),
1310     cheat_option(9),
1311     string_selection_option(NULL, "Clock speed",
1312      clock_speed_options, &clock_speed_number,
1313 #ifdef GP2X_BUILD
1314      15,
1315 #else
1316      10,
1317 #endif
1318      "Change the clock speed of the device. Higher clock\n"
1319      "speed will yield better performance, but will drain\n"
1320      "battery life further.", 11),
1321     string_selection_option(NULL, "Update backup",
1322      update_backup_options, &update_backup_flag, 2,
1323 #ifdef GP2X_BUILD
1324      "Determines when in-game save files should be\n"
1325      "written back to SD card.",
1326 #else
1327      "Determines when in-game save files should be written back to\n"
1328      "memstick. If set to 'automatic' writebacks will occur shortly after\n"
1329      "the game's backup is altered. On 'exit only' it will only be written\n"
1330      "back when you exit from this menu (NOT from using the home button).\n"
1331      "Use the latter with extreme care.",
1332 #endif
1333      12),
1334     submenu_option(NULL, "Back", "Return to the main menu.", 14)
1335   };
1336
1337   make_menu(cheats_misc, submenu_cheats_misc, NULL);
1338
1339   menu_option_type savestate_options[] =
1340   {
1341     numeric_selection_action_hide_option(menu_load_state, menu_change_state,
1342      "Load savestate from current slot", &savestate_slot, 10,
1343      "Select to load the game state from the current slot\n"
1344      "for this game.\n"
1345      "Press left + right to change the current slot.", 6),
1346     numeric_selection_action_hide_option(menu_save_state, menu_change_state,
1347      "Save savestate to current slot", &savestate_slot, 10,
1348      "Select to save the game state to the current slot\n"
1349      "for this game.\n"
1350      "Press left + right to change the current slot.", 7),
1351     numeric_selection_action_hide_option(menu_load_state_file,
1352       menu_change_state,
1353      "Load savestate from file", &savestate_slot, 10,
1354      "Restore gameplay from a savestate file.\n"
1355      "Note: The same file used to save the state must be\n"
1356      "present.\n", 9),
1357     numeric_selection_option(menu_change_state,
1358      "Current savestate slot", &savestate_slot, 10,
1359      "Change the current savestate slot.\n", 11),
1360     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1361   };
1362
1363   make_menu(savestate, submenu_savestate, NULL);
1364
1365 #ifdef PSP_BUILD
1366
1367   menu_option_type gamepad_config_options[] =
1368   {
1369     gamepad_config_option("D-pad up     ", 0),
1370     gamepad_config_option("D-pad down   ", 1),
1371     gamepad_config_option("D-pad left   ", 2),
1372     gamepad_config_option("D-pad right  ", 3),
1373     gamepad_config_option("Circle       ", 4),
1374     gamepad_config_option("Cross        ", 5),
1375     gamepad_config_option("Square       ", 6),
1376     gamepad_config_option("Triangle     ", 7),
1377     gamepad_config_option("Left Trigger ", 8),
1378     gamepad_config_option("Right Trigger", 9),
1379     gamepad_config_option("Start        ", 10),
1380     gamepad_config_option("Select       ", 11),
1381     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1382   };
1383
1384
1385   menu_option_type analog_config_options[] =
1386   {
1387     analog_config_option("Analog up   ", 0),
1388     analog_config_option("Analog down ", 1),
1389     analog_config_option("Analog left ", 2),
1390     analog_config_option("Analog right", 3),
1391     string_selection_option(NULL, "Enable analog", yes_no_options,
1392      &global_enable_analog, 2,
1393      "Select 'no' to block analog input entirely.", 7),
1394     numeric_selection_option(NULL, "Analog sensitivity",
1395      &analog_sensitivity_level, 10,
1396      "Determine sensitivity/responsiveness of the analog input.\n"
1397      "Lower numbers are less sensitive.", 8),
1398     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1399   };
1400
1401 #endif
1402
1403 #ifdef GP2X_BUILD
1404
1405   menu_option_type gamepad_config_options[] =
1406   {
1407     gamepad_config_option("D-pad up     ", 0),
1408     gamepad_config_option("D-pad down   ", 1),
1409     gamepad_config_option("D-pad left   ", 2),
1410     gamepad_config_option("D-pad right  ", 3),
1411     gamepad_config_option("A            ", 4),
1412     gamepad_config_option("B            ", 5),
1413     gamepad_config_option("X            ", 6),
1414     gamepad_config_option("Y            ", 7),
1415     gamepad_config_option("Left Trigger ", 8),
1416     gamepad_config_option("Right Trigger", 9),
1417     gamepad_config_option("Start        ", 10),
1418     gamepad_config_option("Select       ", 11),
1419     gamepad_config_option("Stick Push   ", 12),
1420     submenu_option(NULL, "Back", "Return to the main menu.", 14)
1421   };
1422
1423
1424   menu_option_type analog_config_options[] =
1425   {
1426     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1427   };
1428
1429 #endif
1430
1431 #ifdef PC_BUILD
1432
1433   menu_option_type gamepad_config_options[] =
1434   {
1435     submenu_option(NULL, "Back", "Return to the main menu.", 13)
1436   };
1437
1438   menu_option_type analog_config_options[] =
1439   {
1440     submenu_option(NULL, "Back", "Return to the main menu.", 11)
1441   };
1442
1443 #endif
1444
1445   make_menu(gamepad_config, submenu_gamepad, NULL);
1446   make_menu(analog_config, submenu_analog, NULL);
1447
1448   menu_option_type main_options[] =
1449   {
1450     submenu_option(&graphics_sound_menu, "Graphics and Sound options",
1451      "Select to set display parameters and frameskip\n"
1452      "behavior, audio on/off, buffer size, and filtering.", 0),
1453     numeric_selection_action_option(menu_load_state, NULL,
1454      "Load state from slot", &savestate_slot, 10,
1455      "Select to load the game state from the current slot\n"
1456      "for this game, if it exists.\n"
1457      "Press left + right to change the current slot.", 2),
1458     numeric_selection_action_option(menu_save_state, NULL,
1459      "Save state to slot", &savestate_slot, 10,
1460      "Select to save the game state to the current slot\n"
1461      "for this game. See the extended menu for more info.\n"
1462      "Press left + right to change the current slot.", 3),
1463     submenu_option(&savestate_menu, "Savestate options",
1464      "Select to enter a menu for loading, saving, and\n"
1465      "viewing the currently active savestate for this game\n"
1466      "(or to load a savestate file from another game)", 4),
1467     submenu_option(&gamepad_config_menu, "Configure gamepad input",
1468      "Select to change the in-game behavior of buttons\n"
1469      "and d-pad.", 6),
1470 #ifndef GP2X_BUILD
1471     submenu_option(&analog_config_menu, "Configure analog input",
1472      "Select to change the in-game behavior of the PSP analog nub.", 7),
1473 #endif
1474     submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options",
1475      "Select to manage cheats, set backup behavior,\n"
1476      "and set device clock speed.", 9),
1477     action_option(menu_load, NULL, "Load new game",
1478      "Select to load a new game\n"
1479      "(will exit a game if currently playing).", 11),
1480     action_option(menu_restart, NULL, "Restart game",
1481      "Select to reset the GBA with the current game\n"
1482      "loaded.", 12),
1483     action_option(menu_exit, NULL, "Return to game",
1484      "Select to exit this menu and resume gameplay.", 13),
1485     action_option(menu_quit, NULL, "Exit gpSP",
1486      "Select to exit gpSP and return to the menu.", 15)
1487   };
1488
1489   make_menu(main, submenu_main, NULL);
1490
1491   void choose_menu(menu_type *new_menu)
1492   {
1493     if(new_menu == NULL)
1494       new_menu = &main_menu;
1495
1496     clear_screen(COLOR_BG);
1497
1498 #ifndef GP2X_BUILD
1499     blit_to_screen(original_screen, 240, 160, 230, 40);
1500 #endif
1501
1502     current_menu = new_menu;
1503     current_option = new_menu->options;
1504     current_option_num = 0;
1505     if(current_menu->init_function)
1506      current_menu->init_function();
1507   }
1508
1509   void clear_help()
1510   {
1511     for(i = 0; i < 6; i++)
1512     {
1513       print_string_pad(" ", COLOR_BG, COLOR_BG, 8, 210 + (i * 10), 70);
1514     }
1515   }
1516
1517 #ifdef PSP_BUILD
1518   clock_speed_number = (clock_speed / 33) - 1;
1519 #elif defined(GP2X_BUILD)
1520   clock_speed_number = (clock_speed - 150) / 10;
1521 #endif
1522
1523   video_resolution_large();
1524
1525 #ifndef GP2X_BUILD
1526   SDL_LockMutex(sound_mutex);
1527 #endif
1528   SDL_PauseAudio(1);
1529
1530 #ifndef GP2X_BUILD
1531   SDL_UnlockMutex(sound_mutex);
1532 #endif
1533
1534   if(gamepak_filename[0] == 0)
1535   {
1536     first_load = 1;
1537     memset(original_screen, 0x00, 240 * 160 * 2);
1538     print_string_ext("No game loaded yet.", 0xFFFF, 0x0000,
1539      60, 75,original_screen, 240, 0);
1540   }
1541
1542   choose_menu(&main_menu);
1543
1544   for(i = 0; i < 10; i++)
1545   {
1546     if(i >= num_cheats)
1547     {
1548       sprintf(cheat_format_str[i], "cheat %d (none loaded)", i);
1549     }
1550     else
1551     {
1552       sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i,
1553        cheats[i].cheat_name);
1554     }
1555   }
1556
1557   current_menu->init_function();
1558
1559   while(repeat)
1560   {
1561     display_option = current_menu->options;
1562
1563     for(i = 0; i < current_menu->num_options; i++, display_option++)
1564     {
1565       if(display_option->option_type & NUMBER_SELECTION_OPTION)
1566       {
1567         sprintf(line_buffer, display_option->display_string,
1568          *(display_option->current_option));
1569       }
1570       else
1571
1572       if(display_option->option_type & STRING_SELECTION_OPTION)
1573       {
1574         sprintf(line_buffer, display_option->display_string,
1575          ((u32 *)display_option->options)[*(display_option->current_option)]);
1576       }
1577       else
1578       {
1579         strcpy(line_buffer, display_option->display_string);
1580       }
1581
1582       if(display_option == current_option)
1583       {
1584         print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 10,
1585          (display_option->line_number * 10) + 40, 36);
1586       }
1587       else
1588       {
1589         print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 10,
1590          (display_option->line_number * 10) + 40, 36);
1591       }
1592     }
1593
1594     print_string(current_option->help_string, COLOR_HELP_TEXT,
1595      COLOR_BG, 8, 210);
1596
1597     flip_screen();
1598
1599     gui_action = get_gui_input();
1600
1601     switch(gui_action)
1602     {
1603       case CURSOR_DOWN:
1604         current_option_num = (current_option_num + 1) %
1605           current_menu->num_options;
1606
1607         current_option = current_menu->options + current_option_num;
1608         clear_help();
1609         break;
1610
1611       case CURSOR_UP:
1612         if(current_option_num)
1613           current_option_num--;
1614         else
1615           current_option_num = current_menu->num_options - 1;
1616
1617         current_option = current_menu->options + current_option_num;
1618         clear_help();
1619         break;
1620
1621       case CURSOR_RIGHT:
1622         if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1623          STRING_SELECTION_OPTION))
1624         {
1625           *(current_option->current_option) =
1626            (*current_option->current_option + 1) %
1627            current_option->num_options;
1628
1629           if(current_option->passive_function)
1630             current_option->passive_function();
1631         }
1632         break;
1633
1634       case CURSOR_LEFT:
1635         if(current_option->option_type & (NUMBER_SELECTION_OPTION |
1636          STRING_SELECTION_OPTION))
1637         {
1638           u32 current_option_val = *(current_option->current_option);
1639
1640           if(current_option_val)
1641             current_option_val--;
1642           else
1643             current_option_val = current_option->num_options - 1;
1644
1645           *(current_option->current_option) = current_option_val;
1646
1647           if(current_option->passive_function)
1648             current_option->passive_function();
1649         }
1650         break;
1651
1652       case CURSOR_EXIT:
1653         if(current_menu == &main_menu)
1654           menu_exit();
1655
1656         choose_menu(&main_menu);
1657         break;
1658
1659       case CURSOR_SELECT:
1660         if(current_option->option_type & ACTION_OPTION)
1661           current_option->action_function();
1662
1663         if(current_option->option_type & SUBMENU_OPTION)
1664           choose_menu(current_option->sub_menu);
1665         break;
1666     }
1667   }
1668
1669   set_gba_resolution(screen_scale);
1670   video_resolution_small();
1671
1672   #ifdef PSP_BUILD
1673     clock_speed = (clock_speed_number + 1) * 33;
1674     scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
1675   #elif defined(GP2X_BUILD)
1676     clock_speed = 150 + clock_speed_number * 10;
1677     if (clock_speed != clock_speed_old)
1678     {
1679       printf("about to set CPU clock to %iMHz\n", clock_speed);
1680       set_FCLK(clock_speed);
1681       clock_speed_old = clock_speed;
1682     }
1683   #endif
1684
1685   SDL_PauseAudio(0);
1686
1687   return return_value;
1688 }