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