menu.c 54 KB

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