some leaks fixed
[fceu.git] / drivers / gp2x / menu.c
CommitLineData
b2b95d2e 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#include <stdio.h>\r
7#include <string.h>\r
8#include <stdlib.h>\r
9#include <stdarg.h>\r
10#include <unistd.h>\r
11#include <dirent.h>\r
12\r
13#include "minimal.h"\r
14#include "usbjoy.h"\r
15#include "asmutils.h"\r
16#include "menu.h"\r
17#include "main.h"\r
18#include "fonts.h"\r
19#include "gp2x.h"\r
20\r
b547bda7 21#include "../../input.h"\r
22#include "../../state.h"\r
23\r
b2b95d2e 24#ifndef _DIRENT_HAVE_D_TYPE\r
25#error "need d_type for file browser\r
26#endif\r
27\r
e6ee7529 28extern int GP2X_PORT_REV;\r
b2b95d2e 29extern char lastLoadedGameName[PATH_MAX];\r
989672f4 30extern int mmuhack_status;\r
31extern int soundvol;\r
b2b95d2e 32extern uint8 Exit; // exit emu loop flag\r
989672f4 33extern int InitSound(void);\r
b2b95d2e 34\r
b547bda7 35#define CONFIGURABLE_KEYS (GP2X_UP|GP2X_LEFT|GP2X_DOWN|GP2X_RIGHT|GP2X_START|GP2X_SELECT|GP2X_L|GP2X_R|GP2X_A|GP2X_B|GP2X_X|GP2X_Y|GP2X_PUSH)\r
36\r
b2b95d2e 37static char *gp2xKeyNames[] = {\r
38 "UP", "01???", "LEFT", "03???", "DOWN", "05???", "RIGHT", "07???",\r
39 "START", "SELECT", "L", "R", "A", "B", "X", "Y",\r
40 "10???", "11???", "12???","13???", "14???","15???", "VOL DOWN", "VOL UP",\r
41 "18???", "19???", "1a???","PUSH", "1c???","1d???", "1e???", "1f???"\r
42};\r
43\r
44char menuErrorMsg[40] = {0, };\r
45\r
46// TODO\r
47void gp2x_fceu_copy_bg(void)\r
48{\r
49 memset(gp2x_screen, 0, 320*240*2);\r
50}\r
51\r
52// draws white text to current bbp15 screen\r
53static void gp2x_text_out15_(int x, int y, const char *text)\r
54{\r
55 int i,l;\r
56 unsigned short *screen = gp2x_screen;\r
57\r
58 screen = screen + x + y*320;\r
59\r
60 for (i = 0; i < strlen(text); i++)\r
61 {\r
62 for (l=0;l<8;l++)\r
63 {\r
64 if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff;\r
65 if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff;\r
66 if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff;\r
67 if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff;\r
68 if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff;\r
69 if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff;\r
70 if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff;\r
71 if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff;\r
72 }\r
73 screen += 8;\r
74 }\r
75}\r
76\r
77void gp2x_text_out15(int x, int y, const char *texto, ...)\r
78{\r
79 va_list args;\r
80 char buffer[512];\r
81\r
82 va_start(args,texto);\r
83 vsprintf(buffer,texto,args);\r
84 va_end(args);\r
85\r
86 gp2x_text_out15_(x,y,buffer);\r
87}\r
88\r
89\r
90void gp2x_text_out15_lim(int x, int y, const char *texto, int max)\r
91{\r
92 char buffer[320/8+1];\r
93\r
94 strncpy(buffer, texto, 320/8);\r
95 if (max > 320/8) max = 320/8;\r
96 if (max < 0) max = 0;\r
97 buffer[max] = 0;\r
98\r
99 gp2x_text_out15(x,y,buffer);\r
100}\r
101\r
102static void gp2x_smalltext16(int x, int y, const char *texto)\r
103{\r
104 int i;\r
105 unsigned char *src;\r
106 unsigned short *dst;\r
107\r
108 for (i = 0;; i++, x += 6)\r
109 {\r
110 unsigned char c = (unsigned char) texto[i];\r
111 int h = 8;\r
112\r
113 if (!c) break;\r
114\r
115 src = fontdata6x8[c];\r
116 dst = (unsigned short *)gp2x_screen + x + y*320;\r
117\r
118 while (h--)\r
119 {\r
120 int w = 0x20;\r
121 while (w)\r
122 {\r
123 if( *src & w ) *dst = 0xffff;\r
124 dst++;\r
125 w>>=1;\r
126 }\r
127 src++;\r
128\r
129 dst += 320-6;\r
130 }\r
131 }\r
132}\r
133\r
134static void gp2x_smalltext8_lim(int x, int y, const char *texto, int max)\r
135{\r
136 char buffer[320/6+1];\r
137\r
138 strncpy(buffer, texto, 320/6);\r
139 if (max > 320/6) max = 320/6;\r
140 if (max < 0) max = 0;\r
141 buffer[max] = 0;\r
142\r
143 gp2x_smalltext16(x, y, buffer);\r
144}\r
145\r
146\r
147static unsigned long inp_prev = 0;\r
148static int inp_prevjoy = 0;\r
149\r
150static unsigned long wait_for_input(unsigned long interesting)\r
151{\r
152 unsigned long ret;\r
153 static int repeats = 0, wait = 50*1000;\r
154 int release = 0, i;\r
155\r
156 if (repeats == 2 || repeats == 4) wait /= 2;\r
157 if (repeats == 6) wait = 15 * 1000;\r
158\r
159 for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
160 if (i == 0) repeats++;\r
161 if (wait >= 30*1000) usleep(wait); // usleep sleeps for ~30ms minimum\r
162 else spend_cycles(wait * Settings.cpuclock);\r
163 }\r
164\r
165 while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
166 usleep(50000);\r
167 release = 1;\r
168 }\r
169\r
170 if (release || ret != inp_prev) {\r
171 repeats = 0;\r
172 wait = 50*1000;\r
173 }\r
174 inp_prev = ret;\r
175 inp_prevjoy = 0;\r
176\r
177 // we don't need diagonals in menus\r
178 if ((ret&GP2X_UP) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;\r
179 if ((ret&GP2X_UP) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
180 if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;\r
181 if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
182\r
183 return ret;\r
184}\r
185\r
186static unsigned long input2_read(unsigned long interesting, int *joy)\r
187{\r
188 unsigned long ret;\r
189 int i;\r
190\r
191 do\r
192 {\r
193 *joy = 0;\r
194 if ((ret = gp2x_joystick_read(0) & interesting)) break;\r
195 gp2x_usbjoy_update();\r
196 for (i = 0; i < num_of_joys; i++) {\r
197 ret = gp2x_usbjoy_check2(i);\r
198 if (ret) { *joy = i + 1; break; }\r
199 }\r
200 if (ret) break;\r
201 }\r
202 while(0);\r
203\r
204 return ret;\r
205}\r
206\r
207// similar to wait_for_input(), but returns joy num\r
208static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)\r
209{\r
210 unsigned long ret;\r
211 const int wait = 300*1000;\r
212 int i;\r
213\r
214 if (inp_prevjoy == 0) inp_prev &= interesting;\r
215 for (i = 0; i < 6; i++) {\r
216 ret = input2_read(interesting, joy);\r
217 if (*joy != inp_prevjoy || ret != inp_prev) break;\r
218 usleep(wait/6);\r
219 }\r
220\r
221 while ( !(ret = input2_read(interesting, joy)) ) {\r
222 usleep(50000);\r
223 }\r
224\r
225 inp_prev = ret;\r
226 inp_prevjoy = *joy;\r
227\r
228 return ret;\r
229}\r
230\r
231\r
232\r
233// -------------- ROM selector --------------\r
234\r
235static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
236{\r
237 int start, i, pos;\r
238\r
239 start = 12 - sel;\r
240 n--; // exclude current dir (".")\r
241\r
242 //memset(gp2x_screen, 0, 320*240);\r
243 gp2x_fceu_copy_bg();\r
244\r
245 if(start - 2 >= 0)\r
246 gp2x_smalltext8_lim(14, (start - 2)*10, curdir, 53-2);\r
247 for (i = 0; i < n; i++) {\r
248 pos = start + i;\r
249 if (pos < 0) continue;\r
250 if (pos > 23) break;\r
251 if (namelist[i+1]->d_type == DT_DIR) {\r
252 gp2x_smalltext8_lim(14, pos*10, "/", 1);\r
253 gp2x_smalltext8_lim(14+6, pos*10, namelist[i+1]->d_name, 53-3);\r
254 } else {\r
255 gp2x_smalltext8_lim(14, pos*10, namelist[i+1]->d_name, 53-2);\r
256 }\r
257 }\r
258 gp2x_text_out15(5, 120, ">");\r
259 gp2x_video_flip();\r
260}\r
261\r
262static int scandir_cmp(const void *p1, const void *p2)\r
263{\r
264 struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
265 if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
266 if ((*d1)->d_type == DT_DIR) return -1; // put before\r
267 if ((*d2)->d_type == DT_DIR) return 1;\r
268 return alphasort(d1, d2);\r
269}\r
270\r
271static char *filter_exts[] = {\r
b547bda7 272 ".gpe", ".png", "ck.o", ".txt"\r
b2b95d2e 273};\r
274\r
275static int scandir_filter(const struct dirent *ent)\r
276{\r
277 const char *p;\r
278 int i;\r
279\r
280 if (ent == NULL || ent->d_name == NULL) return 0;\r
281 if (strlen(ent->d_name) < 5) return 1;\r
282\r
283 p = ent->d_name + strlen(ent->d_name) - 4;\r
284\r
285 for (i = 0; i < sizeof(filter_exts)/sizeof(filter_exts[0]); i++)\r
286 {\r
287 if (strcmp(p, filter_exts[i]) == 0) return 0;\r
288 }\r
289\r
290 return 1;\r
291}\r
292\r
293static char *romsel_loop(char *curr_path)\r
294{\r
295 struct dirent **namelist;\r
296 DIR *dir;\r
297 int n, sel = 0;\r
298 unsigned long inp = 0;\r
299 char *ret = NULL, *fname = NULL;\r
300\r
301 // is this a dir or a full path?\r
302 if ((dir = opendir(curr_path))) {\r
303 closedir(dir);\r
304 } else {\r
305 char *p;\r
306 for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);\r
307 *p = 0;\r
308 fname = p+1;\r
309 }\r
310\r
311 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
312 if (n < 0) {\r
313 // try root\r
314 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
315 if (n < 0) {\r
316 // oops, we failed\r
317 printf("dir: %s\n", curr_path);\r
318 perror("scandir");\r
319 return NULL;\r
320 }\r
321 }\r
322\r
323 // try to find sel\r
324 if (fname != NULL) {\r
325 int i;\r
326 for (i = 1; i < n; i++) {\r
327 if (strcmp(namelist[i]->d_name, fname) == 0) {\r
328 sel = i - 1;\r
329 break;\r
330 }\r
331 }\r
332 }\r
333\r
334 for (;;)\r
335 {\r
336 draw_dirlist(curr_path, namelist, n, sel);\r
337 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
338 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = n-2; }\r
339 if(inp & GP2X_DOWN) { sel++; if (sel > n-2) sel = 0; }\r
340 if(inp & GP2X_LEFT) { sel-=10; if (sel < 0) sel = 0; }\r
341 if(inp & GP2X_L) { sel-=24; if (sel < 0) sel = 0; }\r
342 if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
343 if(inp & GP2X_R) { sel+=24; if (sel > n-2) sel = n-2; }\r
344 if(inp & GP2X_B) { // enter dir/select\r
345 again:\r
346 if (namelist[sel+1]->d_type == DT_REG) {\r
347 strcpy(lastLoadedGameName, curr_path);\r
348 strcat(lastLoadedGameName, "/");\r
349 strcat(lastLoadedGameName, namelist[sel+1]->d_name);\r
350 ret = lastLoadedGameName;\r
351 break;\r
352 } else if (namelist[sel+1]->d_type == DT_DIR) {\r
353 int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
354 char *p, *newdir = malloc(newlen);\r
355 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
356 char *start = curr_path;\r
357 p = start + strlen(start) - 1;\r
358 while (*p == '/' && p > start) p--;\r
359 while (*p != '/' && p > start) p--;\r
360 if (p <= start) strcpy(newdir, "/");\r
361 else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
362 } else {\r
363 strcpy(newdir, curr_path);\r
364 p = newdir + strlen(newdir) - 1;\r
365 while (*p == '/' && p >= newdir) *p-- = 0;\r
366 strcat(newdir, "/");\r
367 strcat(newdir, namelist[sel+1]->d_name);\r
368 }\r
369 ret = romsel_loop(newdir);\r
370 free(newdir);\r
371 break;\r
372 } else {\r
373 // unknown file type, happens on NTFS mounts. Try to guess.\r
374 FILE *tstf; int tmp;\r
375 strcpy(lastLoadedGameName, curr_path);\r
376 strcat(lastLoadedGameName, "/");\r
377 strcat(lastLoadedGameName, namelist[sel+1]->d_name);\r
378 tstf = fopen(lastLoadedGameName, "rb");\r
379 if (tstf != NULL)\r
380 {\r
381 if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
382 namelist[sel+1]->d_type = DT_REG;\r
383 else namelist[sel+1]->d_type = DT_DIR;\r
384 fclose(tstf);\r
385 goto again;\r
386 }\r
387 }\r
388 }\r
389 if(inp & GP2X_X) break; // cancel\r
390 }\r
391\r
392 if (n > 0) {\r
393 while(n--) free(namelist[n]);\r
394 free(namelist);\r
395 }\r
396\r
397 return ret;\r
398}\r
399\r
400// ------------ patch/gg menu ------------\r
401\r
402#if 0 // TODO?\r
403static void draw_patchlist(int sel)\r
404{\r
405 int start, i, pos;\r
406\r
407 start = 12 - sel;\r
408\r
409 gp2x_fceu_copy_bg();\r
410\r
411 for (i = 0; i < PicoPatchCount; i++) {\r
412 pos = start + i;\r
413 if (pos < 0) continue;\r
414 if (pos > 23) break;\r
415 gp2x_smalltext8_lim(14, pos*10, PicoPatches[i].active ? "ON " : "OFF", 3);\r
416 gp2x_smalltext8_lim(14+6*4, pos*10, PicoPatches[i].name, 53-6);\r
417 }\r
418 pos = start + i;\r
419 if (pos < 24) gp2x_smalltext8_lim(14, pos*10, "done", 4);\r
420\r
421 gp2x_text_out15(5, 120, ">");\r
422 gp2x_video_flip();\r
423}\r
424\r
425\r
426void patches_menu_loop(void)\r
427{\r
428 int menu_sel = 0;\r
429 unsigned long inp = 0;\r
430\r
431 for(;;)\r
432 {\r
433 draw_patchlist(menu_sel);\r
434 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
435 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
436 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
437 if(inp &(GP2X_LEFT|GP2X_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
438 if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
439 if(inp & GP2X_B) { // action\r
440 if (menu_sel < PicoPatchCount)\r
441 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
442 else return;\r
443 }\r
444 if(inp & GP2X_X) return;\r
445 }\r
446\r
447}\r
448\r
449// ------------ savestate loader ------------\r
450\r
451static void menu_prepare_bg(void);\r
452\r
453static int state_slot_flags = 0;\r
454\r
455static void state_check_slots(void)\r
456{\r
457 int slot;\r
458\r
459 state_slot_flags = 0;\r
460\r
461 for (slot = 0; slot < 10; slot++)\r
462 {\r
463 if (emu_check_save_file(slot))\r
464 {\r
465 state_slot_flags |= 1 << slot;\r
466 }\r
467 }\r
468}\r
469\r
470static void draw_savestate_bg(int slot)\r
471{\r
472 struct PicoVideo tmp_pv;\r
473 unsigned short tmp_cram[0x40];\r
474 unsigned short tmp_vsram[0x40];\r
475 void *tmp_vram, *file;\r
476 char *fname;\r
477\r
478 fname = emu_GetSaveFName(1, 0, slot);\r
479 if (!fname) return;\r
480\r
481 tmp_vram = malloc(sizeof(Pico.vram));\r
482 if (tmp_vram == NULL) return;\r
483\r
484 memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
485 memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
486 memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
487 memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
488\r
489 if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
490 file = gzopen(fname, "rb");\r
491 emu_set_save_cbs(1);\r
492 } else {\r
493 file = fopen(fname, "rb");\r
494 emu_set_save_cbs(0);\r
495 }\r
496\r
497 if (file) {\r
498 if (PicoMCD & 1) {\r
499 PicoCdLoadStateGfx(file);\r
500 } else {\r
501 areaSeek(file, 0x10020, SEEK_SET); // skip header and RAM in state file\r
502 areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
503 areaSeek(file, 0x2000, SEEK_CUR);\r
504 areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
505 areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
506 areaSeek(file, 0x221a0, SEEK_SET);\r
507 areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
508 }\r
509 areaClose(file);\r
510 }\r
511\r
512 emu_forced_frame();\r
513 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
514 menu_prepare_bg();\r
515\r
516 memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
517 memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
518 memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
519 memcpy(&Pico.video, &tmp_pv, sizeof(Pico.video));\r
520 free(tmp_vram);\r
521}\r
522\r
523static void draw_savestate_menu(int menu_sel, int is_loading)\r
524{\r
525 int tl_x = 25, tl_y = 60, y, i;\r
526\r
527 if (state_slot_flags & (1 << menu_sel))\r
528 draw_savestate_bg(menu_sel);\r
529 gp2x_fceu_copy_bg();\r
530\r
531 gp2x_text_out15(tl_x, 30, is_loading ? "Load state" : "Save state");\r
532\r
533 /* draw all 10 slots */\r
534 y = tl_y;\r
535 for (i = 0; i < 10; i++, y+=10)\r
536 {\r
537 gp2x_text_out15(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
538 }\r
539 gp2x_text_out15(tl_x, y, "back");\r
540\r
541 // draw cursor\r
542 gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
543\r
544 gp2x_video_flip();\r
545}\r
546\r
547static int savestate_menu_loop(int is_loading)\r
548{\r
549 int menu_sel = 10, menu_sel_max = 10;\r
550 unsigned long inp = 0;\r
551\r
552 state_check_slots();\r
553\r
554 for(;;)\r
555 {\r
556 draw_savestate_menu(menu_sel, is_loading);\r
557 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
558 if(inp & GP2X_UP ) {\r
559 do {\r
560 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;\r
561 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
562 }\r
563 if(inp & GP2X_DOWN) {\r
564 do {\r
565 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;\r
566 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
567 }\r
568 if(inp & GP2X_B) { // save/load\r
569 if (menu_sel < 10) {\r
570 state_slot = menu_sel;\r
571 if (emu_SaveLoadGame(is_loading, 0)) {\r
572 strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
573 return 1;\r
574 }\r
575 return 0;\r
576 } else return 1;\r
577 }\r
578 if(inp & GP2X_X) return 1;\r
579 }\r
580}\r
b547bda7 581#endif\r
b2b95d2e 582\r
583// -------------- key config --------------\r
584\r
585static char *usb_joy_key_name(int joy, int num)\r
586{\r
587 static char name[16];\r
588 switch (num)\r
589 {\r
590 case 0: sprintf(name, "Joy%i UP", joy); break;\r
591 case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
592 case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
593 case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
594 default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
595 }\r
596 return name;\r
597}\r
598\r
b547bda7 599static char *action_binds(int player_idx, int action_mask)\r
b2b95d2e 600{\r
b547bda7 601 static char strkeys[32*5];\r
b2b95d2e 602 int joy, i;\r
603\r
604 strkeys[0] = 0;\r
b547bda7 605 for (i = 0; i < 32; i++) // i is key index\r
b2b95d2e 606 {\r
b547bda7 607 if (Settings.KeyBinds[i] & action_mask)\r
b2b95d2e 608 {\r
b547bda7 609 if (player_idx >= 0 && ((Settings.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
b2b95d2e 610 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }\r
611 else strcpy(strkeys, gp2xKeyNames[i]);\r
612 }\r
613 }\r
614 for (joy = 0; joy < num_of_joys; joy++)\r
615 {\r
616 for (i = 0; i < 32; i++)\r
617 {\r
b547bda7 618 if (Settings.JoyBinds[joy][i] & action_mask)\r
b2b95d2e 619 {\r
b547bda7 620 if (player_idx >= 0 && ((Settings.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
b2b95d2e 621 if (strkeys[0]) {\r
622 strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
623 break;\r
624 }\r
625 else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
626 }\r
627 }\r
628 }\r
629\r
b547bda7 630 return strkeys;\r
631}\r
632\r
633static void unbind_action(int action)\r
634{\r
635 int i, u;\r
636\r
637 for (i = 0; i < 32; i++)\r
638 Settings.KeyBinds[i] &= ~action;\r
639 for (u = 0; u < 4; u++)\r
640 for (i = 0; i < 32; i++)\r
641 Settings.JoyBinds[u][i] &= ~action;\r
642}\r
643\r
644static int count_bound_keys(int action, int is_joy)\r
645{\r
646 int i, u, keys = 0;\r
647\r
648 if (is_joy)\r
649 {\r
650 for (u = 0; u < 4; u++)\r
651 for (i = 0; i < 32; i++)\r
652 if (Settings.JoyBinds[u][i] & action) keys++;\r
653 }\r
654 else\r
655 {\r
656 for (i = 0; i < 32; i++)\r
657 if (Settings.KeyBinds[i] & action) keys++;\r
658 }\r
659 return keys;\r
660}\r
661\r
662typedef struct { char *name; int mask; } bind_action_t;\r
663\r
b547bda7 664static void draw_key_config(const bind_action_t *opts, int opt_cnt, int player_idx, int sel)\r
665{\r
666 int x, y, tl_y = 40, i;\r
667\r
b2b95d2e 668 gp2x_fceu_copy_bg();\r
989672f4 669 if (player_idx >= 0)\r
670 gp2x_text_out15(80, 20, "Player %i controls", player_idx + 1);\r
671 else gp2x_text_out15(80, 20, "Emulator controls");\r
b547bda7 672\r
673 x = 40; y = tl_y;\r
674 for (i = 0; i < opt_cnt; i++, y+=10)\r
675 gp2x_text_out15(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
676\r
677 gp2x_text_out15(x, y, "Done");\r
678\r
679 // draw cursor\r
680 gp2x_text_out15(x - 16, tl_y + sel*10, ">");\r
b2b95d2e 681\r
971a1d07 682 if (sel < opt_cnt) {\r
b547bda7 683 gp2x_text_out15(30, 190, "Press a button to bind/unbind");\r
684 gp2x_text_out15(30, 200, "Use VOL+ to clear");\r
685 gp2x_text_out15(30, 210, "To bind UP/DOWN, hold VOL-");\r
686 gp2x_text_out15(30, 220, "Select \"Done\" to exit");\r
687 } else {\r
688 gp2x_text_out15(30, 220, "Press B or X to exit");\r
689 }\r
b2b95d2e 690 gp2x_video_flip();\r
691}\r
692\r
b547bda7 693static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
b2b95d2e 694{\r
b547bda7 695 int joy = 0, sel = 0, menu_sel_max = opt_cnt, i;\r
b2b95d2e 696 unsigned long inp = 0;\r
697\r
698 for (;;)\r
699 {\r
b547bda7 700 draw_key_config(opts, opt_cnt, player_idx, sel);\r
701 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS|GP2X_VOL_DOWN|GP2X_VOL_UP, &joy);\r
b2b95d2e 702 // printf("got %08lX from joy %i\n", inp, joy);\r
703 if (joy == 0) {\r
b547bda7 704 if (!(inp & GP2X_VOL_DOWN)) {\r
705 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
706 if(inp & GP2X_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
707 }\r
708 if (sel >= opt_cnt) {\r
709 if (inp & (GP2X_B|GP2X_X)) break;\r
710 else continue;\r
b2b95d2e 711 }\r
b547bda7 712 // if we are here, we want to bind/unbind something\r
713 if (inp & GP2X_VOL_UP)\r
714 unbind_action(opts[sel].mask);\r
b2b95d2e 715 inp &= CONFIGURABLE_KEYS;\r
b2b95d2e 716 for (i = 0; i < 32; i++)\r
717 if (inp & (1 << i)) {\r
b547bda7 718 if (count_bound_keys(opts[sel].mask, 0) >= 2)\r
719 Settings.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
720 else Settings.KeyBinds[i] ^= opts[sel].mask;\r
989672f4 721 if (player_idx >= 0) {\r
722 Settings.KeyBinds[i] &= ~(3 << 16);\r
723 Settings.KeyBinds[i] |= player_idx << 16;\r
724 }\r
b2b95d2e 725 }\r
b547bda7 726 } else if (sel < opt_cnt) {\r
b2b95d2e 727 for (i = 0; i < 32; i++)\r
728 if (inp & (1 << i)) {\r
b547bda7 729 if (count_bound_keys(opts[sel].mask, 1) >= 1) // disallow combos for usbjoy\r
730 Settings.JoyBinds[joy-1][i] &= ~opts[sel].mask;\r
731 else Settings.JoyBinds[joy-1][i] ^= opts[sel].mask;\r
989672f4 732 if (player_idx >= 0) {\r
733 Settings.JoyBinds[joy-1][i] &= ~(3 << 16);\r
734 Settings.JoyBinds[joy-1][i] |= player_idx << 16;\r
735 }\r
b2b95d2e 736 }\r
737 }\r
738 }\r
739}\r
740\r
741static void draw_kc_sel(int menu_sel)\r
742{\r
743 int tl_x = 25+40, tl_y = 60, y, i;\r
744 char joyname[36];\r
745\r
746 y = tl_y;\r
b2b95d2e 747 gp2x_fceu_copy_bg();\r
748 gp2x_text_out15(tl_x, y, "Player 1");\r
749 gp2x_text_out15(tl_x, (y+=10), "Player 2");\r
b547bda7 750 gp2x_text_out15(tl_x, (y+=10), "Emulator controls");\r
b2b95d2e 751 gp2x_text_out15(tl_x, (y+=10), "Done");\r
752\r
753 // draw cursor\r
754 gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
755\r
756 tl_x = 25;\r
757 gp2x_text_out15(tl_x, (y=110), "USB joys detected:");\r
758 if (num_of_joys > 0) {\r
759 for (i = 0; i < num_of_joys; i++) {\r
760 strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
761 gp2x_text_out15(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
762 }\r
763 } else {\r
764 gp2x_text_out15(tl_x, (y+=10), "none");\r
765 }\r
766\r
b2b95d2e 767 gp2x_video_flip();\r
768}\r
769\r
989672f4 770// b_turbo,a_turbo RLDU SEBA\r
771static bind_action_t ctrl_actions[] =\r
772{\r
773 { "UP ", 0x010 },\r
774 { "DOWN ", 0x020 },\r
775 { "LEFT ", 0x040 },\r
776 { "RIGHT ", 0x080 },\r
777 { "A ", 0x001 },\r
778 { "B ", 0x002 },\r
779 { "A TURBO", 0x100 },\r
780 { "B TURBO", 0x200 },\r
781 { "START ", 0x008 },\r
782 { "SELECT ", 0x004 },\r
783};\r
784\r
785static bind_action_t emuctrl_actions[] =\r
786{\r
e328100e 787 { "Save State ", 1<<31 },\r
788 { "Load State ", 1<<30 },\r
789 { "Next State Slot ", 1<<29 },\r
790 { "Prev State Slot ", 1<<28 },\r
791 { "FDS Insert/Eject ", 1<<27 },\r
792 { "FDS Select Disk ", 1<<26 },\r
793 { "VSUni Insert Coin", 1<<25 },\r
989672f4 794};\r
795\r
b2b95d2e 796static void kc_sel_loop(void)\r
797{\r
b547bda7 798 int menu_sel = 3, menu_sel_max = 3;\r
b2b95d2e 799 unsigned long inp = 0;\r
800\r
801 for(;;)\r
802 {\r
803 draw_kc_sel(menu_sel);\r
804 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
805 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
806 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
807 if(inp & GP2X_B) {\r
808 switch (menu_sel) {\r
b547bda7 809 case 0: key_config_loop(ctrl_actions, 10, 0); return;\r
810 case 1: key_config_loop(ctrl_actions, 8, 1); return;\r
989672f4 811 case 2: key_config_loop(emuctrl_actions,\r
812 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]), -1); return;\r
b2b95d2e 813 default: return;\r
814 }\r
815 }\r
816 if(inp & GP2X_X) return;\r
817 }\r
818}\r
b2b95d2e 819\r
820\r
989672f4 821// --------- FCEU options ----------\r
b2b95d2e 822\r
21afaa36 823extern int ntsccol,ntschue,ntsctint;\r
824extern int srendlinev[2];\r
825extern int erendlinev[2];\r
826extern int eoptions;\r
827\r
828\r
829static void int_incdec(int *p, int inc, int min, int max)\r
830{\r
831 *p += inc;\r
832 if (*p < min) *p = min;\r
833 else if (*p > max) *p = max;\r
834}\r
835\r
989672f4 836static void draw_fcemenu_options(int menu_sel)\r
b2b95d2e 837{\r
838 int tl_x = 25, tl_y = 60, y;\r
b2b95d2e 839\r
840 y = tl_y;\r
b2b95d2e 841 gp2x_fceu_copy_bg();\r
842\r
21afaa36 843 gp2x_text_out15(tl_x, y, "NTSC Color Emulation %s", ntsccol?"ON":"OFF"); // 0\r
844 gp2x_text_out15(tl_x, (y+=10), " Tint (default: 56) %i", ntsctint);\r
845 gp2x_text_out15(tl_x, (y+=10), " Hue (default: 72) %i", ntschue);\r
846 gp2x_text_out15(tl_x, (y+=10), "First visible line (NTSC) %i", srendlinev[0]);\r
847 gp2x_text_out15(tl_x, (y+=10), "Last visible line (NTSC) %i", erendlinev[0]);\r
848 gp2x_text_out15(tl_x, (y+=10), "First visible line (PAL) %i", srendlinev[1]); // 5\r
849 gp2x_text_out15(tl_x, (y+=10), "Last visible line (PAL) %i", erendlinev[1]);\r
850 gp2x_text_out15(tl_x, (y+=10), "Clip 8 left/right columns %s", (eoptions&EO_CLIPSIDES)?"ON":"OFF");\r
851 gp2x_text_out15(tl_x, (y+=10), "Disable 8 sprite limit %s", "TODO");\r
852 gp2x_text_out15(tl_x, (y+=10), "Done"); // 9\r
b2b95d2e 853\r
854 // draw cursor\r
855 gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
856\r
857 gp2x_video_flip();\r
858}\r
859\r
989672f4 860static void fcemenu_loop_options(void)\r
b2b95d2e 861{\r
21afaa36 862 int menu_sel = 0, menu_sel_max = 9, i;\r
b2b95d2e 863 unsigned long inp = 0;\r
864\r
21afaa36 865 FCEUI_GetNTSCTH(&ntsctint, &ntschue);\r
866\r
b2b95d2e 867 for(;;)\r
868 {\r
989672f4 869 draw_fcemenu_options(menu_sel);\r
b2b95d2e 870 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
871 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
872 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
873 if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
874 switch (menu_sel) {\r
21afaa36 875 case 0: ntsccol = !ntsccol; break;\r
876 case 7: eoptions^=EO_CLIPSIDES; break;\r
877 case 9: return;\r
878 }\r
879 }\r
880 if(inp & (GP2X_X|GP2X_A)) {\r
881 for(i=0;i<2;i++)\r
882 {\r
883 if(srendlinev[i]<0 || srendlinev[i]>239) srendlinev[i]=0;\r
884 if(erendlinev[i]<srendlinev[i] || erendlinev[i]>239) erendlinev[i]=239;\r
b2b95d2e 885 }\r
21afaa36 886 FCEUI_SetNTSCTH(ntsccol, ntsctint, ntschue);\r
887 FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);\r
888 return;\r
b2b95d2e 889 }\r
b2b95d2e 890 if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
891 switch (menu_sel) {\r
21afaa36 892 case 1: int_incdec(&ntsctint, (inp & GP2X_LEFT) ? -1 : 1, 0, 128); break;\r
893 case 2: int_incdec(&ntschue, (inp & GP2X_LEFT) ? -1 : 1, 0, 128); break;\r
894 case 3: int_incdec(&srendlinev[0], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
895 case 4: int_incdec(&erendlinev[0], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
896 case 5: int_incdec(&srendlinev[1], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
897 case 6: int_incdec(&erendlinev[1], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
b2b95d2e 898 }\r
899 }\r
900 }\r
901}\r
b2b95d2e 902\r
903// -------------- options --------------\r
904\r
905static void draw_menu_options(int menu_sel)\r
906{\r
907 int tl_x = 25, tl_y = 32, y;\r
989672f4 908 char strframeskip[8], *strscaling, *strssconfirm;\r
909 char *mms = mmuhack_status ? "active) " : "inactive)";\r
b2b95d2e 910\r
b2b95d2e 911 if (Settings.frameskip < 0)\r
912 strcpy(strframeskip, "Auto");\r
913 else sprintf(strframeskip, "%i", Settings.frameskip);\r
914 switch (Settings.scaling) {\r
915 default: strscaling = " OFF"; break;\r
916 case 1: strscaling = "hw horizontal"; break;\r
917 case 2: strscaling = "hw horiz. + vert."; break;\r
918 case 3: strscaling = "sw horizontal"; break;\r
919 }\r
920 switch (Settings.sstate_confirm) {\r
921 default: strssconfirm = "OFF"; break;\r
922 case 1: strssconfirm = "writes"; break;\r
923 case 2: strssconfirm = "loads"; break;\r
924 case 3: strssconfirm = "both"; break;\r
925 }\r
926\r
927 y = tl_y;\r
b2b95d2e 928 gp2x_fceu_copy_bg();\r
929\r
b547bda7 930 gp2x_text_out15(tl_x, y, "Scaling: %s", strscaling); // 0\r
b2b95d2e 931 gp2x_text_out15(tl_x, (y+=10), "Show FPS %s", Settings.showfps?"ON":"OFF"); // 1\r
932 gp2x_text_out15(tl_x, (y+=10), "Frameskip %s", strframeskip); // 2\r
989672f4 933 gp2x_text_out15(tl_x, (y+=10), "Enable sound %s", soundvol?"ON":"OFF");\r
934 gp2x_text_out15(tl_x, (y+=10), "Sound Rate: %5iHz", Settings.sound_rate); // 4\r
935 gp2x_text_out15(tl_x, (y+=10), "Force Region: %s",\r
936 Settings.region_force == 2 ? "PAL" : Settings.region_force == 1 ? "NTSC" : "OFF"); // 5\r
b2b95d2e 937 gp2x_text_out15(tl_x, (y+=10), "Use SRAM savestates %s", "OFF");\r
989672f4 938 gp2x_text_out15(tl_x, (y+=10), "Turbo rate %iHz", (Settings.turbo_rate_add*60/2) >> 24);\r
939 gp2x_text_out15(tl_x, (y+=10), "Confirm savestate %s", strssconfirm); // 8\r
940 gp2x_text_out15(tl_x, (y+=10), "Save slot %i", CurrentState);\r
941 gp2x_text_out15(tl_x, (y+=10), "Faster RAM timings %s", Settings.ramtimings?"ON":"OFF");\r
942 gp2x_text_out15(tl_x, (y+=10), "squidgehack (now %s %s", mms, Settings.mmuhack?"ON":"OFF"); // 11\r
943 gp2x_text_out15(tl_x, (y+=10), "Gamma correction %i.%02i", Settings.gamma / 100, Settings.gamma%100);\r
944 gp2x_text_out15(tl_x, (y+=10), "GP2X CPU clock %iMhz", Settings.cpuclock); // 13\r
945 gp2x_text_out15(tl_x, (y+=10), "[FCE Ultra options]");\r
b2b95d2e 946 gp2x_text_out15(tl_x, (y+=10), "Save cfg as default");\r
947 if (fceugi)\r
948 gp2x_text_out15(tl_x, (y+=10), "Save cfg for current game only");\r
949\r
950 // draw cursor\r
951 gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
952\r
953 gp2x_video_flip();\r
954}\r
955\r
b2b95d2e 956static int sndrate_prevnext(int rate, int dir)\r
957{\r
958 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
959\r
960 for (i = 0; i < 5; i++)\r
961 if (rates[i] == rate) break;\r
962\r
963 i += dir ? 1 : -1;\r
964 if (i > 4) return dir ? 44100 : 22050;\r
965 if (i < 0) return dir ? 11025 : 8000;\r
966 return rates[i];\r
967}\r
989672f4 968\r
989672f4 969static void config_commit(void)\r
970{\r
971 gp2x_cpuclock_gamma_update();\r
972 if (Settings.region_force)\r
973 FCEUI_SetVidSystem(Settings.region_force - 1);\r
974}\r
975\r
b2b95d2e 976static int menu_loop_options(void)\r
977{\r
989672f4 978 static int menu_sel = 0;\r
979 int menu_sel_max = 15;\r
b2b95d2e 980 unsigned long inp = 0;\r
981\r
982 if (fceugi) menu_sel_max++;\r
983\r
984 for(;;)\r
985 {\r
986 draw_menu_options(menu_sel);\r
987 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
988 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
989 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
990 if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
991 switch (menu_sel) {\r
989672f4 992 case 1: Settings.showfps = !Settings.showfps; break;\r
993 case 3: soundvol = soundvol ? 0 : 100; break;\r
994 case 10: Settings.ramtimings = !Settings.ramtimings; break;\r
995 case 11: Settings.mmuhack = !Settings.mmuhack; break;\r
996 case 14: fcemenu_loop_options(); break;\r
997 case 15: // done (update and write)\r
998 config_commit();\r
b547bda7 999 SaveConfig(NULL);\r
b2b95d2e 1000 return 1;\r
989672f4 1001 case 16: // done (update and write for current game)\r
1002 config_commit();\r
b547bda7 1003 if (lastLoadedGameName[0])\r
1004 SaveConfig(lastLoadedGameName);\r
b2b95d2e 1005 return 1;\r
1006 }\r
1007 }\r
1008 if(inp & (GP2X_X|GP2X_A)) {\r
989672f4 1009 config_commit();\r
b2b95d2e 1010 return 0; // done (update, no write)\r
1011 }\r
1012 if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1013 switch (menu_sel) {\r
1014 case 0: int_incdec(&Settings.scaling, (inp & GP2X_LEFT) ? -1 : 1, 0, 3); break;\r
1015 case 2: int_incdec(&Settings.frameskip, (inp & GP2X_LEFT) ? -1 : 1, -1, 32); break;\r
b2b95d2e 1016 case 4:\r
989672f4 1017 Settings.sound_rate = sndrate_prevnext(Settings.sound_rate, inp & GP2X_RIGHT);\r
1018 InitSound();\r
b2b95d2e 1019 break;\r
b2b95d2e 1020 case 5: int_incdec(&Settings.region_force, (inp & GP2X_LEFT) ? -1 : 1, 0, 2); break;\r
989672f4 1021 case 7: {\r
1022 int hz = Settings.turbo_rate_add*60/2 >> 24;\r
1023 int_incdec(&hz, (inp & GP2X_LEFT) ? -1 : 1, 1, 30);\r
1024 Settings.turbo_rate_add = (hz*2 << 24) / 60 + 1;\r
1025 break;\r
1026 }\r
1027 case 8: int_incdec(&Settings.sstate_confirm, (inp & GP2X_LEFT) ? -1 : 1, 0, 3); break;\r
1028 case 9: int_incdec(&CurrentState, (inp & GP2X_LEFT) ? -1 : 1, 0, 9); break;\r
1029 case 12: int_incdec(&Settings.gamma, (inp & GP2X_LEFT) ? -1 : 1, 0, 300); break;\r
1030 case 13:\r
b2b95d2e 1031 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1032 Settings.cpuclock += (inp & GP2X_LEFT) ? -1 : 1;\r
b547bda7 1033 if (Settings.cpuclock < 0) Settings.cpuclock = 0; // 0 ~ do not change\r
b2b95d2e 1034 draw_menu_options(menu_sel);\r
1035 usleep(50*1000);\r
1036 }\r
1037 break;\r
1038 }\r
1039 }\r
1040 }\r
1041}\r
1042\r
1043// -------------- credits --------------\r
1044\r
1045static void draw_menu_credits(void)\r
1046{\r
32a0f49f 1047 char vstr[32];\r
971a1d07 1048\r
b547bda7 1049 //int tl_x = 15, tl_y = 70;\r
b2b95d2e 1050 gp2x_fceu_copy_bg();\r
1051\r
971a1d07 1052 sprintf(vstr, "GPFCE v" GP2X_PORT_VERSION " rev%i", GP2X_PORT_REV);\r
1053 gp2x_text_out15(20, 30, vstr);\r
1054 gp2x_text_out15(20, 40, "(c) notaz, 2007");\r
1055\r
1056 gp2x_text_out15(20, 70, "Based on FCE Ultra versions");\r
32a0f49f 1057 gp2x_text_out15(20, 80, "0.81 and 0.98.1x");\r
971a1d07 1058\r
1059 gp2x_text_out15(20, 110, " - Credits - ");\r
1060 gp2x_text_out15(20, 130, "Bero: FCE");\r
1061 gp2x_text_out15(20, 140, "Xodnizel: FCE Ultra");\r
1062 gp2x_text_out15(20, 150, "zzhu8192: original port");\r
1063 gp2x_text_out15(20, 160, "rlyeh: minimal lib");\r
1064 gp2x_text_out15(20, 170, "Hermes, theoddbot, god_at_hell:");\r
1065 gp2x_text_out15(20, 180, " cpuctrl, gamma libs");\r
1066 gp2x_text_out15(20, 190, "Squidge: squidgehack");\r
b2b95d2e 1067\r
1068 gp2x_video_flip();\r
1069}\r
1070\r
1071\r
1072// -------------- root menu --------------\r
1073\r
1074static void draw_menu_root(int menu_sel)\r
1075{\r
b547bda7 1076 int tl_x = 30, tl_y = 128, y;\r
b2b95d2e 1077 gp2x_fceu_copy_bg();\r
1078\r
1079 y = tl_y;\r
1080 if (fceugi) {\r
1081 gp2x_text_out15(tl_x, y, "Resume game");\r
1082 gp2x_text_out15(tl_x, (y+=10), "Save State");\r
1083 gp2x_text_out15(tl_x, (y+=10), "Load State");\r
1084 gp2x_text_out15(tl_x, (y+=10), "Reset game");\r
1085 } else {\r
1086 y += 30;\r
1087 }\r
1088 gp2x_text_out15(tl_x, (y+=10), "Load new ROM");\r
b547bda7 1089 gp2x_text_out15(tl_x, (y+=10), "Options");\r
1090 gp2x_text_out15(tl_x, (y+=10), "Controls");\r
b2b95d2e 1091 gp2x_text_out15(tl_x, (y+=10), "Credits");\r
1092 gp2x_text_out15(tl_x, (y+=10), "Exit");\r
1093// TODO\r
1094// if (PicoPatches)\r
1095// gp2x_text_out15(tl_x, (y+=10), "Patches / GameGenie");\r
1096\r
1097 // draw cursor\r
1098 gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
1099 // error\r
b547bda7 1100 if (menuErrorMsg[0]) gp2x_text_out15(1, 230, menuErrorMsg);\r
1101 else {\r
1102 char vstr[16];\r
1103 sprintf(vstr, "v" GP2X_PORT_VERSION " r%i", GP2X_PORT_REV);\r
1104 gp2x_text_out15(320-strlen(vstr)*8-1, 230, vstr);\r
1105 }\r
b2b95d2e 1106 gp2x_video_flip();\r
1107}\r
1108\r
1109\r
1110static int menu_loop_root(void)\r
1111{\r
b547bda7 1112 int ret, menu_sel_max = 8, menu_sel_min = 4;\r
1113 static int menu_sel = 4;\r
b2b95d2e 1114 unsigned long inp = 0;\r
1115 char curr_path[PATH_MAX], *selfname;\r
1116 FILE *tstf;\r
1117\r
1118 if ( (tstf = fopen(lastLoadedGameName, "rb")) )\r
1119 {\r
1120 fclose(tstf);\r
1121 strncpy(curr_path, lastLoadedGameName, sizeof(curr_path));\r
1122 curr_path[sizeof(curr_path)-1] = 0;\r
1123 }\r
1124 else\r
1125 {\r
1126 getcwd(curr_path, PATH_MAX);\r
1127 }\r
1128\r
b547bda7 1129 if (fceugi) menu_sel_min = 0;\r
b2b95d2e 1130// TODO if (PicoPatches) menu_sel_max = 9;\r
b547bda7 1131 if (menu_sel < menu_sel_min || menu_sel > menu_sel_max)\r
1132 menu_sel = menu_sel_min;\r
b2b95d2e 1133\r
1134 /* make sure action buttons are not pressed on entering menu */\r
1135 draw_menu_root(menu_sel);\r
1136 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1137\r
1138 for (;;)\r
1139 {\r
1140 draw_menu_root(menu_sel);\r
1141 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
1142 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; }\r
1143 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; }\r
1144 if(inp &(GP2X_SELECT|GP2X_X)){\r
1145 if (fceugi) {\r
1146 while (gp2x_joystick_read(1) & GP2X_X) usleep(50*1000); // wait until X is released\r
1147 Exit = 0;\r
1148 return 0;\r
1149 }\r
1150 }\r
1151 if(inp & GP2X_B ) {\r
1152 switch (menu_sel) {\r
1153 case 0: // resume game\r
1154 if (fceugi) {\r
1155 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1156 Exit = 0;\r
1157 return 0;\r
1158 }\r
1159 break;\r
1160 case 1: // save state\r
1161 if (fceugi) {\r
1162 /*if(savestate_menu_loop(0))\r
1163 continue;*/\r
b547bda7 1164 FCEUI_SaveState();\r
971a1d07 1165 Exit = 0;\r
1166 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
b2b95d2e 1167 return 0;\r
1168 }\r
1169 break;\r
1170 case 2: // load state\r
1171 if (fceugi) {\r
1172 /*if(savestate_menu_loop(1))\r
1173 continue;*/\r
b547bda7 1174 FCEUI_LoadState();\r
971a1d07 1175 Exit = 0;\r
1176 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
b2b95d2e 1177 return 0;\r
1178 }\r
1179 break;\r
1180 case 3: // reset game\r
1181 if (fceugi) {\r
b547bda7 1182 FCEU_DoSimpleCommand(FCEUNPCMD_RESET);\r
1183 Exit = 0;\r
b2b95d2e 1184 return 0;\r
1185 }\r
1186 break;\r
1187 case 4: // select rom\r
1188 selfname = romsel_loop(curr_path);\r
1189 if (selfname) {\r
1190 printf("selected file: %s\n", selfname);\r
21afaa36 1191 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
b2b95d2e 1192 return 2;\r
1193 }\r
1194 break;\r
1195 case 5: // options\r
1196 ret = menu_loop_options();\r
1197 if (ret == 1) continue; // status update\r
1198 break;\r
1199 case 6: // controls\r
b547bda7 1200 kc_sel_loop();\r
b2b95d2e 1201 break;\r
1202 case 7: // credits\r
1203 draw_menu_credits();\r
1204 usleep(500*1000);\r
1205 inp = wait_for_input(GP2X_B|GP2X_X);\r
1206 break;\r
1207 case 8: // exit\r
1208 return 1;\r
1209 case 9: // patches/gg\r
1210 break;\r
1211 }\r
1212 }\r
1213 menuErrorMsg[0] = 0; // clear error msg\r
1214 }\r
1215}\r
1216\r
1217\r
1218static void menu_prepare_bg(void)\r
1219{\r
1220 // TODO...\r
1221}\r
1222\r
1223static void menu_gfx_prepare(void)\r
1224{\r
1225 menu_prepare_bg();\r
1226\r
1227 // switch bpp\r
1228 gp2x_video_changemode(16);\r
989672f4 1229 gp2x_video_set_offs(0);\r
b2b95d2e 1230 gp2x_video_RGB_setscaling(0, 320, 240);\r
1231 gp2x_video_flip();\r
1232}\r
1233\r
1234\r
1235int gp2x_menu_do(void)\r
1236{\r
1237 int ret;\r
1238\r
1239 menu_gfx_prepare();\r
1240\r
1241 ret = menu_loop_root();\r
1242\r
1243 menuErrorMsg[0] = 0;\r
1244\r
1245 return ret;\r
1246}\r
1247\r
1248\r