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