menu.c 48 KB

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