2c6c7fd2daf73de818d5842aca5187b1d6df5065
[libpicofe.git] / psp / menu.c
1 // (c) Copyright 2007 notaz, All rights reserved.
2 // Free for non-commercial use.
3
4 // For commercial use, separate licencing terms must be obtained.
5
6 // don't like to use loads of #ifdefs, so duplicating GP2X code
7 // horribly instead
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <wchar.h>
12 #include <unistd.h>
13 #include <sys/syslimits.h> // PATH_MAX
14
15 #include <pspdisplay.h>
16 #include <pspgu.h>
17 #include <pspiofilemgr.h>
18 #include <psputils.h>
19
20 #include "psp.h"
21 #include "emu.h"
22 #include "menu.h"
23 #include "mp3.h"
24 #include "../common/menu.h"
25 #include "../common/emu.h"
26 #include "../common/readpng.h"
27 #include "../common/lprintf.h"
28 #include "../common/input.h"
29 #include "version.h"
30
31 #include <pico/pico_int.h>
32 #include <pico/patch.h>
33 #include <zlib/zlib.h>
34
35
36 #define pspKeyUnkn "???"
37 const char * const keyNames[] = {
38         "SELECT",   pspKeyUnkn, pspKeyUnkn, "START",    "UP",       "RIGHT",     "DOWN",     "LEFT",
39         "L",        "R",        pspKeyUnkn, pspKeyUnkn, "TRIANGLE", "CIRCLE",    "X",        "SQUARE",
40         "HOME",     "HOLD",     "WLAN_UP",  "REMOTE",   "VOLUP",    "VOLDOWN",   "SCREEN",   "NOTE",
41         pspKeyUnkn, pspKeyUnkn, pspKeyUnkn, pspKeyUnkn, "NUB UP",   "NUB RIGHT", "NUB DOWN", "NUB LEFT" // fake
42 };
43
44 static unsigned short bg_buffer[480*272] __attribute__((aligned(16)));
45 #define menu_screen psp_screen
46
47 void menu_darken_bg(void *dst, const void *src, int pixels, int darker);
48 static void menu_prepare_bg(int use_game_bg, int use_fg);
49
50
51 static unsigned int inp_prev = 0;
52
53 void menu_draw_begin(void)
54 {
55         // short *src = (short *)bg_buffer, *dst = (short *)menu_screen;
56         // int i;
57
58         // for (i = 272; i >= 0; i--, dst += 512, src += 480)
59         //      memcpy32((int *)dst, (int *)src, 480*2/4);
60
61         sceGuSync(0,0); // sync with prev
62         sceGuStart(GU_DIRECT, guCmdList);
63         sceGuCopyImage(GU_PSM_5650, 0, 0, 480, 272, 480, bg_buffer, 0, 0, 512, menu_screen);
64         sceGuFinish();
65         sceGuSync(0,0);
66 }
67
68
69 void menu_draw_end(void)
70 {
71         psp_video_flip(1);
72 }
73
74
75 // --------- loading ROM screen ----------
76
77 static int lcdr_line = 0;
78
79 static void load_progress_cb(int percent)
80 {
81         int ln, len = percent * 480 / 100;
82         unsigned short *dst;
83
84         //sceDisplayWaitVblankStart();
85
86         dst = (unsigned short *)menu_screen + 512*10*lcdr_line;
87
88         if (len > 480) len = 480;
89         for (ln = 8; ln > 0; ln--, dst += 512)
90                 memset(dst, 0xff, len*2);
91 }
92
93 static void cdload_progress_cb(int percent)
94 {
95         int ln, len = percent * 480 / 100;
96         unsigned short *dst;
97
98         if (lcdr_line <= 2) {
99                 lcdr_line++;
100                 smalltext_out16(1, lcdr_line++ * 10, "Processing CD image / MP3s", 0xffff);
101                 smalltext_out16_lim(1, lcdr_line++ * 10, romFileName, 0xffff, 80);
102         }
103
104         dst = (unsigned short *)menu_screen + 512*10*lcdr_line;
105
106         if (len > 480) len = 480;
107         for (ln = 8; ln > 0; ln--, dst += 512)
108                 memset(dst, 0xff, len*2);
109 }
110
111 void menu_romload_prepare(const char *rom_name)
112 {
113         const char *p = rom_name + strlen(rom_name);
114         while (p > rom_name && *p != '/') p--;
115
116         psp_video_switch_to_single();
117         if (rom_loaded) menu_draw_begin();
118         else memset32_uncached(psp_screen, 0, 512*272*2/4);
119
120         smalltext_out16(1, 1, "Loading", 0xffff);
121         smalltext_out16_lim(1, 10, p, 0xffff, 80);
122         PicoCartLoadProgressCB = load_progress_cb;
123         PicoCDLoadProgressCB = cdload_progress_cb;
124         lcdr_line = 2;
125 }
126
127 void menu_romload_end(void)
128 {
129         PicoCartLoadProgressCB = PicoCDLoadProgressCB = NULL;
130         smalltext_out16(1, ++lcdr_line*10, "Starting emulation...", 0xffff);
131 }
132
133 // -------------- ROM selector --------------
134
135 struct my_dirent
136 {
137         unsigned int d_type;
138         char d_name[255];
139 };
140
141 // bbbb bggg gggr rrrr
142 static unsigned short file2color(const char *fname)
143 {
144         const char *ext = fname + strlen(fname) - 3;
145         static const char *rom_exts[]   = { "zip", "bin", "smd", "gen", "iso", "cso", "cue" };
146         static const char *other_exts[] = { "gmv", "pat" };
147         int i;
148
149         if (ext < fname) ext = fname;
150         for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)
151                 if (strcasecmp(ext, rom_exts[i]) == 0) return 0xfdf7;
152         for (i = 0; i < sizeof(other_exts)/sizeof(other_exts[0]); i++)
153                 if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5;
154         return 0xffff;
155 }
156
157 static void draw_dirlist(char *curdir, struct my_dirent **namelist, int n, int sel)
158 {
159         int start, i, pos;
160
161         start = 13 - sel;
162         n--; // exclude current dir (".")
163
164         menu_draw_begin();
165
166         if (!rom_loaded) {
167 //              menu_darken_bg(menu_screen, menu_screen, 321*240, 0);
168         }
169
170         menu_darken_bg((char *)menu_screen + 512*129*2, (char *)menu_screen + 512*129*2, 512*10, 0);
171
172         if (start - 2 >= 0)
173                 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);
174         for (i = 0; i < n; i++) {
175                 pos = start + i;
176                 if (pos < 0)  continue;
177                 if (pos > 26) break;
178                 if (namelist[i+1]->d_type & FIO_S_IFDIR) {
179                         smalltext_out16_lim(14,   pos*10, "/", 0xd7ff, 1);
180                         smalltext_out16_lim(14+6, pos*10, namelist[i+1]->d_name, 0xd7ff, 80-3);
181                 } else {
182                         unsigned short color = file2color(namelist[i+1]->d_name);
183                         smalltext_out16_lim(14,   pos*10, namelist[i+1]->d_name, color, 80-2);
184                 }
185         }
186         text_out16(5, 130, ">");
187         menu_draw_end();
188 }
189
190 static int scandir_cmp(const void *p1, const void *p2)
191 {
192         struct my_dirent **d1 = (struct my_dirent **)p1, **d2 = (struct my_dirent **)p2;
193         if ((*d1)->d_type & (*d2)->d_type & FIO_S_IFDIR)
194                 return strcasecmp((*d1)->d_name, (*d2)->d_name);
195         if ((*d1)->d_type & FIO_S_IFDIR) return -1; // put before
196         if ((*d2)->d_type & FIO_S_IFDIR) return  1;
197         return strcasecmp((*d1)->d_name, (*d2)->d_name);
198 }
199
200 static char *filter_exts[] = {
201         ".mp3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",
202         ".jpg", ".pbp"
203 };
204
205 static int scandir_filter(const struct my_dirent *ent)
206 {
207         const char *p;
208         int i;
209
210         if (ent == NULL || ent->d_name == NULL) return 0;
211         if (strlen(ent->d_name) < 5) return 1;
212
213         p = ent->d_name + strlen(ent->d_name) - 4;
214
215         for (i = 0; i < sizeof(filter_exts)/sizeof(filter_exts[0]); i++)
216         {
217                 if (strcasecmp(p, filter_exts[i]) == 0) return 0;
218         }
219
220         return 1;
221 }
222
223 static int my_scandir(const char *dir, struct my_dirent ***namelist_out,
224                 int(*filter)(const struct my_dirent *),
225                 int(*compar)(const void *, const void *))
226 {
227         int ret = -1, dir_uid = -1, name_alloc = 4, name_count = 0;
228         struct my_dirent **namelist = NULL, *ent;
229         SceIoDirent sce_ent;
230
231         namelist = malloc(sizeof(*namelist) * name_alloc);
232         if (namelist == NULL) { lprintf("%s:%i: OOM\n", __FILE__, __LINE__); goto fail; }
233
234         // try to read first..
235         dir_uid = sceIoDopen(dir);
236         if (dir_uid >= 0)
237         {
238                 /* it is very important to clear SceIoDirent to be passed to sceIoDread(), */
239                 /* or else it may crash, probably misinterpreting something in it. */
240                 memset(&sce_ent, 0, sizeof(sce_ent));
241                 ret = sceIoDread(dir_uid, &sce_ent);
242                 if (ret < 0)
243                 {
244                         lprintf("sceIoDread(\"%s\") failed with %i\n", dir, ret);
245                         goto fail;
246                 }
247         }
248         else
249                 lprintf("sceIoDopen(\"%s\") failed with %i\n", dir, dir_uid);
250
251         while (ret > 0)
252         {
253                 ent = malloc(sizeof(*ent));
254                 if (ent == NULL) { lprintf("%s:%i: OOM\n", __FILE__, __LINE__); goto fail; }
255                 ent->d_type = sce_ent.d_stat.st_mode;
256                 strncpy(ent->d_name, sce_ent.d_name, sizeof(ent->d_name));
257                 ent->d_name[sizeof(ent->d_name)-1] = 0;
258                 if (filter == NULL || filter(ent))
259                      namelist[name_count++] = ent;
260                 else free(ent);
261
262                 if (name_count >= name_alloc)
263                 {
264                         void *tmp;
265                         name_alloc *= 2;
266                         tmp = realloc(namelist, sizeof(*namelist) * name_alloc);
267                         if (tmp == NULL) { lprintf("%s:%i: OOM\n", __FILE__, __LINE__); goto fail; }
268                         namelist = tmp;
269                 }
270
271                 memset(&sce_ent, 0, sizeof(sce_ent));
272                 ret = sceIoDread(dir_uid, &sce_ent);
273         }
274
275         // sort
276         if (compar != NULL && name_count > 3) qsort(&namelist[2], name_count - 2, sizeof(namelist[0]), compar);
277
278         // all done.
279         ret = name_count;
280         *namelist_out = namelist;
281         goto end;
282
283 fail:
284         if (namelist != NULL)
285         {
286                 while (name_count--)
287                         free(namelist[name_count]);
288                 free(namelist);
289         }
290 end:
291         if (dir_uid >= 0) sceIoDclose(dir_uid);
292         return ret;
293 }
294
295
296 static SceIoStat cpstat;
297
298 static char *romsel_loop(char *curr_path)
299 {
300         struct my_dirent **namelist;
301         int n, iret, sel = 0;
302         unsigned long inp = 0;
303         char *ret = NULL, *fname = NULL;
304
305         // is this a dir or a full path?
306         memset(&cpstat, 0, sizeof(cpstat));
307         iret = sceIoGetstat(curr_path, &cpstat);
308         if (iret >= 0 && (cpstat.st_mode & FIO_S_IFDIR)); // dir
309         else if (iret >= 0 && (cpstat.st_mode & FIO_S_IFREG)) { // file
310                 char *p;
311                 for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);
312                 if (p > curr_path) {
313                         *p = 0;
314                         fname = p+1;
315                 }
316                 else strcpy(curr_path, "ms0:/");
317         }
318         else strcpy(curr_path, "ms0:/"); // something else
319
320         n = my_scandir(curr_path, &namelist, scandir_filter, scandir_cmp);
321         if (n < 0) {
322                 // try root..
323                 n = my_scandir("ms0:/", &namelist, scandir_filter, scandir_cmp);
324                 if (n < 0) {
325                         // oops, we failed
326                         lprintf("scandir failed, dir: "); lprintf(curr_path); lprintf("\n");
327                         return NULL;
328                 }
329         }
330
331         // try to find sel
332         if (fname != NULL) {
333                 int i;
334                 for (i = 1; i < n; i++) {
335                         if (strcmp(namelist[i]->d_name, fname) == 0) {
336                                 sel = i - 1;
337                                 break;
338                         }
339                 }
340         }
341
342         for (;;)
343         {
344                 draw_dirlist(curr_path, namelist, n, sel);
345                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R|PBTN_X|PBTN_CIRCLE, 0);
346                 if(inp & PBTN_UP  )  { sel--;   if (sel < 0)   sel = n-2; }
347                 if(inp & PBTN_DOWN)  { sel++;   if (sel > n-2) sel = 0; }
348                 if(inp & PBTN_LEFT)  { sel-=10; if (sel < 0)   sel = 0; }
349                 if(inp & PBTN_L)     { sel-=24; if (sel < 0)   sel = 0; }
350                 if(inp & PBTN_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }
351                 if(inp & PBTN_R)     { sel+=24; if (sel > n-2) sel = n-2; }
352                 if(inp & PBTN_CIRCLE) // enter dir/select
353                 {
354                         if (namelist[sel+1]->d_type & FIO_S_IFDIR)
355                         {
356                                 int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;
357                                 char *p, *newdir = malloc(newlen);
358                                 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {
359                                         char *start = curr_path;
360                                         p = start + strlen(start) - 1;
361                                         while (*p == '/' && p > start) p--;
362                                         while (*p != '/' && *p != ':' && p > start) p--;
363                                         if (p <= start || *p == ':') strcpy(newdir, "ms0:/");
364                                         else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }
365                                 } else {
366                                         strcpy(newdir, curr_path);
367                                         p = newdir + strlen(newdir) - 1;
368                                         while (*p == '/' && p >= newdir) *p-- = 0;
369                                         strcat(newdir, "/");
370                                         strcat(newdir, namelist[sel+1]->d_name);
371                                 }
372                                 ret = romsel_loop(newdir);
373                                 free(newdir);
374                                 break;
375                         }
376                         else if (namelist[sel+1]->d_type & FIO_S_IFREG)
377                         {
378                                 strcpy(romFileName, curr_path);
379                                 strcat(romFileName, "/");
380                                 strcat(romFileName, namelist[sel+1]->d_name);
381                                 ret = romFileName;
382                                 break;
383                         }
384                 }
385                 if(inp & PBTN_X) break; // cancel
386         }
387
388         if (n > 0) {
389                 while(n--) free(namelist[n]);
390                 free(namelist);
391         }
392
393         return ret;
394 }
395
396 // ------------ patch/gg menu ------------
397
398 static void draw_patchlist(int sel)
399 {
400         int start, i, pos, active;
401
402         start = 13 - sel;
403
404         menu_draw_begin();
405
406         for (i = 0; i < PicoPatchCount; i++) {
407                 pos = start + i;
408                 if (pos < 0)  continue;
409                 if (pos > 26) break;
410                 active = PicoPatches[i].active;
411                 smalltext_out16_lim(14,     pos*10, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff, 3);
412                 smalltext_out16_lim(14+6*4, pos*10, PicoPatches[i].name, active ? 0xfff6 : 0xffff, 53-6);
413         }
414         pos = start + i;
415         if (pos < 27) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);
416
417         text_out16(5, 130, ">");
418         menu_draw_end();
419 }
420
421
422 static void patches_menu_loop(void)
423 {
424         int menu_sel = 0;
425         unsigned long inp = 0;
426
427         for(;;)
428         {
429                 draw_patchlist(menu_sel);
430                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R|PBTN_X|PBTN_CIRCLE, 0);
431                 if(inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }
432                 if(inp & PBTN_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }
433                 if(inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
434                 if(inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }
435                 if(inp & PBTN_CIRCLE) { // action
436                         if (menu_sel < PicoPatchCount)
437                                 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;
438                         else    return;
439                 }
440                 if(inp & PBTN_X) return;
441         }
442
443 }
444
445 // ------------ savestate loader ------------
446
447 static int state_slot_flags = 0;
448
449 static void state_check_slots(void)
450 {
451         int slot;
452
453         state_slot_flags = 0;
454
455         for (slot = 0; slot < 10; slot++)
456         {
457                 if (emu_checkSaveFile(slot))
458                 {
459                         state_slot_flags |= 1 << slot;
460                 }
461         }
462 }
463
464 static void *get_oldstate_for_preview(void)
465 {
466         unsigned char *ptr = malloc(sizeof(Pico.vram) + sizeof(Pico.cram) + sizeof(Pico.vsram) + sizeof(Pico.video));
467         if (ptr == NULL) return NULL;
468
469         memcpy(ptr, Pico.vram, sizeof(Pico.vram));
470         memcpy(ptr + sizeof(Pico.vram), Pico.cram, sizeof(Pico.cram));
471         memcpy(ptr + sizeof(Pico.vram) + sizeof(Pico.cram), Pico.vsram, sizeof(Pico.vsram));
472         memcpy(ptr + sizeof(Pico.vram) + sizeof(Pico.cram) + sizeof(Pico.vsram), &Pico.video, sizeof(Pico.video));
473         return ptr;
474 }
475
476 static void restore_oldstate(void *ptrx)
477 {
478         unsigned char *ptr = ptrx;
479         memcpy(Pico.vram,  ptr,  sizeof(Pico.vram));
480         memcpy(Pico.cram,  ptr + sizeof(Pico.vram), sizeof(Pico.cram));
481         memcpy(Pico.vsram, ptr + sizeof(Pico.vram) + sizeof(Pico.cram), sizeof(Pico.vsram));
482         memcpy(&Pico.video,ptr + sizeof(Pico.vram) + sizeof(Pico.cram) + sizeof(Pico.vsram), sizeof(Pico.video));
483         free(ptrx);
484 }
485
486 static void draw_savestate_bg(int slot)
487 {
488         void *file, *oldstate;
489         char *fname;
490
491         fname = emu_GetSaveFName(1, 0, slot);
492         if (!fname) return;
493
494         oldstate = get_oldstate_for_preview();
495         if (oldstate == NULL) return;
496
497         if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {
498                 file = gzopen(fname, "rb");
499                 emu_setSaveStateCbs(1);
500         } else {
501                 file = fopen(fname, "rb");
502                 emu_setSaveStateCbs(0);
503         }
504
505         if (file) {
506                 if (PicoAHW & PAHW_MCD) {
507                         PicoCdLoadStateGfx(file);
508                 } else {
509                         areaSeek(file, 0x10020, SEEK_SET);  // skip header and RAM in state file
510                         areaRead(Pico.vram, 1, sizeof(Pico.vram), file);
511                         areaSeek(file, 0x2000, SEEK_CUR);
512                         areaRead(Pico.cram, 1, sizeof(Pico.cram), file);
513                         areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);
514                         areaSeek(file, 0x221a0, SEEK_SET);
515                         areaRead(&Pico.video, 1, sizeof(Pico.video), file);
516                 }
517                 areaClose(file);
518         }
519
520         emu_forcedFrame(0);
521         menu_prepare_bg(1, 0);
522
523         restore_oldstate(oldstate);
524 }
525
526 static void draw_savestate_menu(int menu_sel, int is_loading)
527 {
528         int tl_x = 80+25, tl_y = 16+60, y, i;
529
530         if (state_slot_flags & (1 << menu_sel))
531                 draw_savestate_bg(menu_sel);
532         menu_draw_begin();
533
534         text_out16(tl_x, 16+30, is_loading ? "Load state" : "Save state");
535
536         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);
537
538         /* draw all 10 slots */
539         y = tl_y;
540         for (i = 0; i < 10; i++, y+=10)
541         {
542                 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");
543         }
544         text_out16(tl_x, y, "back");
545
546         menu_draw_end();
547 }
548
549 static int savestate_menu_loop(int is_loading)
550 {
551         static int menu_sel = 10;
552         int menu_sel_max = 10;
553         unsigned long inp = 0;
554
555         state_check_slots();
556
557         for(;;)
558         {
559                 draw_savestate_menu(menu_sel, is_loading);
560                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_X|PBTN_CIRCLE, 0);
561                 if(inp & PBTN_UP  ) {
562                         do {
563                                 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;
564                         } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);
565                 }
566                 if(inp & PBTN_DOWN) {
567                         do {
568                                 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;
569                         } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);
570                 }
571                 if(inp & PBTN_CIRCLE) { // save/load
572                         if (menu_sel < 10) {
573                                 state_slot = menu_sel;
574                                 PicoStateProgressCB = emu_msg_cb; /* also suitable for menu */
575                                 if (emu_SaveLoadGame(is_loading, 0)) {
576                                         strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");
577                                         return 1;
578                                 }
579                                 return 0;
580                         } else  return 1;
581                 }
582                 if(inp & PBTN_X) return 1;
583         }
584 }
585
586 // -------------- key config --------------
587
588 static char *action_binds(int player_idx, int action_mask)
589 {
590         static char strkeys[32*5];
591         int i;
592
593         strkeys[0] = 0;
594         for (i = 0; i < 32; i++) // i is key index
595         {
596                 if (currentConfig.KeyBinds[i] & action_mask)
597                 {
598                         if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;
599                         if (strkeys[0]) {
600                                 strcat(strkeys, i >= 28 ? ", " : " + "); // nub "buttons" don't create combos
601                                 strcat(strkeys, keyNames[i]);
602                                 break;
603                         }
604                         else strcpy(strkeys, keyNames[i]);
605                 }
606         }
607
608         return strkeys;
609 }
610
611 static void unbind_action(int action)
612 {
613         int i;
614
615         for (i = 0; i < 32; i++)
616                 currentConfig.KeyBinds[i] &= ~action;
617 }
618
619 static int count_bound_keys(int action, int pl_idx)
620 {
621         int i, keys = 0;
622
623         for (i = 0; i < 32; i++)
624         {
625                 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;
626                 if (currentConfig.KeyBinds[i] & action) keys++;
627         }
628
629         return keys;
630 }
631
632 static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx, int sel)
633 {
634         int x, y, tl_y = 16+20, i;
635
636         menu_draw_begin();
637         if (player_idx >= 0) {
638                 text_out16(80+80, 16, "Player %i controls", player_idx + 1);
639                 x = 80+80;
640         } else {
641                 text_out16(80+80, 16, "Emulator controls");
642                 x = 80+40;
643         }
644
645         menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);
646
647         y = tl_y;
648         for (i = 0; i < opt_cnt; i++, y+=10)
649                 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));
650
651         text_out16(x, y, "Done");
652
653         if (sel < opt_cnt) {
654                 text_out16(80+30, 220, "Press a button to bind/unbind");
655                 text_out16(80+30, 230, "Use SELECT to clear");
656                 text_out16(80+30, 240, "To bind UP/DOWN, hold SELECT");
657                 text_out16(80+30, 250, "Select \"Done\" to exit");
658         } else {
659                 text_out16(80+30, 230, "Use Options -> Save cfg");
660                 text_out16(80+30, 240, "to save controls");
661                 text_out16(80+30, 250, "Press X or O to exit");
662         }
663         menu_draw_end();
664 }
665
666 static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)
667 {
668         int sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;
669         unsigned long inp = 0;
670
671         for (;;)
672         {
673                 draw_key_config(opts, opt_cnt, player_idx, sel);
674                 inp = in_menu_wait(CONFIGURABLE_KEYS|PBTN_SELECT, 1);
675                 if (!(inp & PBTN_SELECT)) {
676                         prev_select = 0;
677                         if(inp & PBTN_UP  ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }
678                         if(inp & PBTN_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }
679                 }
680                 if (sel >= opt_cnt) {
681                         if (inp & (PBTN_X|PBTN_CIRCLE)) break;
682                         else continue;
683                 }
684                 // if we are here, we want to bind/unbind something
685                 if ((inp & PBTN_SELECT) && !prev_select)
686                         unbind_action(opts[sel].mask);
687                 prev_select = inp & PBTN_SELECT;
688                 inp &= CONFIGURABLE_KEYS;
689                 inp &= ~PBTN_SELECT;
690                 for (i = 0; i < 32; i++)
691                         if (inp & (1 << i)) {
692                                 if (count_bound_keys(opts[sel].mask, player_idx) >= 2)
693                                      currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only
694                                 else currentConfig.KeyBinds[i] ^=  opts[sel].mask;
695                                 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {
696                                         currentConfig.KeyBinds[i] &= ~(3 << 16);
697                                         currentConfig.KeyBinds[i] |= player_idx << 16;
698                                 }
699                         }
700         }
701 }
702
703 menu_entry ctrlopt_entries[] =
704 {
705         { "Player 1",                  MB_NONE,  MA_CTRL_PLAYER1,       NULL, 0, 0, 0, 1, 0 },
706         { "Player 2",                  MB_NONE,  MA_CTRL_PLAYER2,       NULL, 0, 0, 0, 1, 0 },
707         { "Emulator controls",         MB_NONE,  MA_CTRL_EMU,           NULL, 0, 0, 0, 1, 0 },
708         { "6 button pad",              MB_ONOFF, MA_OPT_6BUTTON_PAD,   &PicoOpt, 0x020, 0, 0, 1, 1 },
709         { "Turbo rate",                MB_RANGE, MA_CTRL_TURBO_RATE,   &currentConfig.turbo_rate, 0, 1, 30, 1, 1 },
710         { "Done",                      MB_NONE,  MA_CTRL_DONE,          NULL, 0, 0, 0, 1, 0 },
711 };
712
713 #define CTRLOPT_ENTRY_COUNT (sizeof(ctrlopt_entries) / sizeof(ctrlopt_entries[0]))
714 const int ctrlopt_entry_count = CTRLOPT_ENTRY_COUNT;
715
716 static void draw_kc_sel(int menu_sel)
717 {
718         int tl_x = 80+25+40, tl_y = 16+60, y;
719
720         y = tl_y;
721         menu_draw_begin();
722         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);
723
724         me_draw(ctrlopt_entries, ctrlopt_entry_count, tl_x, tl_y, NULL, NULL);
725
726         menu_draw_end();
727 }
728
729
730 // player2_flag, ?, ?, ?, ?, ?, ?, menu
731 // "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",
732 // "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"
733 me_bind_action emuctrl_actions[] =
734 {
735         { "Load State       ", 1<<28 },
736         { "Save State       ", 1<<27 },
737         { "Prev Save Slot   ", 1<<25 },
738         { "Next Save Slot   ", 1<<24 },
739         { "Switch Renderer  ", 1<<26 },
740         { "Fast forward     ", 1<<22 },
741         { "Pico Next page   ", 1<<21 },
742         { "Pico Prev page   ", 1<<20 },
743         { "Pico Switch input", 1<<19 },
744         { NULL,                0     }
745 };
746
747 static void kc_sel_loop(void)
748 {
749         int menu_sel = 5, menu_sel_max = 5;
750         unsigned long inp = 0;
751         menu_id selected_id;
752
753         while (1)
754         {
755                 draw_kc_sel(menu_sel);
756                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_X|PBTN_CIRCLE, 0);
757                 selected_id = me_index2id(ctrlopt_entries, CTRLOPT_ENTRY_COUNT, menu_sel);
758                 if (inp & (PBTN_LEFT|PBTN_RIGHT)) // multi choise
759                         me_process(ctrlopt_entries, CTRLOPT_ENTRY_COUNT, selected_id, (inp&PBTN_RIGHT) ? 1 : 0);
760                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
761                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
762                 if (inp & PBTN_CIRCLE) {
763                         int is_6button = PicoOpt & POPT_6BTN_PAD;
764                         switch (selected_id) {
765                                 case MA_CTRL_PLAYER1: key_config_loop(me_ctrl_actions, is_6button ? 15 : 11, 0); return;
766                                 case MA_CTRL_PLAYER2: key_config_loop(me_ctrl_actions, is_6button ? 15 : 11, 1); return;
767                                 case MA_CTRL_EMU:     key_config_loop(emuctrl_actions,
768                                                         sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]) - 1, -1); return;
769                                 case MA_CTRL_DONE:    if (!rom_loaded) emu_WriteConfig(0); return;
770                                 default: return;
771                         }
772                 }
773                 if (inp & PBTN_X) return;
774         }
775 }
776
777
778 // --------- sega/mega cd options ----------
779
780 menu_entry cdopt_entries[] =
781 {
782         { NULL,                        MB_NONE,  MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1, 0 },
783         { NULL,                        MB_NONE,  MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 },
784         { NULL,                        MB_NONE,  MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 },
785         { "CD LEDs",                   MB_ONOFF, MA_CDOPT_LEDS,         &currentConfig.EmuOpt,  0x0400, 0, 0, 1, 1 },
786         { "CDDA audio",                MB_ONOFF, MA_CDOPT_CDDA,         &PicoOpt, 0x0800, 0, 0, 1, 1 },
787         { "PCM audio",                 MB_ONOFF, MA_CDOPT_PCM,          &PicoOpt, 0x0400, 0, 0, 1, 1 },
788         { NULL,                        MB_NONE,  MA_CDOPT_READAHEAD,    NULL, 0, 0, 0, 1, 1 },
789         { "SaveRAM cart",              MB_ONOFF, MA_CDOPT_SAVERAM,      &PicoOpt, 0x8000, 0, 0, 1, 1 },
790         { "Scale/Rot. fx (slow)",      MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&PicoOpt, 0x1000, 0, 0, 1, 1 },
791         { "Better sync (slow)",        MB_ONOFF, MA_CDOPT_BETTER_SYNC,  &PicoOpt, 0x2000, 0, 0, 1, 1 },
792         { "done",                      MB_NONE,  MA_CDOPT_DONE,         NULL, 0, 0, 0, 1, 0 },
793 };
794
795 #define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))
796 const int cdopt_entry_count = CDOPT_ENTRY_COUNT;
797
798
799 struct bios_names_t
800 {
801         char us[32], eu[32], jp[32];
802 };
803
804 static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)
805 {
806         struct bios_names_t *bios_names = param;
807         char ra_buff[16];
808
809         switch (entry->id)
810         {
811                 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS:     %s", bios_names->us); break;
812                 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS:     %s", bios_names->eu); break;
813                 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS:     %s", bios_names->jp); break;
814                 case MA_CDOPT_READAHEAD:
815                         if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);
816                         else strcpy(ra_buff, "     OFF");
817                         text_out16(x, y, "ReadAhead buffer      %s", ra_buff);
818                         break;
819                 default:break;
820         }
821 }
822
823 static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)
824 {
825         int tl_x = 80+25, tl_y = 16+60;
826         menu_id selected_id;
827         char ra_buff[16];
828
829         if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);
830         else strcpy(ra_buff, "     OFF");
831
832         menu_draw_begin();
833
834         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);
835
836         me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);
837
838         selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);
839         if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||
840                 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||
841                 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))
842                         text_out16(tl_x, 250, "Press start to test selected BIOS");
843
844         menu_draw_end();
845 }
846
847 static void cd_menu_loop_options(void)
848 {
849         static int menu_sel = 0;
850         int menu_sel_max = 10;
851         unsigned long inp = 0;
852         struct bios_names_t bios_names;
853         menu_id selected_id;
854         char *bios, *p;
855
856         if (emu_findBios(4, &bios)) { // US
857                 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--);
858                 if (*p == '/') p++;
859                 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;
860         } else  strcpy(bios_names.us, "NOT FOUND");
861
862         if (emu_findBios(8, &bios)) { // EU
863                 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--);
864                 if (*p == '/') p++;
865                 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;
866         } else  strcpy(bios_names.eu, "NOT FOUND");
867
868         if (emu_findBios(1, &bios)) { // JP
869                 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--);
870                 if (*p == '/') p++;
871                 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;
872         } else  strcpy(bios_names.jp, "NOT FOUND");
873
874         menuErrorMsg[0] = 0;
875
876         for (;;)
877         {
878                 draw_cd_menu_options(menu_sel, &bios_names);
879                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_X|PBTN_CIRCLE|PBTN_START, 0);
880                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
881                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
882                 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);
883                 if (inp & (PBTN_LEFT|PBTN_RIGHT)) { // multi choise
884                         if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&PBTN_RIGHT) ? 1 : 0) &&
885                             selected_id == MA_CDOPT_READAHEAD) {
886                                 if (inp & PBTN_LEFT) {
887                                         PicoCDBuffers >>= 1;
888                                         if (PicoCDBuffers < 2) PicoCDBuffers = 0;
889                                 } else {
890                                         if (PicoCDBuffers < 2) PicoCDBuffers = 2;
891                                         else PicoCDBuffers <<= 1;
892                                         if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M
893                                 }
894                         }
895                 }
896                 if (inp & PBTN_CIRCLE) // toggleable options
897                         if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&
898                             selected_id == MA_CDOPT_DONE) {
899                                 return;
900                         }
901                 if (inp & PBTN_START) {
902                         switch (selected_id) { // BIOS testers
903                                 case MA_CDOPT_TESTBIOS_USA:
904                                         if (emu_findBios(4, &bios)) { // test US
905                                                 strcpy(romFileName, bios);
906                                                 engineState = PGS_ReloadRom;
907                                                 return;
908                                         }
909                                         break;
910                                 case MA_CDOPT_TESTBIOS_EUR:
911                                         if (emu_findBios(8, &bios)) { // test EU
912                                                 strcpy(romFileName, bios);
913                                                 engineState = PGS_ReloadRom;
914                                                 return;
915                                         }
916                                         break;
917                                 case MA_CDOPT_TESTBIOS_JAP:
918                                         if (emu_findBios(1, &bios)) { // test JP
919                                                 strcpy(romFileName, bios);
920                                                 engineState = PGS_ReloadRom;
921                                                 return;
922                                         }
923                                         break;
924                                 default:
925                                         break;
926                         }
927                 }
928                 if (inp & PBTN_X) return;
929         }
930 }
931
932 // --------- display options ----------
933
934 menu_entry opt3_entries[] =
935 {
936         { NULL,                        MB_NONE,  MA_OPT3_SCALE,         NULL, 0, 0, 0, 1, 1 },
937         { NULL,                        MB_NONE,  MA_OPT3_HSCALE32,      NULL, 0, 0, 0, 1, 1 },
938         { NULL,                        MB_NONE,  MA_OPT3_HSCALE40,      NULL, 0, 0, 0, 1, 1 },
939         { NULL,                        MB_ONOFF, MA_OPT3_FILTERING,     &currentConfig.scaling, 1,  0,  0, 1, 1 },
940         { NULL,                        MB_RANGE, MA_OPT3_GAMMAA,        &currentConfig.gamma,   0, -4, 16, 1, 1 },
941         { NULL,                        MB_RANGE, MA_OPT3_BLACKLVL,      &currentConfig.gamma2,  0,  0,  2, 1, 1 },
942         { NULL,                        MB_NONE,  MA_OPT3_VSYNC,         NULL, 0, 0, 0, 1, 1 },
943         { "Set to unscaled centered",  MB_NONE,  MA_OPT3_PRES_NOSCALE,  NULL, 0, 0, 0, 1, 0 },
944         { "Set to 4:3 scaled",         MB_NONE,  MA_OPT3_PRES_SCALE43,  NULL, 0, 0, 0, 1, 0 },
945         { "Set to fullscreen",         MB_NONE,  MA_OPT3_PRES_FULLSCR,  NULL, 0, 0, 0, 1, 0 },
946         { "done",                      MB_NONE,  MA_OPT3_DONE,          NULL, 0, 0, 0, 1, 0 },
947 };
948
949 #define OPT3_ENTRY_COUNT (sizeof(opt3_entries) / sizeof(opt3_entries[0]))
950 const int opt3_entry_count = OPT3_ENTRY_COUNT;
951
952
953 static void menu_opt3_cust_draw(const menu_entry *entry, int x, int y, void *param)
954 {
955         switch (entry->id)
956         {
957                 case MA_OPT3_SCALE:
958                         text_out16(x, y, "Scale factor:                      %.2f", currentConfig.scale);
959                         break;
960                 case MA_OPT3_HSCALE32:
961                         text_out16(x, y, "Hor. scale (for low res. games):   %.2f", currentConfig.hscale32);
962                         break;
963                 case MA_OPT3_HSCALE40:
964                         text_out16(x, y, "Hor. scale (for hi res. games):    %.2f", currentConfig.hscale40);
965                         break;
966                 case MA_OPT3_FILTERING:
967                         text_out16(x, y, "Bilinear filtering                 %s", currentConfig.scaling?"ON":"OFF");
968                         break;
969                 case MA_OPT3_GAMMAA:
970                         text_out16(x, y, "Gamma adjustment                  %2i", currentConfig.gamma);
971                         break;
972                 case MA_OPT3_BLACKLVL:
973                         text_out16(x, y, "Black level                       %2i", currentConfig.gamma2);
974                         break;
975                 case MA_OPT3_VSYNC: {
976                         char *val = "    never";
977                         if (currentConfig.EmuOpt & 0x2000)
978                                 val = (currentConfig.EmuOpt & 0x10000) ? "sometimes" : "   always";
979                         text_out16(x, y, "Wait for vsync (slow)         %s", val);
980                         break;
981                 }
982                 default: break;
983         }
984 }
985
986 static void menu_opt3_preview(int is_32col)
987 {
988         void *oldstate = NULL;
989
990         if (!rom_loaded || ((Pico.video.reg[12]&1)^1) != is_32col)
991         {
992                 extern char bgdatac32_start[], bgdatac40_start[];
993                 extern int bgdatac32_size, bgdatac40_size;
994                 void *bgdata = is_32col ? bgdatac32_start : bgdatac40_start;
995                 unsigned long insize = is_32col ? bgdatac32_size : bgdatac40_size, outsize = 65856;
996                 int ret;
997                 ret = uncompress((Bytef *)bg_buffer, &outsize, bgdata, insize);
998                 if (ret == 0)
999                 {
1000                         if (rom_loaded) oldstate = get_oldstate_for_preview();
1001                         memcpy(Pico.vram,  bg_buffer, sizeof(Pico.vram));
1002                         memcpy(Pico.cram,  (char *)bg_buffer + 0x10000, 0x40*2);
1003                         memcpy(Pico.vsram, (char *)bg_buffer + 0x10080, 0x40*2);
1004                         memcpy(&Pico.video,(char *)bg_buffer + 0x10100, 0x40);
1005                 }
1006                 else
1007                         lprintf("uncompress returned %i\n", ret);
1008         }
1009
1010         memset32_uncached(psp_screen, 0, 512*272*2/4);
1011         emu_forcedFrame(0);
1012         menu_prepare_bg(1, 0);
1013
1014         if (oldstate) restore_oldstate(oldstate);
1015 }
1016
1017 static void draw_dispmenu_options(int menu_sel)
1018 {
1019         int tl_x = 80, tl_y = 16+50;
1020
1021         menu_draw_begin();
1022
1023         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 316);
1024
1025         me_draw(opt3_entries, OPT3_ENTRY_COUNT, tl_x, tl_y, menu_opt3_cust_draw, NULL);
1026
1027         menu_draw_end();
1028 }
1029
1030 static void dispmenu_loop_options(void)
1031 {
1032         static int menu_sel = 0;
1033         int menu_sel_max, is_32col = (Pico.video.reg[12]&1)^1;
1034         unsigned long inp = 0;
1035         menu_id selected_id;
1036
1037         menu_sel_max = me_count_enabled(opt3_entries, OPT3_ENTRY_COUNT) - 1;
1038
1039         for (;;)
1040         {
1041                 draw_dispmenu_options(menu_sel);
1042                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_X|PBTN_CIRCLE, 0);
1043                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
1044                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
1045                 selected_id = me_index2id(opt3_entries, OPT3_ENTRY_COUNT, menu_sel);
1046                 if (selected_id == MA_OPT3_HSCALE40 &&  is_32col) { is_32col = 0; menu_opt3_preview(is_32col); }
1047                 if (selected_id == MA_OPT3_HSCALE32 && !is_32col) { is_32col = 1; menu_opt3_preview(is_32col); }
1048
1049                 if (inp & (PBTN_LEFT|PBTN_RIGHT)) // multi choise
1050                 {
1051                         float *setting = NULL;
1052                         int tmp;
1053                         me_process(opt3_entries, OPT3_ENTRY_COUNT, selected_id, (inp&PBTN_RIGHT) ? 1 : 0);
1054                         switch (selected_id) {
1055                                 case MA_OPT3_SCALE:     setting = &currentConfig.scale; break;
1056                                 case MA_OPT3_HSCALE40:  setting = &currentConfig.hscale40; is_32col = 0; break;
1057                                 case MA_OPT3_HSCALE32:  setting = &currentConfig.hscale32; is_32col = 1; break;
1058                                 case MA_OPT3_FILTERING:
1059                                 case MA_OPT3_GAMMAA:
1060                                 case MA_OPT3_BLACKLVL:  menu_opt3_preview(is_32col); break;
1061                                 case MA_OPT3_VSYNC:
1062                                         tmp = ((currentConfig.EmuOpt>>13)&1) | ((currentConfig.EmuOpt>>15)&2);
1063                                         tmp = (inp & PBTN_LEFT) ? (tmp>>1) : ((tmp<<1)|1);
1064                                         if (tmp > 3) tmp = 3;
1065                                         currentConfig.EmuOpt &= ~0x12000;
1066                                         currentConfig.EmuOpt |= ((tmp&2)<<15) | ((tmp&1)<<13);
1067                                         break;
1068                                 default: break;
1069                         }
1070                         if (setting != NULL) {
1071                                 while ((inp = psp_pad_read(0)) & (PBTN_LEFT|PBTN_RIGHT)) {
1072                                         *setting += (inp & PBTN_LEFT) ? -0.01 : 0.01;
1073                                         if (*setting <= 0) *setting = 0.01;
1074                                         menu_opt3_preview(is_32col);
1075                                         draw_dispmenu_options(menu_sel); // will wait vsync
1076                                 }
1077                         }
1078                 }
1079                 if (inp & PBTN_CIRCLE) { // toggleable options
1080                         me_process(opt3_entries, OPT3_ENTRY_COUNT, selected_id, 1);
1081                         switch (selected_id) {
1082                                 case MA_OPT3_DONE:
1083                                         return;
1084                                 case MA_OPT3_PRES_NOSCALE:
1085                                         currentConfig.scale = currentConfig.hscale40 = currentConfig.hscale32 = 1.0;
1086                                         menu_opt3_preview(is_32col);
1087                                         break;
1088                                 case MA_OPT3_PRES_SCALE43:
1089                                         currentConfig.scale = 1.20;
1090                                         currentConfig.hscale40 = 1.00;
1091                                         currentConfig.hscale32 = 1.25;
1092                                         menu_opt3_preview(is_32col);
1093                                         break;
1094                                 case MA_OPT3_PRES_FULLSCR:
1095                                         currentConfig.scale = 1.20;
1096                                         currentConfig.hscale40 = 1.25;
1097                                         currentConfig.hscale32 = 1.56;
1098                                         menu_opt3_preview(is_32col);
1099                                         break;
1100                                 case MA_OPT3_FILTERING:
1101                                         menu_opt3_preview(is_32col);
1102                                         break;
1103                                 default: break;
1104                         }
1105                 }
1106                 if (inp & PBTN_X) return;
1107         }
1108 }
1109
1110
1111 // --------- advanced options ----------
1112
1113 menu_entry opt2_entries[] =
1114 {
1115         { "Disable sprite limit",      MB_ONOFF, MA_OPT2_NO_SPRITE_LIM,  &PicoOpt, 0x40000, 0, 0, 1, 1 },
1116         { "Emulate Z80",               MB_ONOFF, MA_OPT2_ENABLE_Z80,     &PicoOpt, 0x00004, 0, 0, 1, 1 },
1117         { "Emulate YM2612 (FM)",       MB_ONOFF, MA_OPT2_ENABLE_YM2612,  &PicoOpt, 0x00001, 0, 0, 1, 1 },
1118         { "Emulate SN76496 (PSG)",     MB_ONOFF, MA_OPT2_ENABLE_SN76496, &PicoOpt, 0x00002, 0, 0, 1, 1 },
1119         { "gzip savestates",           MB_ONOFF, MA_OPT2_GZIP_STATES,    &currentConfig.EmuOpt, 0x00008, 0, 0, 1, 1 },
1120         { "Don't save last used ROM",  MB_ONOFF, MA_OPT2_NO_LAST_ROM,    &currentConfig.EmuOpt, 0x00020, 0, 0, 1, 1 },
1121         { "Status line in main menu",  MB_ONOFF, MA_OPT2_STATUS_LINE,    &currentConfig.EmuOpt, 0x20000, 0, 0, 1, 1 },
1122         { "Disable idle loop patching",MB_ONOFF, MA_OPT2_NO_IDLE_LOOPS,  &PicoOpt, 0x80000, 0, 0, 1, 1 },
1123         { "Disable frame limiter",     MB_ONOFF, MA_OPT2_NO_FRAME_LIMIT, &currentConfig.EmuOpt, 0x40000, 0, 0, 1, 1 },
1124         { "done",                      MB_NONE,  MA_OPT2_DONE,           NULL, 0, 0, 0, 1, 0 },
1125 };
1126
1127 #define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))
1128 const int opt2_entry_count = OPT2_ENTRY_COUNT;
1129
1130
1131 static void draw_amenu_options(int menu_sel)
1132 {
1133         int tl_x = 80+25, tl_y = 16+50;
1134
1135         menu_draw_begin();
1136
1137         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);
1138
1139         me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);
1140
1141         menu_draw_end();
1142 }
1143
1144 static void amenu_loop_options(void)
1145 {
1146         static int menu_sel = 0;
1147         int menu_sel_max;
1148         unsigned long inp = 0;
1149         menu_id selected_id;
1150
1151         menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;
1152
1153         for(;;)
1154         {
1155                 draw_amenu_options(menu_sel);
1156                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_X|PBTN_CIRCLE, 0);
1157                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
1158                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
1159                 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);
1160                 if (inp & (PBTN_LEFT|PBTN_RIGHT)) { // multi choise
1161                         if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&PBTN_RIGHT) ? 1 : 0) &&
1162                             selected_id == MA_OPT2_GAMMA) {
1163                                 // TODO?
1164                         }
1165                 }
1166                 if (inp & PBTN_CIRCLE) { // toggleable options
1167                         if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&
1168                             selected_id == MA_OPT2_DONE) {
1169                                 return;
1170                         }
1171                 }
1172                 if (inp & PBTN_X) return;
1173         }
1174 }
1175
1176 // -------------- options --------------
1177
1178
1179 menu_entry opt_entries[] =
1180 {
1181         { NULL,                        MB_NONE,  MA_OPT_RENDERER,      NULL, 0, 0, 0, 1, 1 },
1182         { "Accurate sprites",          MB_ONOFF, MA_OPT_ACC_SPRITES,   &PicoOpt, 0x080, 0, 0, 0, 1 },
1183         { "Show FPS",                  MB_ONOFF, MA_OPT_SHOW_FPS,      &currentConfig.EmuOpt,  0x0002,  0,  0, 1, 1 },
1184         { NULL,                        MB_RANGE, MA_OPT_FRAMESKIP,     &currentConfig.Frameskip,    0, -1, 16, 1, 1 },
1185         { "Enable sound",              MB_ONOFF, MA_OPT_ENABLE_SOUND,  &currentConfig.EmuOpt,  0x0004,  0,  0, 1, 1 },
1186         { NULL,                        MB_NONE,  MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 },
1187         { NULL,                        MB_NONE,  MA_OPT_REGION,        NULL, 0, 0, 0, 1, 1 },
1188         { "Use SRAM/BRAM savestates",  MB_ONOFF, MA_OPT_SRAM_STATES,   &currentConfig.EmuOpt,  0x0001, 0, 0, 1, 1 },
1189         { NULL,                        MB_NONE,  MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1, 1 },
1190         { "Save slot",                 MB_RANGE, MA_OPT_SAVE_SLOT,     &state_slot, 0, 0, 9, 1, 1 },
1191         { NULL,                        MB_NONE,  MA_OPT_CPU_CLOCKS,    NULL, 0, 0, 0, 1, 1 },
1192         { "[Display options]",         MB_NONE,  MA_OPT_DISP_OPTS,     NULL, 0, 0, 0, 1, 0 },
1193         { "[Sega/Mega CD options]",    MB_NONE,  MA_OPT_SCD_OPTS,      NULL, 0, 0, 0, 1, 0 },
1194         { "[Advanced options]",        MB_NONE,  MA_OPT_ADV_OPTS,      NULL, 0, 0, 0, 1, 0 },
1195         { NULL,                        MB_NONE,  MA_OPT_SAVECFG,       NULL, 0, 0, 0, 1, 0 },
1196         { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1, 0 },
1197         { NULL,                        MB_NONE,  MA_OPT_LOADCFG,       NULL, 0, 0, 0, 1, 0 },
1198 };
1199
1200 #define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))
1201 const int opt_entry_count = OPT_ENTRY_COUNT;
1202
1203
1204 static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)
1205 {
1206         char *str, str24[24];
1207
1208         switch (entry->id)
1209         {
1210                 case MA_OPT_RENDERER:
1211                         if (PicoOpt & 0x10)
1212                                 str = "fast";
1213                         else if (currentConfig.EmuOpt & 0x80)
1214                                 str = "accurate";
1215                         else
1216                                 str = " 8bit accurate"; // n/a
1217                         text_out16(x, y, "Renderer:                  %s", str);
1218                         break;
1219                 case MA_OPT_FRAMESKIP:
1220                         if (currentConfig.Frameskip < 0)
1221                              strcpy(str24, "Auto");
1222                         else sprintf(str24, "%i", currentConfig.Frameskip);
1223                         text_out16(x, y, "Frameskip                  %s", str24);
1224                         break;
1225                 case MA_OPT_SOUND_QUALITY:
1226                         str = (PicoOpt&0x08)?"stereo":"mono";
1227                         text_out16(x, y, "Sound Quality:     %5iHz %s", PsndRate, str);
1228                         break;
1229                 case MA_OPT_REGION:
1230                         text_out16(x, y, "Region:              %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));
1231                         break;
1232                 case MA_OPT_CONFIRM_STATES:
1233                         switch ((currentConfig.EmuOpt >> 9) & 5) {
1234                                 default: str = "OFF";    break;
1235                                 case 1:  str = "writes"; break;
1236                                 case 4:  str = "loads";  break;
1237                                 case 5:  str = "both";   break;
1238                         }
1239                         text_out16(x, y, "Confirm savestate          %s", str);
1240                         break;
1241                 case MA_OPT_CPU_CLOCKS:
1242                         text_out16(x, y, "CPU/bus clock       %3i/%3iMHz", currentConfig.CPUclock, currentConfig.CPUclock/2);
1243                         break;
1244                 case MA_OPT_SAVECFG:
1245                         str24[0] = 0;
1246                         if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);
1247                         text_out16(x, y, "Save cfg as default%s", str24);
1248                         break;
1249                 case MA_OPT_LOADCFG:
1250                         text_out16(x, y, "Load cfg from profile %i", config_slot);
1251                         break;
1252                 default:
1253                         lprintf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);
1254                         break;
1255         }
1256 }
1257
1258
1259 static void draw_menu_options(int menu_sel)
1260 {
1261         int tl_x = 80+25, tl_y = 16+24;
1262
1263         menu_draw_begin();
1264
1265         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);
1266
1267         me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);
1268
1269         menu_draw_end();
1270 }
1271
1272 static int sndrate_prevnext(int rate, int dir)
1273 {
1274         int i, rates[] = { 11025, 22050, 44100 };
1275
1276         for (i = 0; i < 5; i++)
1277                 if (rates[i] == rate) break;
1278
1279         i += dir ? 1 : -1;
1280         if (i > 2) return dir ? 44100 : 22050;
1281         if (i < 0) return dir ? 22050 : 11025;
1282         return rates[i];
1283 }
1284
1285 static void region_prevnext(int right)
1286 {
1287         // jp_ntsc=1, jp_pal=2, usa=4, eu=8
1288         static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };
1289         int i;
1290         if (right) {
1291                 if (!PicoRegionOverride) {
1292                         for (i = 0; i < 6; i++)
1293                                 if (rgn_orders[i] == PicoAutoRgnOrder) break;
1294                         if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];
1295                         else PicoRegionOverride=1;
1296                 }
1297                 else PicoRegionOverride<<=1;
1298                 if (PicoRegionOverride > 8) PicoRegionOverride = 8;
1299         } else {
1300                 if (!PicoRegionOverride) {
1301                         for (i = 0; i < 6; i++)
1302                                 if (rgn_orders[i] == PicoAutoRgnOrder) break;
1303                         if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];
1304                 }
1305                 else PicoRegionOverride>>=1;
1306         }
1307 }
1308
1309 static void menu_options_save(void)
1310 {
1311         if (PicoRegionOverride) {
1312                 // force setting possibly changed..
1313                 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;
1314         }
1315         if (!(PicoOpt & POPT_6BTN_PAD)) {
1316                 // unbind XYZ MODE, just in case
1317                 unbind_action(0xf00);
1318         }
1319 }
1320
1321 static int menu_loop_options(void)
1322 {
1323         static int menu_sel = 0;
1324         int menu_sel_max, ret;
1325         unsigned long inp = 0;
1326         menu_id selected_id;
1327
1328         me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);
1329         me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);
1330         menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;
1331         if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;
1332
1333         while (1)
1334         {
1335                 draw_menu_options(menu_sel);
1336                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_X|PBTN_CIRCLE, 0);
1337                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
1338                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
1339                 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);
1340                 if (inp & (PBTN_LEFT|PBTN_RIGHT)) { // multi choise
1341                         if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&PBTN_RIGHT) ? 1 : 0)) {
1342                                 switch (selected_id) {
1343                                         case MA_OPT_RENDERER:
1344                                                 if ((PicoOpt & 0x10) || !(currentConfig.EmuOpt & 0x80)) {
1345                                                         PicoOpt &= ~0x10;
1346                                                         currentConfig.EmuOpt |=  0x80;
1347                                                 } else {
1348                                                         PicoOpt |=  0x10;
1349                                                         currentConfig.EmuOpt &= ~0x80;
1350                                                 }
1351                                                 break;
1352                                         case MA_OPT_SOUND_QUALITY:
1353                                                 PsndRate = sndrate_prevnext(PsndRate, inp & PBTN_RIGHT);
1354                                                 break;
1355                                         case MA_OPT_REGION:
1356                                                 region_prevnext(inp & PBTN_RIGHT);
1357                                                 break;
1358                                         case MA_OPT_CONFIRM_STATES: {
1359                                                          int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);
1360                                                          n += (inp & PBTN_LEFT) ? -1 : 1;
1361                                                          if (n < 0) n = 0; else if (n > 3) n = 3;
1362                                                          n |= n << 1; n &= ~2;
1363                                                          currentConfig.EmuOpt &= ~0xa00;
1364                                                          currentConfig.EmuOpt |= n << 9;
1365                                                          break;
1366                                                  }
1367                                         case MA_OPT_SAVE_SLOT:
1368                                                  if (inp & PBTN_RIGHT) {
1369                                                          state_slot++; if (state_slot > 9) state_slot = 0;
1370                                                  } else {state_slot--; if (state_slot < 0) state_slot = 9;
1371                                                  }
1372                                                  break;
1373                                         case MA_OPT_CPU_CLOCKS:
1374                                                  while ((inp = psp_pad_read(0)) & (PBTN_LEFT|PBTN_RIGHT)) {
1375                                                          currentConfig.CPUclock += (inp & PBTN_LEFT) ? -1 : 1;
1376                                                          if (currentConfig.CPUclock <  19) currentConfig.CPUclock = 19;
1377                                                          if (currentConfig.CPUclock > 333) currentConfig.CPUclock = 333;
1378                                                          draw_menu_options(menu_sel); // will wait vsync
1379                                                  }
1380                                                  break;
1381                                         case MA_OPT_SAVECFG:
1382                                         case MA_OPT_SAVECFG_GAME:
1383                                         case MA_OPT_LOADCFG:
1384                                                  config_slot += (inp&PBTN_RIGHT) ? 1 : -1;
1385                                                  if (config_slot > 9) config_slot = 0;
1386                                                  if (config_slot < 0) config_slot = 9;
1387                                                  me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);
1388                                                  menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;
1389                                                  if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;
1390                                                  break;
1391                                         default:
1392                                                 //lprintf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);
1393                                                 break;
1394                                 }
1395                         }
1396                 }
1397                 if (inp & PBTN_CIRCLE) {
1398                         if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))
1399                         {
1400                                 switch (selected_id)
1401                                 {
1402                                         case MA_OPT_DISP_OPTS:
1403                                                 dispmenu_loop_options();
1404                                                 break;
1405                                         case MA_OPT_SCD_OPTS:
1406                                                 cd_menu_loop_options();
1407                                                 if (engineState == PGS_ReloadRom)
1408                                                         return 0; // test BIOS
1409                                                 break;
1410                                         case MA_OPT_ADV_OPTS:
1411                                                 amenu_loop_options();
1412                                                 break;
1413                                         case MA_OPT_SAVECFG: // done (update and write)
1414                                                 menu_options_save();
1415                                                 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");
1416                                                 else strcpy(menuErrorMsg, "failed to write config");
1417                                                 return 1;
1418                                         case MA_OPT_SAVECFG_GAME: // done (update and write for current game)
1419                                                 menu_options_save();
1420                                                 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");
1421                                                 else strcpy(menuErrorMsg, "failed to write config");
1422                                                 return 1;
1423                                         case MA_OPT_LOADCFG:
1424                                                 ret = emu_ReadConfig(1, 1);
1425                                                 if (!ret) ret = emu_ReadConfig(0, 1);
1426                                                 if (ret)  strcpy(menuErrorMsg, "config loaded");
1427                                                 else      strcpy(menuErrorMsg, "failed to load config");
1428                                                 return 1;
1429                                         default:
1430                                                 //lprintf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);
1431                                                 break;
1432                                 }
1433                         }
1434                 }
1435                 if(inp & PBTN_X) {
1436                         menu_options_save();
1437                         return 0;  // done (update, no write)
1438                 }
1439         }
1440 }
1441
1442 // -------------- credits --------------
1443
1444 static void draw_menu_credits(void)
1445 {
1446         int tl_x = 80+15, tl_y = 16+64, y;
1447         menu_draw_begin();
1448
1449         text_out16(tl_x, 16+20, "PicoDrive v" VERSION " (c) notaz, 2006-2008");
1450
1451         y = tl_y;
1452         text_out16(tl_x, y, "Credits:");
1453         text_out16(tl_x, (y+=10), "fDave: base code of PicoDrive");
1454         text_out16(tl_x, (y+=10), "Chui: Fame/C");
1455         text_out16(tl_x, (y+=10), "NJ: CZ80");
1456         text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");
1457         text_out16(tl_x, (y+=10), "ps2dev.org people: PSP SDK/code");
1458         text_out16(tl_x, (y+=10), "ketchupgun: skin design");
1459
1460         text_out16(tl_x, (y+=20), "special thanks (for docs, ideas):");
1461         text_out16(tl_x, (y+=10), " Charles MacDonald, Haze,");
1462         text_out16(tl_x, (y+=10), " Stephane Dallongeville,");
1463         text_out16(tl_x, (y+=10), " Lordus, Exophase, Rokas,");
1464         text_out16(tl_x, (y+=10), " Nemesis, Tasco Deluxe");
1465
1466         menu_draw_end();
1467 }
1468
1469
1470 // -------------- root menu --------------
1471
1472 menu_entry main_entries[] =
1473 {
1474         { "Resume game",        MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0 },
1475         { "Save State",         MB_NONE, MA_MAIN_SAVE_STATE,  NULL, 0, 0, 0, 0 },
1476         { "Load State",         MB_NONE, MA_MAIN_LOAD_STATE,  NULL, 0, 0, 0, 0 },
1477         { "Reset game",         MB_NONE, MA_MAIN_RESET_GAME,  NULL, 0, 0, 0, 0 },
1478         { "Load new ROM/ISO",   MB_NONE, MA_MAIN_LOAD_ROM,    NULL, 0, 0, 0, 1 },
1479         { "Change options",     MB_NONE, MA_MAIN_OPTIONS,     NULL, 0, 0, 0, 1 },
1480         { "Configure controls", MB_NONE, MA_MAIN_CONTROLS,    NULL, 0, 0, 0, 1 },
1481         { "Credits",            MB_NONE, MA_MAIN_CREDITS,     NULL, 0, 0, 0, 1 },
1482         { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES,     NULL, 0, 0, 0, 0 },
1483         { "Exit",               MB_NONE, MA_MAIN_EXIT,        NULL, 0, 0, 0, 1 }
1484 };
1485
1486 #define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))
1487
1488 static void draw_menu_root(int menu_sel)
1489 {
1490         const int tl_x = 86+70, tl_y = 16+70;
1491         char *stat = NULL;
1492
1493         menu_draw_begin();
1494
1495         if ((currentConfig.EmuOpt&0x20000) && (stat = psp_get_status_line()))
1496                 text_out16(287, 12, "%s", stat);
1497
1498         text_out16(tl_x, 48, "PicoDrive v" VERSION);
1499
1500         menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);
1501
1502         me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);
1503
1504         // error
1505         if (menuErrorMsg[0])
1506                 text_out16(10, 252, menuErrorMsg);
1507         menu_draw_end();
1508 }
1509
1510
1511 static void menu_loop_root(void)
1512 {
1513         static int menu_sel = 0;
1514         int ret, menu_sel_max;
1515         unsigned long inp = 0;
1516
1517         me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_loaded);
1518         me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE,  rom_loaded);
1519         me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE,  rom_loaded);
1520         me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME,  rom_loaded);
1521         me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES,     PicoPatches != NULL);
1522
1523         menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;
1524         if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;
1525
1526         // mp3 errors?
1527         if (mp3_last_error != 0) {
1528                 if (mp3_last_error == -1)
1529                      sprintf(menuErrorMsg, "Unsupported mp3 format, use 44kHz stereo");
1530                 else sprintf(menuErrorMsg, "mp3 init failed, code %08x", mp3_last_error);
1531                 mp3_last_error = 0;
1532         }
1533
1534         /* make sure action buttons are not pressed on entering menu */
1535         draw_menu_root(menu_sel);
1536
1537         while (psp_pad_read(1) & (PBTN_X|PBTN_CIRCLE|PBTN_SELECT)) psp_msleep(50);
1538
1539         for (;;)
1540         {
1541                 draw_menu_root(menu_sel);
1542                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_X|PBTN_CIRCLE|PBTN_SELECT|PBTN_L|PBTN_R, 0);
1543                 if(inp & PBTN_UP  )  { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
1544                 if(inp & PBTN_DOWN)  { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
1545                 if((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R)) debug_menu_loop();
1546                 if( inp & (PBTN_SELECT|PBTN_X)) {
1547                         if (rom_loaded) {
1548                                 while (psp_pad_read(1) & (PBTN_SELECT|PBTN_X)) psp_msleep(50); // wait until released
1549                                 engineState = PGS_Running;
1550                                 break;
1551                         }
1552                 }
1553                 if(inp & PBTN_CIRCLE)  {
1554                         menuErrorMsg[0] = 0; // clear error msg
1555                         switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))
1556                         {
1557                                 case MA_MAIN_RESUME_GAME:
1558                                         if (rom_loaded) {
1559                                                 while (psp_pad_read(1) & PBTN_CIRCLE) psp_msleep(50);
1560                                                 engineState = PGS_Running;
1561                                                 return;
1562                                         }
1563                                         break;
1564                                 case MA_MAIN_SAVE_STATE:
1565                                         if (rom_loaded) {
1566                                                 if(savestate_menu_loop(0))
1567                                                         continue;
1568                                                 engineState = PGS_Running;
1569                                                 return;
1570                                         }
1571                                         break;
1572                                 case MA_MAIN_LOAD_STATE:
1573                                         if (rom_loaded) {
1574                                                 if(savestate_menu_loop(1))
1575                                                         continue;
1576                                                 while (psp_pad_read(1) & PBTN_CIRCLE) psp_msleep(50);
1577                                                 engineState = PGS_Running;
1578                                                 return;
1579                                         }
1580                                         break;
1581                                 case MA_MAIN_RESET_GAME:
1582                                         if (rom_loaded) {
1583                                                 emu_ResetGame();
1584                                                 while (psp_pad_read(1) & PBTN_CIRCLE) psp_msleep(50);
1585                                                 engineState = PGS_Running;
1586                                                 return;
1587                                         }
1588                                         break;
1589                                 case MA_MAIN_LOAD_ROM:
1590                                 {
1591                                         char curr_path[PATH_MAX], *selfname;
1592                                         FILE *tstf;
1593                                         if ( (tstf = fopen(loadedRomFName, "rb")) )
1594                                         {
1595                                                 fclose(tstf);
1596                                                 strcpy(curr_path, loadedRomFName);
1597                                         }
1598                                         else
1599                                                 getcwd(curr_path, PATH_MAX);
1600                                         selfname = romsel_loop(curr_path);
1601                                         if (selfname) {
1602                                                 lprintf("selected file: %s\n", selfname);
1603                                                 engineState = PGS_ReloadRom;
1604                                                 return;
1605                                         }
1606                                         break;
1607                                 }
1608                                 case MA_MAIN_OPTIONS:
1609                                         ret = menu_loop_options();
1610                                         if (ret == 1) continue; // status update
1611                                         if (engineState == PGS_ReloadRom)
1612                                                 return; // BIOS test
1613                                         break;
1614                                 case MA_MAIN_CONTROLS:
1615                                         kc_sel_loop();
1616                                         break;
1617                                 case MA_MAIN_CREDITS:
1618                                         draw_menu_credits();
1619                                         psp_msleep(500);
1620                                         inp = 0;
1621                                         while (!(inp & (PBTN_X|PBTN_CIRCLE)))
1622                                                 inp = in_menu_wait(PBTN_X|PBTN_CIRCLE, 0);
1623                                         break;
1624                                 case MA_MAIN_EXIT:
1625                                         engineState = PGS_Quit;
1626                                         return;
1627                                 case MA_MAIN_PATCHES:
1628                                         if (rom_loaded && PicoPatches) {
1629                                                 patches_menu_loop();
1630                                                 PicoPatchApply();
1631                                                 strcpy(menuErrorMsg, "Patches applied");
1632                                                 continue;
1633                                         }
1634                                         break;
1635                                 default:
1636                                         lprintf("%s: something unknown selected\n", __FUNCTION__);
1637                                         break;
1638                         }
1639                 }
1640         }
1641 }
1642
1643 void menu_darken_bg(void *dst, const void *src, int pixels, int darker)
1644 {
1645         unsigned int *dest = dst;
1646         const unsigned int *srce = src;
1647         pixels /= 2;
1648         if (darker)
1649         {
1650                 while (pixels--)
1651                 {
1652                         unsigned int p = *srce++;
1653                         *dest++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);
1654                 }
1655         }
1656         else
1657         {
1658                 while (pixels--)
1659                 {
1660                         unsigned int p = *srce++;
1661                         *dest++ = (p&0xf79ef79e)>>1;
1662                 }
1663         }
1664 }
1665
1666 static void menu_prepare_bg(int use_game_bg, int use_fg)
1667 {
1668         if (use_game_bg)
1669         {
1670                 // darken the active framebuffer
1671                 unsigned short *dst = bg_buffer;
1672                 unsigned short *src = use_fg ? psp_video_get_active_fb() : psp_screen;
1673                 int i;
1674                 for (i = 272; i > 0; i--, dst += 480, src += 512)
1675                         menu_darken_bg(dst, src, 480, 1);
1676                 //memset32_uncached((int *)(bg_buffer + 480*264), 0, 480*8*2/4);
1677         }
1678         else
1679         {
1680                 // should really only happen once, on startup..
1681                 memset32_uncached((int *)(void *)bg_buffer, 0, sizeof(bg_buffer)/4);
1682                 readpng(bg_buffer, "skin/background.png", READPNG_BG);
1683         }
1684         sceKernelDcacheWritebackAll();
1685 }
1686
1687 static void menu_gfx_prepare(void)
1688 {
1689         menu_prepare_bg(rom_loaded, 1);
1690
1691         menu_draw_begin();
1692         menu_draw_end();
1693 }
1694
1695
1696 void menu_loop(void)
1697 {
1698         menu_gfx_prepare();
1699
1700         menu_loop_root();
1701
1702         menuErrorMsg[0] = 0;
1703 }
1704
1705
1706 // --------- CD tray close menu ----------
1707
1708 static void draw_menu_tray(int menu_sel)
1709 {
1710         int tl_x = 70, tl_y = 90, y;
1711
1712         menu_draw_begin();
1713
1714         text_out16(tl_x, 20, "The unit is about to");
1715         text_out16(tl_x, 30, "close the CD tray.");
1716
1717         y = tl_y;
1718         text_out16(tl_x, y,       "Load new CD image");
1719         text_out16(tl_x, (y+=10), "Insert nothing");
1720
1721         // draw cursor
1722         text_out16(tl_x - 16, tl_y + menu_sel*10, ">");
1723         // error
1724         if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);
1725         menu_draw_end();
1726 }
1727
1728
1729 int menu_loop_tray(void)
1730 {
1731         int menu_sel = 0, menu_sel_max = 1;
1732         unsigned long inp = 0;
1733         char curr_path[PATH_MAX], *selfname;
1734         FILE *tstf;
1735
1736         menu_gfx_prepare();
1737
1738         if ( (tstf = fopen(loadedRomFName, "rb")) )
1739         {
1740                 fclose(tstf);
1741                 strcpy(curr_path, loadedRomFName);
1742         }
1743         else
1744         {
1745                 getcwd(curr_path, PATH_MAX);
1746         }
1747
1748         /* make sure action buttons are not pressed on entering menu */
1749         draw_menu_tray(menu_sel);
1750         while (psp_pad_read(1) & PBTN_CIRCLE) psp_msleep(50);
1751
1752         for (;;)
1753         {
1754                 draw_menu_tray(menu_sel);
1755                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_CIRCLE, 0);
1756                 if(inp & PBTN_UP  )  { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
1757                 if(inp & PBTN_DOWN)  { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
1758                 if(inp & PBTN_CIRCLE)  {
1759                         switch (menu_sel) {
1760                                 case 0: // select image
1761                                         selfname = romsel_loop(curr_path);
1762                                         if (selfname) {
1763                                                 int ret = -1;
1764                                                 cd_img_type cd_type;
1765                                                 cd_type = emu_cdCheck(NULL, romFileName);
1766                                                 if (cd_type != CIT_NOT_CD)
1767                                                         ret = Insert_CD(romFileName, cd_type);
1768                                                 if (ret != 0) {
1769                                                         sprintf(menuErrorMsg, "Load failed, invalid CD image?");
1770                                                         lprintf("%s\n", menuErrorMsg);
1771                                                         continue;
1772                                                 }
1773                                                 engineState = PGS_RestartRun;
1774                                                 return 1;
1775                                         }
1776                                         break;
1777                                 case 1: // insert nothing
1778                                         engineState = PGS_RestartRun;
1779                                         return 0;
1780                         }
1781                 }
1782                 menuErrorMsg[0] = 0; // clear error msg
1783         }
1784 }
1785
1786