menu.c 53 KB

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