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