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