giz dblbuff, scanline mode
[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
c77ca79e 95 giz_screen = Framework2D_LockBuffer(0);\r
c9077ab4 96 if (giz_screen == NULL)\r
97 {\r
98 lprintf_al("%s: Framework2D_LockBuffer() returned NULL\n", __FUNCTION__);\r
99 return;\r
100 }\r
101 memcpy32(giz_screen, (int *)menu_screen, 321*240*2/4);\r
e5f426aa 102 Framework2D_UnlockBuffer();\r
103 giz_screen = NULL;\r
c77ca79e 104 Framework2D_Flip(1);\r
e5f426aa 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
c77ca79e 638 PicoStateProgressCB = emu_stateCb; /* also suitable for menu */\r
e5f426aa 639 if (emu_SaveLoadGame(is_loading, 0)) {\r
640 strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
641 return 1;\r
642 }\r
643 return 0;\r
644 } else return 1;\r
645 }\r
646 if(inp & BTN_STOP) return 1;\r
647 }\r
648}\r
649\r
650// -------------- key config --------------\r
651\r
652static char *action_binds(int player_idx, int action_mask)\r
653{\r
654 static char strkeys[32*5];\r
655 int i;\r
656\r
657 strkeys[0] = 0;\r
658 for (i = 0; i < 32; i++) // i is key index\r
659 {\r
660 if (currentConfig.KeyBinds[i] & action_mask)\r
661 {\r
662 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
663 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gizKeyNames[i]); break; }\r
664 else strcpy(strkeys, gizKeyNames[i]);\r
665 }\r
666 }\r
667\r
668 return strkeys;\r
669}\r
670\r
671static void unbind_action(int action)\r
672{\r
673 int i;\r
674\r
675 for (i = 0; i < 32; i++)\r
676 currentConfig.KeyBinds[i] &= ~action;\r
677}\r
678\r
c9077ab4 679static int count_bound_keys(int action, int pl_idx)\r
e5f426aa 680{\r
681 int i, keys = 0;\r
682\r
683 for (i = 0; i < 32; i++)\r
c9077ab4 684 {\r
685 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
e5f426aa 686 if (currentConfig.KeyBinds[i] & action) keys++;\r
c9077ab4 687 }\r
e5f426aa 688\r
689 return keys;\r
690}\r
691\r
692typedef struct { char *name; int mask; } bind_action_t;\r
693\r
694static void draw_key_config(const bind_action_t *opts, int opt_cnt, int player_idx, int sel)\r
695{\r
696 int x, y, tl_y = 40, i;\r
697\r
698 menu_draw_begin(1);\r
699 if (player_idx >= 0) {\r
700 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
c9077ab4 701 x = 80;\r
e5f426aa 702 } else {\r
703 text_out16(80, 20, "Emulator controls");\r
704 x = 40;\r
705 }\r
706\r
707 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
708\r
709 y = tl_y;\r
710 for (i = 0; i < opt_cnt; i++, y+=10)\r
711 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
712\r
713 text_out16(x, y, "Done");\r
714\r
715 if (sel < opt_cnt) {\r
716 text_out16(30, 180, "Press a button to bind/unbind");\r
9839d126 717 text_out16(30, 190, "Use HOME to clear");\r
718 text_out16(30, 200, "To bind UP/DOWN, hold HOME");\r
e5f426aa 719 text_out16(30, 210, "Select \"Done\" to exit");\r
720 } else {\r
721 text_out16(30, 190, "Use Options -> Save cfg");\r
722 text_out16(30, 200, "to save controls");\r
9839d126 723 text_out16(30, 210, "Press PLAY or STOP to exit");\r
e5f426aa 724 }\r
725 menu_draw_end();\r
726}\r
727\r
728static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
729{\r
730 int sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
731 unsigned long inp = 0;\r
732\r
733 for (;;)\r
734 {\r
735 draw_key_config(opts, opt_cnt, player_idx, sel);\r
9839d126 736 inp = wait_for_input(CONFIGURABLE_KEYS|BTN_HOME);\r
e5f426aa 737 if (!(inp & BTN_HOME)) {\r
738 prev_select = 0;\r
739 if(inp & BTN_UP ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
740 if(inp & BTN_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
741 }\r
742 if (sel >= opt_cnt) {\r
743 if (inp & (BTN_PLAY|BTN_STOP)) break;\r
744 else continue;\r
745 }\r
746 // if we are here, we want to bind/unbind something\r
747 if ((inp & BTN_HOME) && !prev_select)\r
748 unbind_action(opts[sel].mask);\r
749 prev_select = inp & BTN_HOME;\r
750 inp &= CONFIGURABLE_KEYS;\r
751 inp &= ~BTN_HOME;\r
752 for (i = 0; i < 32; i++)\r
753 if (inp & (1 << i)) {\r
c9077ab4 754 if (count_bound_keys(opts[sel].mask, player_idx) >= 2)\r
755 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
e5f426aa 756 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
c9077ab4 757 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
e5f426aa 758 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
759 currentConfig.KeyBinds[i] |= player_idx << 16;\r
760 }\r
761 }\r
762 }\r
763}\r
764\r
765static void draw_kc_sel(int menu_sel)\r
766{\r
767 int tl_x = 25+40, tl_y = 60, y;\r
768\r
769 y = tl_y;\r
770 menu_draw_begin(1);\r
771 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
772\r
773 text_out16(tl_x, y, "Player 1");\r
774 text_out16(tl_x, (y+=10), "Player 2");\r
775 text_out16(tl_x, (y+=10), "Emulator controls");\r
776 text_out16(tl_x, (y+=10), "Done");\r
777\r
778 menu_draw_end();\r
779}\r
780\r
781\r
782// PicoPad[] format: MXYZ SACB RLDU\r
783static bind_action_t ctrl_actions[] =\r
784{\r
785 { "UP ", 0x001 },\r
786 { "DOWN ", 0x002 },\r
787 { "LEFT ", 0x004 },\r
788 { "RIGHT ", 0x008 },\r
789 { "A ", 0x040 },\r
790 { "B ", 0x010 },\r
791 { "C ", 0x020 },\r
792 { "START ", 0x080 },\r
793 { "MODE ", 0x800 },\r
794 { "X ", 0x400 },\r
795 { "Y ", 0x200 },\r
796 { "Z ", 0x100 },\r
797};\r
798\r
799// player2_flag, ?, ?, ?, ?, ?, ?, menu\r
800// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
801// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
802static bind_action_t emuctrl_actions[] =\r
803{\r
804 { "Load State ", 1<<28 },\r
805 { "Save State ", 1<<27 },\r
806 { "Prev Save Slot ", 1<<25 },\r
807 { "Next Save Slot ", 1<<24 },\r
808 { "Switch Renderer", 1<<26 },\r
809 { "Volume Down ", 1<<30 },\r
810 { "Volume Up ", 1<<29 },\r
e5f426aa 811};\r
812\r
813static void kc_sel_loop(void)\r
814{\r
815 int menu_sel = 3, menu_sel_max = 3;\r
816 unsigned long inp = 0;\r
817 int is_6button = currentConfig.PicoOpt & 0x020;\r
818\r
819 while (1)\r
820 {\r
821 draw_kc_sel(menu_sel);\r
822 inp = wait_for_input(BTN_UP|BTN_DOWN|BTN_PLAY|BTN_STOP);\r
823 if (inp & BTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
824 if (inp & BTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
825 if (inp & BTN_PLAY) {\r
826 switch (menu_sel) {\r
827 case 0: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 0); return;\r
828 case 1: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 1); return;\r
829 case 2: key_config_loop(emuctrl_actions,\r
830 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]), -1); return;\r
831 case 3: if (rom_data == NULL) emu_WriteConfig(0); return;\r
832 default: return;\r
833 }\r
834 }\r
835 if (inp & BTN_STOP) return;\r
836 }\r
837}\r
838\r
839\r
840// --------- sega/mega cd options ----------\r
841\r
842menu_entry cdopt_entries[] =\r
843{\r
844 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1 },\r
845 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1 },\r
846 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1 },\r
847 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1 },\r
848 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &currentConfig.PicoOpt, 0x0800, 0, 0, 1 },\r
849 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &currentConfig.PicoOpt, 0x0400, 0, 0, 1 },\r
850 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1 },\r
851 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &currentConfig.PicoOpt, 0x8000, 0, 0, 1 },\r
852 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&currentConfig.PicoOpt, 0x1000, 0, 0, 1 },\r
853 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &currentConfig.PicoOpt, 0x2000, 0, 0, 1 },\r
854 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1 },\r
855};\r
856\r
857#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
858\r
859\r
860struct bios_names_t\r
861{\r
862 char us[32], eu[32], jp[32];\r
863};\r
864\r
865static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
866{\r
867 struct bios_names_t *bios_names = param;\r
868 char ra_buff[16];\r
869\r
870 switch (entry->id)\r
871 {\r
872 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
873 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
874 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
875 case MA_CDOPT_READAHEAD:\r
876 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
877 else strcpy(ra_buff, " OFF");\r
878 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
879 break;\r
880 default:break;\r
881 }\r
882}\r
883\r
884static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
885{\r
886 int tl_x = 25, tl_y = 60;\r
887 menu_id selected_id;\r
888 char ra_buff[16];\r
889\r
890 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
891 else strcpy(ra_buff, " OFF");\r
892\r
893 menu_draw_begin(1);\r
894\r
895 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
896\r
897 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
898\r
899 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
900 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
901 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
902 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
903 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
904\r
905 menu_draw_end();\r
906}\r
907\r
908static void cd_menu_loop_options(void)\r
909{\r
910 static int menu_sel = 0;\r
911 int menu_sel_max = 10;\r
912 unsigned long inp = 0;\r
913 struct bios_names_t bios_names;\r
914 menu_id selected_id;\r
915 char *bios, *p;\r
916\r
ea8c405f 917 if (emu_findBios(4, &bios)) { // US\r
e5f426aa 918 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
919 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
920 } else strcpy(bios_names.us, "NOT FOUND");\r
921\r
ea8c405f 922 if (emu_findBios(8, &bios)) { // EU\r
e5f426aa 923 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
924 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
925 } else strcpy(bios_names.eu, "NOT FOUND");\r
926\r
ea8c405f 927 if (emu_findBios(1, &bios)) { // JP\r
e5f426aa 928 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
929 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
930 } else strcpy(bios_names.jp, "NOT FOUND");\r
931\r
932 for(;;)\r
933 {\r
934 draw_cd_menu_options(menu_sel, &bios_names);\r
935 inp = wait_for_input(BTN_UP|BTN_DOWN|BTN_LEFT|BTN_RIGHT|BTN_PLAY|BTN_STOP|BTN_REW);\r
936 if (inp & BTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
937 if (inp & BTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
938 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
939 if (inp & (BTN_LEFT|BTN_RIGHT)) { // multi choise\r
940 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&BTN_RIGHT) ? 1 : 0) &&\r
941 selected_id == MA_CDOPT_READAHEAD) {\r
942 if (inp & BTN_LEFT) {\r
943 PicoCDBuffers >>= 1;\r
944 if (PicoCDBuffers < 64) PicoCDBuffers = 0;\r
945 } else {\r
946 if (PicoCDBuffers < 64) PicoCDBuffers = 64;\r
947 else PicoCDBuffers <<= 1;\r
948 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
949 }\r
950 }\r
951 }\r
952 if (inp & BTN_PLAY) { // toggleable options\r
953 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
954 selected_id == MA_CDOPT_DONE) {\r
955 return;\r
956 }\r
957 switch (selected_id) { // BIOS testers\r
958 case MA_CDOPT_TESTBIOS_USA:\r
ea8c405f 959 if (emu_findBios(4, &bios)) { // test US\r
e5f426aa 960 strcpy(romFileName, bios);\r
961 engineState = PGS_ReloadRom;\r
962 return;\r
963 }\r
964 break;\r
965 case MA_CDOPT_TESTBIOS_EUR:\r
ea8c405f 966 if (emu_findBios(8, &bios)) { // test EU\r
e5f426aa 967 strcpy(romFileName, bios);\r
968 engineState = PGS_ReloadRom;\r
969 return;\r
970 }\r
971 break;\r
972 case MA_CDOPT_TESTBIOS_JAP:\r
ea8c405f 973 if (emu_findBios(1, &bios)) { // test JP\r
e5f426aa 974 strcpy(romFileName, bios);\r
975 engineState = PGS_ReloadRom;\r
976 return;\r
977 }\r
978 break;\r
979 default:\r
980 break;\r
981 }\r
982 }\r
983 if (inp & (BTN_STOP|BTN_REW)) return;\r
984 }\r
985}\r
986\r
987\r
988// --------- advanced options ----------\r
989\r
990menu_entry opt2_entries[] =\r
991{\r
992 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &currentConfig.PicoOpt,0x0004, 0, 0, 1 },\r
993 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &currentConfig.PicoOpt,0x0001, 0, 0, 1 },\r
994 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&currentConfig.PicoOpt,0x0002, 0, 0, 1 },\r
c77ca79e 995 { "Double buffering", MB_ONOFF, MA_OPT2_DBLBUFF, &currentConfig.EmuOpt, 0x8000, 0, 0, 1 },\r
a8869ad1 996 { "Wait for V-sync (slow)", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
e5f426aa 997 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1 },\r
998 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1 },\r
999 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1 },\r
1000};\r
1001\r
1002#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
1003\r
1004\r
1005static void draw_amenu_options(int menu_sel)\r
1006{\r
1007 int tl_x = 25, tl_y = 50;\r
1008\r
1009 menu_draw_begin(1);\r
1010\r
1011 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
1012\r
1013 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
1014\r
1015 menu_draw_end();\r
1016}\r
1017\r
1018static void amenu_loop_options(void)\r
1019{\r
1020 static int menu_sel = 0;\r
1021 int menu_sel_max;\r
1022 unsigned long inp = 0;\r
1023 menu_id selected_id;\r
1024\r
1025 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
1026\r
1027 for(;;)\r
1028 {\r
1029 draw_amenu_options(menu_sel);\r
1030 inp = wait_for_input(BTN_UP|BTN_DOWN|BTN_LEFT|BTN_RIGHT|BTN_PLAY|BTN_STOP|BTN_REW);\r
1031 if (inp & BTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1032 if (inp & BTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1033 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1034 if (inp & (BTN_LEFT|BTN_RIGHT)) { // multi choise\r
1035 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&BTN_RIGHT) ? 1 : 0) &&\r
1036 selected_id == MA_OPT2_GAMMA) {\r
2ec14aec 1037 while ((inp = Framework_PollGetButtons()) & (BTN_LEFT|BTN_RIGHT)) {\r
e5f426aa 1038 currentConfig.gamma += (inp & BTN_LEFT) ? -1 : 1;\r
1039 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1040 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1041 draw_amenu_options(menu_sel);\r
1042 Sleep(18);\r
1043 }\r
1044 }\r
1045 }\r
1046 if (inp & BTN_PLAY) { // toggleable options\r
1047 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1048 selected_id == MA_OPT2_DONE) {\r
1049 return;\r
1050 }\r
1051 }\r
1052 if (inp & (BTN_STOP|BTN_REW)) return;\r
1053 }\r
1054}\r
1055\r
1056// -------------- options --------------\r
1057\r
1058\r
1059menu_entry opt_entries[] =\r
1060{\r
1061 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1 },\r
c77ca79e 1062 { "Scanline mode (faster)", MB_ONOFF, MA_OPT_INTERLACED, &currentConfig.EmuOpt, 0x4000, 0, 0, 1 },\r
a8869ad1 1063 { "Scale low res mode", MB_ONOFF, MA_OPT_SCALING, &currentConfig.scaling, 0x0001, 0, 3, 1 },\r
1064 { "Accurate timing (slower)", MB_ONOFF, MA_OPT_ACC_TIMING, &currentConfig.PicoOpt, 0x0040, 0, 0, 1 },\r
1065 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &currentConfig.PicoOpt, 0x0080, 0, 0, 1 },\r
1066 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x0002, 0, 0, 1 },\r
e5f426aa 1067 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1 },\r
a8869ad1 1068 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x0004, 0, 0, 1 },\r
e5f426aa 1069 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1 },\r
a8869ad1 1070 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &currentConfig.PicoOpt, 0x0020, 0, 0, 1 },\r
e5f426aa 1071 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1 },\r
a8869ad1 1072 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x0001, 0, 0, 1 },\r
e5f426aa 1073 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1 },\r
1074 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1 },\r
1075 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1 },\r
1076 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1 },\r
1077 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1 },\r
1078 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1 },\r
1079 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1 },\r
1080};\r
1081\r
1082#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
1083\r
1084\r
1085static const char *region_name(unsigned int code)\r
1086{\r
1087 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };\r
1088 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
1089 int u, i = 0;\r
1090 if (code) {\r
1091 code <<= 1;\r
1092 while((code >>= 1)) i++;\r
1093 if (i > 4) return "unknown";\r
1094 return names[i];\r
1095 } else {\r
1096 static char name[24];\r
1097 strcpy(name, "Auto:");\r
1098 for (u = 0; u < 3; u++) {\r
1099 i = 0; code = ((PicoAutoRgnOrder >> u*4) & 0xf) << 1;\r
1100 while((code >>= 1)) i++;\r
1101 strcat(name, names_short[i]);\r
1102 }\r
1103 return name;\r
1104 }\r
1105}\r
1106\r
1107\r
1108static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1109{\r
1110 char *str, str24[24];\r
1111\r
1112 switch (entry->id)\r
1113 {\r
1114 case MA_OPT_RENDERER:\r
1115 if (currentConfig.PicoOpt&0x10)\r
1116 str = " 8bit fast";\r
1117 else if (currentConfig.EmuOpt&0x80)\r
1118 str = "16bit accurate";\r
1119 else\r
1120 str = " 8bit accurate";\r
1121 text_out16(x, y, "Renderer: %s", str);\r
1122 break;\r
1123 case MA_OPT_FRAMESKIP:\r
1124 if (currentConfig.Frameskip < 0)\r
1125 strcpy(str24, "Auto");\r
1126 else sprintf(str24, "%i", currentConfig.Frameskip);\r
1127 text_out16(x, y, "Frameskip %s", str24);\r
1128 break;\r
1129 case MA_OPT_SOUND_QUALITY:\r
1130 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
1131 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
1132 break;\r
1133 case MA_OPT_REGION:\r
1134 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
1135 break;\r
1136 case MA_OPT_CONFIRM_STATES:\r
1137 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1138 default: str = "OFF"; break;\r
1139 case 1: str = "writes"; break;\r
1140 case 4: str = "loads"; break;\r
1141 case 5: str = "both"; break;\r
1142 }\r
1143 text_out16(x, y, "Confirm savestate %s", str);\r
1144 break;\r
1145 case MA_OPT_SAVECFG:\r
1146 str24[0] = 0;\r
1147 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
1148 text_out16(x, y, "Save cfg as default%s", str24);\r
1149 break;\r
1150 case MA_OPT_LOADCFG:\r
1151 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
1152 break;\r
1153 default:\r
1154 lprintf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1155 break;\r
1156 }\r
1157}\r
1158\r
1159\r
1160\r
1161static void draw_menu_options(int menu_sel)\r
1162{\r
1163 int tl_x = 25, tl_y = 24;\r
1164\r
1165 menu_draw_begin(1);\r
1166\r
1167 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
1168\r
1169 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
1170\r
1171 menu_draw_end();\r
1172}\r
1173\r
1174static int sndrate_prevnext(int rate, int dir)\r
1175{\r
fa283c9a 1176 int i, rates[] = { 11025, 22050, 44100 };\r
e5f426aa 1177\r
1178 for (i = 0; i < 5; i++)\r
1179 if (rates[i] == rate) break;\r
1180\r
1181 i += dir ? 1 : -1;\r
f3d1de29 1182 if (i > 2) return dir ? 44100 : 22050;\r
fa283c9a 1183 if (i < 0) return dir ? 22050 : 11025;\r
e5f426aa 1184 return rates[i];\r
1185}\r
1186\r
1187static void region_prevnext(int right)\r
1188{\r
1189 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1190 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1191 int i;\r
1192 if (right) {\r
1193 if (!currentConfig.PicoRegion) {\r
1194 for (i = 0; i < 6; i++)\r
1195 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1196 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
1197 else currentConfig.PicoRegion=1;\r
1198 }\r
1199 else currentConfig.PicoRegion<<=1;\r
1200 if (currentConfig.PicoRegion > 8) currentConfig.PicoRegion = 8;\r
1201 } else {\r
1202 if (!currentConfig.PicoRegion) {\r
1203 for (i = 0; i < 6; i++)\r
1204 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1205 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1206 }\r
1207 else currentConfig.PicoRegion>>=1;\r
1208 }\r
1209}\r
1210\r
1211static void menu_options_save(void)\r
1212{\r
1213 PicoOpt = currentConfig.PicoOpt;\r
1214 PsndRate = currentConfig.PsndRate;\r
1215 PicoRegionOverride = currentConfig.PicoRegion;\r
1216 if (!(PicoOpt & 0x20)) {\r
1217 // unbind XYZ MODE, just in case\r
1218 unbind_action(0xf00);\r
1219 }\r
e5f426aa 1220}\r
1221\r
1222static int menu_loop_options(void)\r
1223{\r
1224 static int menu_sel = 0;\r
1225 int menu_sel_max, ret;\r
1226 unsigned long inp = 0;\r
1227 menu_id selected_id;\r
1228\r
1229 currentConfig.PicoOpt = PicoOpt;\r
1230 currentConfig.PsndRate = PsndRate;\r
1231 currentConfig.PicoRegion = PicoRegionOverride;\r
1232\r
1233 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_data != NULL);\r
1234 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1235 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1236 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1237\r
1238 while (1)\r
1239 {\r
1240 draw_menu_options(menu_sel);\r
1241 inp = wait_for_input(BTN_UP|BTN_DOWN|BTN_LEFT|BTN_RIGHT|BTN_PLAY|BTN_STOP|BTN_REW);\r
1242 if (inp & BTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1243 if (inp & BTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1244 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
1245 if (inp & (BTN_LEFT|BTN_RIGHT)) { // multi choise\r
1246 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&BTN_RIGHT) ? 1 : 0)) {\r
1247 switch (selected_id) {\r
1248 case MA_OPT_RENDERER:\r
1249 if (inp & BTN_LEFT) {\r
a8869ad1 1250 if ((currentConfig.PicoOpt&0x10) || !(currentConfig.EmuOpt &0x80)) {\r
1251 currentConfig.PicoOpt&= ~0x10;\r
1252 currentConfig.EmuOpt |= 0x80;\r
1253 }\r
e5f426aa 1254 } else {\r
a8869ad1 1255 if (!(currentConfig.PicoOpt&0x10) || (currentConfig.EmuOpt &0x80)) {\r
1256 currentConfig.PicoOpt|= 0x10;\r
1257 currentConfig.EmuOpt &= ~0x80;\r
1258 }\r
e5f426aa 1259 }\r
1260 break;\r
1261 case MA_OPT_SOUND_QUALITY:\r
da42200b 1262 if ((inp & BTN_RIGHT) && currentConfig.PsndRate == 44100 &&\r
1263 !(currentConfig.PicoOpt&0x08))\r
1264 {\r
1265 currentConfig.PsndRate = 11025;\r
1266 currentConfig.PicoOpt |= 8;\r
1267 } else if ((inp & BTN_LEFT) && currentConfig.PsndRate == 11025 &&\r
1268 (currentConfig.PicoOpt&0x08) && !(PicoMCD&1))\r
1269 {\r
1270 currentConfig.PsndRate = 44100;\r
1271 currentConfig.PicoOpt &= ~8;\r
1272 } else\r
1273 currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & BTN_RIGHT);\r
e5f426aa 1274 break;\r
1275 case MA_OPT_REGION:\r
1276 region_prevnext(inp & BTN_RIGHT);\r
1277 break;\r
1278 case MA_OPT_CONFIRM_STATES: {\r
1279 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1280 n += (inp & BTN_LEFT) ? -1 : 1;\r
1281 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1282 n |= n << 1; n &= ~2;\r
1283 currentConfig.EmuOpt &= ~0xa00;\r
1284 currentConfig.EmuOpt |= n << 9;\r
1285 break;\r
1286 }\r
1287 case MA_OPT_SAVE_SLOT:\r
1288 if (inp & BTN_RIGHT) {\r
1289 state_slot++; if (state_slot > 9) state_slot = 0;\r
1290 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1291 }\r
1292 break;\r
1293 case MA_OPT_SAVECFG:\r
1294 case MA_OPT_SAVECFG_GAME:\r
1295 case MA_OPT_LOADCFG:\r
1296 config_slot += (inp&BTN_RIGHT) ? 1 : -1;\r
1297 if (config_slot > 9) config_slot = 0;\r
1298 if (config_slot < 0) config_slot = 9;\r
1299 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1300 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1301 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1302 break;\r
1303 default:\r
1304 //lprintf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1305 break;\r
1306 }\r
1307 }\r
1308 }\r
1309 if (inp & BTN_PLAY) {\r
1310 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1311 {\r
1312 switch (selected_id)\r
1313 {\r
1314 case MA_OPT_SCD_OPTS:\r
1315 cd_menu_loop_options();\r
1316 if (engineState == PGS_ReloadRom)\r
1317 return 0; // test BIOS\r
1318 break;\r
1319 case MA_OPT_ADV_OPTS:\r
1320 amenu_loop_options();\r
1321 break;\r
1322 case MA_OPT_SAVECFG: // done (update and write)\r
1323 menu_options_save();\r
1324 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1325 else strcpy(menuErrorMsg, "failed to write config");\r
1326 return 1;\r
1327 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1328 menu_options_save();\r
1329 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1330 else strcpy(menuErrorMsg, "failed to write config");\r
1331 return 1;\r
1332 case MA_OPT_LOADCFG:\r
1333 ret = emu_ReadConfig(1, 1);\r
1334 if (!ret) ret = emu_ReadConfig(0, 1);\r
1335 if (ret) strcpy(menuErrorMsg, "config loaded");\r
1336 else strcpy(menuErrorMsg, "failed to load config");\r
1337 return 1;\r
1338 default:\r
1339 //lprintf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1340 break;\r
1341 }\r
1342 }\r
1343 }\r
1344 if(inp & (BTN_STOP|BTN_REW)) {\r
1345 menu_options_save();\r
1346 return 0; // done (update, no write)\r
1347 }\r
1348 }\r
1349}\r
1350\r
1351// -------------- credits --------------\r
1352\r
1353static void draw_menu_credits(void)\r
1354{\r
1355 int tl_x = 15, tl_y = 64, y;\r
1356 menu_draw_begin(1);\r
1357\r
1358 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
da42200b 1359text_out16(tl_x, 30, "rc1");\r
e5f426aa 1360 y = tl_y;\r
1361 text_out16(tl_x, y, "Credits:");\r
1362 text_out16(tl_x, (y+=10), "Dave: Cyclone 68000 core,");\r
1363 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1364 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1365 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1366 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1367 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1368 text_out16(tl_x, (y+=10), " opensource Gens");\r
1369 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
2ec14aec 1370 text_out16(tl_x, (y+=10), "Reesy: kgsdk wrapper, sound code");\r
1371 text_out16(tl_x, (y+=10), "jens.l: gizmondo hardware");\r
e5f426aa 1372 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
1373\r
1374 menu_draw_end();\r
1375}\r
1376\r
1377\r
1378// -------------- root menu --------------\r
1379\r
1380menu_entry main_entries[] =\r
1381{\r
1382 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0 },\r
1383 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0 },\r
1384 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0 },\r
1385 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0 },\r
1386 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1 },\r
1387 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1 },\r
1388 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1 },\r
1389 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1 },\r
1390 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0 },\r
1391 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1 }\r
1392};\r
1393\r
1394#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1395\r
1396static void draw_menu_root(int menu_sel)\r
1397{\r
1398 const int tl_x = 70, tl_y = 70;\r
1399\r
1400 menu_draw_begin(1);\r
1401\r
1402 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1403\r
1404 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
1405\r
1406 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
1407\r
1408 // error\r
1409 if (menuErrorMsg[0]) {\r
c9077ab4 1410 memset((char *)menu_screen + 321*224*2, 0, 321*16*2);\r
e5f426aa 1411 text_out16(5, 226, menuErrorMsg);\r
1412 }\r
1413 menu_draw_end();\r
1414}\r
1415\r
1416\r
1417static void menu_loop_root(void)\r
1418{\r
1419 static int menu_sel = 0;\r
1420 int ret, menu_sel_max;\r
1421 unsigned long inp = 0;\r
1422\r
1423 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_data != NULL);\r
1424 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_data != NULL);\r
1425 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_data != NULL);\r
1426 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_data != NULL);\r
1427 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1428\r
1429 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
1430 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1431\r
1432 /* make sure action buttons are not pressed on entering menu */\r
1433 draw_menu_root(menu_sel);\r
c9077ab4 1434\r
2ec14aec 1435 while (Framework_PollGetButtons() & (BTN_PLAY|BTN_STOP|BTN_HOME)) Sleep(50);\r
e5f426aa 1436\r
1437 for (;;)\r
1438 {\r
1439 draw_menu_root(menu_sel);\r
1440 inp = wait_for_input(BTN_UP|BTN_DOWN|BTN_PLAY|BTN_STOP|BTN_HOME|BTN_L|BTN_R);\r
1441 if(inp & BTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1442 if(inp & BTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
fa283c9a 1443 if((inp & (BTN_L|BTN_R)) == (BTN_L|BTN_R)) debug_menu_loop();\r
1444 if( inp & (BTN_HOME|BTN_STOP)) {\r
e5f426aa 1445 if (rom_data) {\r
2ec14aec 1446 while (Framework_PollGetButtons() & (BTN_HOME|BTN_STOP)) Sleep(50); // wait until released\r
e5f426aa 1447 engineState = PGS_Running;\r
1448 break;\r
1449 }\r
1450 }\r
1451 if(inp & BTN_PLAY) {\r
1452 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1453 {\r
1454 case MA_MAIN_RESUME_GAME:\r
1455 if (rom_data) {\r
2ec14aec 1456 while (Framework_PollGetButtons() & BTN_PLAY) Sleep(50);\r
e5f426aa 1457 engineState = PGS_Running;\r
1458 return;\r
1459 }\r
1460 break;\r
1461 case MA_MAIN_SAVE_STATE:\r
1462 if (rom_data) {\r
1463 if(savestate_menu_loop(0))\r
1464 continue;\r
1465 engineState = PGS_Running;\r
1466 return;\r
1467 }\r
1468 break;\r
1469 case MA_MAIN_LOAD_STATE:\r
1470 if (rom_data) {\r
1471 if(savestate_menu_loop(1))\r
1472 continue;\r
1473 engineState = PGS_Running;\r
1474 return;\r
1475 }\r
1476 break;\r
1477 case MA_MAIN_RESET_GAME:\r
1478 if (rom_data) {\r
1479 emu_ResetGame();\r
1480 engineState = PGS_Running;\r
1481 return;\r
1482 }\r
1483 break;\r
1484 case MA_MAIN_LOAD_ROM:\r
1485 {\r
1486 char curr_path[MAX_PATH], *selfname;\r
1487 FILE *tstf;\r
1488 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1489 {\r
1490 fclose(tstf);\r
1491 strcpy(curr_path, currentConfig.lastRomFile);\r
1492 }\r
1493 else\r
1494 getcwd(curr_path, MAX_PATH);\r
1495 selfname = romsel_loop(curr_path);\r
1496 if (selfname) {\r
1497 lprintf("selected file: %s\n", selfname);\r
1498 engineState = PGS_ReloadRom;\r
1499 return;\r
1500 }\r
1501 break;\r
1502 }\r
1503 case MA_MAIN_OPTIONS:\r
1504 ret = menu_loop_options();\r
1505 if (ret == 1) continue; // status update\r
1506 if (engineState == PGS_ReloadRom)\r
1507 return; // BIOS test\r
1508 break;\r
1509 case MA_MAIN_CONTROLS:\r
1510 kc_sel_loop();\r
1511 break;\r
1512 case MA_MAIN_CREDITS:\r
1513 draw_menu_credits();\r
1514 Sleep(500);\r
1515 inp = wait_for_input(BTN_PLAY|BTN_STOP);\r
1516 break;\r
1517 case MA_MAIN_EXIT:\r
1518 engineState = PGS_Quit;\r
1519 return;\r
1520 case MA_MAIN_PATCHES:\r
1521 if (rom_data && PicoPatches) {\r
1522 patches_menu_loop();\r
1523 PicoPatchApply();\r
1524 strcpy(menuErrorMsg, "Patches applied");\r
1525 continue;\r
1526 }\r
1527 break;\r
1528 default:\r
1529 lprintf("%s: something unknown selected\n", __FUNCTION__);\r
1530 break;\r
1531 }\r
1532 }\r
1533 menuErrorMsg[0] = 0; // clear error msg\r
1534 }\r
1535}\r
1536\r
1537// warning: alignment\r
1538static void menu_darken_bg(void *dst, const void *src, int pixels, int darker)\r
1539{\r
1540 unsigned int *dest = dst;\r
1541 const unsigned int *srce = src;\r
1542 pixels /= 2;\r
1543 if (darker)\r
1544 {\r
1545 while (pixels--)\r
1546 {\r
1547 unsigned int p = *srce++;\r
1548 *dest++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1549 }\r
1550 }\r
1551 else\r
1552 {\r
1553 while (pixels--)\r
1554 {\r
1555 unsigned int p = *srce++;\r
1556 *dest++ = (p&0xf79ef79e)>>1;\r
1557 }\r
1558 }\r
1559}\r
1560\r
1561static void menu_prepare_bg(int use_game_bg)\r
1562{\r
1563 if (use_game_bg)\r
1564 {\r
1565 // darken the active framebuffer\r
2ec14aec 1566 if (giz_screen == NULL)\r
c77ca79e 1567 giz_screen = Framework2D_LockBuffer(1);\r
e5f426aa 1568 memset(bg_buffer, 0, 321*8*2);\r
1569 menu_darken_bg(bg_buffer + 321*8*2, (char *)giz_screen + 321*8*2, 321*224, 1);\r
1570 memset(bg_buffer + 321*232*2, 0, 321*8*2);\r
2ec14aec 1571 Framework2D_UnlockBuffer();\r
1572 giz_screen = NULL;\r
e5f426aa 1573 }\r
1574 else\r
1575 {\r
c9077ab4 1576 int i;\r
e5f426aa 1577 // should really only happen once, on startup..\r
1578 readpng(bg_buffer, "skin/background.png", READPNG_BG);\r
c9077ab4 1579 // adjust 320 width to 321\r
1580 for (i = 239; i > 0; i--)\r
1581 memmove(bg_buffer + 321*2*i, bg_buffer + 320*2*i, 320*2);\r
e5f426aa 1582 }\r
1583}\r
1584\r
1585static void menu_gfx_prepare(void)\r
1586{\r
1587 menu_prepare_bg(rom_data != NULL);\r
1588\r
1589 menu_draw_begin(1);\r
1590 menu_draw_end();\r
1591}\r
1592\r
1593\r
1594void menu_loop(void)\r
1595{\r
1596 menu_gfx_prepare();\r
1597\r
1598 menu_loop_root();\r
1599\r
1600 menuErrorMsg[0] = 0;\r
1601}\r
1602\r
1603\r
1604// --------- CD tray close menu ----------\r
1605\r
1606static void draw_menu_tray(int menu_sel)\r
1607{\r
1608 int tl_x = 70, tl_y = 90, y;\r
1609\r
1610 menu_draw_begin(1);\r
1611\r
1612 text_out16(tl_x, 20, "The unit is about to");\r
1613 text_out16(tl_x, 30, "close the CD tray.");\r
1614\r
1615 y = tl_y;\r
1616 text_out16(tl_x, y, "Load new CD image");\r
1617 text_out16(tl_x, (y+=10), "Insert nothing");\r
1618\r
1619 // draw cursor\r
1620 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
1621 // error\r
1622 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1623 menu_draw_end();\r
1624}\r
1625\r
1626\r
1627int menu_loop_tray(void)\r
1628{\r
1629 int menu_sel = 0, menu_sel_max = 1;\r
1630 unsigned long inp = 0;\r
1631 char curr_path[MAX_PATH], *selfname;\r
1632 FILE *tstf;\r
1633\r
1634 menu_gfx_prepare();\r
1635\r
1636 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1637 {\r
1638 fclose(tstf);\r
1639 strcpy(curr_path, currentConfig.lastRomFile);\r
1640 }\r
1641 else\r
1642 {\r
1643 getcwd(curr_path, MAX_PATH);\r
1644 }\r
1645\r
1646 /* make sure action buttons are not pressed on entering menu */\r
1647 draw_menu_tray(menu_sel);\r
2ec14aec 1648 while (Framework_PollGetButtons() & BTN_PLAY) Sleep(50);\r
e5f426aa 1649\r
1650 for (;;)\r
1651 {\r
1652 draw_menu_tray(menu_sel);\r
1653 inp = wait_for_input(BTN_UP|BTN_DOWN|BTN_PLAY);\r
1654 if(inp & BTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1655 if(inp & BTN_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1656 if(inp & BTN_PLAY ) {\r
1657 switch (menu_sel) {\r
1658 case 0: // select image\r
1659 selfname = romsel_loop(curr_path);\r
1660 if (selfname) {\r
1661 int ret = -1, cd_type;\r
ea8c405f 1662 cd_type = emu_cdCheck(NULL);\r
e5f426aa 1663 if (cd_type > 0)\r
1664 ret = Insert_CD(romFileName, cd_type == 2);\r
1665 if (ret != 0) {\r
1666 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1667 lprintf("%s\n", menuErrorMsg);\r
1668 continue;\r
1669 }\r
1670 engineState = PGS_RestartRun;\r
1671 return 1;\r
1672 }\r
1673 break;\r
1674 case 1: // insert nothing\r
1675 engineState = PGS_RestartRun;\r
1676 return 0;\r
1677 }\r
1678 }\r
1679 menuErrorMsg[0] = 0; // clear error msg\r
1680 }\r
1681}\r
1682\r
1683\r