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