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