menu.c 52 KB

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