1 /*
2 * IceBreaker
3 * Copyright (c) 2002 Matthew Miller <mattdm@mattdm.org>
4 *
5 * <http://www.mattdm.org/icebreaker/>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc., 59
19 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22 
23 #include <SDL.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include "icebreaker.h"
31 #include "globals.h"
32 #include "options.h"
33 #include "sound.h"
34 #include "themes.h"
35 
36 ThemeColorsType color;
37 SDL_Surface* spriteimage=NULL;
38 SDL_Surface* spritemirrorimage=NULL;
39 
40 static int setrandomtheme();
41 static void setdefaultcolors();
42 static SDL_Surface* loadsprite(char* themename, char* imagefile);
43 
44 static int alphasortstringarray(const void *a, const void *b);
45 
getthemenames(char *** themenamelist)46 int getthemenames(char*** themenamelist)
47 {
48 	int s;
49 	int numthemes;
50 	int listsize=8;
51 	char **listtemp;
52 
53 	struct dirent* dirtemp;
54 	DIR* themedir;
55 
56 	*themenamelist=malloc(sizeof(char*)*listsize);
57 	if (themenamelist==NULL)
58 	{
59 		fprintf(stderr,"Error: Couldn't get memory to get list of themes. That's bad.\n");
60 		return -2;
61 	}
62 
63 	themedir=opendir(DATAPREFIX);
64 	if (themedir==NULL)
65 	{
66 		fprintf(stderr, "Couldn't list data directory. This is going to be a problem.\n");
67 		return -1;
68 	}
69 
70 
71 	numthemes=0;
72 	while ((dirtemp = readdir(themedir)) != NULL)
73 	{
74 		if (strlen(dirtemp->d_name) <= strlen(THEMEFILEEXTENSION))
75 		{
76 			// filename too short
77 
78 			// this space intentionally left blank
79 		}
80 		else if (strcasecmp( THEMEFILEEXTENSION, dirtemp->d_name+(strlen(dirtemp->d_name)-strlen(THEMEFILEEXTENSION)) ))
81 		{
82 			// wrong extension
83 
84 			// this space intentionally left blank
85 		}
86 		else if (strlen(dirtemp->d_name)-strlen(THEMEFILEEXTENSION)>MAXTHEMENAMELENGTH)
87 		{
88 			fprintf(stderr, "Theme name %s is too long.\n"
89 			                "For display reasons, the maximum allowed is %d + %s.\n",dirtemp->d_name,MAXTHEMENAMELENGTH,THEMEFILEEXTENSION);
90 		}
91 		else
92 		{ // Valid theme name found
93 
94 			if (numthemes>=listsize)
95 			{
96 				// trying to put numthemes+1 themes in a listsize-sized bucket
97 				// so double the size of the bucket.
98 				listsize *= 2;
99 				listtemp = (char **) realloc (*themenamelist,sizeof(char*)*listsize);
100 				if (listtemp == NULL)
101 				{
102 					fprintf(stderr, "Warning: No more memory for theme names.\n");
103 					break;
104 				}
105 				*themenamelist = listtemp;
106 
107 			}
108 
109 			s=strlen(dirtemp->d_name)-strlen(THEMEFILEEXTENSION)+1;
110 			(*themenamelist)[numthemes]=malloc(s);
111 			if ((*themenamelist)[numthemes]==NULL)
112 			{
113 				fprintf(stderr, "Error: couldn't get memory for theme name entry '%s' (#%d).\n"
114 				                "Trying to continue with what we've got.\n",dirtemp->d_name,numthemes);
115 			}
116 			else
117 			{
118 				snprintf((*themenamelist)[numthemes],s,"%s",dirtemp->d_name);
119 				numthemes++;
120 			}
121 		}
122 	}
123 
124 	closedir(themedir);
125 
126 	qsort(*themenamelist,numthemes,sizeof(**themenamelist),alphasortstringarray);
127 
128 	return numthemes;
129 }
130 
freethemenames(char *** themenamelist,int numthemes)131 void freethemenames(char*** themenamelist,int numthemes)
132 {
133 	int i;
134 	for (i=0;i<numthemes;i++)
135 	{
136 		free((*themenamelist)[i]);
137 	}
138 	if (numthemes>0) free(*themenamelist);
139 }
140 
settheme(char * themename)141 int settheme(char* themename)
142 {
143 	int rc=0;
144 	int i;
145 	FILE* themefile;
146 	char linebuf[300];
147 	char themefilename[256]; // fix -- use defined OS constant (here and throughout this function)
148 	char optbuf[21];
149 	char valbuf[256];
150 	char loadfilebuf[256];
151 	int r,g,b;
152 	ThemeColorsType foundcolor;
153 
154 	if (!strcmp(themename,"random"))
155 	{
156 		return setrandomtheme(); // whooo recursion!
157 	}
158 
159 	setdefaultcolors();
160 	if (spritemirrorimage==spriteimage) spritemirrorimage=NULL; // don't want to double free!
161 	if (spriteimage!=NULL)
162 	{
163 		SDL_FreeSurface(spriteimage);
164 		spriteimage=NULL;
165 	}
166 	if (spritemirrorimage!=NULL)
167 	{
168 		SDL_FreeSurface(spritemirrorimage);
169 		spritemirrorimage=NULL;
170 	}
171 
172 	foundcolor.background      = 0;
173 	foundcolor.normaltext      = 0;
174 	foundcolor.gridline        = 0;
175 	foundcolor.gridhighlight   = 0;
176 	foundcolor.line1           = 0;
177 	foundcolor.line2           = 0;
178 	foundcolor.menuhighlight   = 0;
179 	foundcolor.gameovertext    = 0;
180 	foundcolor.scorescrolltext = 0;
181 	foundcolor.bonusscrolltext = 0;
182 	foundcolor.textentrybox    = 0;
183 	foundcolor.textentrytext   = 0;
184 	foundcolor.copyrighttext   = 0;
185 
186 	foundcolor.boardfillminr = 0;
187  	foundcolor.boardfillming = 0;
188 	foundcolor.boardfillminb = 0;
189 	foundcolor.boardfillmaxr = 0;
190 	foundcolor.boardfillmaxg = 0;
191 	foundcolor.boardfillmaxb = 0;
192 
193 	foundcolor.spritetransparent = 0;
194 
195 	snprintf(themefilename, sizeof(themefilename),"%s/%s%s",DATAPREFIX,themename,THEMEFILEEXTENSION);
196 
197 	themefile=fopen(themefilename,"r");
198 	if (themefile==NULL)
199 	{
200 		// FIX -- ??? question: should this actually change the
201 		// current setting, or is leaving it at the currently-unavailable
202 		// value the right thing to do?
203 		fprintf(stderr, "Warning: Can't open theme file %s.\n"
204 		                "Using default look and sound.\n",themefilename);
205 		rc=-18;
206 	}
207 	else
208 	{
209 		while(fgets(linebuf,300,themefile))
210 		{
211 			for (i=0;i<300;i++)
212 			{
213 				if (linebuf[i]=='\0' || linebuf[i]==' ' || linebuf[i]=='\t') break;
214 				linebuf[i]=tolower(linebuf[i]);
215 			}
216 
217 			if (sscanf(linebuf,"%20s %5d %5d %5d",optbuf,&r,&g,&b)==4)
218 			{
219 				//printf("N: %s = %d,%d,%d\n",optbuf,r,g,b);
220 
221 				if (r<0 || r>255)
222 				{
223 					fprintf(stderr,"Warning: invalid red value for %s in theme %s\n",optbuf,themename);
224 					r=0;
225 				}
226 				if (g<0 || g>255)
227 				{
228 					fprintf(stderr,"Warning: invalid green value for %s in theme %s\n",optbuf,themename);
229 					g=0;
230 				}
231 				if (b<0 || b>255)
232 				{
233 					fprintf(stderr,"Warning: invalid blue value for %s in theme %s\n",optbuf,themename);
234 					b=0;
235 				}
236 
237 				if (!strcmp(optbuf,"background"))
238 				{
239 					color.background=SDL_MapRGB(screen->format,r,g,b);
240 					foundcolor.background++;
241 				}
242 				else if (!strcmp(optbuf,"normaltext"))
243 				{
244 					color.normaltext=SDL_MapRGB(screen->format,r,g,b);
245 					foundcolor.normaltext++;
246 				}
247 				else if (!strcmp(optbuf,"boardfillmin"))
248 				{
249 					color.boardfillminr=r;
250 					color.boardfillming=g;
251 					color.boardfillminb=b;
252 					foundcolor.boardfillminr++;
253 					foundcolor.boardfillming++;
254 					foundcolor.boardfillminb++;
255 
256 				}
257 				else if (!strcmp(optbuf,"boardfillmax"))
258 				{
259 					color.boardfillmaxr=r;
260 					color.boardfillmaxg=g;
261 					color.boardfillmaxb=b;
262 					foundcolor.boardfillmaxr++;
263 					foundcolor.boardfillmaxg++;
264 					foundcolor.boardfillmaxb++;
265 				}
266 				else if (!strcmp(optbuf,"gridline"))
267 				{
268 					color.gridline=SDL_MapRGB(screen->format,r,g,b);
269 					foundcolor.gridline++;
270 				}
271 				else if (!strcmp(optbuf,"gridhighlight"))
272 				{
273 					color.gridhighlight=SDL_MapRGB(screen->format,r,g,b);
274 					foundcolor.gridhighlight++;
275 				}
276 				else if (!strcmp(optbuf,"line1"))
277 				{
278 					color.line1=SDL_MapRGB(screen->format,r,g,b);
279 					foundcolor.line1++;
280 				}
281 				else if (!strcmp(optbuf,"line2"))
282 				{
283 					color.line2=SDL_MapRGB(screen->format,r,g,b);
284 					foundcolor.line2++;
285 				}
286 				else if (!strcmp(optbuf,"menuhighlight"))
287 				{
288 					color.menuhighlight=SDL_MapRGB(screen->format,r,g,b);
289 					foundcolor.menuhighlight++;
290 
291 				}
292 				else if (!strcmp(optbuf,"gameovertext"))
293 				{
294 					color.gameovertext=SDL_MapRGB(screen->format,r,g,b);
295 					foundcolor.gameovertext++;
296 				}
297 				else if (!strcmp(optbuf,"scorescrolltext"))
298 				{
299 					color.scorescrolltext=SDL_MapRGB(screen->format,r,g,b);
300 					foundcolor.scorescrolltext++;
301 				}
302 				else if (!strcmp(optbuf,"bonusscrolltext"))
303 				{
304 					color.bonusscrolltext=SDL_MapRGB(screen->format,r,g,b);
305 					foundcolor.bonusscrolltext++;
306 				}
307 				else if (!strcmp(optbuf,"textentrybox"))
308 				{
309 					color.textentrybox=SDL_MapRGB(screen->format,r,g,b);
310 					foundcolor.textentrybox++;
311 
312 				}
313 				else if (!strcmp(optbuf,"textentrytext"))
314 				{
315 					color.textentrytext=SDL_MapRGB(screen->format,r,g,b);
316 					foundcolor.textentrytext++;
317 				}
318 				else if (!strcmp(optbuf,"copyrighttext"))
319 				{
320 					color.copyrighttext=SDL_MapRGB(screen->format,r,g,b);
321 					foundcolor.copyrighttext++;
322 
323 				}
324 				else if (!strcmp(optbuf,"spritetransparent"))
325 				{
326 					color.spritetransparent=SDL_MapRGB(screen->format,r,g,b);
327 					foundcolor.spritetransparent++;
328 				}
329 			}
330 			else if (sscanf(linebuf,"%20s %255s",optbuf,valbuf)==2)
331 			{
332 				//printf("S: %s = %s\n",optbuf,valbuf);
333 				if (!strcmp(optbuf,"spritebitmap"))
334 				{
335 					if (strcasecmp( ".bmp", valbuf+(strlen(valbuf)-strlen(".bmp")) ))
336 					{
337 						fprintf(stderr,"Warning: Sprite bitmap file in theme %s does not end with '.bmp'.\n"
338 						               "We'll try to load it anyway, but don't be surpised if there's a problem.\n"
339 						               "(Filename is %s)\n",themename,valbuf);
340 					}
341 					snprintf(loadfilebuf, sizeof(loadfilebuf),"%s/%s",DATAPREFIX,valbuf);
342 					spriteimage=loadsprite(themename,loadfilebuf);
343 				}
344 				else if (!strcmp(optbuf,"spritemirrorbitmap"))
345 				{
346 					if (strcasecmp( ".bmp", valbuf+(strlen(valbuf)-strlen(".bmp")) ))
347 					{
348 						fprintf(stderr,"Warning: Sprite bitmap file in theme %s does not end with '.bmp'.\n"
349 						               "We'll try to load it anyway, but don't be surpised if there's a problem.\n"
350 						               "(Filename is %s)\n",themename,valbuf);
351 					}
352 					snprintf(loadfilebuf, sizeof(loadfilebuf),"%s/%s",DATAPREFIX,valbuf);
353 					spritemirrorimage=loadsprite(themename,loadfilebuf);
354 				}
355 				else if (!strcmp(optbuf,"soundouch"))
356 				{
357 					if (strcasecmp( ".wav", valbuf+(strlen(valbuf)-strlen(".wav")) ))
358 					{
359 						fprintf(stderr,"Warning: Ouch sound file in theme %s does not end with '.wav'.\n"
360 						               "We'll try to load it anyway, but don't be surpised if there's a problem.\n"
361 						               "(Filename is %s)\n",themename,valbuf);
362 					}
363 					snprintf(loadfilebuf, sizeof(loadfilebuf),"%s/%s",DATAPREFIX,valbuf);
364 					loadsounds(themename,loadfilebuf,NULL);
365 				}
366 				else if (!strcmp(optbuf,"soundcrash"))
367 				{
368 					if (strcasecmp( ".wav", valbuf+(strlen(valbuf)-strlen(".wav")) ))
369 					{
370 						fprintf(stderr,"Warning: Crash sound file in theme %s does not end with '.wav'.\n"
371 						               "We'll try to load it anyway, but don't be surpised if there's a problem.\n"
372 						               "(Filename is %s)\n",themename,valbuf);
373 					}
374 					snprintf(loadfilebuf, 256,"%s/%s",DATAPREFIX,valbuf);
375 					loadsounds(themename,NULL,loadfilebuf);
376 				}
377 			}
378 
379 		}
380 		fclose(themefile);
381 	}
382 
383 
384 	if (foundcolor.background == 0)
385 		{ rc--; fprintf(stderr,"Warning: Background not correctly defined in theme %s. Using default.\n",themename); }
386 	if (foundcolor.normaltext == 0)
387 		{ rc--; fprintf(stderr,"Warning: NormalText not correctly defined in theme %s. Using default.\n",themename); }
388 	if (foundcolor.boardfillminr == 0)
389 		{ rc--; fprintf(stderr,"Warning: BoardFillMin not correctly defined in theme %s. Using default.\n",themename); }
390 	if (foundcolor.boardfillmaxr == 0)
391 		{ rc--; fprintf(stderr,"Warning: BoardFillMax not correctly defined in theme %s. Using default.\n",themename); }
392 	if (foundcolor.gridline == 0)
393 		{ rc--; fprintf(stderr,"Warning: Gridline not correctly defined in theme %s. Using default.\n",themename); }
394 	if (foundcolor.gridhighlight == 0)
395 		{ rc--; fprintf(stderr,"Warning: GridHighlight not correctly defined in theme %s. Using default.\n",themename); }
396 	if (foundcolor.line1 == 0)
397 		{ rc--; fprintf(stderr,"Warning: Line1 not correctly defined in theme %s. Using default.\n",themename); }
398 	if (foundcolor.line2 == 0)
399 		{ rc--; fprintf(stderr,"Warning: Line2 not correctly defined in theme %s. Using default.\n",themename); }
400 	if (foundcolor.menuhighlight == 0)
401 		{ rc--; fprintf(stderr,"Warning: MenuHighlight not correctly defined in theme %s. Using default.\n",themename); }
402 	if (foundcolor.gameovertext == 0)
403 		{ rc--; fprintf(stderr,"Warning: GameoverText not correctly defined in theme %s. Using default.\n",themename); }
404 	if (foundcolor.scorescrolltext == 0)
405 		{ rc--; fprintf(stderr,"Warning: ScorescrollText not correctly defined in theme %s. Using default.\n",themename); }
406 	if (foundcolor.bonusscrolltext == 0)
407 		{ rc--; fprintf(stderr,"Warning: BonusscrollText not correctly defined in theme %s. Using default.\n",themename); }
408 	if (foundcolor.textentrybox == 0)
409 		{ rc--; fprintf(stderr,"Warning: TextentryBox not correctly defined in theme %s. Using default.\n",themename); }
410 	if (foundcolor.textentrytext == 0)
411 		{ rc--; fprintf(stderr,"Warning: TextentryText not correctly defined in theme %s. Using default.\n",themename); }
412 	if (foundcolor.copyrighttext == 0)
413 		{ rc--; fprintf(stderr,"Warning: CopyrightText not correctly defined in theme %s. Using default.\n",themename); }
414 
415 	if (foundcolor.spritetransparent == 0)
416 	{
417 		rc--;
418 		fprintf(stderr,"Warning: SpriteTransparent not correctly defined in theme %s.\n",themename);
419 		color.spritetransparent=SDL_MapRGB(screen->format,255,  0,  0);
420 	}
421 
422 
423 	if (color.boardfillmaxr<color.boardfillminr)
424 	{
425 		color.boardfillmaxr=color.boardfillminr;
426 		fprintf(stderr,"Warning: max red value less than min value in theme %s\n",themename);
427 	}
428 	if (color.boardfillmaxg<color.boardfillming)
429 	{
430 		color.boardfillmaxg=color.boardfillming;
431 		fprintf(stderr,"Warning: max green value less than min value in theme %s\n",themename);
432 	}
433 	if (color.boardfillmaxb<color.boardfillminb)
434 	{
435 		color.boardfillmaxb=color.boardfillminb;
436 		fprintf(stderr,"Warning: max blue value less than min value in theme %s\n",themename);
437 	}
438 
439 
440 	if (spriteimage==NULL)
441 	{
442 		rc--;
443 		fprintf(stderr,"Warning: SpriteBitmap not correctly defined in theme %s. Using default.\n",themename);
444 		fprintf(stderr,"         Also using default SpriteTransparent.\n");
445 		color.spritetransparent=SDL_MapRGB(screen->format,255,  0,  0);
446 		spriteimage=loadsprite(themename,DATAPREFIX "/" PENGUINBMPFILE);
447 		if (spriteimage==NULL)
448 		{ // uh oh -- no sprite at all. fix -- do something?
449 			fprintf(stderr,"Error: couldn't load any sprite at all!!!\n");
450 			rc=-255;
451 		}
452 	}
453 
454 	if (spritemirrorimage==NULL)
455 	{
456 		// we don't warn about this -- we just use the
457 		// main sprite image and go on silently
458 		spritemirrorimage=spriteimage;
459 	}
460 
461 	return rc;
462 }
463 
getthemenumber(char ** themenamelist,int numthemes,char * themename)464 int getthemenumber(char** themenamelist,int numthemes,char* themename)
465 {
466 	int i;
467 	for (i=0;i<numthemes;i++)
468 	{
469 		if (!strcmp(themenamelist[i],themename))
470 			return i;
471 	}
472 	return -1;
473 }
474 
setrandomtheme()475 int setrandomtheme()
476 {
477 	char themename[MAXTHEMENAMELENGTH];
478 	char** themelist;
479 	int themecount;
480 
481 	themecount=getthemenames(&themelist);
482 	if (getthemenumber(themelist,themecount,"random")!=-1)
483 	{
484 		fprintf(stderr,"Hey! You can't have a theme named random! Please remove the random.ibt\n"
485 		        "file from your themes directory.\n");
486 		strcpy(themename,"linux");
487 	}
488 	else
489 	{
490 		snprintf(themename,sizeof(themename),"%s",themelist[random() %themecount]);
491 	}
492 	freethemenames(&themelist,themecount);
493 	return settheme(themename);
494 }
495 
setdefaultcolors()496 void setdefaultcolors()
497 {
498 	color.background      = SDL_MapRGB(screen->format,  0, 64,128);
499 	color.normaltext      = SDL_MapRGB(screen->format,255,255,255);
500 	color.gridline        = SDL_MapRGB(screen->format,192,192,192);
501 	color.gridhighlight   = SDL_MapRGB(screen->format,192,192,192);
502 	color.line1           = SDL_MapRGB(screen->format,  0,  0,  0);
503 	color.line2           = SDL_MapRGB(screen->format,192,  0, 64);
504 	color.menuhighlight   = SDL_MapRGB(screen->format,240,240,240);
505 	color.gameovertext    = SDL_MapRGB(screen->format,192,  0, 64);
506 	color.scorescrolltext = SDL_MapRGB(screen->format,  0,  0,  0);
507 	color.bonusscrolltext = SDL_MapRGB(screen->format,192,  0, 64);
508 	color.textentrybox    = SDL_MapRGB(screen->format,  0,  0,  0);
509 	color.textentrytext   = SDL_MapRGB(screen->format,255,255,255);
510 	color.copyrighttext   = SDL_MapRGB(screen->format,192,192,192);
511 
512 	color.boardfillminr = 224;
513  	color.boardfillming = 224;
514 	color.boardfillminb = 224;
515 	color.boardfillmaxr = 255;
516 	color.boardfillmaxg = 255;
517 	color.boardfillmaxb = 255;
518 }
519 
loadsprite(char * themename,char * imagefile)520 SDL_Surface* loadsprite(char* themename, char* imagefile)
521 {
522 	SDL_Surface* imageloadtemp1;
523 	SDL_Surface* imageloadtemp2;
524 
525 	imageloadtemp1 = SDL_LoadBMP(imagefile);
526 	if (imageloadtemp1==NULL)
527 	{
528 		fprintf(stderr, "Couldn't load image %s.\n"
529 		                "SDL error: "
530 		                "%s\n\n", imagefile, SDL_GetError());
531 		return NULL;
532 	}
533 
534 	if (imageloadtemp1->w > BLOCKWIDTH || imageloadtemp1->h > BLOCKHEIGHT)
535 	{
536 		fprintf(stderr, "Error: Image file %s too large!\n", imagefile);
537 		return NULL;
538 	}
539 	if (imageloadtemp1->w < BLOCKWIDTH-2 || imageloadtemp1->h < BLOCKHEIGHT-2)
540 	{
541 		fprintf(stderr, "Error: Image file %s too small!\n", imagefile);
542 		return NULL;
543 	}
544 
545 	imageloadtemp2 = SDL_DisplayFormat(imageloadtemp1);
546 	if (imageloadtemp2==NULL)
547 	{
548 		fprintf(stderr, "Warning: couldn't convert %s to screen format.\n"
549 		                "Perhaps we ran out of memory? We'll live, but there will be a performance hit.\n",
550 		                imagefile);
551 		imageloadtemp2 = imageloadtemp1;
552 	}
553 	else
554 	{
555 		SDL_FreeSurface(imageloadtemp1);
556 	}
557 
558 	if (SDL_SetColorKey(imageloadtemp2, SDL_SRCCOLORKEY, color.spritetransparent))
559 	{
560 		if (themename!=NULL)
561 		{
562 			fprintf(stderr, "Warning: couldn't set colorkey for %s.\n"
563 			                "This is probably a problem in the '%s' theme.\n",
564 			                 imagefile,themename);
565 			return NULL;
566 		}
567 		else
568 		{
569 			fprintf(stderr, "Warning: couldn't set colorkey for %s.\n"
570 			                "Since that's the default image, this probably means there's a serious problem.\n",
571 			                 imagefile);
572 			return NULL;
573 		}
574 	}
575 
576 	return imageloadtemp2;
577 }
578 
alphasortstringarray(const void * a,const void * b)579 static int alphasortstringarray(const void *a, const void *b)
580 {
581 	return strcoll ( *(const char **)a, *(const char **)b);
582 }
583