menu.c 53 KB

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