1 /* $Id: highscore.c,v 1.2 1998/04/30 05:11:55 mrogre Exp $ */
2 /* Copyright (c) 1998 Joe Rumsey (mrogre@mediaone.net) */
3 #include "copyright.h"
4 
5 #include <config.h>
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sys/types.h>
10 #ifdef HAVE_UNISTD_H
11 # include <unistd.h>
12 #endif
13 #include <sys/stat.h>
14 #ifdef HAVE_FCNTL_H
15 # include <fcntl.h>
16 #endif
17 #include <ctype.h>
18 #include <pwd.h>
19 #include <string.h>
20 #ifdef _AIX
21 #include <net/nh.h>
22 #else /* This is for ntohl, htonl.
23 	 This file is correct for hpux and linux for sure,
24 	 probably others as well. */
25 #include <netinet/in.h>
26 #endif
27 
28 #include "Wlib.h"
29 #include "defs.h"
30 #include "struct.h"
31 #include "proto.h"
32 #include "data.h"
33 
34 /* Global score file is insecure and rather useless today. */
35 #define NO_GLOBAL_SCORES 1
36 #ifdef NO_GLOBAL_SCORES
37 #define NUM_GLOBAL_SCORES 0
38 #else
39 #define NUM_GLOBAL_SCORES 20
40 #endif
41 #define NUM_MY_SCORES 10
42 
43 static char new_name[20];
44 static int nnpos=0;
45 static int thisplace = -1, my_thisplace = -1;
46 
47 static struct high_score {
48 #ifdef NOSCOREHOGS
49     long uid;
50 #endif
51     char name[20];
52     long score;
53     long level;
54 }
55 #ifndef NO_GLOBAL_SCORES
56 	global_scores[NUM_GLOBAL_SCORES],
57 #endif
58 	my_scores[NUM_MY_SCORES];
59 
undo_name()60 void undo_name()
61 {
62     W_ClearArea(baseWin,
63 		WINWIDTH/2-(20*W_Textwidth), 250,
64 		(40*W_Textwidth), W_Textheight*2);
65 }
66 
do_name()67 void do_name()
68 {
69     char buf[21];
70     static int init = 0;
71 
72     if(!init) {
73 	strcpy(new_name, getUsersFullName());
74 	nnpos = strlen(new_name);
75 	init = 1;
76     }
77     center_text("Great score! Enter your name:", 250, W_Red);
78     sprintf(buf, "%s_", new_name);
79     center_text(buf, 250 + W_Textheight, W_Cyan);
80 }
81 
getUsersFullName()82 char *getUsersFullName()
83 {
84     struct passwd *pass;
85     char *comma;
86     char *cp1, *cp2;
87     static char fullname[80];
88 
89     /* Get user information from password file */
90     if (!(pass = getpwuid(getuid())))
91         return("Anonymous?");       /* Unknown user oops. */
92 
93     /* find a comma indicating further info after name */
94     comma = strchr(pass->pw_gecos, ',');
95 
96     /* NULL out the comma */
97     if (comma) *comma = '\0';
98 
99     /* Use the nickname if not null otherwise password file name */
100     cp1 = pass->pw_gecos;
101     cp2 = fullname;
102 
103     /* Search through the gecos field looking for an '&' which on very
104      * old UNIX systems is supposed to be the users user name with the
105      * first letter uppercased.
106      */
107     while(*cp1)
108     {
109         /* Look for the '&' symbol */
110         if(*cp1 != '&')
111             *cp2++ = *cp1++;
112         else
113         {
114             /* A ha. Now copy the users name to be in place of '&' */
115             strcpy(cp2, pass->pw_name);
116 
117             /* Convert the first letter to uppercase. */
118             if(islower(*cp2))
119                 *cp2 = toupper(*cp2);
120 
121             /* Continue with the remaining string */
122             while(*cp2) cp2++;
123                 cp1++;
124         }
125     }
126 
127     /* shorten to 20 chars */
128     fullname[19] = 0;
129 
130     /* Return their name without any trailing stuff */
131     return(fullname);
132 }
133 
save_scores()134 static void save_scores()
135 {
136     int i;
137     int hsf;
138     long x;
139     char my_file_name [256], *home;
140 #ifndef NO_GLOBAL_SCORES
141     hsf = open(SCOREFILE, O_WRONLY | O_TRUNC | O_CREAT, 0666);
142     if(hsf < 0) {
143 	printf("Couldn't write scores file %s\n", SCOREFILE);
144 	return;
145     }
146 
147     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
148 #ifdef NOSCOREHOGS
149         x=htonl(global_scores[i].uid);
150 	if(write(hsf, &x, sizeof(long)) < sizeof(long))
151 	    goto error;
152 #endif
153 	if(write(hsf, global_scores[i].name, 20) < 20)
154 	    goto error;
155 	x=htonl(global_scores[i].score);
156 	if(write(hsf, &x, sizeof(long)) < sizeof(long))
157 	    goto error;
158 	x=htonl(global_scores[i].level);
159 	if(write(hsf, &x, sizeof(long)) < sizeof(long))
160 	    goto error;
161     }
162     close(hsf);
163 #endif /* NO_GLOBAL_SCORES */
164     if((home = getenv("HOME"))) {
165 #ifndef NO_GLOBAL_SCORES
166 	gid_t my_egid = getegid();
167 	if (setgid(getgid()) != 0) {
168 		perror("setgid");
169 		return;
170 	}
171 #endif
172 	snprintf(my_file_name, sizeof(my_file_name)-1, "%s/.xgalscores", home);
173 	hsf = open(my_file_name, O_WRONLY | O_TRUNC | O_CREAT, 0644);
174 #ifndef NO_GLOBAL_SCORES
175 	if (setgid(my_egid) != 0) {
176 		perror("setgid back");
177 		exit(1);
178 	}
179 #endif
180 	if(hsf < 0) {
181 	    printf("Couldn't write scores file %s\n", my_file_name);
182 	    return;
183 	}
184 	for(i=0;i<NUM_MY_SCORES;i++) {
185 	    if(write(hsf, my_scores[i].name, 20) < 20)
186 		goto error2;
187 	    x=htonl(my_scores[i].score);
188 	    if(write(hsf, &x, sizeof(long)) < sizeof(long))
189 		goto error2;
190 	    x=htonl(my_scores[i].level);
191 	    if(write(hsf, &x, sizeof(long)) < sizeof(long))
192 		goto error2;
193 	}
194         close(hsf);
195     }
196 
197     return;
198 #ifndef NO_GLOBAL_SCORES
199   error:
200     printf("Error saving high scores file %s\n", SCOREFILE);
201     return;
202 #endif
203   error2:
204     printf("Error saving high scores file %s\n", my_file_name);
205     return;
206 }
207 
add_score(char * name,int score)208 void add_score(char *name, int score)
209 {
210     int i,j ; /* ,k; */
211 
212     thisplace = my_thisplace = -1;
213 
214     load_scores();
215 
216 #ifndef NO_GLOBAL_SCORES
217 #ifdef NOSCOREHOGS
218     /* /lib 27jul95: want to allow only one global high-score per person,
219      * by uid (getuid)
220      */
221     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
222 	if(score > global_scores[i].score) {
223 	    /* /lib 27jul95 uniq-global-highscore: */
224 	    /* find old position of high-score: */
225 	    for(k=i;k<NUM_GLOBAL_SCORES;k++) {
226 		if(global_scores[k].uid == getuid()) {
227 		    break;
228 		}
229 	    }
230 	    if(k==NUM_GLOBAL_SCORES) {
231 		k--;
232 	    }
233 	    /* found old pos, copy from i..k-1 to i+1..k */
234 	    for(j=k;j>i;j--) {
235 		global_scores[j].uid = global_scores[j-1].uid;
236 		strcpy(global_scores[j].name, global_scores[j-1].name);
237 		global_scores[j].score = global_scores[j-1].score;
238 		global_scores[j].level = global_scores[j-1].level;
239 	    }
240 	    global_scores[i].uid = getuid();
241 	    strcpy(global_scores[i].name, name);
242 	    global_scores[i].score = score;
243 	    global_scores[i].level = level;
244 	    thisplace = i;
245 	    break;
246 	}
247 	/* high-score stuff by /lib (uid is found, score is too low): */
248 	else if(global_scores[i].uid == getuid()) {
249 	    break;
250 	}
251     }
252 
253 #else /* NOSCOREHOGS */
254     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
255 	if(score > global_scores[i].score) {
256 	    for(j=NUM_GLOBAL_SCORES-1;j>i;j--) {
257 		strcpy(global_scores[j].name, global_scores[j-1].name);
258 		global_scores[j].score = global_scores[j-1].score;
259 		global_scores[j].level = global_scores[j-1].level;
260 	    }
261 	    strcpy(global_scores[i].name, name);
262 	    global_scores[i].score = score;
263 	    global_scores[i].level = level;
264 	    thisplace = i;
265 	    break;
266 	}
267     }
268 #endif /* NOSCOREHOGS */
269 #endif /* NO_GLOBAL_SCORES */
270 
271     for(i=0;i<NUM_MY_SCORES;i++) {
272 	if(score > my_scores[i].score) {
273 	    for(j=NUM_MY_SCORES-1;j>i;j--) {
274 		strcpy(my_scores[j].name, my_scores[j-1].name);
275 		my_scores[j].score = my_scores[j-1].score;
276 		my_scores[j].level = my_scores[j-1].level;
277 	    }
278 	    strcpy(my_scores[i].name, name);
279 	    my_scores[i].score = score;
280 	    my_scores[i].level = level;
281 	    my_thisplace = i;
282 	    break;
283 	}
284     }
285     save_scores();
286 }
287 
score_key(W_Event * ev)288 int score_key(W_Event *ev)
289 {
290     if(getting_name) {
291 	switch(ev->key) {
292 	  case 13:
293 	  case 10:
294           case 269:
295 	    getting_name = 0;
296 	    add_score(new_name, score);
297 	    title_page = 1;
298 	    pagetimer = 300;
299 	    W_ClearWindow(baseWin);
300 	    break;
301 	  case 8:
302 	  case 127:
303           case 264:
304 	    if(nnpos > 0) {
305 		nnpos--;
306 		new_name[nnpos] = 0;
307 	    }
308 	    break;
309 	  case 'u'+128:
310 	    nnpos = 0;
311 	    new_name[nnpos] = 0;
312 	    break;
313 	  default:
314 	    if(nnpos < 19) {
315 		new_name[nnpos++] = ev->key;
316 		new_name[nnpos] = 0;
317 	    }
318 	    break;
319 	}
320 
321 	return 1;
322     }
323     return 0;
324 }
325 
326 
check_score(int score)327 int check_score(int score)
328 {
329     int i;
330 
331     load_scores(); /* in case someone else has gotten a high score */
332 #ifndef NO_GLOBAL_SCORES
333     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
334 	if(score > global_scores[i].score) {
335 	    return 1;
336 	}
337 #ifdef NOSCOREHOGS
338  	if(global_scores[i].uid == getuid()) {
339 	    break;
340  	}
341 #endif
342     }
343 #endif /* NO_GLOBAL_SCORES */
344     for(i=0;i<NUM_MY_SCORES;i++) {
345 	if(score > my_scores[i].score)
346 	    return 1;
347     }
348 
349     my_thisplace = -1;
350     thisplace = -1;
351     return 0;
352 }
353 
354 
show_scores()355 void show_scores()
356 {
357     int i;
358     char buf[60];
359     W_Color color;
360 
361     W_SetRGB16(W_DarkGrey, random()%65535,random()%65535,random()%65535);
362 
363     color = W_DarkGrey;
364 
365 
366 #ifndef NO_GLOBAL_SCORES
367     center_text("Global high scores", 90, W_Yellow);
368     sprintf(buf, "Rank  Name                      Score   Level");
369     center_text(buf, 100, W_Yellow);
370     W_MakeLine(baseWin, WINWIDTH/2-((strlen(buf)*W_Textwidth)/2), 111,
371 	       WINWIDTH/2 + ((strlen(buf)*W_Textwidth)/2), 111, W_Red);
372     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
373 	sprintf(buf, "  %2d. %-20s     %7ld %5ld", i+1,
374 		global_scores[i].name, global_scores[i].score,global_scores[i].level);
375 	center_text(buf, 112+i*W_Textheight, (i==thisplace ? color : W_Grey));
376     }
377 #endif
378 
379     center_text("Your high scores", 112+NUM_GLOBAL_SCORES*W_Textheight, W_Yellow);
380     sprintf(buf, "Rank  Name                      Score   Level");
381     center_text(buf, 112+(NUM_GLOBAL_SCORES+1)*W_Textheight, W_Yellow);
382     W_MakeLine(baseWin, WINWIDTH/2-((strlen(buf)*W_Textwidth)/2), 123+(NUM_GLOBAL_SCORES+1)*W_Textheight,
383 	       WINWIDTH/2 + ((strlen(buf)*W_Textwidth)/2), 123+(NUM_GLOBAL_SCORES+1)*W_Textheight, W_Red);
384     for(i=0;i<NUM_MY_SCORES;i++) {
385 	sprintf(buf, "  %2d. %-20s     %7ld %5ld", i+1,
386 		my_scores[i].name, my_scores[i].score,my_scores[i].level);
387 	center_text(buf, 124+(NUM_GLOBAL_SCORES+1)*W_Textheight + i*W_Textheight,
388 		    (i==my_thisplace ? color : W_Grey));
389     }
390 }
391 
load_scores()392 void load_scores()
393 {
394     int i;
395     int hsf;
396     char my_file_name[256], *home;
397 
398 #ifndef NO_GLOBAL_SCORES
399     hsf = open(SCOREFILE, O_RDONLY);
400     if(hsf <0 ) {
401 	printf("Trouble opening high scores file '%s'\n", SCOREFILE);
402 	for(i=0;i<NUM_GLOBAL_SCORES;i++) {
403 #ifdef NOSCOREHOGS
404 	    global_scores[i].uid = 0;
405 #endif
406 	    global_scores[i].name[0]=0;
407 	    global_scores[i].score = 0;
408 	    global_scores[i].level = 0;
409 	}
410     } else {
411 	for(i=0;i<NUM_GLOBAL_SCORES;i++) {
412 #ifdef NOSCOREHOGS
413 	    if(read(hsf, &global_scores[i].uid, sizeof(long)) < sizeof(long))
414 		goto error;
415 #endif
416 	    if(read(hsf, global_scores[i].name, 20) < 20)
417 		goto error;
418 	    if(read(hsf, &global_scores[i].score, sizeof(long)) < sizeof(long))
419 		goto error;
420 	    if(read(hsf, &global_scores[i].level, sizeof(long)) < sizeof(long))
421 		goto error;
422 #ifdef NOSCOREHOGS
423 	    global_scores[i].uid = ntohl(global_scores[i].uid);
424 #endif
425 	    global_scores[i].score = ntohl(global_scores[i].score);
426  	    global_scores[i].level = ntohl(global_scores[i].level);
427 	}
428     }
429     close(hsf);
430 #endif /* NO_GLOBAL_SCORES */
431 
432     if((home = getenv("HOME"))) {
433 	snprintf(my_file_name, sizeof(my_file_name)-1, "%s/.xgalscores", home);
434 	hsf = open(my_file_name, O_RDONLY);
435 	if(hsf <0 ) {
436 	    printf("Trouble opening high scores file '%s'\n", my_file_name);
437 	    for(i=0;i<NUM_MY_SCORES;i++) {
438 		my_scores[i].name[0]=0;
439 		my_scores[i].score = 0;
440 		my_scores[i].level = 0;
441 	    }
442 	} else {
443 	    for(i=0;i<NUM_MY_SCORES;i++) {
444 		if(read(hsf, my_scores[i].name, 20) < 20)
445 		    goto error2;
446 		if(read(hsf, &my_scores[i].score, sizeof(long)) < sizeof(long))
447 		    goto error2;
448 		if(read(hsf, &my_scores[i].level, sizeof(long)) < sizeof(long))
449 		    goto error2;
450 		my_scores[i].score = ntohl(my_scores[i].score);
451 		my_scores[i].level = ntohl(my_scores[i].level);
452 	    }
453 	}
454 	close(hsf);
455     } else {
456 	printf("No HOME variable, so no personal score file.\n");
457 	for(i=0;i<NUM_MY_SCORES;i++) {
458 	    my_scores[i].name[0]=0;
459 	    my_scores[i].score = 0;
460 	    my_scores[i].level = 0;
461 	}
462     }
463     return;
464 #ifndef NO_GLOBAL_SCORES
465   error:
466     if(i>0)
467 	printf("Error reading high scores file '%s'\n", SCOREFILE);
468     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
469 #ifdef NOSCOREHOGS
470 	global_scores[i].uid = 0;
471 #endif
472 	global_scores[i].name[0]=0;
473 	global_scores[i].score = 0;
474 	global_scores[i].level = 0;
475     }
476     close(hsf);
477     return;
478 #endif
479   error2:
480     if(i>0)
481 	printf("Error reading high scores file '%s'\n", my_file_name);
482     for(i=0;i<NUM_MY_SCORES;i++) {
483 	my_scores[i].name[0]=0;
484 	my_scores[i].score = 0;
485 	my_scores[i].level = 0;
486     }
487     close(hsf);
488 }
489 
print_scores()490 void print_scores() {
491     int i;
492 
493     load_scores();
494 #ifndef NO_GLOBAL_SCORES
495     printf("\nGlobal High Scores:\n");
496     printf("-----------------------------------------------\n");
497     printf("%8s %-20s %8s %8s\n", "uid", "name", "score", "level");
498     printf("-----------------------------------------------\n");
499     for(i=0;i<NUM_GLOBAL_SCORES;i++) {
500 	if(global_scores[i].score == 0)
501 	    break;
502 #ifdef NOSCOREHOGS
503 	printf("%8ld %-20s %8ld %8ld\n", global_scores[i].uid,
504 	       global_scores[i].name, global_scores[i].score,
505 	       global_scores[i].level);
506 #else
507 	printf("      -- %-20s %8ld %8ld\n",
508 	       global_scores[i].name, global_scores[i].score,
509 	       global_scores[i].level);
510 #endif
511     }
512 #endif /* NO_GLOBAL_SCORES */
513     printf("-----------------------------------------------\n");
514     printf("\nYour High Scores:\n");
515     printf("--------------------------------------\n");
516     printf("%-20s %8s %8s\n", "name", "score", "level");
517     printf("--------------------------------------\n");
518     for(i=0;i<NUM_MY_SCORES;i++) {
519 	if(my_scores[i].score == 0)
520 	    break;
521 	printf("%-20s %8ld %8ld\n", my_scores[i].name,
522 	       my_scores[i].score, my_scores[i].level);
523     }
524     printf("--------------------------------------\n");
525 }
526