menu.c 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235
  1. // (c) Copyright 2006-2009 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 "menu.h"
  9. #include "fonts.h"
  10. #include "readpng.h"
  11. #include "lprintf.h"
  12. #include "input.h"
  13. #include "emu.h"
  14. #include "plat.h"
  15. #include "posix.h"
  16. #include <version.h>
  17. #include <revision.h>
  18. #include <pico/pico.h>
  19. #include <pico/patch.h>
  20. static char static_buff[64];
  21. static int menu_error_time = 0;
  22. char menu_error_msg[64] = { 0, };
  23. void *g_menubg_ptr;
  24. #ifndef UIQ3
  25. static unsigned char *menu_font_data = NULL;
  26. static int menu_text_color = 0xffff; // default to white
  27. static int menu_sel_color = -1; // disabled
  28. /* note: these might become non-constant in future */
  29. #if MENU_X2
  30. static const int me_mfont_w = 16, me_mfont_h = 20;
  31. static const int me_sfont_w = 12, me_sfont_h = 20;
  32. #else
  33. static const int me_mfont_w = 8, me_mfont_h = 10;
  34. static const int me_sfont_w = 6, me_sfont_h = 10;
  35. #endif
  36. // draws text to current bbp16 screen
  37. static void text_out16_(int x, int y, const char *text, int color)
  38. {
  39. int i, lh, tr, tg, tb, len;
  40. unsigned short *dest = (unsigned short *)g_screen_ptr + x + y * g_screen_width;
  41. tr = (color & 0xf800) >> 8;
  42. tg = (color & 0x07e0) >> 3;
  43. tb = (color & 0x001f) << 3;
  44. if (text == (void *)1)
  45. {
  46. // selector symbol
  47. text = "";
  48. len = 1;
  49. }
  50. else
  51. {
  52. const char *p;
  53. for (p = text; *p != 0 && *p != '\n'; p++)
  54. ;
  55. len = p - text;
  56. }
  57. lh = me_mfont_h;
  58. if (y + lh > g_screen_height)
  59. lh = g_screen_height - y;
  60. for (i = 0; i < len; i++)
  61. {
  62. unsigned char *src = menu_font_data + (unsigned int)text[i] * me_mfont_w * me_mfont_h / 2;
  63. unsigned short *dst = dest;
  64. int u, l;
  65. for (l = 0; l < lh; l++, dst += g_screen_width - me_mfont_w)
  66. {
  67. for (u = me_mfont_w / 2; u > 0; u--, src++)
  68. {
  69. int c, r, g, b;
  70. c = *src >> 4;
  71. r = (*dst & 0xf800) >> 8;
  72. g = (*dst & 0x07e0) >> 3;
  73. b = (*dst & 0x001f) << 3;
  74. r = (c^0xf)*r/15 + c*tr/15;
  75. g = (c^0xf)*g/15 + c*tg/15;
  76. b = (c^0xf)*b/15 + c*tb/15;
  77. *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);
  78. c = *src & 0xf;
  79. r = (*dst & 0xf800) >> 8;
  80. g = (*dst & 0x07e0) >> 3;
  81. b = (*dst & 0x001f) << 3;
  82. r = (c^0xf)*r/15 + c*tr/15;
  83. g = (c^0xf)*g/15 + c*tg/15;
  84. b = (c^0xf)*b/15 + c*tb/15;
  85. *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);
  86. }
  87. }
  88. dest += me_mfont_w;
  89. }
  90. }
  91. void text_out16(int x, int y, const char *texto, ...)
  92. {
  93. va_list args;
  94. char buffer[256];
  95. int maxw = (g_screen_width - x) / me_mfont_w;
  96. if (maxw < 0)
  97. return;
  98. va_start(args, texto);
  99. vsnprintf(buffer, sizeof(buffer), texto, args);
  100. va_end(args);
  101. if (maxw > sizeof(buffer) - 1)
  102. maxw = sizeof(buffer) - 1;
  103. buffer[maxw] = 0;
  104. text_out16_(x,y,buffer,menu_text_color);
  105. }
  106. /* draws in 6x8 font, might multiply size by integer */
  107. static void smalltext_out16_(int x, int y, const char *texto, int color)
  108. {
  109. unsigned char *src;
  110. unsigned short *dst;
  111. int multiplier = me_sfont_w / 6;
  112. int i;
  113. for (i = 0;; i++, x += me_sfont_w)
  114. {
  115. unsigned char c = (unsigned char) texto[i];
  116. int h = 8;
  117. if (!c || c == '\n')
  118. break;
  119. src = fontdata6x8[c];
  120. dst = (unsigned short *)g_screen_ptr + x + y * g_screen_width;
  121. while (h--)
  122. {
  123. int m, w2, h2;
  124. for (h2 = multiplier; h2 > 0; h2--)
  125. {
  126. for (m = 0x20; m; m >>= 1) {
  127. if (*src & m)
  128. for (w2 = multiplier; w2 > 0; w2--)
  129. *dst++ = color;
  130. else
  131. dst += multiplier;
  132. }
  133. dst += g_screen_width - me_sfont_w;
  134. }
  135. src++;
  136. }
  137. }
  138. }
  139. static void smalltext_out16(int x, int y, const char *texto, int color)
  140. {
  141. char buffer[128];
  142. int maxw = (g_screen_width - x) / 6;
  143. if (maxw < 0)
  144. return;
  145. strncpy(buffer, texto, sizeof(buffer));
  146. if (maxw > sizeof(buffer) - 1)
  147. maxw = sizeof(buffer) - 1;
  148. buffer[maxw] = 0;
  149. smalltext_out16_(x, y, buffer, color);
  150. }
  151. static void menu_draw_selection(int x, int y, int w)
  152. {
  153. int i, h;
  154. unsigned short *dst, *dest;
  155. text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);
  156. if (menu_sel_color < 0) return; // no selection hilight
  157. if (y > 0) y--;
  158. dest = (unsigned short *)g_screen_ptr + x + y * g_screen_width + me_mfont_w * 2 - 2;
  159. for (h = me_mfont_h + 1; h > 0; h--)
  160. {
  161. dst = dest;
  162. for (i = w - (me_mfont_w * 2 - 2); i > 0; i--)
  163. *dst++ = menu_sel_color;
  164. dest += g_screen_width;
  165. }
  166. }
  167. static int parse_hex_color(char *buff)
  168. {
  169. char *endp = buff;
  170. int t = (int) strtoul(buff, &endp, 16);
  171. if (endp != buff)
  172. #ifdef PSP
  173. return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);
  174. #else
  175. return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);
  176. #endif
  177. return -1;
  178. }
  179. void menu_init(void)
  180. {
  181. int i, c, l;
  182. unsigned char *fd, *fds;
  183. char buff[256];
  184. FILE *f;
  185. if (menu_font_data != NULL)
  186. free(menu_font_data);
  187. menu_font_data = calloc((MENU_X2 ? 256 * 320 : 128 * 160) / 2, 1);
  188. if (menu_font_data == NULL)
  189. return;
  190. // generate default 8x10 font from fontdata8x8
  191. for (c = 0, fd = menu_font_data; c < 256; c++)
  192. {
  193. for (l = 0; l < 8; l++)
  194. {
  195. unsigned char fd8x8 = fontdata8x8[c*8+l];
  196. if (fd8x8&0x80) *fd = 0xf0;
  197. if (fd8x8&0x40) *fd |= 0x0f; fd++;
  198. if (fd8x8&0x20) *fd = 0xf0;
  199. if (fd8x8&0x10) *fd |= 0x0f; fd++;
  200. if (fd8x8&0x08) *fd = 0xf0;
  201. if (fd8x8&0x04) *fd |= 0x0f; fd++;
  202. if (fd8x8&0x02) *fd = 0xf0;
  203. if (fd8x8&0x01) *fd |= 0x0f; fd++;
  204. }
  205. fd += 8*2/2; // 2 empty lines
  206. }
  207. if (MENU_X2) {
  208. // expand default font
  209. fds = menu_font_data + 128 * 160 / 2 - 4;
  210. fd = menu_font_data + 256 * 320 / 2 - 1;
  211. for (c = 255; c >= 0; c--)
  212. {
  213. for (l = 9; l >= 0; l--, fds -= 4)
  214. {
  215. for (i = 3; i >= 0; i--) {
  216. int px = fds[i] & 0x0f;
  217. *fd-- = px | (px << 4);
  218. px = (fds[i] >> 4) & 0x0f;
  219. *fd-- = px | (px << 4);
  220. }
  221. for (i = 3; i >= 0; i--) {
  222. int px = fds[i] & 0x0f;
  223. *fd-- = px | (px << 4);
  224. px = (fds[i] >> 4) & 0x0f;
  225. *fd-- = px | (px << 4);
  226. }
  227. }
  228. }
  229. }
  230. // load custom font and selector (stored as 1st symbol in font table)
  231. emu_make_path(buff, "skin/font.png", sizeof(buff));
  232. readpng(menu_font_data, buff, READPNG_FONT,
  233. MENU_X2 ? 256 : 128, MENU_X2 ? 320 : 160);
  234. // default selector symbol is '>'
  235. memcpy(menu_font_data, menu_font_data + ((int)'>') * me_mfont_w * me_mfont_h / 2,
  236. me_mfont_w * me_mfont_h / 2);
  237. emu_make_path(buff, "skin/selector.png", sizeof(buff));
  238. readpng(menu_font_data, buff, READPNG_SELECTOR, MENU_X2 ? 16 : 8, MENU_X2 ? 20 : 10);
  239. // load custom colors
  240. emu_make_path(buff, "skin/skin.txt", sizeof(buff));
  241. f = fopen(buff, "r");
  242. if (f != NULL)
  243. {
  244. lprintf("found skin.txt\n");
  245. while (!feof(f))
  246. {
  247. if (fgets(buff, sizeof(buff), f) == NULL)
  248. break;
  249. if (buff[0] == '#' || buff[0] == '/') continue; // comment
  250. if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line
  251. if (strncmp(buff, "text_color=", 11) == 0)
  252. {
  253. int tmp = parse_hex_color(buff+11);
  254. if (tmp >= 0) menu_text_color = tmp;
  255. else lprintf("skin.txt: parse error for text_color\n");
  256. }
  257. else if (strncmp(buff, "selection_color=", 16) == 0)
  258. {
  259. int tmp = parse_hex_color(buff+16);
  260. if (tmp >= 0) menu_sel_color = tmp;
  261. else lprintf("skin.txt: parse error for selection_color\n");
  262. }
  263. else
  264. lprintf("skin.txt: parse error: %s\n", buff);
  265. }
  266. fclose(f);
  267. }
  268. }
  269. static void menu_darken_bg(void *dst, void *src, int pixels, int darker)
  270. {
  271. unsigned int *dest = dst;
  272. unsigned int *sorc = src;
  273. pixels /= 2;
  274. if (darker)
  275. {
  276. while (pixels--)
  277. {
  278. unsigned int p = *sorc++;
  279. *dest++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);
  280. }
  281. }
  282. else
  283. {
  284. while (pixels--)
  285. {
  286. unsigned int p = *sorc++;
  287. *dest++ = (p&0xf79ef79e)>>1;
  288. }
  289. }
  290. }
  291. static void menu_enter(int is_rom_loaded)
  292. {
  293. if (is_rom_loaded)
  294. {
  295. // darken the active framebuffer
  296. menu_darken_bg(g_menubg_ptr, g_screen_ptr, g_screen_width * g_screen_height, 1);
  297. }
  298. else
  299. {
  300. char buff[256];
  301. // should really only happen once, on startup..
  302. emu_make_path(buff, "skin/background.png", sizeof(buff));
  303. if (readpng(g_menubg_ptr, buff, READPNG_BG, g_screen_width, g_screen_height) < 0)
  304. memset(g_menubg_ptr, 0, g_screen_width * g_screen_height * 2);
  305. }
  306. plat_video_menu_enter(is_rom_loaded);
  307. }
  308. static int me_id2offset(const menu_entry *ent, menu_id id)
  309. {
  310. int i;
  311. for (i = 0; ent->name; ent++, i++)
  312. if (ent->id == id) return i;
  313. lprintf("%s: id %i not found\n", __FUNCTION__, id);
  314. return 0;
  315. }
  316. static void me_enable(menu_entry *entries, menu_id id, int enable)
  317. {
  318. int i = me_id2offset(entries, id);
  319. entries[i].enabled = enable;
  320. }
  321. static int me_count(const menu_entry *ent)
  322. {
  323. int ret;
  324. for (ret = 0; ent->name; ent++, ret++)
  325. ;
  326. return ret;
  327. }
  328. static void me_draw(const menu_entry *entries, int sel, void (*draw_more)(void))
  329. {
  330. const menu_entry *ent, *ent_sel = entries;
  331. int x, y, w = 0, h = 0;
  332. int offs, col2_offs = 27 * me_mfont_w;
  333. int vi_sel_ln = 0;
  334. const char *name;
  335. int i, n;
  336. /* calculate size of menu rect */
  337. for (ent = entries, i = n = 0; ent->name; ent++, i++)
  338. {
  339. int wt;
  340. if (!ent->enabled)
  341. continue;
  342. if (i == sel) {
  343. ent_sel = ent;
  344. vi_sel_ln = n;
  345. }
  346. name = NULL;
  347. wt = strlen(ent->name) * me_mfont_w;
  348. if (wt == 0 && ent->generate_name)
  349. name = ent->generate_name(ent->id, &offs);
  350. if (name != NULL)
  351. wt = strlen(name) * me_mfont_w;
  352. if (ent->beh != MB_NONE)
  353. {
  354. if (wt > col2_offs)
  355. col2_offs = wt + me_mfont_w;
  356. wt = col2_offs;
  357. switch (ent->beh) {
  358. case MB_NONE: break;
  359. case MB_OPT_ONOFF:
  360. case MB_OPT_RANGE: wt += me_mfont_w * 3; break;
  361. case MB_OPT_CUSTOM:
  362. case MB_OPT_CUSTONOFF:
  363. case MB_OPT_CUSTRANGE:
  364. name = NULL;
  365. offs = 0;
  366. if (ent->generate_name != NULL)
  367. name = ent->generate_name(ent->id, &offs);
  368. if (name != NULL)
  369. wt += (strlen(name) + offs) * me_mfont_w;
  370. break;
  371. case MB_OPT_ENUM:
  372. wt += 10 * me_mfont_w;
  373. break;
  374. }
  375. }
  376. if (wt > w)
  377. w = wt;
  378. n++;
  379. }
  380. h = n * me_mfont_h;
  381. w += me_mfont_w * 2; /* selector */
  382. if (w > g_screen_width) {
  383. lprintf("width %d > %d\n", w, g_screen_width);
  384. w = g_screen_width;
  385. }
  386. if (h > g_screen_height) {
  387. lprintf("height %d > %d\n", w, g_screen_height);
  388. h = g_screen_height;
  389. }
  390. x = g_screen_width / 2 - w / 2;
  391. y = g_screen_height / 2 - h / 2;
  392. /* draw */
  393. plat_video_menu_begin();
  394. menu_draw_selection(x, y + vi_sel_ln * me_mfont_h, w);
  395. x += me_mfont_w * 2;
  396. for (ent = entries; ent->name; ent++)
  397. {
  398. const char **names;
  399. int len;
  400. if (!ent->enabled)
  401. continue;
  402. name = ent->name;
  403. if (strlen(name) == 0) {
  404. if (ent->generate_name)
  405. name = ent->generate_name(ent->id, &offs);
  406. }
  407. if (name != NULL)
  408. text_out16(x, y, name);
  409. switch (ent->beh) {
  410. case MB_NONE:
  411. break;
  412. case MB_OPT_ONOFF:
  413. text_out16(x + col2_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");
  414. break;
  415. case MB_OPT_RANGE:
  416. text_out16(x + col2_offs, y, "%i", *(int *)ent->var);
  417. break;
  418. case MB_OPT_CUSTOM:
  419. case MB_OPT_CUSTONOFF:
  420. case MB_OPT_CUSTRANGE:
  421. name = NULL;
  422. offs = 0;
  423. if (ent->generate_name)
  424. name = ent->generate_name(ent->id, &offs);
  425. if (name != NULL)
  426. text_out16(x + col2_offs + offs * me_mfont_w, y, "%s", name);
  427. break;
  428. case MB_OPT_ENUM:
  429. names = (const char **)ent->data;
  430. offs = 0;
  431. for (i = 0; names[i] != NULL; i++) {
  432. len = strlen(names[i]);
  433. if (len > 10)
  434. offs = 10 - len - 2;
  435. if (i == *(int *)ent->var) {
  436. text_out16(x + col2_offs + offs * me_mfont_w, y, "%s", names[i]);
  437. break;
  438. }
  439. }
  440. break;
  441. }
  442. y += me_mfont_h;
  443. }
  444. /* display help or message if we have one */
  445. h = (g_screen_height - h) / 2; // bottom area height
  446. if (menu_error_msg[0] != 0) {
  447. if (h >= me_mfont_h + 4)
  448. text_out16(5, g_screen_height - me_mfont_h - 4, menu_error_msg);
  449. else
  450. lprintf("menu msg doesn't fit!\n");
  451. if (plat_get_ticks_ms() - menu_error_time > 2048)
  452. menu_error_msg[0] = 0;
  453. }
  454. else if (ent_sel->help != NULL) {
  455. const char *tmp = ent_sel->help;
  456. int l;
  457. for (l = 0; tmp != NULL && *tmp != 0; l++)
  458. tmp = strchr(tmp + 1, '\n');
  459. if (h >= l * me_sfont_h + 4)
  460. for (tmp = ent_sel->help; l > 0; l--, tmp = strchr(tmp, '\n') + 1)
  461. smalltext_out16(5, g_screen_height - (l * me_sfont_h + 4), tmp, 0xffff);
  462. }
  463. if (draw_more != NULL)
  464. draw_more();
  465. plat_video_menu_end();
  466. }
  467. static int me_process(menu_entry *entry, int is_next, int is_lr)
  468. {
  469. const char **names;
  470. int c;
  471. switch (entry->beh)
  472. {
  473. case MB_OPT_ONOFF:
  474. case MB_OPT_CUSTONOFF:
  475. *(int *)entry->var ^= entry->mask;
  476. return 1;
  477. case MB_OPT_RANGE:
  478. case MB_OPT_CUSTRANGE:
  479. c = is_lr ? 10 : 1;
  480. *(int *)entry->var += is_next ? c : -c;
  481. if (*(int *)entry->var < (int)entry->min)
  482. *(int *)entry->var = (int)entry->max;
  483. if (*(int *)entry->var > (int)entry->max)
  484. *(int *)entry->var = (int)entry->min;
  485. return 1;
  486. case MB_OPT_ENUM:
  487. names = (const char **)entry->data;
  488. for (c = 0; names[c] != NULL; c++)
  489. ;
  490. *(int *)entry->var += is_next ? 1 : -1;
  491. if (*(int *)entry->var < 0)
  492. *(int *)entry->var = 0;
  493. if (*(int *)entry->var >= c)
  494. *(int *)entry->var = c - 1;
  495. return 1;
  496. default:
  497. return 0;
  498. }
  499. }
  500. static void debug_menu_loop(void);
  501. static void me_loop(menu_entry *menu, int *menu_sel, void (*draw_more)(void))
  502. {
  503. int ret, inp, sel = *menu_sel, menu_sel_max;
  504. menu_sel_max = me_count(menu) - 1;
  505. if (menu_sel_max < 0) {
  506. lprintf("no enabled menu entries\n");
  507. return;
  508. }
  509. while ((!menu[sel].enabled || !menu[sel].selectable) && sel < menu_sel_max)
  510. sel++;
  511. /* make sure action buttons are not pressed on entering menu */
  512. me_draw(menu, sel, NULL);
  513. while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU));
  514. for (;;)
  515. {
  516. me_draw(menu, sel, draw_more);
  517. inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|
  518. PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R, 70);
  519. if (inp & (PBTN_MENU|PBTN_MBACK))
  520. break;
  521. if (inp & PBTN_UP ) {
  522. do {
  523. sel--;
  524. if (sel < 0)
  525. sel = menu_sel_max;
  526. }
  527. while (!menu[sel].enabled || !menu[sel].selectable);
  528. }
  529. if (inp & PBTN_DOWN) {
  530. do {
  531. sel++;
  532. if (sel > menu_sel_max)
  533. sel = 0;
  534. }
  535. while (!menu[sel].enabled || !menu[sel].selectable);
  536. }
  537. /* a bit hacky but oh well */
  538. if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R))
  539. debug_menu_loop();
  540. if (inp & (PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R)) { /* multi choice */
  541. if (me_process(&menu[sel], (inp & (PBTN_RIGHT|PBTN_R)) ? 1 : 0,
  542. inp & (PBTN_L|PBTN_R)))
  543. continue;
  544. }
  545. if (inp & (PBTN_MOK|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R))
  546. {
  547. /* require PBTN_MOK for MB_NONE */
  548. if (menu[sel].handler != NULL && (menu[sel].beh != MB_NONE || (inp & PBTN_MOK))) {
  549. ret = menu[sel].handler(menu[sel].id, inp);
  550. if (ret) break;
  551. menu_sel_max = me_count(menu) - 1; /* might change, so update */
  552. }
  553. }
  554. }
  555. *menu_sel = sel;
  556. }
  557. /* ***************************************** */
  558. /* platform specific options and handlers */
  559. #if defined(__GP2X__)
  560. #include "../gp2x/menu.c"
  561. #elif defined(PANDORA)
  562. #include "../pandora/menu.c"
  563. #else
  564. #define MENU_OPTIONS_GFX
  565. #define MENU_OPTIONS_ADV
  566. #define menu_main_plat_draw NULL
  567. #endif
  568. static void draw_menu_credits(void)
  569. {
  570. const char *creds, *p;
  571. int x, y, h, w, wt;
  572. p = creds = plat_get_credits();
  573. for (h = 1, w = 0; *p != 0; h++) {
  574. for (wt = 0; *p != 0 && *p != '\n'; p++)
  575. wt++;
  576. if (wt > w)
  577. w = wt;
  578. if (*p == 0)
  579. break;
  580. p++;
  581. }
  582. x = g_screen_width / 2 - w * me_mfont_w / 2;
  583. y = g_screen_height / 2 - h * me_mfont_h / 2;
  584. if (x < 0) x = 0;
  585. if (y < 0) y = 0;
  586. plat_video_menu_begin();
  587. for (p = creds; *p != 0 && y <= g_screen_height - me_mfont_h; y += me_mfont_h) {
  588. text_out16(x, y, p);
  589. for (; *p != 0 && *p != '\n'; p++)
  590. ;
  591. if (*p != 0)
  592. p++;
  593. }
  594. plat_video_menu_end();
  595. }
  596. // --------- loading ROM screen ----------
  597. static int cdload_called = 0;
  598. static void load_progress_cb(int percent)
  599. {
  600. int ln, len = percent * g_screen_width / 100;
  601. unsigned short *dst = (unsigned short *)g_screen_ptr + g_screen_width * 10 * 2;
  602. if (len > g_screen_width)
  603. len = g_screen_width;
  604. for (ln = 10 - 2; ln > 0; ln--, dst += g_screen_width)
  605. memset(dst, 0xff, len * 2);
  606. plat_video_menu_end();
  607. }
  608. static void cdload_progress_cb(const char *fname, int percent)
  609. {
  610. int ln, len = percent * g_screen_width / 100;
  611. unsigned short *dst = (unsigned short *)g_screen_ptr + g_screen_width * 10 * 2;
  612. memset(dst, 0xff, g_screen_width * (me_sfont_h - 2) * 2);
  613. smalltext_out16(1, 3 * me_sfont_h, "Processing CD image / MP3s", 0xffff);
  614. smalltext_out16(1, 4 * me_sfont_h, fname, 0xffff);
  615. dst += g_screen_width * me_sfont_h * 3;
  616. if (len > g_screen_width)
  617. len = g_screen_width;
  618. for (ln = (me_sfont_h - 2); ln > 0; ln--, dst += g_screen_width)
  619. memset(dst, 0xff, len * 2);
  620. plat_video_menu_end();
  621. cdload_called = 1;
  622. }
  623. void menu_romload_prepare(const char *rom_name)
  624. {
  625. const char *p = rom_name + strlen(rom_name);
  626. int i;
  627. while (p > rom_name && *p != '/')
  628. p--;
  629. /* fill all buffers, callbacks won't update in full */
  630. for (i = 0; i < 3; i++) {
  631. plat_video_menu_begin();
  632. smalltext_out16(1, 1, "Loading", 0xffff);
  633. smalltext_out16(1, me_sfont_h, p, 0xffff);
  634. plat_video_menu_end();
  635. }
  636. PicoCartLoadProgressCB = load_progress_cb;
  637. PicoCDLoadProgressCB = cdload_progress_cb;
  638. cdload_called = 0;
  639. }
  640. void menu_romload_end(void)
  641. {
  642. PicoCartLoadProgressCB = NULL;
  643. PicoCDLoadProgressCB = NULL;
  644. smalltext_out16(1, (cdload_called ? 6 : 3) * me_sfont_h,
  645. "Starting emulation...", 0xffff);
  646. plat_video_menu_end();
  647. }
  648. // -------------- del confirm ---------------
  649. static void do_delete(const char *fpath, const char *fname)
  650. {
  651. int len, mid, inp;
  652. const char *nm;
  653. char tmp[64];
  654. plat_video_menu_begin();
  655. if (!rom_loaded)
  656. menu_darken_bg(g_screen_ptr, g_screen_ptr, g_screen_width * g_screen_height, 0);
  657. len = strlen(fname);
  658. if (len > g_screen_width/6)
  659. len = g_screen_width/6;
  660. mid = g_screen_width / 2;
  661. text_out16(mid - me_mfont_w * 15 / 2, 8 * me_mfont_h, "About to delete");
  662. smalltext_out16(mid - len * me_sfont_w / 2, 9 * me_mfont_h + 5, fname, 0xbdff);
  663. text_out16(mid - me_mfont_w * 13 / 2, 11 * me_mfont_h, "Are you sure?");
  664. nm = in_get_key_name(-1, -PBTN_MA3);
  665. snprintf(tmp, sizeof(tmp), "(%s - confirm, ", nm);
  666. len = strlen(tmp);
  667. nm = in_get_key_name(-1, -PBTN_MBACK);
  668. snprintf(tmp + len, sizeof(tmp) - len, "%s - cancel)", nm);
  669. len = strlen(tmp);
  670. text_out16(mid - me_mfont_w * len / 2, 12 * me_mfont_h, tmp);
  671. plat_video_menu_end();
  672. while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MA2));
  673. inp = in_menu_wait(PBTN_MA3|PBTN_MBACK, 100);
  674. if (inp & PBTN_MA3)
  675. remove(fpath);
  676. }
  677. // -------------- ROM selector --------------
  678. // rrrr rggg gggb bbbb
  679. static unsigned short file2color(const char *fname)
  680. {
  681. const char *ext = fname + strlen(fname) - 3;
  682. static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso", "cue" };
  683. static const char *other_exts[] = { "gmv", "pat" };
  684. int i;
  685. if (ext < fname) ext = fname;
  686. for (i = 0; i < array_size(rom_exts); i++)
  687. if (strcasecmp(ext, rom_exts[i]) == 0) return 0xbdff; // FIXME: mk defines
  688. for (i = 0; i < array_size(other_exts); i++)
  689. if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5;
  690. return 0xffff;
  691. }
  692. static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)
  693. {
  694. int max_cnt, start, i, x, pos;
  695. void *darken_ptr;
  696. max_cnt = g_screen_height / me_sfont_h;
  697. start = max_cnt / 2 - sel;
  698. n--; // exclude current dir (".")
  699. plat_video_menu_begin();
  700. // if (!rom_loaded)
  701. // menu_darken_bg(gp2x_screen, 320*240, 0);
  702. darken_ptr = (short *)g_screen_ptr + g_screen_width * max_cnt/2 * me_sfont_h;
  703. menu_darken_bg(darken_ptr, darken_ptr, g_screen_width * me_sfont_h * 8 / 10, 0);
  704. x = 5 + me_mfont_w + 1;
  705. if (start - 2 >= 0)
  706. smalltext_out16(14, (start - 2) * me_sfont_h, curdir, 0xffff);
  707. for (i = 0; i < n; i++) {
  708. pos = start + i;
  709. if (pos < 0) continue;
  710. if (pos >= max_cnt) break;
  711. if (namelist[i+1]->d_type == DT_DIR) {
  712. smalltext_out16(x, pos * me_sfont_h, "/", 0xfff6);
  713. smalltext_out16(x + me_sfont_w, pos * me_sfont_h, namelist[i+1]->d_name, 0xfff6);
  714. } else {
  715. unsigned short color = file2color(namelist[i+1]->d_name);
  716. smalltext_out16(x, pos * me_sfont_h, namelist[i+1]->d_name, color);
  717. }
  718. }
  719. smalltext_out16(5, max_cnt/2 * me_sfont_h, ">", 0xffff);
  720. plat_video_menu_end();
  721. }
  722. static int scandir_cmp(const void *p1, const void *p2)
  723. {
  724. const struct dirent **d1 = (const struct dirent **)p1;
  725. const struct dirent **d2 = (const struct dirent **)p2;
  726. if ((*d1)->d_type == (*d2)->d_type)
  727. return alphasort(d1, d2);
  728. if ((*d1)->d_type == DT_DIR)
  729. return -1; // put before
  730. if ((*d2)->d_type == DT_DIR)
  731. return 1;
  732. return alphasort(d1, d2);
  733. }
  734. static const char *filter_exts[] = {
  735. ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",
  736. ".jpg", ".gpe"
  737. };
  738. static int scandir_filter(const struct dirent *ent)
  739. {
  740. const char *p;
  741. int i;
  742. if (ent == NULL || ent->d_name == NULL) return 0;
  743. if (strlen(ent->d_name) < 5) return 1;
  744. p = ent->d_name + strlen(ent->d_name) - 4;
  745. for (i = 0; i < array_size(filter_exts); i++)
  746. if (strcmp(p, filter_exts[i]) == 0)
  747. return 0;
  748. return 1;
  749. }
  750. static char *menu_loop_romsel(char *curr_path, int len)
  751. {
  752. struct dirent **namelist;
  753. int n, inp, sel = 0;
  754. char *ret = NULL, *fname = NULL;
  755. rescan:
  756. // is this a dir or a full path?
  757. if (!plat_is_dir(curr_path)) {
  758. char *p = curr_path + strlen(curr_path) - 1;
  759. for (; p > curr_path && *p != '/'; p--)
  760. ;
  761. *p = 0;
  762. fname = p+1;
  763. }
  764. n = scandir(curr_path, &namelist, scandir_filter, (void *)scandir_cmp);
  765. if (n < 0) {
  766. char *t;
  767. lprintf("menu_loop_romsel failed, dir: %s\n", curr_path);
  768. // try root
  769. t = getcwd(curr_path, len);
  770. if (t == NULL)
  771. plat_get_root_dir(curr_path, len);
  772. n = scandir(curr_path, &namelist, scandir_filter, (void *)scandir_cmp);
  773. if (n < 0) {
  774. // oops, we failed
  775. lprintf("menu_loop_romsel failed, dir: %s\n", curr_path);
  776. return NULL;
  777. }
  778. }
  779. // try to find sel
  780. if (fname != NULL) {
  781. int i;
  782. for (i = 1; i < n; i++) {
  783. if (strcmp(namelist[i]->d_name, fname) == 0) {
  784. sel = i - 1;
  785. break;
  786. }
  787. }
  788. }
  789. /* make sure action buttons are not pressed on entering menu */
  790. draw_dirlist(curr_path, namelist, n, sel);
  791. while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU))
  792. ;
  793. for (;;)
  794. {
  795. draw_dirlist(curr_path, namelist, n, sel);
  796. inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|
  797. PBTN_L|PBTN_R|PBTN_MA2|PBTN_MOK|PBTN_MBACK|PBTN_MENU, 33);
  798. if (inp & PBTN_UP ) { sel--; if (sel < 0) sel = n-2; }
  799. if (inp & PBTN_DOWN) { sel++; if (sel > n-2) sel = 0; }
  800. if (inp & PBTN_LEFT) { sel-=10; if (sel < 0) sel = 0; }
  801. if (inp & PBTN_L) { sel-=24; if (sel < 0) sel = 0; }
  802. if (inp & PBTN_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }
  803. if (inp & PBTN_R) { sel+=24; if (sel > n-2) sel = n-2; }
  804. if ((inp & PBTN_MOK) || (inp & (PBTN_MENU|PBTN_MA2)) == (PBTN_MENU|PBTN_MA2))
  805. {
  806. again:
  807. if (namelist[sel+1]->d_type == DT_REG)
  808. {
  809. strcpy(rom_fname_reload, curr_path);
  810. strcat(rom_fname_reload, "/");
  811. strcat(rom_fname_reload, namelist[sel+1]->d_name);
  812. if (inp & PBTN_MOK) { // return sel
  813. ret = rom_fname_reload;
  814. break;
  815. }
  816. do_delete(rom_fname_reload, namelist[sel+1]->d_name);
  817. if (n > 0) {
  818. while (n--) free(namelist[n]);
  819. free(namelist);
  820. }
  821. goto rescan;
  822. }
  823. else if (namelist[sel+1]->d_type == DT_DIR)
  824. {
  825. int newlen;
  826. char *p, *newdir;
  827. if (!(inp & PBTN_MOK)) continue;
  828. newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;
  829. newdir = malloc(newlen);
  830. if (strcmp(namelist[sel+1]->d_name, "..") == 0) {
  831. char *start = curr_path;
  832. p = start + strlen(start) - 1;
  833. while (*p == '/' && p > start) p--;
  834. while (*p != '/' && p > start) p--;
  835. if (p <= start) strcpy(newdir, "/");
  836. else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }
  837. } else {
  838. strcpy(newdir, curr_path);
  839. p = newdir + strlen(newdir) - 1;
  840. while (*p == '/' && p >= newdir) *p-- = 0;
  841. strcat(newdir, "/");
  842. strcat(newdir, namelist[sel+1]->d_name);
  843. }
  844. ret = menu_loop_romsel(newdir, newlen);
  845. free(newdir);
  846. break;
  847. }
  848. else
  849. {
  850. // unknown file type, happens on NTFS mounts. Try to guess.
  851. FILE *tstf; int tmp;
  852. strcpy(rom_fname_reload, curr_path);
  853. strcat(rom_fname_reload, "/");
  854. strcat(rom_fname_reload, namelist[sel+1]->d_name);
  855. tstf = fopen(rom_fname_reload, "rb");
  856. if (tstf != NULL)
  857. {
  858. if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)
  859. namelist[sel+1]->d_type = DT_REG;
  860. else namelist[sel+1]->d_type = DT_DIR;
  861. fclose(tstf);
  862. goto again;
  863. }
  864. }
  865. }
  866. if (inp & PBTN_MBACK)
  867. break;
  868. }
  869. if (n > 0) {
  870. while (n--) free(namelist[n]);
  871. free(namelist);
  872. }
  873. return ret;
  874. }
  875. // ------------ patch/gg menu ------------
  876. static void draw_patchlist(int sel)
  877. {
  878. int max_cnt, start, i, pos, active;
  879. max_cnt = g_screen_height / 10;
  880. start = max_cnt / 2 - sel;
  881. plat_video_menu_begin();
  882. for (i = 0; i < PicoPatchCount; i++) {
  883. pos = start + i;
  884. if (pos < 0) continue;
  885. if (pos >= max_cnt) break;
  886. active = PicoPatches[i].active;
  887. smalltext_out16(14, pos * me_sfont_h, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
  888. smalltext_out16(14+6*4, pos * me_sfont_h, PicoPatches[i].name, active ? 0xfff6 : 0xffff);
  889. }
  890. pos = start + i;
  891. if (pos < max_cnt)
  892. smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
  893. text_out16(5, max_cnt / 2 * me_sfont_h, ">");
  894. plat_video_menu_end();
  895. }
  896. static void menu_loop_patches(void)
  897. {
  898. static int menu_sel = 0;
  899. int inp;
  900. for (;;)
  901. {
  902. draw_patchlist(menu_sel);
  903. inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R|PBTN_MOK|PBTN_MBACK, 33);
  904. if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }
  905. if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }
  906. if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
  907. if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }
  908. if (inp & PBTN_MOK) { // action
  909. if (menu_sel < PicoPatchCount)
  910. PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;
  911. else break;
  912. }
  913. if (inp & PBTN_MBACK)
  914. break;
  915. }
  916. }
  917. // ------------ savestate loader ------------
  918. static int state_slot_flags = 0;
  919. static void state_check_slots(void)
  920. {
  921. int slot;
  922. state_slot_flags = 0;
  923. for (slot = 0; slot < 10; slot++) {
  924. if (emu_check_save_file(slot))
  925. state_slot_flags |= 1 << slot;
  926. }
  927. }
  928. static void draw_savestate_bg(int slot)
  929. {
  930. const char *fname;
  931. void *tmp_state;
  932. fname = emu_get_save_fname(1, 0, slot);
  933. if (!fname)
  934. return;
  935. tmp_state = PicoTmpStateSave();
  936. PicoStateLoadGfx(fname);
  937. /* do a frame and fetch menu bg */
  938. pemu_forced_frame(POPT_EN_SOFTSCALE);
  939. menu_enter(1);
  940. PicoTmpStateRestore(tmp_state);
  941. }
  942. static void draw_savestate_menu(int menu_sel, int is_loading)
  943. {
  944. int i, x, y, w, h;
  945. if (state_slot_flags & (1 << menu_sel))
  946. draw_savestate_bg(menu_sel);
  947. w = (13 + 2) * me_mfont_w;
  948. h = (1+2+10+1) * me_mfont_h;
  949. x = g_screen_width / 2 - w / 2;
  950. if (x < 0) x = 0;
  951. y = g_screen_height / 2 - h / 2;
  952. if (y < 0) y = 0;
  953. plat_video_menu_begin();
  954. text_out16(x, y, is_loading ? "Load state" : "Save state");
  955. y += 3 * me_mfont_h;
  956. menu_draw_selection(x - me_mfont_w * 2, y + menu_sel * me_mfont_h, (13 + 2) * me_mfont_w + 4);
  957. /* draw all 10 slots */
  958. for (i = 0; i < 10; i++, y += me_mfont_h)
  959. {
  960. text_out16(x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");
  961. }
  962. text_out16(x, y, "back");
  963. plat_video_menu_end();
  964. }
  965. static int menu_loop_savestate(int is_loading)
  966. {
  967. static int menu_sel = 10;
  968. int menu_sel_max = 10;
  969. unsigned long inp = 0;
  970. int ret = 0;
  971. state_check_slots();
  972. if (!(state_slot_flags & (1 << menu_sel)) && is_loading)
  973. menu_sel = menu_sel_max;
  974. for (;;)
  975. {
  976. draw_savestate_menu(menu_sel, is_loading);
  977. inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK, 100);
  978. if (inp & PBTN_UP) {
  979. do {
  980. menu_sel--;
  981. if (menu_sel < 0)
  982. menu_sel = menu_sel_max;
  983. } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);
  984. }
  985. if (inp & PBTN_DOWN) {
  986. do {
  987. menu_sel++;
  988. if (menu_sel > menu_sel_max)
  989. menu_sel = 0;
  990. } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);
  991. }
  992. if (inp & PBTN_MOK) { // save/load
  993. if (menu_sel < 10) {
  994. state_slot = menu_sel;
  995. if (emu_save_load_game(is_loading, 0)) {
  996. me_update_msg(is_loading ? "Load failed" : "Save failed");
  997. break;
  998. }
  999. ret = 1;
  1000. break;
  1001. }
  1002. break;
  1003. }
  1004. if (inp & PBTN_MBACK)
  1005. break;
  1006. }
  1007. return ret;
  1008. }
  1009. // -------------- key config --------------
  1010. static char *action_binds(int player_idx, int action_mask, int dev_id)
  1011. {
  1012. int k, count, can_combo, type;
  1013. const int *binds;
  1014. static_buff[0] = 0;
  1015. binds = in_get_dev_binds(dev_id);
  1016. if (binds == NULL)
  1017. return static_buff;
  1018. count = in_get_dev_info(dev_id, IN_INFO_BIND_COUNT);
  1019. can_combo = in_get_dev_info(dev_id, IN_INFO_DOES_COMBOS);
  1020. type = IN_BINDTYPE_EMU;
  1021. if (player_idx >= 0) {
  1022. can_combo = 0;
  1023. type = IN_BINDTYPE_PLAYER12;
  1024. }
  1025. if (player_idx == 1)
  1026. action_mask <<= 16;
  1027. for (k = 0; k < count; k++)
  1028. {
  1029. const char *xname;
  1030. int len;
  1031. if (!(binds[IN_BIND_OFFS(k, type)] & action_mask))
  1032. continue;
  1033. xname = in_get_key_name(dev_id, k);
  1034. len = strlen(static_buff);
  1035. if (len) {
  1036. strncat(static_buff, can_combo ? " + " : ", ",
  1037. sizeof(static_buff) - len - 1);
  1038. len += can_combo ? 3 : 2;
  1039. }
  1040. strncat(static_buff, xname, sizeof(static_buff) - len - 1);
  1041. }
  1042. return static_buff;
  1043. }
  1044. static int count_bound_keys(int dev_id, int action_mask, int bindtype)
  1045. {
  1046. const int *binds;
  1047. int k, keys = 0;
  1048. int count;
  1049. binds = in_get_dev_binds(dev_id);
  1050. if (binds == NULL)
  1051. return 0;
  1052. count = in_get_dev_info(dev_id, IN_INFO_BIND_COUNT);
  1053. for (k = 0; k < count; k++)
  1054. {
  1055. if (binds[IN_BIND_OFFS(k, bindtype)] & action_mask)
  1056. keys++;
  1057. }
  1058. return keys;
  1059. }
  1060. static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx,
  1061. int sel, int dev_id, int dev_count, int is_bind)
  1062. {
  1063. char buff[64], buff2[32];
  1064. const char *dev_name;
  1065. int x, y, w, i;
  1066. w = ((player_idx >= 0) ? 20 : 30) * me_mfont_w;
  1067. x = g_screen_width / 2 - w / 2;
  1068. y = (g_screen_height - 4 * me_mfont_h) / 2 - (2 + opt_cnt) * me_mfont_h / 2;
  1069. if (x < me_mfont_w * 2)
  1070. x = me_mfont_w * 2;
  1071. plat_video_menu_begin();
  1072. if (player_idx >= 0)
  1073. text_out16(x, y, "Player %i controls", player_idx + 1);
  1074. else
  1075. text_out16(x, y, "Emulator controls");
  1076. y += 2 * me_mfont_h;
  1077. menu_draw_selection(x - me_mfont_w * 2, y + sel * me_mfont_h, w + 2 * me_mfont_w);
  1078. for (i = 0; i < opt_cnt; i++, y += me_mfont_h)
  1079. text_out16(x, y, "%s : %s", opts[i].name,
  1080. action_binds(player_idx, opts[i].mask, dev_id));
  1081. dev_name = in_get_dev_name(dev_id, 1, 1);
  1082. w = strlen(dev_name) * me_mfont_w;
  1083. if (w < 30 * me_mfont_w)
  1084. w = 30 * me_mfont_w;
  1085. if (w > g_screen_width)
  1086. w = g_screen_width;
  1087. x = g_screen_width / 2 - w / 2;
  1088. if (!is_bind) {
  1089. snprintf(buff2, sizeof(buff2), "%s", in_get_key_name(-1, -PBTN_MOK));
  1090. snprintf(buff, sizeof(buff), "%s - bind, %s - clear", buff2,
  1091. in_get_key_name(-1, -PBTN_MA2));
  1092. text_out16(x, g_screen_height - 4 * me_mfont_h, buff);
  1093. }
  1094. else
  1095. text_out16(x, g_screen_height - 4 * me_mfont_h, "Press a button to bind/unbind");
  1096. if (dev_count > 1) {
  1097. text_out16(x, g_screen_height - 3 * me_mfont_h, dev_name);
  1098. text_out16(x, g_screen_height - 2 * me_mfont_h, "Press left/right for other devs");
  1099. }
  1100. plat_video_menu_end();
  1101. }
  1102. static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)
  1103. {
  1104. int i, sel = 0, menu_sel_max = opt_cnt - 1;
  1105. int dev_id, dev_count, kc, is_down, mkey;
  1106. int unbind, bindtype, mask_shift;
  1107. for (i = 0, dev_id = -1, dev_count = 0; i < IN_MAX_DEVS; i++) {
  1108. if (in_get_dev_name(i, 1, 0) != NULL) {
  1109. dev_count++;
  1110. if (dev_id < 0)
  1111. dev_id = i;
  1112. }
  1113. }
  1114. if (dev_id == -1) {
  1115. lprintf("no devs, can't do config\n");
  1116. return;
  1117. }
  1118. mask_shift = 0;
  1119. if (player_idx == 1)
  1120. mask_shift = 16;
  1121. bindtype = player_idx >= 0 ? IN_BINDTYPE_PLAYER12 : IN_BINDTYPE_EMU;
  1122. for (;;)
  1123. {
  1124. draw_key_config(opts, opt_cnt, player_idx, sel, dev_id, dev_count, 0);
  1125. mkey = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_MBACK|PBTN_MOK|PBTN_MA2, 100);
  1126. switch (mkey) {
  1127. case PBTN_UP: sel--; if (sel < 0) sel = menu_sel_max; continue;
  1128. case PBTN_DOWN: sel++; if (sel > menu_sel_max) sel = 0; continue;
  1129. case PBTN_LEFT:
  1130. for (i = 0, dev_id--; i < IN_MAX_DEVS; i++, dev_id--) {
  1131. if (dev_id < 0)
  1132. dev_id = IN_MAX_DEVS - 1;
  1133. if (in_get_dev_name(dev_id, 1, 0) != NULL)
  1134. break;
  1135. }
  1136. continue;
  1137. case PBTN_RIGHT:
  1138. for (i = 0, dev_id++; i < IN_MAX_DEVS; i++, dev_id++) {
  1139. if (dev_id >= IN_MAX_DEVS)
  1140. dev_id = 0;
  1141. if (in_get_dev_name(dev_id, 1, 0) != NULL)
  1142. break;
  1143. }
  1144. continue;
  1145. case PBTN_MBACK: return;
  1146. case PBTN_MOK:
  1147. if (sel >= opt_cnt)
  1148. return;
  1149. while (in_menu_wait_any(30) & PBTN_MOK);
  1150. break;
  1151. case PBTN_MA2:
  1152. in_unbind_all(dev_id, opts[sel].mask << mask_shift, bindtype);
  1153. continue;
  1154. default:continue;
  1155. }
  1156. draw_key_config(opts, opt_cnt, player_idx, sel, dev_id, dev_count, 1);
  1157. /* wait for some up event */
  1158. for (is_down = 1; is_down; )
  1159. kc = in_update_keycode(&dev_id, &is_down, -1);
  1160. i = count_bound_keys(dev_id, opts[sel].mask << mask_shift, bindtype);
  1161. unbind = (i > 0);
  1162. /* allow combos if device supports them */
  1163. if (i == 1 && bindtype == IN_BINDTYPE_EMU &&
  1164. in_get_dev_info(dev_id, IN_INFO_DOES_COMBOS))
  1165. unbind = 0;
  1166. in_bind_key(dev_id, kc, opts[sel].mask << mask_shift, bindtype, unbind);
  1167. }
  1168. }
  1169. // PicoPad[] format: MXYZ SACB RLDU
  1170. me_bind_action me_ctrl_actions[15] =
  1171. {
  1172. { "UP ", 0x0001 },
  1173. { "DOWN ", 0x0002 },
  1174. { "LEFT ", 0x0004 },
  1175. { "RIGHT ", 0x0008 },
  1176. { "A ", 0x0040 },
  1177. { "B ", 0x0010 },
  1178. { "C ", 0x0020 },
  1179. { "A turbo", 0x4000 },
  1180. { "B turbo", 0x1000 },
  1181. { "C turbo", 0x2000 },
  1182. { "START ", 0x0080 },
  1183. { "MODE ", 0x0800 },
  1184. { "X ", 0x0400 },
  1185. { "Y ", 0x0200 },
  1186. { "Z ", 0x0100 }
  1187. };
  1188. me_bind_action emuctrl_actions[] =
  1189. {
  1190. { "Load State ", PEV_STATE_LOAD },
  1191. { "Save State ", PEV_STATE_SAVE },
  1192. { "Prev Save Slot ", PEV_SSLOT_PREV },
  1193. { "Next Save Slot ", PEV_SSLOT_NEXT },
  1194. { "Switch Renderer ", PEV_SWITCH_RND },
  1195. { "Volume Down ", PEV_VOL_DOWN },
  1196. { "Volume Up ", PEV_VOL_UP },
  1197. { "Fast forward ", PEV_FF },
  1198. { "Enter Menu ", PEV_MENU },
  1199. { "Pico Next page ", PEV_PICO_PNEXT },
  1200. { "Pico Prev page ", PEV_PICO_PPREV },
  1201. { "Pico Switch input", PEV_PICO_SWINP },
  1202. { NULL, 0 }
  1203. };
  1204. static int key_config_loop_wrap(menu_id id, int keys)
  1205. {
  1206. switch (id) {
  1207. case MA_CTRL_PLAYER1:
  1208. key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions), 0);
  1209. break;
  1210. case MA_CTRL_PLAYER2:
  1211. key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions), 1);
  1212. break;
  1213. case MA_CTRL_EMU:
  1214. key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
  1215. break;
  1216. default:
  1217. break;
  1218. }
  1219. return 0;
  1220. }
  1221. static const char *mgn_dev_name(menu_id id, int *offs)
  1222. {
  1223. const char *name = NULL;
  1224. static int it = 0;
  1225. if (id == MA_CTRL_DEV_FIRST)
  1226. it = 0;
  1227. for (; it < IN_MAX_DEVS; it++) {
  1228. name = in_get_dev_name(it, 1, 1);
  1229. if (name != NULL)
  1230. break;
  1231. }
  1232. it++;
  1233. return name;
  1234. }
  1235. static int mh_saveloadcfg(menu_id id, int keys);
  1236. static const char *mgn_saveloadcfg(menu_id id, int *offs);
  1237. static menu_entry e_menu_keyconfig[] =
  1238. {
  1239. mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
  1240. mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
  1241. mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
  1242. mee_onoff ("6 button pad", MA_OPT_6BUTTON_PAD, PicoOpt, POPT_6BTN_PAD),
  1243. mee_range ("Turbo rate", MA_CTRL_TURBO_RATE, currentConfig.turbo_rate, 1, 30),
  1244. mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_saveloadcfg, mgn_saveloadcfg),
  1245. mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_saveloadcfg, mgn_saveloadcfg),
  1246. mee_label (""),
  1247. mee_label ("Input devices:"),
  1248. mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
  1249. mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
  1250. mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
  1251. mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
  1252. mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
  1253. mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
  1254. mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
  1255. mee_end,
  1256. };
  1257. static int menu_loop_keyconfig(menu_id id, int keys)
  1258. {
  1259. static int sel = 0;
  1260. me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, rom_loaded);
  1261. me_loop(e_menu_keyconfig, &sel, NULL);
  1262. return 0;
  1263. }
  1264. // ------------ SCD options menu ------------
  1265. static const char *mgn_cdopt_ra(menu_id id, int *offs)
  1266. {
  1267. *offs = -5;
  1268. if (PicoCDBuffers <= 0)
  1269. return " OFF";
  1270. sprintf(static_buff, "%5iK", PicoCDBuffers * 2);
  1271. return static_buff;
  1272. }
  1273. static int mh_cdopt_ra(menu_id id, int keys)
  1274. {
  1275. if (keys & PBTN_LEFT) {
  1276. PicoCDBuffers >>= 1;
  1277. if (PicoCDBuffers < 2)
  1278. PicoCDBuffers = 0;
  1279. } else {
  1280. if (PicoCDBuffers <= 0)
  1281. PicoCDBuffers = 1;
  1282. PicoCDBuffers <<= 1;
  1283. if (PicoCDBuffers > 8*1024)
  1284. PicoCDBuffers = 8*1024; // 16M
  1285. }
  1286. return 0;
  1287. }
  1288. static const char h_cdleds[] = "Show power/CD LEDs of emulated console";
  1289. static const char h_cdda[] = "Play audio tracks from mp3s/wavs/bins";
  1290. static const char h_cdpcm[] = "Emulate PCM audio chip for effects/voices/music";
  1291. static const char h_srcart[] = "Emulate the save RAM cartridge accessory\n"
  1292. "most games don't need this";
  1293. static const char h_scfx[] = "Emulate scale/rotate ASIC chip for graphics effects\n"
  1294. "disable to improve performance";
  1295. static const char h_bsync[] = "More accurate mode for CPUs (needed for some games)\n"
  1296. "disable to improve performance";
  1297. static menu_entry e_menu_cd_options[] =
  1298. {
  1299. mee_onoff_h("CD LEDs", MA_CDOPT_LEDS, currentConfig.EmuOpt, EOPT_EN_CD_LEDS, h_cdleds),
  1300. mee_onoff_h("CDDA audio", MA_CDOPT_CDDA, PicoOpt, POPT_EN_MCD_CDDA, h_cdda),
  1301. mee_onoff_h("PCM audio", MA_CDOPT_PCM, PicoOpt, POPT_EN_MCD_PCM, h_cdpcm),
  1302. mee_cust ("ReadAhead buffer", MA_CDOPT_READAHEAD, mh_cdopt_ra, mgn_cdopt_ra),
  1303. mee_onoff_h("SaveRAM cart", MA_CDOPT_SAVERAM, PicoOpt, POPT_EN_MCD_RAMCART, h_srcart),
  1304. mee_onoff_h("Scale/Rot. fx (slow)", MA_CDOPT_SCALEROT_CHIP, PicoOpt, POPT_EN_MCD_GFX, h_scfx),
  1305. mee_onoff_h("Better sync (slow)", MA_CDOPT_BETTER_SYNC, PicoOpt, POPT_EN_MCD_PSYNC, h_bsync),
  1306. mee_end,
  1307. };
  1308. static int menu_loop_cd_options(menu_id id, int keys)
  1309. {
  1310. static int sel = 0;
  1311. me_loop(e_menu_cd_options, &sel, NULL);
  1312. return 0;
  1313. }
  1314. // ------------ 32X options menu ------------
  1315. // convert from multiplier of VClk
  1316. static int mh_opt_sh2cycles(menu_id id, int keys)
  1317. {
  1318. int *mul = (id == MA_32XOPT_MSH2_CYCLES) ? &p32x_msh2_multiplier : &p32x_ssh2_multiplier;
  1319. if (keys & (PBTN_LEFT|PBTN_RIGHT))
  1320. *mul += (keys & PBTN_LEFT) ? -10 : 10;
  1321. if (keys & (PBTN_L|PBTN_R))
  1322. *mul += (keys & PBTN_L) ? -100 : 100;
  1323. if (*mul < 1)
  1324. *mul = 1;
  1325. else if (*mul > (10 << SH2_MULTI_SHIFT))
  1326. *mul = 10 << SH2_MULTI_SHIFT;
  1327. return 0;
  1328. }
  1329. static const char *mgn_opt_sh2cycles(menu_id id, int *offs)
  1330. {
  1331. int mul = (id == MA_32XOPT_MSH2_CYCLES) ? p32x_msh2_multiplier : p32x_ssh2_multiplier;
  1332. sprintf(static_buff, "%d", 7670 * mul >> SH2_MULTI_SHIFT);
  1333. return static_buff;
  1334. }
  1335. static const char h_32x_enable[] = "Enable emulation of the 32X addon";
  1336. static const char h_pwm[] = "Disabling may improve performance, but break sound";
  1337. static const char h_sh2cycles[] = "Cycles/millisecond (similar to DOSBox)\n"
  1338. "lower values speed up emulation but break games\n"
  1339. "at least 11000 recommended for compatibility";
  1340. static menu_entry e_menu_32x_options[] =
  1341. {
  1342. mee_onoff_h ("32X enabled", MA_32XOPT_ENABLE_32X, PicoOpt, POPT_EN_32X, h_32x_enable),
  1343. mee_enum ("32X renderer", MA_32XOPT_RENDERER, currentConfig.renderer32x, renderer_names32x),
  1344. mee_onoff_h ("PWM sound", MA_32XOPT_PWM, PicoOpt, POPT_EN_PWM, h_pwm),
  1345. mee_cust_h ("Master SH2 cycles", MA_32XOPT_MSH2_CYCLES, mh_opt_sh2cycles, mgn_opt_sh2cycles, h_sh2cycles),
  1346. mee_cust_h ("Slave SH2 cycles", MA_32XOPT_SSH2_CYCLES, mh_opt_sh2cycles, mgn_opt_sh2cycles, h_sh2cycles),
  1347. mee_end,
  1348. };
  1349. static int menu_loop_32x_options(menu_id id, int keys)
  1350. {
  1351. static int sel = 0;
  1352. me_enable(e_menu_32x_options, MA_32XOPT_RENDERER, renderer_names32x != NULL);
  1353. me_loop(e_menu_32x_options, &sel, NULL);
  1354. return 0;
  1355. }
  1356. // ------------ adv options menu ------------
  1357. static menu_entry e_menu_adv_options[] =
  1358. {
  1359. mee_onoff ("SRAM/BRAM saves", MA_OPT_SRAM_STATES, currentConfig.EmuOpt, EOPT_EN_SRAM),
  1360. mee_onoff ("Disable sprite limit", MA_OPT2_NO_SPRITE_LIM, PicoOpt, POPT_DIS_SPRITE_LIM),
  1361. mee_onoff ("Emulate Z80", MA_OPT2_ENABLE_Z80, PicoOpt, POPT_EN_Z80),
  1362. mee_onoff ("Emulate YM2612 (FM)", MA_OPT2_ENABLE_YM2612, PicoOpt, POPT_EN_FM),
  1363. mee_onoff ("Emulate SN76496 (PSG)", MA_OPT2_ENABLE_SN76496,PicoOpt, POPT_EN_PSG),
  1364. mee_onoff ("gzip savestates", MA_OPT2_GZIP_STATES, currentConfig.EmuOpt, EOPT_GZIP_SAVES),
  1365. mee_onoff ("Don't save last used ROM", MA_OPT2_NO_LAST_ROM, currentConfig.EmuOpt, EOPT_NO_AUTOSVCFG),
  1366. mee_onoff ("Disable idle loop patching",MA_OPT2_NO_IDLE_LOOPS,PicoOpt, POPT_DIS_IDLE_DET),
  1367. mee_onoff ("Disable frame limiter", MA_OPT2_NO_FRAME_LIMIT,currentConfig.EmuOpt, EOPT_NO_FRMLIMIT),
  1368. MENU_OPTIONS_ADV
  1369. mee_end,
  1370. };
  1371. static int menu_loop_adv_options(menu_id id, int keys)
  1372. {
  1373. static int sel = 0;
  1374. me_loop(e_menu_adv_options, &sel, NULL);
  1375. return 0;
  1376. }
  1377. // ------------ gfx options menu ------------
  1378. static menu_entry e_menu_gfx_options[] =
  1379. {
  1380. mee_enum("Renderer", MA_OPT_RENDERER, currentConfig.renderer, renderer_names),
  1381. MENU_OPTIONS_GFX
  1382. mee_end,
  1383. };
  1384. static int menu_loop_gfx_options(menu_id id, int keys)
  1385. {
  1386. static int sel = 0;
  1387. me_enable(e_menu_gfx_options, MA_OPT_RENDERER, renderer_names[0] != NULL);
  1388. me_loop(e_menu_gfx_options, &sel, NULL);
  1389. return 0;
  1390. }
  1391. // ------------ options menu ------------
  1392. static menu_entry e_menu_options[];
  1393. static int sndrate_prevnext(int rate, int dir)
  1394. {
  1395. static const int rates[] = { 8000, 11025, 16000, 22050, 44100 };
  1396. int i;
  1397. for (i = 0; i < 5; i++)
  1398. if (rates[i] == rate) break;
  1399. i += dir ? 1 : -1;
  1400. if (i > 4) {
  1401. if (!(PicoOpt & POPT_EN_STEREO)) {
  1402. PicoOpt |= POPT_EN_STEREO;
  1403. return rates[0];
  1404. }
  1405. return rates[4];
  1406. }
  1407. if (i < 0) {
  1408. if (PicoOpt & POPT_EN_STEREO) {
  1409. PicoOpt &= ~POPT_EN_STEREO;
  1410. return rates[4];
  1411. }
  1412. return rates[0];
  1413. }
  1414. return rates[i];
  1415. }
  1416. static void region_prevnext(int right)
  1417. {
  1418. // jp_ntsc=1, jp_pal=2, usa=4, eu=8
  1419. static const int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };
  1420. int i;
  1421. if (right) {
  1422. if (!PicoRegionOverride) {
  1423. for (i = 0; i < 6; i++)
  1424. if (rgn_orders[i] == PicoAutoRgnOrder) break;
  1425. if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];
  1426. else PicoRegionOverride=1;
  1427. }
  1428. else
  1429. PicoRegionOverride <<= 1;
  1430. if (PicoRegionOverride > 8)
  1431. PicoRegionOverride = 8;
  1432. } else {
  1433. if (!PicoRegionOverride) {
  1434. for (i = 0; i < 6; i++)
  1435. if (rgn_orders[i] == PicoAutoRgnOrder) break;
  1436. if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];
  1437. }
  1438. else
  1439. PicoRegionOverride >>= 1;
  1440. }
  1441. }
  1442. static int mh_opt_misc(menu_id id, int keys)
  1443. {
  1444. switch (id) {
  1445. case MA_OPT_SOUND_QUALITY:
  1446. PsndRate = sndrate_prevnext(PsndRate, keys & PBTN_RIGHT);
  1447. break;
  1448. case MA_OPT_REGION:
  1449. region_prevnext(keys & PBTN_RIGHT);
  1450. break;
  1451. default:
  1452. break;
  1453. }
  1454. return 0;
  1455. }
  1456. static int mh_saveloadcfg(menu_id id, int keys)
  1457. {
  1458. int ret;
  1459. if (keys & (PBTN_LEFT|PBTN_RIGHT)) { // multi choice
  1460. config_slot += (keys & PBTN_LEFT) ? -1 : 1;
  1461. if (config_slot < 0) config_slot = 9;
  1462. else if (config_slot > 9) config_slot = 0;
  1463. me_enable(e_menu_options, MA_OPT_LOADCFG, config_slot != config_slot_current);
  1464. return 0;
  1465. }
  1466. switch (id) {
  1467. case MA_OPT_SAVECFG:
  1468. case MA_OPT_SAVECFG_GAME:
  1469. if (emu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0))
  1470. me_update_msg("config saved");
  1471. else
  1472. me_update_msg("failed to write config");
  1473. break;
  1474. case MA_OPT_LOADCFG:
  1475. ret = emu_read_config(rom_fname_loaded, 1);
  1476. if (!ret) ret = emu_read_config(NULL, 1);
  1477. if (ret) me_update_msg("config loaded");
  1478. else me_update_msg("failed to load config");
  1479. break;
  1480. default:
  1481. return 0;
  1482. }
  1483. return 1;
  1484. }
  1485. static int mh_restore_defaults(menu_id id, int keys)
  1486. {
  1487. emu_set_defconfig();
  1488. me_update_msg("defaults restored");
  1489. return 1;
  1490. }
  1491. static const char *mgn_opt_fskip(menu_id id, int *offs)
  1492. {
  1493. if (currentConfig.Frameskip < 0)
  1494. return "Auto";
  1495. sprintf(static_buff, "%d", currentConfig.Frameskip);
  1496. return static_buff;
  1497. }
  1498. static const char *mgn_opt_sound(menu_id id, int *offs)
  1499. {
  1500. const char *str2;
  1501. *offs = -8;
  1502. str2 = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";
  1503. sprintf(static_buff, "%5iHz %s", PsndRate, str2);
  1504. return static_buff;
  1505. }
  1506. static const char *mgn_opt_region(menu_id id, int *offs)
  1507. {
  1508. static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };
  1509. static const char *names_short[] = { "", " JP", " JP", " US", " EU" };
  1510. int code = PicoRegionOverride;
  1511. int u, i = 0;
  1512. *offs = -6;
  1513. if (code) {
  1514. code <<= 1;
  1515. while ((code >>= 1)) i++;
  1516. if (i > 4)
  1517. return "unknown";
  1518. return names[i];
  1519. } else {
  1520. strcpy(static_buff, "Auto:");
  1521. for (u = 0; u < 3; u++) {
  1522. code = (PicoAutoRgnOrder >> u*4) & 0xf;
  1523. for (i = 0; code; code >>= 1, i++)
  1524. ;
  1525. strcat(static_buff, names_short[i]);
  1526. }
  1527. return static_buff;
  1528. }
  1529. }
  1530. static const char *mgn_saveloadcfg(menu_id id, int *offs)
  1531. {
  1532. static_buff[0] = 0;
  1533. if (config_slot != 0)
  1534. sprintf(static_buff, "[%i]", config_slot);
  1535. return static_buff;
  1536. }
  1537. static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
  1538. static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
  1539. "loading state or both";
  1540. static menu_entry e_menu_options[] =
  1541. {
  1542. mee_range ("Save slot", MA_OPT_SAVE_SLOT, state_slot, 0, 9),
  1543. mee_range_cust("Frameskip", MA_OPT_FRAMESKIP, currentConfig.Frameskip, -1, 16, mgn_opt_fskip),
  1544. mee_cust ("Region", MA_OPT_REGION, mh_opt_misc, mgn_opt_region),
  1545. mee_onoff ("Show FPS", MA_OPT_SHOW_FPS, currentConfig.EmuOpt, EOPT_SHOW_FPS),
  1546. mee_onoff ("Enable sound", MA_OPT_ENABLE_SOUND, currentConfig.EmuOpt, EOPT_EN_SOUND),
  1547. mee_cust ("Sound Quality", MA_OPT_SOUND_QUALITY, mh_opt_misc, mgn_opt_sound),
  1548. mee_enum_h ("Confirm savestate", MA_OPT_CONFIRM_STATES,currentConfig.confirm_save, men_confirm_save, h_confirm_save),
  1549. mee_range (cpu_clk_name, MA_OPT_CPU_CLOCKS, currentConfig.CPUclock, 20, 900),
  1550. mee_handler ("[Display options]", menu_loop_gfx_options),
  1551. mee_handler ("[Sega/Mega CD options]", menu_loop_cd_options),
  1552. mee_handler ("[32X options]", menu_loop_32x_options),
  1553. mee_handler ("[Advanced options]", menu_loop_adv_options),
  1554. mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_saveloadcfg, mgn_saveloadcfg),
  1555. mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_saveloadcfg, mgn_saveloadcfg),
  1556. mee_cust_nosave("Load cfg from profile", MA_OPT_LOADCFG, mh_saveloadcfg, mgn_saveloadcfg),
  1557. mee_handler ("Restore defaults", mh_restore_defaults),
  1558. mee_end,
  1559. };
  1560. static int menu_loop_options(menu_id id, int keys)
  1561. {
  1562. static int sel = 0;
  1563. me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, rom_loaded);
  1564. me_enable(e_menu_options, MA_OPT_LOADCFG, config_slot != config_slot_current);
  1565. me_loop(e_menu_options, &sel, NULL);
  1566. return 0;
  1567. }
  1568. // ------------ debug menu ------------
  1569. #include <pico/debug.h>
  1570. extern void SekStepM68k(void);
  1571. static void mplayer_loop(void)
  1572. {
  1573. pemu_sound_start();
  1574. while (1)
  1575. {
  1576. PDebugZ80Frame();
  1577. if (in_menu_wait_any(0) & PBTN_MA3)
  1578. break;
  1579. pemu_sound_wait();
  1580. }
  1581. pemu_sound_stop();
  1582. }
  1583. static void draw_text_debug(const char *str, int skip, int from)
  1584. {
  1585. const char *p;
  1586. int line;
  1587. p = str;
  1588. while (skip-- > 0)
  1589. {
  1590. while (*p && *p != '\n')
  1591. p++;
  1592. if (*p == 0 || p[1] == 0)
  1593. return;
  1594. p++;
  1595. }
  1596. str = p;
  1597. for (line = from; line < g_screen_height / me_sfont_h; line++)
  1598. {
  1599. smalltext_out16(1, line * me_sfont_h, str, 0xffff);
  1600. while (*p && *p != '\n')
  1601. p++;
  1602. if (*p == 0)
  1603. break;
  1604. p++; str = p;
  1605. }
  1606. }
  1607. #ifdef __GNUC__
  1608. #define COMPILER "gcc " __VERSION__
  1609. #else
  1610. #define COMPILER
  1611. #endif
  1612. static void draw_frame_debug(void)
  1613. {
  1614. char layer_str[48] = "layers: ";
  1615. if (PicoDrawMask & PDRAW_LAYERB_ON) memcpy(layer_str + 8, "B", 1);
  1616. if (PicoDrawMask & PDRAW_LAYERA_ON) memcpy(layer_str + 10, "A", 1);
  1617. if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);
  1618. if (PicoDrawMask & PDRAW_SPRITES_HI_ON) memcpy(layer_str + 19, "spr_hi", 6);
  1619. if (PicoDrawMask & PDRAW_32X_ON) memcpy(layer_str + 26, "32x", 4);
  1620. memset(g_screen_ptr, 0, g_screen_width * g_screen_height * 2);
  1621. pemu_forced_frame(0);
  1622. smalltext_out16(4, 1, "build: r" REVISION " "__DATE__ " " __TIME__ " " COMPILER, 0xffff);
  1623. smalltext_out16(4, g_screen_height - me_sfont_h, layer_str, 0xffff);
  1624. }
  1625. static void debug_menu_loop(void)
  1626. {
  1627. int inp, mode = 0;
  1628. int spr_offs = 0, dumped = 0;
  1629. char *tmp;
  1630. while (1)
  1631. {
  1632. switch (mode)
  1633. {
  1634. case 0: plat_video_menu_begin();
  1635. tmp = PDebugMain();
  1636. plat_debug_cat(tmp);
  1637. draw_text_debug(tmp, 0, 0);
  1638. if (dumped) {
  1639. smalltext_out16(g_screen_width - 6 * me_sfont_h,
  1640. g_screen_height - me_mfont_h, "dumped", 0xffff);
  1641. dumped = 0;
  1642. }
  1643. break;
  1644. case 1: draw_frame_debug(); break;
  1645. case 2: memset(g_screen_ptr, 0, g_screen_width * g_screen_height * 2);
  1646. pemu_forced_frame(0);
  1647. menu_darken_bg(g_screen_ptr, g_screen_ptr, g_screen_width * g_screen_height, 0);
  1648. PDebugShowSpriteStats((unsigned short *)g_screen_ptr + (g_screen_height/2 - 240/2)*g_screen_width +
  1649. g_screen_width/2 - 320/2, g_screen_width); break;
  1650. case 3: memset(g_screen_ptr, 0, g_screen_width * g_screen_height * 2);
  1651. PDebugShowPalette(g_screen_ptr, g_screen_width);
  1652. PDebugShowSprite((unsigned short *)g_screen_ptr + g_screen_width*120 + g_screen_width/2 + 16,
  1653. g_screen_width, spr_offs);
  1654. draw_text_debug(PDebugSpriteList(), spr_offs, 6);
  1655. break;
  1656. case 4: plat_video_menu_begin();
  1657. tmp = PDebug32x();
  1658. draw_text_debug(tmp, 0, 0);
  1659. break;
  1660. }
  1661. plat_video_menu_end();
  1662. inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
  1663. PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
  1664. if (inp & PBTN_MBACK) return;
  1665. if (inp & PBTN_L) { mode--; if (mode < 0) mode = 4; }
  1666. if (inp & PBTN_R) { mode++; if (mode > 4) mode = 0; }
  1667. switch (mode)
  1668. {
  1669. case 0:
  1670. if (inp & PBTN_MOK)
  1671. PDebugCPUStep();
  1672. if (inp & PBTN_MA3) {
  1673. while (inp & PBTN_MA3)
  1674. inp = in_menu_wait_any(-1);
  1675. mplayer_loop();
  1676. }
  1677. if ((inp & (PBTN_MA2|PBTN_LEFT)) == (PBTN_MA2|PBTN_LEFT)) {
  1678. mkdir("dumps", 0777);
  1679. PDebugDumpMem();
  1680. while (inp & PBTN_MA2) inp = in_menu_wait_any(-1);
  1681. dumped = 1;
  1682. }
  1683. break;
  1684. case 1:
  1685. if (inp & PBTN_LEFT) PicoDrawMask ^= PDRAW_LAYERB_ON;
  1686. if (inp & PBTN_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;
  1687. if (inp & PBTN_DOWN) PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;
  1688. if (inp & PBTN_UP) PicoDrawMask ^= PDRAW_SPRITES_HI_ON;
  1689. if (inp & PBTN_MA2) PicoDrawMask ^= PDRAW_32X_ON;
  1690. if (inp & PBTN_MOK) {
  1691. PsndOut = NULL; // just in case
  1692. PicoSkipFrame = 1;
  1693. PicoFrame();
  1694. PicoSkipFrame = 0;
  1695. while (inp & PBTN_MOK) inp = in_menu_wait_any(-1);
  1696. }
  1697. break;
  1698. case 3:
  1699. if (inp & PBTN_DOWN) spr_offs++;
  1700. if (inp & PBTN_UP) spr_offs--;
  1701. if (spr_offs < 0) spr_offs = 0;
  1702. break;
  1703. }
  1704. }
  1705. }
  1706. // ------------ main menu ------------
  1707. static char *romsel_run(void)
  1708. {
  1709. char *ret, *sel_name;
  1710. sel_name = malloc(sizeof(rom_fname_loaded));
  1711. if (sel_name == NULL)
  1712. return NULL;
  1713. strcpy(sel_name, rom_fname_loaded);
  1714. ret = menu_loop_romsel(sel_name, sizeof(rom_fname_loaded));
  1715. free(sel_name);
  1716. return ret;
  1717. }
  1718. static int main_menu_handler(menu_id id, int keys)
  1719. {
  1720. char *ret_name;
  1721. switch (id)
  1722. {
  1723. case MA_MAIN_RESUME_GAME:
  1724. if (rom_loaded)
  1725. return 1;
  1726. break;
  1727. case MA_MAIN_SAVE_STATE:
  1728. if (rom_loaded)
  1729. return menu_loop_savestate(0);
  1730. break;
  1731. case MA_MAIN_LOAD_STATE:
  1732. if (rom_loaded)
  1733. return menu_loop_savestate(1);
  1734. break;
  1735. case MA_MAIN_RESET_GAME:
  1736. if (rom_loaded) {
  1737. emu_reset_game();
  1738. return 1;
  1739. }
  1740. break;
  1741. case MA_MAIN_LOAD_ROM:
  1742. ret_name = romsel_run();
  1743. if (ret_name != NULL) {
  1744. lprintf("selected file: %s\n", ret_name);
  1745. engineState = PGS_ReloadRom;
  1746. return 1;
  1747. }
  1748. break;
  1749. case MA_MAIN_CREDITS:
  1750. draw_menu_credits();
  1751. in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
  1752. break;
  1753. case MA_MAIN_EXIT:
  1754. engineState = PGS_Quit;
  1755. return 1;
  1756. case MA_MAIN_PATCHES:
  1757. if (rom_loaded && PicoPatches) {
  1758. menu_loop_patches();
  1759. PicoPatchApply();
  1760. me_update_msg("Patches applied");
  1761. }
  1762. break;
  1763. default:
  1764. lprintf("%s: something unknown selected\n", __FUNCTION__);
  1765. break;
  1766. }
  1767. return 0;
  1768. }
  1769. static menu_entry e_menu_main[] =
  1770. {
  1771. mee_label ("PicoDrive " VERSION),
  1772. mee_label (""),
  1773. mee_label (""),
  1774. mee_label (""),
  1775. mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
  1776. mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
  1777. mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
  1778. mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
  1779. mee_handler_id("Load new ROM/ISO", MA_MAIN_LOAD_ROM, main_menu_handler),
  1780. mee_handler ("Change options", menu_loop_options),
  1781. mee_handler ("Configure controls", menu_loop_keyconfig),
  1782. mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
  1783. mee_handler_id("Patches / GameGenie",MA_MAIN_PATCHES, main_menu_handler),
  1784. mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
  1785. mee_end,
  1786. };
  1787. void menu_loop(void)
  1788. {
  1789. static int sel = 0;
  1790. me_enable(e_menu_main, MA_MAIN_RESUME_GAME, rom_loaded);
  1791. me_enable(e_menu_main, MA_MAIN_SAVE_STATE, rom_loaded);
  1792. me_enable(e_menu_main, MA_MAIN_LOAD_STATE, rom_loaded);
  1793. me_enable(e_menu_main, MA_MAIN_RESET_GAME, rom_loaded);
  1794. me_enable(e_menu_main, MA_MAIN_PATCHES, PicoPatches != NULL);
  1795. menu_enter(rom_loaded);
  1796. in_set_blocking(1);
  1797. me_loop(e_menu_main, &sel, menu_main_plat_draw);
  1798. if (rom_loaded) {
  1799. if (engineState == PGS_Menu)
  1800. engineState = PGS_Running;
  1801. /* wait until menu, ok, back is released */
  1802. while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
  1803. ;
  1804. }
  1805. in_set_blocking(0);
  1806. }
  1807. // --------- CD tray close menu ----------
  1808. static int mh_tray_load_cd(menu_id id, int keys)
  1809. {
  1810. char *ret_name;
  1811. ret_name = romsel_run();
  1812. if (ret_name == NULL)
  1813. return 0;
  1814. engineState = PGS_RestartRun;
  1815. return emu_swap_cd(ret_name);
  1816. }
  1817. static int mh_tray_nothing(menu_id id, int keys)
  1818. {
  1819. return 1;
  1820. }
  1821. static menu_entry e_menu_tray[] =
  1822. {
  1823. mee_label ("The CD tray has opened."),
  1824. mee_label (""),
  1825. mee_label (""),
  1826. mee_handler("Load CD image", mh_tray_load_cd),
  1827. mee_handler("Insert nothing", mh_tray_nothing),
  1828. mee_end,
  1829. };
  1830. int menu_loop_tray(void)
  1831. {
  1832. int ret = 1, sel = 0;
  1833. menu_enter(rom_loaded);
  1834. in_set_blocking(1);
  1835. me_loop(e_menu_tray, &sel, NULL);
  1836. if (engineState != PGS_RestartRun) {
  1837. engineState = PGS_RestartRun;
  1838. ret = 0; /* no CD inserted */
  1839. }
  1840. while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK));
  1841. in_set_blocking(0);
  1842. return ret;
  1843. }
  1844. #endif // !UIQ3
  1845. void me_update_msg(const char *msg)
  1846. {
  1847. strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
  1848. menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
  1849. menu_error_time = plat_get_ticks_ms();
  1850. lprintf("msg: %s\n", menu_error_msg);
  1851. }
  1852. // ------------ util ------------
  1853. /* GP2X/wiz for now, probably extend later */
  1854. void menu_plat_setup(int is_wiz)
  1855. {
  1856. int i;
  1857. if (!is_wiz) {
  1858. me_enable(e_menu_gfx_options, MA_OPT_TEARING_FIX, 0);
  1859. i = me_id2offset(e_menu_gfx_options, MA_OPT_TEARING_FIX);
  1860. e_menu_gfx_options[i].need_to_save = 0;
  1861. return;
  1862. }
  1863. me_enable(e_menu_adv_options, MA_OPT_ARM940_SOUND, 0);
  1864. me_enable(e_menu_gfx_options, MA_OPT2_GAMMA, 0);
  1865. me_enable(e_menu_gfx_options, MA_OPT2_A_SN_GAMMA, 0);
  1866. i = me_id2offset(e_menu_gfx_options, MA_OPT_SCALING);
  1867. e_menu_gfx_options[i].max = 1; /* only off and sw */
  1868. i = me_id2offset(e_menu_gfx_options, MA_OPT_ARM940_SOUND);
  1869. e_menu_gfx_options[i].need_to_save = 0;
  1870. }
  1871. /* hidden options for config engine only */
  1872. static menu_entry e_menu_hidden[] =
  1873. {
  1874. mee_onoff("Accurate sprites", MA_OPT_ACC_SPRITES, PicoOpt, 0x080),
  1875. mee_end,
  1876. };
  1877. static menu_entry *e_menu_table[] =
  1878. {
  1879. e_menu_options,
  1880. e_menu_gfx_options,
  1881. e_menu_adv_options,
  1882. e_menu_cd_options,
  1883. e_menu_32x_options,
  1884. e_menu_keyconfig,
  1885. e_menu_hidden,
  1886. };
  1887. static menu_entry *me_list_table = NULL;
  1888. static menu_entry *me_list_i = NULL;
  1889. menu_entry *me_list_get_first(void)
  1890. {
  1891. me_list_table = me_list_i = e_menu_table[0];
  1892. return me_list_i;
  1893. }
  1894. menu_entry *me_list_get_next(void)
  1895. {
  1896. int i;
  1897. me_list_i++;
  1898. if (me_list_i->name != NULL)
  1899. return me_list_i;
  1900. for (i = 0; i < array_size(e_menu_table); i++)
  1901. if (me_list_table == e_menu_table[i])
  1902. break;
  1903. if (i + 1 < array_size(e_menu_table))
  1904. me_list_table = me_list_i = e_menu_table[i + 1];
  1905. else
  1906. me_list_table = me_list_i = NULL;
  1907. return me_list_i;
  1908. }