1 /* gngb, a game boy color emulator
2 * Copyright (C) 2001 Peponas Thomas & Peponas Mathieu
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include <config.h>
20 #include <memory.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <SDL.h>
24
25 #include "emu.h"
26 #include "menu.h"
27 #include "message.h"
28 #include "vram.h"
29 #include "video_std.h"
30 #include "rom.h"
31 #include "sound.h"
32 #include "message.h"
33 #include "menu_image.h"
34 #include "save.h"
35
36 #define MAX_ITEM 6
37 #define MENUX 7
38 #define MENUY 7
39
40 #define SHADOW_IN 0
41 #define SHADOW_OUT 1
42 #define SHADOW_ETCHED_IN 2
43 #define SHADOW_ETCHED_OUT 3
44 #define SHADOW_NONE 4
45
46 int stop_all=0;
47
48 MENU *current_menu;
49
50 Uint8 radio_group[256];
51 SDL_Color buttonpal[]={{255,255,255},{214,214,214},{150,150,150},{0,0,0},{195,195,195}};
52
53 SDL_Surface *radio_on,*radio_off,*toggle_on,*toggle_off,*arrow_up,*arrow_down;
54
55 /*
56 note on action function:
57 if it return 1, the current menu is closed
58 if it's 2, all menu are closed
59 otherwise, it stays open
60 */
61
action_save_state(MENU_ITEM * self)62 int action_save_state(MENU_ITEM *self)
63 {
64 int i=(int)self->user_data;
65 save_state(NULL,i);
66 return 2;
67 }
68
action_load_state(MENU_ITEM * self)69 int action_load_state(MENU_ITEM *self)
70 {
71 int i=(int)self->user_data;
72 load_state(NULL,i);
73 return 2;
74 }
75
menu_draw_state(MENU_ITEM * self,int menu_pos)76 void menu_draw_state(MENU_ITEM *self,int menu_pos)
77 {
78 int i=(int)self->user_data;
79 SDL_Surface *bmp;
80 SDL_Rect b;
81
82 b.x=94;
83 b.y=78;
84
85 b.w=55;
86 b.h=50;
87 bmp=get_surface_of_save_state(i);
88 if(bmp) {
89 SDL_FillRect(back,&b,0);
90 b.x++;b.y++;
91 SDL_BlitSurface(bmp,NULL,back,&b);
92 }
93 }
94
action_loop_menu(MENU_ITEM * self)95 int action_loop_menu(MENU_ITEM *self)
96 {
97 MENU *m=(MENU*)self->user_data;
98 loop_menu(m);
99 return 0;
100 }
101
toggle_fullscreen(MENU_ITEM * self)102 int toggle_fullscreen(MENU_ITEM *self)
103 {
104 switch_fullscreen();
105 return 2;
106 }
107
toggle_filter(MENU_ITEM * self)108 int toggle_filter(MENU_ITEM *self)
109 {
110 if (conf.gb_type&COLOR_GAMEBOY) {
111 conf.color_filter^=1;
112 GenFilter();
113 update_all_pal();
114 }
115 return 2;
116 }
117
118 /* draw a preview of a pal */
menu_draw_pal(MENU_ITEM * self,int menu_pos)119 void menu_draw_pal(MENU_ITEM *self,int menu_pos)
120 {
121 SDL_Rect b;
122 int i;
123 int pal=(int)self->user_data;
124
125 b.x=100;
126 b.y=MENUY+menu_pos*hl+3;
127 b.w=22;
128 b.h=7;
129 SDL_FillRect(back,&b,COL32_TO_16(0x000000));
130
131 b.w=5;
132 b.h=5;
133 b.y+=1;
134 for(i=0;i<4;i++) {
135 b.x=101+i*5;
136
137 SDL_FillRect(back,&b,COL32_TO_16(conf.pal[pal][3-i]));
138 }
139
140 }
141
action_setpal(MENU_ITEM * self)142 int action_setpal(MENU_ITEM *self)
143 {
144 int p=(int)self->user_data;
145 gb_set_pal(p);
146 return 2;
147 }
148
action_reset(MENU_ITEM * self)149 int action_reset(MENU_ITEM *self)
150 {
151 emu_reset();
152 return 2;
153 }
154
toggle_autofskip(MENU_ITEM * self)155 int toggle_autofskip(MENU_ITEM *self)
156 {
157 conf.autoframeskip^=1;
158 return 2;
159 }
160
toggle_sound(MENU_ITEM * self)161 int toggle_sound(MENU_ITEM *self)
162 {
163 conf.sound^=1;
164 if (conf.sound)
165 gbsound_init();
166 else
167 close_sound();
168
169 return 2;
170 }
171
toggle_sleepidle(MENU_ITEM * self)172 int toggle_sleepidle(MENU_ITEM *self)
173 {
174 conf.sleep_idle^=1;
175 return 2;
176 }
177
toggle_fps(MENU_ITEM * self)178 int toggle_fps(MENU_ITEM *self)
179 {
180 conf.show_fps^=1;
181 if (!conf.show_fps)
182 unset_info();
183 else
184 set_info("fps:..");
185 return 2;
186 }
187
action_set_filter(MENU_ITEM * self)188 int action_set_filter(MENU_ITEM *self)
189 {
190 int p=(int)self->user_data;
191 set_filter(p);
192 return 2;
193 }
194
195 MENU save_state_menu={
196 " Save state ",
197 NULL,
198 0,0,0,0
199 };
200
201 MENU load_state_menu={
202 " Load state ",
203 NULL,
204 0,0,0,0
205 };
206
207 MENU filter_menu={
208 " Filter ",
209 NULL,
210 0,0,0,0
211 };
212
213 MENU video_menu={
214 " Video ",
215 NULL,
216 0,0,0,0
217 };
218
219 MENU fskip_menu={
220 " Frame control ",
221 NULL,
222 0,0,0,0
223 };
224
225 MENU main_menu={
226 " Main ",
227 NULL,
228 0,0,0,0
229 };
230
231 MENU *menu_list[]={
232 &main_menu,
233 &load_state_menu,
234 &save_state_menu,
235 &video_menu,
236 &fskip_menu,
237 NULL
238 };
239
new_menu_item(char * name,Uint8 type)240 MENU_ITEM *new_menu_item(char *name,Uint8 type)
241 {
242 MENU_ITEM *t;
243 t=(MENU_ITEM*)malloc(sizeof(MENU_ITEM));
244 memset(t,0,sizeof(MENU_ITEM));
245 t->name=strdup(name);
246 t->type=type;
247 t->next=NULL;
248 return t;
249 }
250
menu_push_back_item(MENU * m,MENU_ITEM * i)251 void menu_push_back_item(MENU *m,MENU_ITEM *i)
252 {
253 MENU_ITEM *t=m->item;
254 m->size++;
255 m->end=(m->size-1<MAX_ITEM?m->size-1:MAX_ITEM);
256 if (t==NULL) {m->item=i;return;}
257
258 while(t->next!=NULL)
259 t=t->next;
260 t->next=i;
261 }
262
draw_v_line(SDL_Surface * s,int x,int y,int l,Uint32 color)263 void draw_v_line(SDL_Surface *s,int x,int y,int l,Uint32 color)
264 {
265 SDL_Rect r;
266 r.x=x;
267 r.y=y;
268 r.w=1;
269 r.h=l;
270 SDL_FillRect(s,&r,color);
271 }
272
draw_h_line(SDL_Surface * s,int x,int y,int l,Uint32 color)273 void draw_h_line(SDL_Surface *s,int x,int y,int l,Uint32 color)
274 {
275 SDL_Rect r;
276 r.x=x;
277 r.y=y;
278 r.w=l;
279 r.h=1;
280 SDL_FillRect(s,&r,color);
281 }
282
283
draw_border(SDL_Surface * s,int type,SDL_Rect * r)284 void draw_border(SDL_Surface *s,int type,SDL_Rect *r)
285 {
286 SDL_FillRect(s,r,COL32_TO_16(0xd6d6d6));
287 switch(type) {
288 case SHADOW_OUT:
289 draw_h_line(s,r->x,r->y,r->w,COL32_TO_16(0xFFFFFF));
290 draw_h_line(s,r->x+1,r->y+1,r->w-1,COL32_TO_16(0xd6d6d6));
291 draw_h_line(s,r->x,r->y+r->h,r->w+1,COL32_TO_16(0x000000));
292 draw_h_line(s,r->x+2,r->y+r->h-1,r->w-2,COL32_TO_16(0x969696));
293
294 draw_v_line(s,r->x,r->y,r->h,COL32_TO_16(0xFFFFFF));
295 draw_v_line(s,r->x+1,r->y+1,r->h-1,COL32_TO_16(0xd6d6d6));
296 draw_v_line(s,r->x+r->w,r->y,r->h,COL32_TO_16(0x000000));
297 draw_v_line(s,r->x+r->w-1,r->y+1,r->h-1,COL32_TO_16(0x969696));
298 break;
299 case SHADOW_ETCHED_IN:
300 draw_h_line(s,r->x,r->y,r->w,COL32_TO_16(0x969696));
301 draw_h_line(s,r->x+1,r->y+1,r->w-1,COL32_TO_16(0xFFFFFF));
302 draw_h_line(s,r->x,r->y+r->h-1,r->w-1,COL32_TO_16(0x969696));
303 draw_h_line(s,r->x,r->y+r->h,r->w,COL32_TO_16(0xFFFFFF));
304
305 draw_v_line(s,r->x,r->y,r->h-1,COL32_TO_16(0x969696));
306 draw_v_line(s,r->x+1,r->y+1,r->h-2,COL32_TO_16(0xFFFFFF));
307 draw_v_line(s,r->x+r->w-1,r->y+2,r->h-2,COL32_TO_16(0x969696));
308 draw_v_line(s,r->x+r->w,r->y+1,r->h,COL32_TO_16(0xFFFFFF));
309 break;
310 default:
311 break;
312 }
313 }
314
display_menu(MENU * m)315 void display_menu(MENU *m)
316 {
317 SDL_Rect b;
318 int i;
319 MENU_ITEM *item=m->item;
320
321
322 b.x=MENUX-2;
323 b.y=MENUY-2;
324 b.w=wl*21+3;
325 b.h=hl*10+3;
326 draw_border(back,SHADOW_OUT,&b);
327
328 b.x=MENUX;
329 b.y=MENUY+hl*2-hl/2;
330 b.w=wl*21-2;
331 b.h=hl*8;
332 draw_border(back,SHADOW_ETCHED_IN,&b);
333
334
335 if (!item) return;
336
337 for(i=0;i<m->begin;i++)
338 item=item->next;
339
340 draw_message(MENUX,MENUY,m->title);
341
342 for(i=m->begin;i<=m->end;i++) {
343
344 if (i==m->id) {
345 b.x=MENUX+3;
346 b.y=MENUY+(i-m->begin+2)*hl;
347 b.w=wl*21-8;
348 b.h=hl;
349 SDL_FillRect(back,&b,COL32_TO_16(0xeaeaea));
350 if (item->draw_info && item->draw_type==DRAW_WHEN_ACTIVE)
351 item->draw_info(item,i-m->begin+2);
352 }
353 if (item->type==TOGGLE) {
354 b.x=135;
355 b.y=MENUY+(i-m->begin+2)*hl+2;
356 SDL_BlitSurface((item->state?toggle_on:toggle_off),NULL,back,&b);
357 }
358 if (item->type==RADIO) {
359 b.x=135;
360 b.y=MENUY+(i-m->begin+2)*hl+2;
361 SDL_BlitSurface((item->radio==radio_group[item->group]?radio_on:radio_off),NULL,back,&b);
362 }
363 draw_message(MENUX+wl,MENUY+(i-m->begin+2)*hl,item->name);
364 if (item->draw_info && item->draw_type==DRAW_ALWAYS)
365 item->draw_info(item,i-m->begin+2);
366 item=item->next;
367 }
368 if (m->end!=m->size-1) {
369 b.x=75;
370 b.y=MENUY+(MAX_ITEM+1)*hl+hl/2+24;
371 b.w=b.h=9;
372 SDL_BlitSurface(arrow_down,NULL,back,&b);
373 }
374 if (m->begin!=0) {
375 b.x=75;
376 b.y=22;
377 SDL_BlitSurface(arrow_up,NULL,back,&b);
378 }
379
380
381 }
382
init_menu(void)383 void init_menu(void){
384 int i=0;
385 MENU_ITEM *item;
386 char tmpbuf[255];
387
388 /* init all the icon */
389
390 arrow_down=img2surface(img_arrow_down);
391 arrow_up=img2surface(img_arrow_up);
392 radio_on=img2surface(img_radio_on);
393 radio_off=img2surface(img_radio_off);
394 toggle_on=img2surface(img_toggle_on);
395 toggle_off=img2surface(img_toggle_off);
396
397 SDL_SetColors(arrow_down,buttonpal,0,5);
398 SDL_SetColors(arrow_up,buttonpal,0,5);
399 SDL_SetColors(radio_off,buttonpal,0,5);
400 SDL_SetColors(radio_on,buttonpal,0,5);
401 SDL_SetColors(toggle_off,buttonpal,0,5);
402 SDL_SetColors(toggle_on,buttonpal,0,5);
403 SDL_SetColorKey(radio_off,SDL_SRCCOLORKEY|SDL_RLEACCEL,5);
404 SDL_SetColorKey(radio_on,SDL_SRCCOLORKEY|SDL_RLEACCEL,5);
405
406 /* Main Menu creation */
407
408 /* load state */
409 item=new_menu_item("Load state ...",ACTION);
410 item->user_data=(void*)&load_state_menu;
411 item->func=action_loop_menu;
412 menu_push_back_item(&main_menu,item);
413
414 /*save state */
415 item=new_menu_item("Save state ...",ACTION);
416 item->user_data=(void*)&save_state_menu;
417 item->func=action_loop_menu;
418 menu_push_back_item(&main_menu,item);
419
420 /*video */
421 item=new_menu_item("Video ...",ACTION);
422 item->user_data=(void*)&video_menu;
423 item->func=action_loop_menu;
424 menu_push_back_item(&main_menu,item);
425
426 /*Framskip&co */
427 item=new_menu_item("Framskip ...",ACTION);
428 item->user_data=(void*)&fskip_menu;
429 item->func=action_loop_menu;
430 menu_push_back_item(&main_menu,item);
431
432 item=new_menu_item("Sound",TOGGLE);
433 item->state=conf.sound;
434 item->func=toggle_sound;
435 menu_push_back_item(&main_menu,item);
436
437 item=new_menu_item("Reset",ACTION);
438 item->func=action_reset;
439 menu_push_back_item(&main_menu,item);
440
441 /* Load state */
442 for (i=0;i<8;i++) {
443 snprintf(tmpbuf,254,"Slot %d",i+1);
444 item=new_menu_item(tmpbuf,ACTION);
445 item->func=action_load_state;
446 item->draw_info=menu_draw_state;
447 item->draw_type=DRAW_WHEN_ACTIVE;
448 item->user_data=(void*)i;
449 menu_push_back_item(&load_state_menu,item);
450 }
451
452 /* Save state */
453 for (i=0;i<8;i++) {
454 snprintf(tmpbuf,254,"Slot %d",i+1);
455 item=new_menu_item(tmpbuf,ACTION);
456 item->func=action_save_state;
457 item->draw_info=menu_draw_state;
458 item->draw_type=DRAW_WHEN_ACTIVE;
459 item->user_data=(void*)i;
460 menu_push_back_item(&save_state_menu,item);
461 }
462
463 /* Filter Menu */
464 item=new_menu_item("None",RADIO);
465 item->group=1;
466 item->radio=0;
467 item->user_data=(void*)0;
468 item->func=action_set_filter;
469 item->draw_type=DRAW_ALWAYS;
470 menu_push_back_item(&filter_menu,item);
471 for (i=1;i<=5;i++) {
472 snprintf(tmpbuf,254,"Filter %d",i);
473 item=new_menu_item(tmpbuf,RADIO);
474 item->group=1;
475 item->radio=i;
476 item->user_data=(void*)i;
477 item->func=action_set_filter;
478 item->draw_type=DRAW_ALWAYS;
479 menu_push_back_item(&filter_menu,item);
480 }
481
482 /* video menu */
483 item=new_menu_item("Fullscreen",TOGGLE);
484 item->state=conf.fs;
485 item->func=toggle_fullscreen;
486 menu_push_back_item(&video_menu,item);
487
488 if (conf.gb_type&COLOR_GAMEBOY) {
489 item=new_menu_item("Color filter",TOGGLE);
490 item->state=conf.color_filter;
491 item->func=toggle_filter;
492 menu_push_back_item(&video_menu,item);
493 } else if (conf.gb_type&NORMAL_GAMEBOY) {
494 for (i=0;i<5;i++) {
495 snprintf(tmpbuf,254,"Palette %d",i+1);
496 item=new_menu_item(tmpbuf,RADIO);
497 item->group=0;
498 item->radio=i;
499 item->user_data=(void*)i;
500 item->func=action_setpal;
501 item->draw_type=DRAW_ALWAYS;
502 item->draw_info=menu_draw_pal;
503 menu_push_back_item(&video_menu,item);
504 }
505 }
506
507 item=new_menu_item("Filter ...",ACTION);
508 item->user_data=(void*)&filter_menu;
509 item->func=action_loop_menu;
510 menu_push_back_item(&video_menu,item);
511
512 /* frameskip menu */
513 item=new_menu_item("Auto frameskip",TOGGLE);
514 item->state=conf.autoframeskip;
515 item->func=toggle_autofskip;
516 menu_push_back_item(&fskip_menu,item);
517
518 item=new_menu_item("Sleep idle",TOGGLE);
519 item->state=conf.sleep_idle;
520 item->func=toggle_sleepidle;
521 menu_push_back_item(&fskip_menu,item);
522
523 item=new_menu_item("Show FPS",TOGGLE);
524 item->state=conf.show_fps;
525 item->func=toggle_fps;
526 menu_push_back_item(&fskip_menu,item);
527
528 }
529
loop_menu(MENU * m)530 void loop_menu(MENU *m)
531 {
532 SDL_Event event;
533 static int init=1;
534 int done=0,i;
535 MENU_ITEM *item;
536 stop_all=0;
537 if (init) {
538 init_menu();
539 init=0;
540 }
541
542 current_menu=m;
543 SDL_SetColorKey(fontbuf,SDL_SRCCOLORKEY|SDL_RLEACCEL,0);
544
545
546 while(!done && !stop_all) {
547 blit_screen();
548 SDL_WaitEvent(&event);
549 switch (event.type) {
550 #if defined(SDL_YUV) || defined(SDL_GL)
551 case SDL_VIDEORESIZE:
552 if (conf.yuv || conf.gl) {
553 conf.res_w=event.resize.w;
554 conf.res_h=event.resize.h;
555 reinit_vram();
556 blit_screen();
557 }
558 break;
559 #endif
560 case SDL_KEYDOWN:
561 switch(event.key.keysym.sym) {
562 case SDLK_ESCAPE:
563 done=1;
564 break;
565 case SDLK_UP:
566 if (m->id>0) m->id--;
567 if (m->id<m->begin) {
568 m->begin--;
569 m->end--;
570 }
571 break;
572 case SDLK_DOWN:
573 if (m->id<m->size-1) m->id++;
574 if (m->id>m->end) {
575 m->end++;
576 m->begin++;
577 }
578 break;
579 case SDLK_RETURN:
580 item=m->item;
581 for(i=0;i<m->id;i++)
582 item=item->next;
583 if (item->type==TOGGLE)
584 item->state^=1;
585 if (item->type==RADIO)
586 radio_group[item->group]=item->radio;
587 if (item->func) {
588 done=item->func(item);
589 if (done==2) stop_all=1;
590 current_menu=m; /* if func call loop_menu, current_menu is trashed */
591 }
592 break;
593 default:
594 break;
595 }
596 }
597
598 }
599 current_menu=NULL;
600 }
601