Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / apps / plugins / rockboy / menu.c
blobe081a81d18620ab4704b79e62537073a9626e6bd
1 /*********************************************************************/
2 /* menu.c - user menu for rockboy */
3 /* */
4 /* Note: this file only exposes one function: do_user_menu(). */
5 /*********************************************************************/
7 #include "button.h"
8 #include "rockmacros.h"
9 #include "mem.h"
10 #include "save.h"
11 #include "rtc-gb.h"
12 #include "pcm.h"
14 /* load/save state function declarations */
15 static void do_opt_menu(void);
16 static void do_slot_menu(bool is_load);
17 static void munge_name(char *buf, size_t bufsiz);
19 /* directory ROM save slots belong in */
20 #define STATE_DIR ROCKBOX_DIR "/rockboy"
22 static int getbutton(char *text)
24 int fw, fh;
25 rb->lcd_clear_display();
26 rb->font_getstringsize(text, &fw, &fh,0);
27 rb->lcd_putsxy(LCD_WIDTH/2-fw/2, LCD_HEIGHT/2-fh/2, text);
28 rb->lcd_update();
29 rb->sleep(30);
31 while (rb->button_get(false) != BUTTON_NONE)
32 rb->yield();
34 int button;
35 while(true)
37 button = rb->button_get(true);
38 button=button&0x00000FFF;
40 return button;
44 static void setupkeys(void)
46 options.UP=getbutton ("Press Up");
47 options.DOWN=getbutton ("Press Down");
48 options.LEFT=getbutton ("Press Left");
49 options.RIGHT=getbutton ("Press Right");
51 options.A=getbutton ("Press A");
52 options.B=getbutton ("Press B");
54 options.START=getbutton ("Press Start");
55 options.SELECT=getbutton("Press Select");
57 options.MENU=getbutton ("Press Menu");
61 * do_user_menu - create the user menu on the screen.
63 * Returns USER_MENU_QUIT if the user selected "quit", otherwise
64 * returns zero.
66 int do_user_menu(void) {
67 bool done=false;
68 int selected=0, ret=0;
69 int result;
70 int time = 0;
72 #if CONFIG_RTC
73 time = rb->mktime(rb->get_time());
74 #endif
76 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
77 rb->lcd_set_mode(LCD_MODE_RGB565);
78 #endif
80 /* Clean out the button Queue */
81 while (rb->button_get(false) != BUTTON_NONE)
82 rb->yield();
84 MENUITEM_STRINGLIST(menu, "Rockboy Menu", NULL,
85 "Load Game", "Save Game",
86 "Options", "Quit");
88 pcm_init();
90 while(!done)
92 result = rb->do_menu(&menu, &selected, NULL, false);
94 switch (result)
96 case 0: /* Load Game */
97 do_slot_menu(true);
98 break;
99 case 1: /* Save Game */
100 do_slot_menu(false);
101 break;
102 case 2: /* Options */
103 do_opt_menu();
104 break;
105 case 3: /* Quit */
106 ret = USER_MENU_QUIT;
107 done=true;
108 break;
109 default:
110 done=true;
111 break;
115 rb->lcd_setfont(0); /* Reset the font */
116 rb->lcd_clear_display(); /* Clear display for screen size changes */
118 /* Keep the RTC in sync */
119 #if CONFIG_RTC
120 time = (rb->mktime(rb->get_time()) - time) * 60;
121 #endif
122 while (time-- > 0) rtc_tick();
124 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
125 rb->lcd_set_mode(LCD_MODE_PAL256);
126 #endif
128 return ret;
132 * munge_name - munge a string into a filesystem-safe name
134 static void munge_name(char *buf, const size_t bufsiz) {
135 unsigned int i, max;
137 /* check strlen */
138 max = strlen(buf);
139 max = (max < bufsiz) ? max : bufsiz;
141 /* iterate over characters and munge them (if necessary) */
142 for (i = 0; i < max; i++)
143 if (!isalnum(buf[i]))
144 buf[i] = '_';
148 * build_slot_path - build a path to an slot state file for this rom
150 * Note: uses rom.name. Is there a safer way of doing this? Like a ROM
151 * checksum or something like that?
153 static void build_slot_path(char *buf, size_t bufsiz, size_t slot_id) {
154 char name_buf[17];
156 /* munge state file name */
157 strlcpy(name_buf, rom.name, sizeof(name_buf));
158 munge_name(name_buf, strlen(name_buf));
160 /* glom the whole mess together */
161 snprintf(buf, bufsiz, "%s/%s-%zud.rbs", STATE_DIR, name_buf, slot_id + 1);
165 * do_file - load or save game data in the given file
167 * Returns true on success and false on failure.
169 * @desc is a brief user-provided description (<20 bytes) of the state.
170 * If no description is provided, set @desc to NULL.
173 static bool do_file(char *path, char *desc, bool is_load) {
174 char buf[200], desc_buf[20];
175 int fd, file_mode;
177 /* set file mode */
178 file_mode = is_load ? O_RDONLY : (O_WRONLY | O_CREAT);
180 /* attempt to open file descriptor here */
181 if ((fd = open(path, file_mode, 0666)) < 0)
182 return false;
184 /* load/save state */
185 if (is_load)
187 /* load description */
188 read(fd, desc_buf, 20);
190 /* load state */
191 loadstate(fd);
193 /* print out a status message so the user knows the state loaded */
194 snprintf(buf, 200, "Loaded state from \"%s\"", path);
195 rb->splash(HZ * 1, buf);
197 else
199 /* build description buffer */
200 memset(desc_buf, 0, 20);
201 if (desc)
202 strlcpy(desc_buf, desc, 20);
204 /* save state */
205 write(fd, desc_buf, 20);
206 savestate(fd);
209 /* close file descriptor */
210 close(fd);
212 /* return true (for success) */
213 return true;
217 * do_slot - load or save game data in the given slot
219 * Returns true on success and false on failure.
221 static bool do_slot(size_t slot_id, bool is_load) {
222 char path_buf[256], desc_buf[20];
224 /* build slot filename, clear desc buf */
225 build_slot_path(path_buf, 256, slot_id);
226 memset(desc_buf, 0, 20);
228 /* if we're saving to a slot, then get a brief description */
229 if (!is_load)
230 if ( (rb->kbd_input(desc_buf, 20) < 0) || !strlen(desc_buf) )
232 strlcpy(desc_buf, "Untitled", 20);
235 /* load/save file */
236 return do_file(path_buf, desc_buf, is_load);
240 * get information on the given slot
242 static void slot_info(char *info_buf, size_t info_bufsiz, size_t slot_id) {
243 char buf[256];
244 int fd;
246 /* get slot file path */
247 build_slot_path(buf, 256, slot_id);
249 /* attempt to open slot */
250 if ((fd = open(buf, O_RDONLY)) >= 0)
252 /* this slot has a some data in it, read it */
253 if (read(fd, buf, 20) > 0)
255 buf[20] = '\0';
256 snprintf(info_buf, info_bufsiz, "%zud. %s", slot_id + 1, buf);
258 else
259 snprintf(info_buf, info_bufsiz, "%zud. ERROR", slot_id + 1);
261 close(fd);
263 else
265 /* if we couldn't open the file, then the slot is empty */
266 snprintf(info_buf, info_bufsiz, "%zu. %s", slot_id + 1, "<Empty>");
271 * slot_get_name
273 static const char* slot_get_name(int selected_item, void * data,
274 char * buffer, size_t buffer_len)
276 const char (*items)[20] = data;
277 (void) buffer;
278 (void) buffer_len;
279 return items[selected_item];
283 * list_action_callback
285 static int list_action_callback(int action, struct gui_synclist *lists)
287 (void) lists;
288 if (action == ACTION_STD_OK)
289 return ACTION_STD_CANCEL;
290 return action;
294 * do_slot_menu - prompt the user for a load/save memory slot
296 static void do_slot_menu(bool is_load) {
297 bool done=false;
298 char items[5][20];
299 int result;
300 int i;
301 int num_items = sizeof(items) / sizeof(*items);
302 struct simplelist_info info;
304 /* create menu items */
305 for (i = 0; i < num_items; i++)
306 slot_info(items[i], 20, i);
308 rb->simplelist_info_init(&info, NULL, num_items, (void *)items);
309 info.get_name = slot_get_name;
310 info.action_callback = list_action_callback;
312 while(!done)
314 if(rb->simplelist_show_list(&info))
315 break;
317 result = info.selection;
318 if (result<num_items && result >= 0 )
319 done = do_slot(result, is_load);
320 else
321 done = true;
325 static void do_opt_menu(void)
327 bool done=false;
328 int selected=0;
329 int result;
331 static const struct opt_items onoff[2] = {
332 { "Off", -1 },
333 { "On" , -1 },
336 static const struct opt_items frameskip[]= {
337 { "0 Max", -1 },
338 { "1 Max", -1 },
339 { "2 Max", -1 },
340 { "3 Max", -1 },
341 { "4 Max", -1 },
342 { "5 Max", -1 },
343 { "6 Max", -1 },
346 #ifdef HAVE_LCD_COLOR
347 static const struct opt_items rotate[] = {
348 { "No rotation", -1 },
349 { "Rotate Right" , -1 },
350 { "Rotate Left" , -1 },
353 static const struct opt_items scaling[]= {
354 { "Scaled", -1 },
355 { "Scaled - Maintain Ratio", -1 },
356 #if (LCD_WIDTH>=160) && (LCD_HEIGHT>=144)
357 { "Unscaled", -1 },
358 #endif
361 static const struct opt_items palette[]= {
362 { "Brown (Default)", -1 },
363 { "Gray", -1 },
364 { "Light Gray", -1 },
365 { "Multi-Color 1", -1 },
366 { "Multi-Color 2", -1 },
367 { "Adventure Island", -1 },
368 { "Adventure Island 2", -1 },
369 { "Balloon Kid", -1 },
370 { "Batman", -1 },
371 { "Batman: Return of Joker", -1 },
372 { "Bionic Commando", -1 },
373 { "Castlvania Adventure", -1 },
374 { "Donkey Kong Land", -1 },
375 { "Dr. Mario", -1 },
376 { "Kirby", -1 },
377 { "Metroid", -1 },
378 { "Zelda", -1 },
380 #endif
382 MENUITEM_STRINGLIST(menu, "Options", NULL,
383 "Max Frameskip", "Sound", "Stats", "Set Keys (Buggy)",
384 #ifdef HAVE_LCD_COLOR
385 "Screen Size", "Screen Rotate", "Set Palette",
386 #endif
389 options.dirty=1; /* Assume that the settings have been changed */
391 while(!done)
393 result = rb->do_menu(&menu, &selected, NULL, false);
395 switch (result)
397 case 0: /* Frameskip */
398 rb->set_option("Max Frameskip", &options.maxskip, INT, frameskip,
399 sizeof(frameskip)/sizeof(*frameskip), NULL );
400 break;
401 case 1: /* Sound */
402 if(options.sound>1) options.sound=1;
403 rb->set_option("Sound", &options.sound, INT, onoff, 2, NULL );
404 if(options.sound) sound_dirty();
405 break;
406 case 2: /* Stats */
407 rb->set_option("Stats", &options.showstats, INT, onoff, 2, NULL );
408 break;
409 case 3: /* Keys */
410 setupkeys();
411 break;
412 #ifdef HAVE_LCD_COLOR
413 case 4: /* Screen Size */
414 rb->set_option("Screen Size", &options.scaling, INT, scaling,
415 sizeof(scaling)/sizeof(*scaling), NULL );
416 setvidmode();
417 break;
418 case 5: /* Screen rotate */
419 rb->set_option("Screen Rotate", &options.rotate, INT, rotate,
420 sizeof(rotate)/sizeof(*rotate), NULL );
421 setvidmode();
422 break;
423 case 6: /* Palette */
424 rb->set_option("Set Palette", &options.pal, INT, palette, 17, NULL );
425 set_pal();
426 break;
427 #endif
428 default:
429 done=true;
430 break;