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