1 /*
2 * IceBreaker
3 * Copyright (c) 2001-2002 Matthew Miller <mattdm@mattdm.org> and
4 *   Enrico Tassi <gareuselesinge@infinito.it>
5 *
6 * <http://www.mattdm.org/icebreaker/>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc., 59
20 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23 
24 #include <SDL.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include "icebreaker.h"
30 #include "globals.h"
31 #include "icebreaker.h"
32 #include "options.h"
33 #include "themes.h"
34 #include "stdarg.h"
35 
36 GameOptionsType options;
37 GameOptionsType commandline;
38 
39 GameFlagsType gameflags;
40 
41 static int findflag(int argc,char** argv,char* option);
42 static void printversion(void);
43 static void printhelp(void);
44 static void printthemelist(void);
45 
46 #ifdef WIN32
47 #  include <windows.h>
48 typedef struct
49 {
50 	HKEY hK;
51 	DWORD line;
52 }reg_file;
53 #  define ICEBREAKER_KEY "Software\\IceBreaker"
54 #  define OPTFILEHANDLER reg_file
55 #else
56 #  define OPTFILEHANDLER FILE
57 #endif
58 
59 
60 OPTFILEHANDLER *openoptionfile(char* filename,char* mode);
61 void closeoptionfile(OPTFILEHANDLER *f);
62 char* readoptionline(char* buffer,int len,OPTFILEHANDLER *f);
63 int writeoptionline(OPTFILEHANDLER *f,char* format,...);
64 
65 #define FLAGVERSION        "-v"
66 #define FLAGVERSIONLONG    "--version"
67 #define FLAGHELP           "-h"
68 #define FLAGHELPLONG       "--help"
69 #define FLAGSOUND          "-s"
70 #define FLAGSOUNDLONG      "--sound"
71 #define FLAGNOSOUND        "-n"
72 #define FLAGNOSOUNDLONG    "--nosound"
73 #define FLAGFULLSCREEN     "-f"
74 #define FLAGFULLSCREENLONG "--fullscreen"
75 #define FLAGWINDOWED       "-w"
76 #define FLAGWINDOWEDLONG   "--windowed"
77 #define FLAGTHEME          "-t"
78 #define FLAGTHEMELONG      "--theme"
79 #define FLAGLISTTHEMES     "-l"
80 #define FLAGLISTTHEMESLONG "--listthemes"
81 #define FLAGBENCHMARK      "-b"
82 #define FLAGBENCHMARKLONG  "--benchmark"
83 
setdefaultoptions(void)84 void setdefaultoptions(void)
85 {
86 	options.sound=SOUNDON;
87 	options.autopause=AUTOPAUSEOFF;
88 	options.difficulty=NORMAL;
89 	options.fullscreen=FULLSCREENOFF;
90 	strcpy(options.theme,"linux");
91 }
92 
readoptions(void)93 int readoptions(void)
94 {
95 	OPTFILEHANDLER * optionfile;
96 	char linebuf[50];
97 	char filename[255]; // fix -- use defined OS constant
98 	char optbuf[21];
99 	char valbuf[10+MAXTHEMENAMELENGTH];
100 	char scanformat[20];
101 	int i;
102 
103 	setdefaultoptions();
104 
105 	snprintf(filename,sizeof(filename),"%s/%s",homedir,OPTIONFILE);
106 
107 	optionfile=openoptionfile(filename,"r");
108 	if (optionfile==NULL)
109 	{
110 		fprintf(stderr, OPTIONFILE " doesn't exist.\nWelcome to IceBreaker.\n");
111 		return true;
112 	}
113 
114 	while(readoptionline(linebuf,50,optionfile))
115 	{
116 		for (i=0;i<50;i++)
117 		{
118 			if (linebuf[i]=='\0') break;
119 			linebuf[i]=tolower(linebuf[i]);
120 		}
121 
122 		sprintf(scanformat,"%%20s %%%ds",10+MAXTHEMENAMELENGTH);
123 		if (sscanf(linebuf,"%19s %9s",optbuf,valbuf)==2)
124 		{
125 			if (!strcmp(optbuf,"sound"))
126 			{
127 				if (!strcmp(valbuf,"on"))
128 					options.sound=SOUNDON;
129 				else if (!strcmp(valbuf,"off"))
130 					options.sound=SOUNDOFF;
131 			}
132 			else if (!strcmp(optbuf,"autopause"))
133 			{
134 				if (!strcmp(valbuf,"on"))
135 					options.autopause=AUTOPAUSEON;
136 				else if (!strcmp(valbuf,"off"))
137 					options.autopause=AUTOPAUSEOFF;
138 			}
139 			else if (!strcmp(optbuf,"fullscreen"))
140 			{
141 				if (!strcmp(valbuf,"off"))
142 					options.fullscreen=FULLSCREENOFF;
143 				else if (!strcmp(valbuf,"on"))
144 					options.fullscreen=FULLSCREENON;
145 				else if (!strcmp(valbuf,"always"))
146 					options.fullscreen=FULLSCREENALWAYS;
147 			}
148 			else if (!strcmp(optbuf,"difficulty"))
149 			{
150 				if (!strcmp(valbuf,"normal"))
151 					options.difficulty=NORMAL;
152 				else if (!strcmp(valbuf,"hard"))
153 					options.difficulty=HARD;
154 				else if (!strcmp(valbuf,"easy"))
155 					options.difficulty=EASY;
156 			}
157 			else if (!strcmp(optbuf,"theme"))
158 			{
159 				snprintf(options.theme,sizeof(options.theme),"%s",valbuf);
160 			}
161 			// FIX: add username
162 		}
163 	}
164 
165 	closeoptionfile(optionfile);
166 
167 	return false;
168 }
169 
writeoptions(void)170 int writeoptions(void)
171 {
172 	OPTFILEHANDLER * optionfile;
173 	char filename[255];
174 	snprintf(filename,sizeof(filename),"%s/%s",homedir,OPTIONFILE);
175 
176 	optionfile=openoptionfile(filename,"w");
177 	if (optionfile==NULL)
178 	{
179 		fprintf(stderr, "Can't write to " OPTIONFILE ".\n");
180 		return true;
181 	}
182 
183 	writeoptionline(optionfile,"# Icebreaker config file 1.0\n#\n");
184 	writeoptionline(optionfile,"# Separate keywords from values by whitespace. Not case sensitive.\n#\n");
185 	writeoptionline(optionfile,"# %s/" OPTIONFILE " will be overwritten automatically.\n#\n",homedir);
186 
187 	writeoptionline(optionfile,"\n# Change this if the crashing noise annoys your neighbors.\n");
188 	if (options.sound==SOUNDON)
189 		writeoptionline(optionfile,"Sound On\n");
190 	else if (options.sound==SOUNDOFF)
191 		writeoptionline(optionfile,"Sound Off\n");
192 
193 	writeoptionline(optionfile,"\n# AutoPause makes the game pause when the window is out of focus.\n");
194 	if (options.autopause==AUTOPAUSEON)
195 		writeoptionline(optionfile,"AutoPause On\n");
196 	else if (options.autopause==AUTOPAUSEOFF)
197 		writeoptionline(optionfile,"AutoPause Off\n");
198 
199 	writeoptionline(optionfile,"\n# Set FullScreen to Always if you want it that way every time.\n");
200 	writeoptionline(optionfile,"# On will use full screen mode once, but then change back to Off.\n");
201 	if (options.fullscreen==FULLSCREENOFF || options.fullscreen==FULLSCREENON)
202 		writeoptionline(optionfile,"FullScreen Off\n");
203 	else if (options.fullscreen==FULLSCREENALWAYS)
204 		writeoptionline(optionfile,"FullScreen Always\n");
205 
206 	writeoptionline(optionfile,"\n# Normal is the best way to play. Easy is okay to get started,\n");
207 	writeoptionline(optionfile,"# but you won't get very high scores. Hard is for those who really\n");
208 	writeoptionline(optionfile,"# want a challenge, but scores only slightly higher than normal.\n");
209 
210 
211 	if (options.difficulty==NORMAL)
212 		writeoptionline(optionfile,"Difficulty Normal\n");
213 	else if (options.difficulty==HARD)
214 		writeoptionline(optionfile,"Difficulty Hard\n");
215 	else if (options.difficulty==EASY)
216 		writeoptionline(optionfile,"Difficulty Easy\n");
217 
218 
219 	writeoptionline(optionfile,"\n# Themes provide an easy way to select (or tweak!) the appearance\n");
220 	writeoptionline(optionfile,"# and sound of the game. Select from " THEMEFILEEXTENSION " files in the game data\n");
221 	writeoptionline(optionfile,"# directory. Check the docs for info on creating your own themes, too.\n");
222 
223 	writeoptionline(optionfile,"Theme %s\n",options.theme);
224 
225 	closeoptionfile(optionfile);
226 
227 	return false;
228 }
229 
parsecommandline(int argc,char ** argv)230 int parsecommandline(int argc, char** argv)
231 {
232 	int i;
233 
234 	commandline.sound=SOUNDON;
235 	commandline.autopause=AUTOPAUSEOFF;
236 	commandline.difficulty=NORMAL;
237 	commandline.fullscreen=FULLSCREENUNKNOWN;
238 	*(commandline.theme)='\0'; // makes this ""
239 
240 	if ( findflag(argc,argv,FLAGVERSION) || findflag(argc,argv,FLAGVERSIONLONG) )
241 	{
242 		printversion();
243 		return 1;
244 	}
245 
246 	if ( findflag(argc,argv,FLAGHELP) || findflag(argc,argv,FLAGHELPLONG) )
247 	{
248 		printhelp();
249 		return 2;
250 	}
251 
252 	if ( findflag(argc,argv,FLAGLISTTHEMES) || findflag(argc,argv,FLAGLISTTHEMESLONG) )
253 	{
254 		printthemelist();
255 		return 3;
256 	}
257 
258 	if ( findflag(argc,argv,FLAGNOSOUND) || findflag(argc,argv,FLAGNOSOUNDLONG) )
259 		commandline.sound=SOUNDOFF;
260 
261 	if ( findflag(argc,argv,FLAGBENCHMARK) || findflag(argc,argv,FLAGBENCHMARKLONG) )
262 		gameflags.benchmarkmode=true;
263 	else
264 		gameflags.benchmarkmode =false;
265 
266 
267 	if ( findflag(argc,argv,FLAGSOUND) || findflag(argc,argv,FLAGSOUNDLONG) )
268 	{
269 		if (commandline.sound==SOUNDOFF)
270 		{
271 			fprintf(stderr,"You asked for sound to be both on and off. Sorry Schrodinger, we can't do that.\n"
272 			               "We'll assume \'off\' is what you really meant.\n");
273 		}
274 		else
275 		{
276 			commandline.sound=SOUNDON;  // redundant. but more clear. :)
277 		}
278 	}
279 
280 
281 	if ( findflag(argc,argv,FLAGFULLSCREEN) || findflag(argc,argv,FLAGFULLSCREENLONG) )
282 		commandline.fullscreen=FULLSCREENON;
283 
284 	if ( findflag(argc,argv,FLAGWINDOWED) || findflag(argc,argv,FLAGWINDOWEDLONG) )
285 	{
286 		if (commandline.fullscreen==FULLSCREENON)
287 		{
288 			fprintf(stderr,"You asked for both fullscreen and windowed mode. You're mad! Mad, I tell you.\n"
289 			               "We'll assume \'windowed\' is what you really meant.\n");
290 		}
291 		commandline.fullscreen=FULLSCREENOFF;
292 	}
293 
294 	// find theme name -- can't use findflag because this option
295 	// takes a parameter and that function isn't that smart.
296 	for(i=1; i<argc; i++)
297 	{
298 		if (strncmp(argv[i],FLAGTHEME,strlen(FLAGTHEME)) == 0)
299 		{
300 			if (strlen(argv[i])==strlen(FLAGTHEME))
301 			{
302 				fprintf(stderr,"Warning: " FLAGTHEME " option given but no theme specified. Perhaps this is because you\n"
303 				               " wrote \"" FLAGTHEME " themename\" or \"" FLAGTHEME "=themename\". Sorry, I'm too dumb to understand\n"
304 		               	               " that -- you'll have to use the form " FLAGTHEME "themename or " FLAGTHEMELONG "=themename. As it\n"
305 	               		               " is, I'm just going to ignore you.\n");
306 			}
307 			else if (strlen(argv[i]) > MAXTHEMENAMELENGTH+strlen(FLAGTHEME))
308 			{
309 				fprintf(stderr,"Warning: the theme name you've given is too long. The maximum is %d characters.\n"
310 	               		               " Not because I don't understand long filenames, by the way -- it's so the\n"
311 				               " options menu looks nice. I'm very fussy about my appearance.\n",MAXTHEMENAMELENGTH);
312 			}
313 			else
314 			{
315 				// fix -- we should probably search for malicious characters here.
316 				snprintf(commandline.theme,sizeof(commandline.theme),"%s",argv[i]+strlen(FLAGTHEME));
317 			}
318 		}
319 		else if (strncmp(argv[i],FLAGTHEMELONG "=" ,strlen(FLAGTHEMELONG "=")) == 0)
320 		{
321 			if (strlen(argv[i])==strlen(FLAGTHEMELONG "="))
322 			{
323 				fprintf(stderr,"Warning: " FLAGTHEMELONG "= what exactly? The theme name has to go right after the equals\n"
324 				               " sign or else I will ignore you. Which is what I'm doing now. La la la la la la\n"
325 		               	               " la la la I can't hear you la la la....\n");
326 			}
327 			else if (strlen(argv[i]) > MAXTHEMENAMELENGTH+strlen(FLAGTHEMELONG "="))
328 			{
329 				fprintf(stderr,"Warning: the theme name you've given is too long. The maximum is %d characters.\n"
330 	               		               " Not because I don't understand long filenames, by the way -- it's so the\n"
331 				               " options menu looks nice. I'm very fussy about my appearance.\n",MAXTHEMENAMELENGTH);
332 			}
333 			else
334 			{
335 				// fix -- we should probably search for malicious characters here.
336 				snprintf(commandline.theme,sizeof(commandline.theme),"%s",argv[i]+strlen(FLAGTHEMELONG "="));
337 			}
338 		}
339 		else if (strncmp(argv[i],FLAGTHEMELONG,strlen(FLAGTHEMELONG)) == 0)
340 		{
341 			fprintf(stderr,"Warning: the " FLAGTHEMELONG " parameter needs to be followed directly by an \'=\' and\n"
342 			               " then the name of the theme you want. Like, \"" FLAGTHEMELONG "=linux\". Otherwise, I\n"
343 	               	               " will pretend to not understand you.\n");
344 		}
345 
346 
347 	}
348 
349 	return 0;
350 }
351 
findflag(int argc,char ** argv,char * option)352 int findflag(int argc,char** argv, char* option)
353 {
354 	int i;
355 	for (i=1; i<argc; i++)
356 	{
357 		//printf("[%d] = %s\n",i,argv[i]);
358 		if (strcmp(argv[i],option) == 0)
359 			return true;
360 	}
361 	return false;
362 }
363 
printversion()364 void printversion()
365 {
366 	printf("\nIceBreaker v%d.%d.%d  Copyright 2000-2002 Matthew Miller, et al.\n\n",VERMAJOR,VERMINOR,VERSUB);
367 
368 	printf("Written by Matthew Miller with additional code and help from Enrico Tassi\n"
369 	       "and others. Like to contribute to the project? Good code, graphics, sounds,\n"
370        	       "and even just advice are all appreciated and often even accepted.\n\n"
371 	       "Visit the IceBreaker web site at <http://www.mattdm.org/icebreaker/> for more\n"
372                "information or to download the latest version.\n\n"
373 	       "This program is free software; you can redistribute it and/or modify it\n"
374                "under the terms of the GNU General Public License as published by the Free\n"
375 	       "Software Foundation; either version 2 of the License, or (at your option)\n"
376                "any later version. IceBreaker uses the SDL library, which is distributed\n"
377 	       "under the GNU LGPL; see <http://www.libsdl.org/> for details.\n\n");
378 }
379 
printhelp()380 void printhelp()
381 {
382 	printf("Usage: icebreaker [OPTION...]\n");
383 	printf("%20s, %-15s %-35s\n",FLAGNOSOUND,FLAGNOSOUNDLONG,"disable game sounds");
384 	printf("%20s, %-15s %-35s\n",FLAGFULLSCREEN,FLAGFULLSCREENLONG,"start fullscreen if possible");
385 	printf("%20s, %-15s %-35s\n",FLAGWINDOWED,FLAGWINDOWEDLONG,"start windowed (resets \"Always\" option)");
386 	printf("%20s, %-15s %-35s\n",FLAGVERSION,FLAGVERSIONLONG,"display version and copyright info");
387 	printf("%20s, %-15s %-35s\n",FLAGHELP,FLAGHELPLONG,"display this help screen");
388 	printf("%20s, %-15s %-35s\n",FLAGLISTTHEMES,FLAGLISTTHEMESLONG,"list available themes");
389 	printf("%18s%s,\n","",FLAGTHEME "themename");
390 	printf("%18s %-18s %-35s\n","",FLAGTHEMELONG "=themename","select theme");
391 
392 	printf("\nFor game play help, see the included documentation, the in-game help, or the\n"
393 	       "web site at <http://www.mattdm.org/icebreaker/>\n\n");
394 }
395 
printthemelist()396 void printthemelist()
397 {
398 	char** themelist;
399 	int themecount;
400 	int t;
401 
402 	printf("I've found the following themes:\n\n");
403 
404 	themecount=getthemenames(&themelist);
405 	if (themecount==0)
406 	{
407 		printf("  (none)\n");
408 		printf("\nThis probably means IceBreaker was installed incorrectly.\n");
409 	}
410 	else
411 	{
412 		for (t=0;t<themecount;t++)
413 			printf("  %s\n",themelist[t]);
414 
415 		printf("\nAdditionally, you can use \'random\', which does about what you'd expect.\n");
416         }
417         freethemenames(&themelist,themecount);
418 }
419 
openoptionfile(char * filename,char * mode)420 OPTFILEHANDLER *openoptionfile(char* filename,char* mode)
421 {
422 	OPTFILEHANDLER *rc = NULL;
423 
424 	#ifdef WIN32
425 	DWORD res,err;
426 	rc = (OPTFILEHANDLER *)malloc(sizeof(OPTFILEHANDLER));
427 	rc->line = 0;
428 	err = RegCreateKeyExA(HKEY_CURRENT_USER,ICEBREAKER_KEY,0,NULL,REG_OPTION_NON_VOLATILE,KEY_READ|KEY_WRITE,NULL,&(rc->hK),&res);
429 	if(err != ERROR_SUCCESS)
430 	{
431 		fprintf(stderr,"Unable to open the regisrty\n");
432 		// fix - handle better
433 	}
434 	if (res == REG_CREATED_NEW_KEY)
435 	{
436 		closeoptionfile(rc);
437 		rc = NULL;
438 	}
439 	#else
440 	rc = fopen(filename,mode);
441 	#endif
442 
443 	return rc;
444 }
445 
closeoptionfile(OPTFILEHANDLER * f)446 void closeoptionfile(OPTFILEHANDLER *f)
447 {
448 	#ifdef WIN32
449 	RegCloseKey(f->hK);
450 	free(f);
451 	#else
452 	fclose(f);
453 	#endif
454 }
455 
readoptionline(char * buffer,int len,OPTFILEHANDLER * f)456 char* readoptionline(char* buffer,int len,OPTFILEHANDLER *f)
457 {
458 	char* rc = NULL;
459 
460 	#ifdef WIN32
461 	DWORD err;
462 	char name[50],value[50];
463 	DWORD  name_len = 50, value_len = 50, type;
464 
465 	err = RegEnumValueA(f->hK,f->line,name,&name_len,NULL,&type,value,&value_len);
466 	if(err == ERROR_NO_MORE_ITEMS )
467 	{
468 		rc = NULL;
469 	}
470 	else if ( err != ERROR_SUCCESS )
471 	{
472 		rc = NULL;
473 		fprintf(stderr,"Unable to read the registry.\n");
474 		// fix - handle	better
475 	}
476 	else if ( type != REG_SZ )
477 	{
478 		rc = NULL;
479 		fprintf(stderr,"Registry key has a strange type.\n");
480 	}
481 	else
482 	{
483 		if(name_len + value_len > len)
484 		{
485 			fprintf(stderr,"Registry key is too long for buffer\n");
486 			return NULL;
487 		}
488 		snprintf(buffer,len,"%s %s",name,value);
489 		rc = buffer;
490 	}
491 
492 	f->line ++;
493 	#else
494 	rc = fgets(buffer,len,f);
495 	#endif
496 	return rc;
497 }
498 
writeoptionline(OPTFILEHANDLER * f,char * fmt,...)499 int writeoptionline(OPTFILEHANDLER *f,char* fmt,...)
500 {
501 	int rc = 0;
502 
503 	#ifdef WIN32
504 	DWORD err;
505 	char name[50],value[50],line[100];
506 	DWORD  value_len = 0, i;
507 
508 	va_list args;
509 	va_start(args, fmt);
510 	vsnprintf(line,100,fmt,args);
511 	va_end(args);
512 
513 	if( line[0] == '#' || line[0] == '\n')
514 	{
515 		return 0; //skip comments
516 	}
517 
518 	else
519 
520 	{
521 		rc = strlen(line) + 1;
522 		//erase \n, they could be in the middle ??
523 		for(i = 0 ; line[i] != '\0'; i++)
524 			if(line[i] == '\n')
525 				line[i] = ' ';
526 		// find the separator
527 		for(i = 0 ; line[i] != '\0' && line[i]!=' ' ; i++);
528 
529 		if(line[i+1] != '\0' && line[i] == ' ')
530 		{
531 		        line[i] = '\0';
532 		        snprintf(name,50,"%s",line);
533 		        snprintf(value,50,"%s",&line[i+1]);
534 		        value_len = sizeof(char) * (strlen(value)+1);
535 			err = RegSetValueExA(f->hK,name,0,REG_SZ,value,value_len);
536 			if(err != ERROR_SUCCESS)
537 			{
538 				rc = 0;
539 				fprintf(stderr,"Unable to write the registry\n");
540 				// fix - handle	better
541 			}
542 		}
543 
544 	}
545 
546 	#else
547 	va_list args;
548 	va_start(args, fmt);
549 	rc = vfprintf(f,fmt,args);
550 	va_end(args);
551 	#endif
552 	return rc;
553 }
554