Rockboy: Added an short stats option which only shows the two numbers
[kugel-rb.git] / apps / plugins / rockboy / menu.c
blobda9783cf79bcd04aae4e44e69d6f6a96ff5fdf3a
1 /*********************************************************************/
2 /* menu.c - user menu for rockboy */
3 /* */
4 /* Note: this file only exposes one function: do_user_menu(). */
5 /*********************************************************************/
7 #include "lib/helper.h"
8 #include "button.h"
9 #include "rockmacros.h"
10 #include "mem.h"
11 #include "save.h"
12 #include "rtc-gb.h"
13 #include "pcm.h"
14 #include "emu.h"
16 #define SLOT_COUNT 50
17 #define DESC_SIZE 20
19 /* load/save state function declarations */
20 static void do_opt_menu(void);
21 static void do_slot_menu(bool is_load);
22 static void munge_name(char *buf, size_t bufsiz);
24 /* directory ROM save slots belong in */
25 #define STATE_DIR ROCKBOX_DIR "/rockboy"
27 static int getbutton(char *text)
29 int fw, fh;
30 int button;
31 rb->lcd_clear_display();
32 rb->font_getstringsize(text, &fw, &fh,0);
33 rb->lcd_putsxy(LCD_WIDTH/2-fw/2, LCD_HEIGHT/2-fh/2, text);
34 rb->lcd_update();
35 rb->sleep(30);
37 while (rb->button_get(false) != BUTTON_NONE)
38 rb->yield();
40 while(true)
42 button = rb->button_get(true);
43 button = button&(BUTTON_MAIN|BUTTON_REMOTE);
45 return button;
49 static void setupkeys(void)
51 options.UP = getbutton("Press Up");
52 options.DOWN = getbutton("Press Down");
53 options.LEFT = getbutton("Press Left");
54 options.RIGHT = getbutton("Press Right");
56 options.A = getbutton("Press A");
57 options.B = getbutton("Press B");
59 options.START = getbutton("Press Start");
60 options.SELECT = getbutton("Press Select");
62 options.MENU = getbutton("Press Menu");
66 * do_user_menu - create the user menu on the screen.
68 * Returns USER_MENU_QUIT if the user selected "quit", otherwise
69 * returns zero.
71 int do_user_menu(void) {
72 bool done=false;
73 int selected=0, ret=0;
74 int result;
75 int time = 0;
77 #if CONFIG_RTC
78 time = rb->mktime(rb->get_time());
79 #endif
81 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
82 rb->lcd_set_mode(LCD_MODE_RGB565);
83 #endif
85 backlight_use_settings();
87 /* Clean out the button Queue */
88 while (rb->button_get(false) != BUTTON_NONE)
89 rb->yield();
91 MENUITEM_STRINGLIST(menu, "Rockboy Menu", NULL,
92 "Load Game", "Save Game",
93 "Options", "Reset", "Quit");
95 rockboy_pcm_init();
97 while(!done)
99 result = rb->do_menu(&menu, &selected, NULL, false);
101 switch (result)
103 case 0: /* Load Game */
104 do_slot_menu(true);
105 break;
106 case 1: /* Save Game */
107 do_slot_menu(false);
108 break;
109 case 2: /* Options */
110 do_opt_menu();
111 break;
112 case 3: /* Reset */
113 emu_reset();
114 done=true;
115 break;
116 case 4: /* Quit */
117 ret = USER_MENU_QUIT;
118 done=true;
119 break;
120 default:
121 done=true;
122 break;
126 rb->lcd_setfont(0); /* Reset the font */
127 rb->lcd_clear_display(); /* Clear display for screen size changes */
129 /* Keep the RTC in sync */
130 #if CONFIG_RTC
131 time = (rb->mktime(rb->get_time()) - time) * 60;
132 #endif
133 while (time-- > 0) rtc_tick();
135 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
136 rb->lcd_set_mode(LCD_MODE_PAL256);
137 #endif
139 /* ignore backlight time out */
140 backlight_force_on();
142 return ret;
146 * munge_name - munge a string into a filesystem-safe name
148 static void munge_name(char *buf, const size_t bufsiz) {
149 unsigned int i, max;
151 /* check strlen */
152 max = strlen(buf);
153 max = (max < bufsiz) ? max : bufsiz;
155 /* iterate over characters and munge them (if necessary) */
156 for (i = 0; i < max; i++)
157 if (!isalnum(buf[i]))
158 buf[i] = '_';
162 * build_slot_path - build a path to an slot state file for this rom
164 * Note: uses rom.name. Is there a safer way of doing this? Like a ROM
165 * checksum or something like that?
167 static void build_slot_path(char *buf, size_t bufsiz, int slot_id) {
168 char name_buf[17];
170 /* munge state file name */
171 strlcpy(name_buf, rom.name, sizeof(name_buf));
172 munge_name(name_buf, strlen(name_buf));
174 /* glom the whole mess together */
175 snprintf(buf, bufsiz, "%s/%s-%d.rbs", STATE_DIR, name_buf, slot_id + 1);
179 * do_file - load or save game data in the given file
181 * Returns true on success and false on failure.
183 * @desc is a brief user-provided description of the state.
184 * If no description is provided, set @desc to NULL.
187 static bool do_file(char *path, char *desc, bool is_load) {
188 char desc_buf[DESC_SIZE];
189 int fd, file_mode;
191 /* set file mode */
192 file_mode = is_load ? O_RDONLY : (O_WRONLY | O_CREAT);
194 /* attempt to open file descriptor here */
195 if ((fd = open(path, file_mode, 0666)) < 0)
196 return false;
198 /* load/save state */
199 if (is_load)
201 /* load description */
202 read(fd, desc_buf, sizeof(desc_buf));
204 /* load state */
205 loadstate(fd);
207 /* print out a status message so the user knows the state loaded */
208 rb->splashf(HZ * 1, "Loaded state from \"%s\"", path);
210 else
212 /* build description buffer */
213 memset(desc_buf, 0, sizeof(desc_buf));
214 if (desc)
215 strlcpy(desc_buf, desc, sizeof(desc_buf));
217 /* save state */
218 write(fd, desc_buf, sizeof(desc_buf));
219 savestate(fd);
222 /* close file descriptor */
223 close(fd);
225 /* return true (for success) */
226 return true;
230 * get information on the given slot
232 static void slot_info(char *info_buf, size_t info_bufsiz, int slot_id,
233 bool number) {
234 char buf[256];
235 int fd;
237 /* get slot file path */
238 build_slot_path(buf, sizeof(buf), slot_id);
240 /* attempt to open slot */
241 if ((fd = open(buf, O_RDONLY)) >= 0)
243 /* this slot has a some data in it, read it */
244 if (read(fd, buf, DESC_SIZE) == DESC_SIZE)
246 buf[DESC_SIZE] = '\0';
247 strlcpy(info_buf, buf, info_bufsiz);
249 else if(number)
250 strlcpy(info_buf, "ERROR", info_bufsiz);
252 close(fd);
254 else if(number)
256 /* if we couldn't open the file, then the slot is empty */
257 strlcpy(info_buf, "<Empty>", info_bufsiz);
262 * do_slot - load or save game data in the given slot
264 * Returns true on success and false on failure.
266 static bool do_slot(int slot_id, bool is_load) {
267 char path_buf[256], desc_buf[DESC_SIZE];
269 /* build slot filename, clear desc buf */
270 build_slot_path(path_buf, sizeof(path_buf), slot_id);
271 memset(desc_buf, 0, sizeof(desc_buf));
273 /* if we're saving to a slot, then get a brief description */
274 if (!is_load)
276 slot_info(desc_buf, sizeof(desc_buf), slot_id, false);
277 if ( rb->kbd_input(desc_buf, sizeof(desc_buf)) < 0 )
278 return false;
279 if ( !strlen(desc_buf) )
280 strlcpy(desc_buf, "Untitled", sizeof(desc_buf));
283 /* load/save file */
284 return do_file(path_buf, desc_buf, is_load);
288 * slot_get_name
290 static const char* slot_get_name(int selected_item, void * data,
291 char * buffer, size_t buffer_len)
293 const char (*items)[DESC_SIZE] = data;
294 snprintf(buffer, buffer_len, "%d. %s",
295 selected_item + 1, items[selected_item]);
296 return buffer;
300 * list_action_callback
302 static int list_action_callback(int action, struct gui_synclist *lists)
304 (void) lists;
305 if (action == ACTION_STD_OK)
306 return ACTION_STD_CANCEL;
307 return action;
311 * do_slot_menu - prompt the user for a load/save memory slot
313 static void do_slot_menu(bool is_load) {
314 bool done=false;
315 char items[SLOT_COUNT][DESC_SIZE];
316 int result;
317 int i;
318 struct simplelist_info info;
320 /* create menu items */
321 for (i = 0; i < SLOT_COUNT; i++)
322 slot_info(items[i], sizeof(*items), i, true);
324 rb->simplelist_info_init(&info, NULL, SLOT_COUNT, (void *)items);
325 info.get_name = slot_get_name;
326 info.action_callback = list_action_callback;
328 while(!done)
330 if(rb->simplelist_show_list(&info))
331 break;
333 result = info.selection;
334 if (result<SLOT_COUNT && result >= 0 )
335 done = do_slot(result, is_load);
336 else
337 done = true;
341 static void do_opt_menu(void)
343 bool done=false;
344 int selected=0;
345 int result;
347 static const struct opt_items onoff[2] = {
348 { "Off", -1 },
349 { "On" , -1 },
352 static const struct opt_items stats[3] = {
353 { "Off", -1 },
354 { "Short" , -1 },
355 { "Full" , -1 },
358 static const struct opt_items frameskip[]= {
359 { "0 Max", -1 },
360 { "1 Max", -1 },
361 { "2 Max", -1 },
362 { "3 Max", -1 },
363 { "4 Max", -1 },
364 { "5 Max", -1 },
365 { "6 Max", -1 },
366 { "7 Max", -1 },
367 { "8 Max", -1 },
368 { "9 Max", -1 },
369 { "10 Max", -1 },
370 { "11 Max", -1 },
371 { "12 Max", -1 },
372 { "13 Max", -1 },
373 { "14 Max", -1 },
374 { "15 Max", -1 },
375 { "16 Max", -1 },
376 { "17 Max", -1 },
377 { "18 Max", -1 },
378 { "19 Max", -1 },
379 { "20 Max", -1 },
382 #ifdef HAVE_LCD_COLOR
383 static const struct opt_items rotate[] = {
384 { "No rotation", -1 },
385 { "Rotate Right" , -1 },
386 { "Rotate Left" , -1 },
389 static const struct opt_items scaling[]= {
390 { "Scaled", -1 },
391 { "Scaled - Maintain Ratio", -1 },
392 #if (LCD_WIDTH>=160) && (LCD_HEIGHT>=144)
393 { "Unscaled", -1 },
394 #endif
397 static const struct opt_items palette[]= {
398 { "Brown (Default)", -1 },
399 { "Gray", -1 },
400 { "Light Gray", -1 },
401 { "Multi-Color 1", -1 },
402 { "Multi-Color 2", -1 },
403 { "Adventure Island", -1 },
404 { "Adventure Island 2", -1 },
405 { "Balloon Kid", -1 },
406 { "Batman", -1 },
407 { "Batman: Return of Joker", -1 },
408 { "Bionic Commando", -1 },
409 { "Castlvania Adventure", -1 },
410 { "Donkey Kong Land", -1 },
411 { "Dr. Mario", -1 },
412 { "Kirby", -1 },
413 { "Metroid", -1 },
414 { "Zelda", -1 },
416 #endif
418 MENUITEM_STRINGLIST(menu, "Options", NULL,
419 "Max Frameskip", "Sound", "Volume", "Stats", "Set Keys (Buggy)",
420 #ifdef HAVE_LCD_COLOR
421 "Screen Size", "Screen Rotate", "Set Palette",
422 #endif
425 options.dirty=1; /* Assume that the settings have been changed */
427 struct viewport *parentvp = NULL;
428 const struct settings_list* vol = rb->find_setting(&rb->global_settings->volume, NULL);
430 while(!done)
432 result = rb->do_menu(&menu, &selected, NULL, false);
434 switch (result)
436 case 0: /* Frameskip */
437 rb->set_option("Max Frameskip", &options.maxskip, INT, frameskip,
438 sizeof(frameskip)/sizeof(*frameskip), NULL );
439 break;
440 case 1: /* Sound */
441 if(options.sound>1) options.sound=1;
442 rb->set_option("Sound", &options.sound, INT, onoff, 2, NULL );
443 if(options.sound) sound_dirty();
444 break;
445 case 2: /* Volume */
446 rb->option_screen((struct settings_list*)vol, parentvp, false, "Volume");
447 break;
448 case 3: /* Stats */
449 rb->set_option("Stats", &options.showstats, INT, stats, 3, NULL );
450 break;
451 case 4: /* Keys */
452 setupkeys();
453 break;
454 #ifdef HAVE_LCD_COLOR
455 case 5: /* Screen Size */
456 rb->set_option("Screen Size", &options.scaling, INT, scaling,
457 sizeof(scaling)/sizeof(*scaling), NULL );
458 setvidmode();
459 break;
460 case 6: /* Screen rotate */
461 rb->set_option("Screen Rotate", &options.rotate, INT, rotate,
462 sizeof(rotate)/sizeof(*rotate), NULL );
463 setvidmode();
464 break;
465 case 7: /* Palette */
466 rb->set_option("Set Palette", &options.pal, INT, palette, 17, NULL );
467 set_pal();
468 break;
469 #endif
470 default:
471 done=true;
472 break;