1 /*
2  * XBoing - An X11 blockout style computer game
3  *
4  * (c) Copyright 1993, 1994, 1995, Justin C. Kibell, All Rights Reserved
5  *
6  * The X Consortium, and any party obtaining a copy of these files from
7  * the X Consortium, directly or indirectly, is granted, free of charge, a
8  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
9  * nonexclusive right and license to deal in this software and
10  * documentation files (the "Software"), including without limitation the
11  * rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons who receive
13  * copies from any such party to do so.  This license includes without
14  * limitation a license to do the foregoing actions under any patents of
15  * the party supplying this software to the X Consortium.
16  *
17  * In no event shall the author be liable to any party for direct, indirect,
18  * special, incidental, or consequential damages arising out of the use of
19  * this software and its documentation, even if the author has been advised
20  * of the possibility of such damage.
21  *
22  * The author specifically disclaims any warranties, including, but not limited
23  * to, the implied warranties of merchantability and fitness for a particular
24  * purpose.  The software provided hereunder is on an "AS IS" basis, and the
25  * author has no obligation to provide maintenance, support, updates,
26  * enhancements, or modifications.
27  */
28 
29 /*
30  * =========================================================================
31  *
32  * $Id: highscore.c,v 1.1.1.1 1994/12/16 01:36:46 jck Exp $
33  * $Source: /usr5/legends/jck/xb/master/xboing/highscore.c,v $
34  * $Revision: 1.1.1.1 $
35  * $Date: 1994/12/16 01:36:46 $
36  *
37  * $Log: highscore.c,v $
38  * Revision 1.1.1.1  1994/12/16  01:36:46  jck
39  * The XBoing distribution requires configuration management. This is why the
40  * cvs utility is being used. This is the initial import of all source etc..
41  *
42  *
43  * =========================================================================
44  */
45 
46 /*
47  *  Include file dependencies:
48  */
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <stddef.h>
53 #include <ctype.h>
54 #include <unistd.h>
55 #include <time.h>
56 #include <file.h>
57 #include <sys/param.h>
58 #include <sys/stat.h>
59 #include <X11/Xlib.h>
60 #include <X11/Xutil.h>
61 #include <X11/Xos.h>
62 #include <netinet/in.h>
63 #include <xpm.h>
64 
65 #include "error.h"
66 #include "misc.h"
67 #include "main.h"
68 #include "preview.h"
69 #include "audio.h"
70 #include "special.h"
71 #include "init.h"
72 #include "inst.h"
73 #include "stage.h"
74 #include "blocks.h"
75 #include "ball.h"
76 #include "sfx.h"
77 #include "score.h"
78 #include "paddle.h"
79 #include "level.h"
80 #include "mess.h"
81 #include "intro.h"
82 #include "presents.h"
83 
84 #include "bitmaps/highscr.xpm"
85 
86 #include "highscore.h"
87 
88 /*
89  *  Internal macro definitions:
90  */
91 
92 #define GAP				13
93 #define NUM_HIGHSCORES	10
94 
95 #ifndef MAXPATHLEN
96 #define MAXPATHLEN 1024
97 #endif
98 
99 /* My locking defines only */
100 #define LOCK_FILE 		0
101 #define UNLOCK_FILE 	1
102 
103 /* System locking defines */
104 
105 #ifndef NO_LOCKING
106 #ifndef LOCK_EX
107 #define LOCK_EX F_LOCK
108 #endif
109 #ifndef LOCK_UN
110 #define LOCK_UN F_ULOCK
111 #endif
112 #endif /* NO_LOCKING */
113 
114 /*
115  *  Internal type declarations:
116  */
117 
118 #if NeedFunctionPrototypes
119 static void SetHighScoreWait(enum HighScoreStates newMode, int waitFrame);
120 static void InitialiseHighScores(void);
121 static void SortHighScores(void);
122 static void DeleteScore(int i);
123 static int LockUnlock(int cmd, int fd);
124 #else
125 static int LockUnlock();
126 static void DeleteScore();
127 static void SetHighScoreWait();
128 static void InitialiseHighScores();
129 static void SortHighScores();
130 #endif
131 
132 /*
133  *  Internal variable declarations:
134  */
135 
136 static int nextFrame = 0;
137 static int endFrame = 0;
138 enum HighScoreStates HighScoreState;
139 static Pixmap titlePixmap, titlePixmapM;
140 static int waitingFrame;
141 extern enum HighScoreStates waitMode;
142 static int sparkley = 0;
143 static int sindex = 0;
144 static int si = 0;
145 static int scoreType = GLOBAL;
146 static char nickName[22];
147 
148 highScoreEntry 	highScores[NUM_HIGHSCORES];
149 highScoreHeader scoresHeader;
150 
151 #if NeedFunctionPrototypes
SetNickName(char * nick)152 void SetNickName(char *nick)
153 #else
154 void SetNickName(nick)
155 	char *nick;
156 #endif
157 {
158 	/* Change the users nick name */
159 	strncpy(nickName, nick, 20);
160 }
161 
162 #if NeedFunctionPrototypes
SetBoingMasterText(char * message)163 void SetBoingMasterText(char *message)
164 #else
165 void SetBoingMasterText(message)
166 	char *message;
167 #endif
168 {
169 	/* Change the boing masters text */
170 	strcpy(scoresHeader.masterText, message);
171 }
172 
173 #if NeedFunctionPrototypes
GetNickName(void)174 char *GetNickName(void)
175 #else
176 char *GetNickName()
177 #endif
178 {
179 	/* Return the nickname or NULL */
180 	if (nickName[0] == '\0')
181 		return NULL;
182 	else
183 		return nickName;
184 }
185 
186 #if NeedFunctionPrototypes
SetUpHighScore(Display * display,Window window,Colormap colormap)187 void SetUpHighScore(Display *display, Window window, Colormap colormap)
188 #else
189 void SetUpHighScore(display, window, colormap)
190 	Display *display;
191 	Window window;
192 	Colormap colormap;
193 #endif
194 {
195 	XpmAttributes   attributes;
196 	int             XpmErrorStatus;
197 
198 	attributes.valuemask = XpmColormap;
199 	attributes.colormap = colormap;
200 
201 	/* Load the highscore title pixmap */
202 	XpmErrorStatus = XpmCreatePixmapFromData(display, window, highscores_xpm,
203 		&titlePixmap, &titlePixmapM, &attributes);
204 	HandleXPMError(display, XpmErrorStatus, "InitialiseHighScore()");
205 
206     /* Free the xpm pixmap attributes */
207 	XpmFreeAttributes(&attributes);
208 
209 	/* Setup the high score table */
210 	InitialiseHighScores();
211 	ResetHighScore(GLOBAL);
212 }
213 
214 #if NeedFunctionPrototypes
DoTitle(Display * display,Window window)215 static void DoTitle(Display *display, Window window)
216 #else
217 static void DoTitle(display, window)
218 	Display *display;
219 	Window window;
220 #endif
221 {
222 	char string[80];
223 
224    	DrawStageBackground(display, window, BACKGROUND_SPACE, True);
225 
226 	/* Draw the highscore title */
227 	RenderShape(display, window, titlePixmap, titlePixmapM,
228 		59, 20, 377, 37, False);
229 	RenderShape(display, window, earthPixmap, earthPixmapM,
230 		PLAY_WIDTH / 2 - 200, PLAY_HEIGHT / 2 - 160, 400, 400, False);
231 
232 	/* Let the dudes know how to start the game */
233 	strcpy(string, "Insert coin to start the game");
234 	DrawShadowCentredText(display, window, textFont,
235 		string, PLAY_HEIGHT - 40, tann, PLAY_WIDTH);
236 
237 	/* Set the message window to have the other display toggle key */
238 	if (scoreType == GLOBAL)
239 		SetCurrentMessage(display, messWindow,
240 			"<H> - Personal Best", False);
241 	else
242 		SetCurrentMessage(display, messWindow,
243 			"<h> - Roll of Honour", False);
244 
245 	SetHighScoreWait(HIGHSCORE_SHOW, frame + 10);
246 }
247 
248 #if NeedFunctionPrototypes
DoHighScores(Display * display,Window window)249 static void DoHighScores(Display *display, Window window)
250 #else
251 static void DoHighScores(display, window)
252 	Display *display;
253 	Window window;
254 #endif
255 {
256 	int i, len, plen;
257 	int xr = 30;
258 	int ym = 75;
259 	int xs = xr + 30;
260 	int xl = xs + 75;
261 	int xt = xl + 40;
262 	int xg = xt + 65;
263 	int xn = xg + 95;
264 	int y = 200;
265 	char string[80];
266 	char string2[80];
267 	char *p;
268 	time_t	theTime;
269 
270 	/* Read the high score table */
271 	if (ReadHighScoreTable(GLOBAL) == False)
272 		InitialiseHighScores();
273 
274 	/* Draw the boing master at top of the list */
275 	strcpy(string, "Boing Master");
276 	DrawShadowCentredText(display, window, textFont,
277 		string, ym, red, PLAY_WIDTH);
278 	ym += textFont->ascent + GAP;
279 
280 	/* Render the boing master's name */
281 	strcpy(string, highScores[0].name);
282 	DrawShadowCentredText(display, window, titleFont,
283 		string, ym, yellow, PLAY_WIDTH);
284 	ym += textFont->ascent + GAP;
285 
286 	/* Render the boing master's words of wisdom */
287 	strcpy(string, scoresHeader.masterText);
288 	DrawShadowCentredText(display, window, dataFont,
289 		string, ym, green, PLAY_WIDTH);
290 	ym += textFont->ascent + GAP * 2;
291 
292 	/* Read the high score table */
293 	if (ReadHighScoreTable(scoreType) == False)
294 		InitialiseHighScores();
295 
296 	/* Explain that this is the roll of honour */
297 	if (scoreType == GLOBAL)
298 		strcpy(string, "- The Roll of Honour -");
299 	else
300 		strcpy(string, "- Personal Best -");
301 	DrawShadowCentredText(display, window, textFont,
302 		string, ym, red, PLAY_WIDTH);
303 	ym += textFont->ascent + GAP;
304 
305 	/* Draw the titles for the highscore table */
306 	strcpy(string, "#");
307 	len = strlen(string);
308 	DrawText(display, window, xr+2, y+2, textFont, black, string, len);
309 	DrawText(display, window, xr, y, textFont, yellow, string, len);
310 
311 	strcpy(string, "Score");
312 	len = strlen(string);
313 	DrawText(display, window, xs+2, y+2, textFont, black, string, len);
314 	DrawText(display, window, xs, y, textFont, yellow, string, len);
315 
316 	strcpy(string, "L");
317 	len = strlen(string);
318 	DrawText(display, window, xl+2, y+2, textFont, black, string, len);
319 	DrawText(display, window, xl, y, textFont, yellow, string, len);
320 
321 	strcpy(string, "Time");
322 	len = strlen(string);
323 	DrawText(display, window, xt+2, y+2, textFont, black, string, len);
324 	DrawText(display, window, xt, y, textFont, yellow, string, len);
325 
326 	strcpy(string, "Date");
327 	len = strlen(string);
328 	DrawText(display, window, xg+2, y+2, textFont, black, string, len);
329 	DrawText(display, window, xg, y, textFont, yellow, string, len);
330 
331 	strcpy(string, "Player");
332 	len = strlen(string);
333 	DrawText(display, window, xn+2, y+2, textFont, black, string, len);
334 	DrawText(display, window, xn, y, textFont, yellow, string, len);
335 
336 	y += textFont->ascent + GAP / 2;
337 
338 	/* Draw the line above the table */
339 	DrawLine(display, window, 22, y+2, PLAY_WIDTH - 18, y+2, black, 3);
340 	DrawLine(display, window, 20, y, PLAY_WIDTH - 20, y, white, 3);
341 
342 	y += textFont->ascent;
343 
344 	/* Draw the scores into the table */
345 	for (i = 0; i < NUM_HIGHSCORES; i++)
346 	{
347 		if (ntohl(highScores[i].score) > (u_long) 0)
348 		{
349 			/* Draw the rank */
350 			sprintf(string, "%d", i+1);
351 			if (ntohl(highScores[i].score) != score)
352 				DrawShadowText(display, window, textFont, string, xr, y, tann);
353 			else
354 				DrawShadowText(display, window, textFont, string, xr, y, green);
355 
356 			/* Draw the score */
357 			sprintf(string, "%ld", ntohl(highScores[i].score));
358 			if (ntohl(highScores[i].score) != score)
359 				DrawShadowText(display, window, textFont, string, xs, y, red);
360 			else
361 				DrawShadowText(display, window, textFont, string, xs, y, green);
362 
363 			/* Write out the level reached */
364 			sprintf(string, "%ld", ntohl(highScores[i].level));
365 			DrawShadowText(display, window, textFont, string, xl, y, green);
366 
367 			/* Game duration in minutes and seconds */
368 			sprintf(string, "%ld'%ld'%ld\"",
369 				ntohl(highScores[i].gameTime) / 3600,
370 				ntohl(highScores[i].gameTime) / 60,
371 				ntohl(highScores[i].gameTime) % 60);
372 
373 			if (ntohl(highScores[i].score) != score)
374 				DrawShadowText(display, window, textFont, string, xt, y, tann);
375 			else
376 				DrawShadowText(display, window, textFont, string, xt, y, green);
377 
378 			/* Construct the date for the highscore entry */
379 			theTime = (time_t) ntohl(highScores[i].time);
380 			strftime(string, 10, "%d %b %y", localtime(&theTime));
381 			string[9] = '\0';	/* Just to be sure */
382 			if (ntohl(highScores[i].score) != score)
383 				DrawShadowText(display, window, textFont, string, xg, y, white);
384 			else
385 				DrawShadowText(display, window, textFont, string, xg, y, green);
386 
387 			/* Name of the boing master */
388 			strcpy(string, highScores[i].name);
389 			plen = XTextWidth(textFont, string, len);
390 
391 			/* Only use the first name if too big for screen */
392 			if ((plen + xn) > PLAY_WIDTH)
393 			{
394 				/* Find the first space and null terminate there */
395 				p = strchr(string, ' ');
396 				*p = '\0';
397 				strcpy(string2, string);
398 
399 				/* Draw a much smaller version of your name */
400 				if (ntohl(highScores[i].score) != score)
401 					DrawShadowText(display, window, textFont, string2, xn, y,
402 						yellow);
403 				else
404 					DrawShadowText(display, window, textFont, string2, xn, y,
405 						green);
406 			}
407 			else
408 			{
409 				/* Write out users name */
410 				if (ntohl(highScores[i].score) != score)
411 					DrawShadowText(display, window, textFont, string, xn, y,
412 						yellow);
413 				else
414 					DrawShadowText(display, window, textFont, string, xn, y,
415 						green);
416 			}
417 
418 		}
419 		else
420 		{
421 			/* This bit is for when the table entry is blank */
422 			sprintf(string, "%d", i+1);
423 			DrawShadowText(display, window, textFont, string, xr, y, tann);
424 
425 			/* Draw dashes for blank entries */
426 			strcpy(string, "--");
427 			DrawShadowText(display, window, textFont, string, xs, y, red);
428 			DrawShadowText(display, window, textFont, string, xl, y, green);
429 			DrawShadowText(display, window, textFont, string, xt, y, tann);
430 			DrawShadowText(display, window, textFont, string, xg, y, white);
431 			DrawShadowText(display, window, textFont, string, xn, y, yellow);
432 		}
433 
434 		y += textFont->ascent + GAP;
435 	}
436 
437 	/* Draw the line above the table */
438 	DrawLine(display, window, 22, y+2, PLAY_WIDTH - 18, y+2, black, 3);
439 	DrawLine(display, window, 20, y, PLAY_WIDTH - 20, y, white, 3);
440 
441 	SetHighScoreWait(HIGHSCORE_SPARKLE, frame + 2);
442 }
443 
444 #if NeedFunctionPrototypes
DoTitleSparkle(Display * display,Window window)445 static void DoTitleSparkle(Display *display, Window window)
446 #else
447 static void DoTitleSparkle(display, window)
448 	Display *display;
449 	Window window;
450 #endif
451 {
452 	static int sindex = 0;
453 	static int delay = 30;
454 
455 	if ((frame % delay) == 0)
456 	{
457 		if (delay == 800) delay = 30;
458 
459 		/* Draw stars either side of high scores title */
460 		RenderShape(display, window, stars[sindex], starsM[sindex],
461 			25, 27, 20, 20, True);
462 		RenderShape(display, window, stars[10-sindex], starsM[10-sindex],
463 			PLAY_WIDTH-45, 27, 20, 20, True);
464 
465 		sindex++;
466 		if (sindex == 11)
467 		{
468 			/* Clear away the stars */
469 			XClearArea(display, window, 25, 27, 20, 20, False);
470 			XClearArea(display, window, PLAY_WIDTH-45, 27, 20, 20, False);
471 
472 			sindex = 0;
473 			if (delay == 30) delay = 800;
474 		}
475 	}
476 }
477 
478 #if NeedFunctionPrototypes
DoSparkle(Display * display,Window window)479 static void DoSparkle(Display *display, Window window)
480 #else
481 static void DoSparkle(display, window)
482 	Display *display;
483 	Window window;
484 #endif
485 {
486 	static Pixmap store;	/* backing store for the sparkle */
487 	static int x = 6;		/* X position of the sparkle */
488 
489 	if (!store)
490 	{
491 		/* Create some backing store for the sparkle star */
492 		store = XCreatePixmap(display, window, 20, 20,
493 			DefaultDepth(display, XDefaultScreen(display)));
494 	}
495 
496 	if (frame == endFrame)
497 	{
498 		/* Bug out of the sparkle and goto next sequence */
499 		si = 0;
500 		sindex = 0;
501 		sparkley = 200 + textFont->ascent + 20;
502 
503 		/* End the sparkle and now set up for finish */
504 		SetHighScoreWait(HIGHSCORE_FINISH, frame + 1);
505 		return;
506 	}
507 
508 	if (sindex == 0)
509 		XCopyArea(display, window, store, gc, x, sparkley, 20, 20, 0, 0);
510 
511 	if (frame == nextFrame)
512 	{
513 		/* Draw the sparkle frame */
514 		RenderShape(display, window, stars[sindex], starsM[sindex],
515 			x, sparkley, 20, 20, True);
516 
517 
518 		sindex++;
519 		nextFrame = frame + 30;
520 
521 		/* Last frame of sparkle so reset */
522 		if (sindex == 11)
523 		{
524 			XCopyArea(display, store, window, gc, 0, 0, 20, 20, x, sparkley);
525 
526 			sindex = 0;
527 			nextFrame = frame + 100;
528 			sparkley += textFont->ascent + GAP;
529 
530 			si++;
531 
532 			if ((ntohl(highScores[si].score) <= (u_long) 0) || (si == 10))
533 			{
534 				si = 0;
535 				sindex = 0;
536 				sparkley = 200 + textFont->ascent + 20;
537 			}
538 		}
539 	}
540 }
541 
542 
543 
544 #if NeedFunctionPrototypes
SetHighScoreWait(enum HighScoreStates newMode,int waitFrame)545 static void SetHighScoreWait(enum HighScoreStates newMode, int waitFrame)
546 #else
547 static void SetHighScoreWait(newMode, waitFrame)
548 	enum HighScoreStates newMode;
549 	int waitFrame;
550 #endif
551 {
552 	waitingFrame = waitFrame;
553 	waitMode = newMode;
554 	HighScoreState = HIGHSCORE_WAIT;
555 }
556 
557 #if NeedFunctionPrototypes
DoHighScoreWait(void)558 void DoHighScoreWait(void)
559 #else
560 void DoHighScoreWait()
561 #endif
562 {
563 	/* Wait for the end frame then change mode */
564 	if (frame == waitingFrame)
565 		HighScoreState = waitMode;
566 }
567 
568 #if NeedFunctionPrototypes
DoFinish(Display * display,Window window)569 static void DoFinish(Display *display, Window window)
570 #else
571 static void DoFinish(display, window)
572 	Display *display;
573 	Window window;
574 #endif
575 {
576 	mode = MODE_PREVIEW;
577 	HighScoreState = HIGHSCORE_TITLE;
578 	ResetPreviewLevel();
579 
580 	if (noSound == False)
581 		playSoundFile("gate", 50);
582 
583 	SetGameSpeed(FAST_SPEED);
584 }
585 
586 #if NeedFunctionPrototypes
HighScore(Display * display,Window window)587 void HighScore(Display *display, Window window)
588 #else
589 void HighScore(display, window)
590 	Display *display;
591 	Window window;
592 #endif
593 {
594 	/* Switch on the highscore table state */
595 	switch (HighScoreState)
596 	{
597 		case HIGHSCORE_TITLE:
598 			if (getSpecialEffects(display) == True)
599 			{
600 				/* Clear bffer and draw title */
601     			DrawStageBackground(display, bufferWindow, BACKGROUND_SPACE,
602 					True);
603     			DrawStageBackground(display, window, BACKGROUND_SPACE,
604 					False);
605 				DoTitle(display, bufferWindow);
606 			}
607 			else
608 				DoTitle(display, window);
609 			break;
610 
611 		case HIGHSCORE_SHOW:
612 			if (getSpecialEffects(display) == True)
613 			{
614 				DoHighScores(display, bufferWindow);
615 				while (WindowBlindEffect(display, window));
616 			}
617 			else
618 				DoHighScores(display, window);
619 			break;
620 
621 		case HIGHSCORE_SPARKLE:
622 			DoTitleSparkle(display, window);
623 			DoSparkle(display, window);
624 			if ((frame % FLASH) == 0)
625 				RandomDrawSpecials(display);
626 			BorderGlow(display, window);
627 			break;
628 
629 		case HIGHSCORE_FINISH:
630 			DoFinish(display, window);
631 			break;
632 
633 		case HIGHSCORE_WAIT:
634 			DoHighScoreWait();
635 			break;
636 
637 		default:
638 			break;
639 	}
640 }
641 
642 #if NeedFunctionPrototypes
CommandlineHighscorePrint(void)643 void CommandlineHighscorePrint(void)
644 #else
645 void CommandlineHighscorePrint()
646 #endif
647 {
648 	char string[11];
649 	int i;
650 	time_t theTime;
651 
652 	InitialiseHighScores();
653 
654 	/* Must have table initialised with scores */
655 	if (ReadHighScoreTable(GLOBAL) == False)
656 		InitialiseHighScores();
657 
658 	/* Print out a pretty title message for scores */
659 	fprintf(stdout,
660 		"                    __  ___      __     ____                   \n");
661     fprintf(stdout,
662 		"                   / /_/ (_)__ _/ /    / __/______  _______ ___\n");
663     fprintf(stdout,
664 	   "                  / __  / / _ `/ _ \\  _\\ \\/ __/ _ \\/ __/ -_|_-<\n");
665     fprintf(stdout,
666 	   "                 /_/ /_/_/\\_, /_//_/ /___/\\__/\\___/_/  \\__/___/\n");
667     fprintf(stdout,
668 		"                         /___/                                 \n");
669 
670 	fprintf(stdout, "\nRank\tScore\t  Level\tTime\tDate       Name\n");
671 	fprintf(stdout,
672 		"----------------------------------------------------------------\n");
673 
674 	/* Zoom through the highscore table from the top */
675 	for (i = 0; i < NUM_HIGHSCORES; i++)
676 	{
677 		theTime = (time_t) ntohl(highScores[i].time);
678 		strftime(string, 10, "%d %b %y", localtime(&theTime));
679 
680 		if (ntohl(highScores[i].score) > 0)
681 		{
682 			/* Print out the actual record */
683 			fprintf(stdout, "%d\t%ld\t  %ld\t%ld'%ld'%ld\"\t%s  %s\n",
684 				i + 1, ntohl(highScores[i].score),
685 				ntohl(highScores[i].level),
686 				ntohl(highScores[i].gameTime) / 3600,
687 				ntohl(highScores[i].gameTime) / 60,
688 				ntohl(highScores[i].gameTime) % 60,
689 				string, highScores[i].name);
690 		}
691 	}
692 
693 	/* Print a trailing line to make the table look good */
694 	fprintf(stdout,
695 		"----------------------------------------------------------------\n");
696 	fflush(stdout);
697 
698 	/* Now display the personal highscore table */
699 
700 	InitialiseHighScores();
701 
702 	/* Must have table initialised with scores */
703 	if (ReadHighScoreTable(PERSONAL) == False)
704 		InitialiseHighScores();
705 
706 	/* Print out a pretty title message for scores */
707 	fprintf(stdout, "\nPersonal Best\n\n");
708 	fprintf(stdout, "Rank\tScore\t  Level\tTime\tDate       Name\n");
709 	fprintf(stdout,
710 		"----------------------------------------------------------------\n");
711 
712 	/* Zoom through the highscore table from the top */
713 	for (i = 0; i < NUM_HIGHSCORES; i++)
714 	{
715 		theTime = (time_t) ntohl(highScores[i].time);
716 		strftime(string, 10, "%d %b %y", localtime(&theTime));
717 
718 		if (ntohl(highScores[i].score) > 0)
719 		{
720 			/* Print out the actual record */
721 			fprintf(stdout, "%d\t%ld\t  %ld\t%ld'%ld'%ld\"\t%s  %s\n",
722 				i + 1, ntohl(highScores[i].score),
723 				ntohl(highScores[i].level),
724 				ntohl(highScores[i].gameTime) / 3600,
725 				ntohl(highScores[i].gameTime) / 60,
726 				ntohl(highScores[i].gameTime) % 60,
727 				string, highScores[i].name);
728 		}
729 	}
730 
731 	/* Print a trailing line to make the table look good */
732 	fprintf(stdout,
733 		"----------------------------------------------------------------\n");
734 	fflush(stdout);
735 }
736 
737 #if NeedFunctionPrototypes
GetHighScoreRanking(u_long score)738 int GetHighScoreRanking(u_long score)
739 #else
740 int GetHighScoreRanking(score)
741 	u_long score;
742 #endif
743 {
744 	int i;
745 
746 	/* Must have table initialised with scores */
747 	if (ReadHighScoreTable(GLOBAL) == False)
748 		InitialiseHighScores();
749 
750 	/* Zoom through the highscore table from the top */
751 	for (i = 0; i < NUM_HIGHSCORES; i++)
752 	{
753 		/* Is my score better than theirs */
754 		if (score >= ntohl(highScores[i].score))
755 			return (i + 1);
756 	}
757 
758 	/* Not even in highscore table yet! */
759 	return -1;
760 }
761 
762 #if NeedFunctionPrototypes
ShiftScoresDown(int j,u_long score,u_long level,time_t gameTime,char * name)763 static void ShiftScoresDown(int j, u_long score, u_long level,
764 	time_t gameTime, char *name)
765 #else
766 static void ShiftScoresDown(j, score, level, gameTime, name)
767 	int j;
768 	u_long score;
769 	u_long level;
770 	time_t gameTime;
771 	char *name;
772 #endif
773 {
774 	/* This function will shift all score below the index down
775 	 * towards the end and kill off the last dude. Sorry mate.
776 	 */
777 	int i;
778 
779 	/* Move all the scores down one notch */
780 	for (i = NUM_HIGHSCORES-1; i > j; i--)
781 	{
782 		/* Shift the scores down one notch */
783 		highScores[i].score 	= highScores[i-1].score;
784 		highScores[i].level 	= highScores[i-1].level;
785 		highScores[i].time 		= highScores[i-1].time;
786 		highScores[i].gameTime 	= highScores[i-1].gameTime;
787 		highScores[i].userId 	= highScores[i-1].userId;
788 		strcpy(highScores[i].name, highScores[i-1].name);
789 	}
790 
791 	/* Add our new high score to the high score table */
792 	highScores[j].score 	= htonl(score);
793 	highScores[j].level 	= htonl(level);
794 	highScores[j].gameTime 	= htonl(gameTime);
795 	highScores[j].userId 	= htonl(getuid());
796 	highScores[j].time 		= htonl(time(NULL));
797 	strcpy(highScores[j].name, name);
798 }
799 
800 #if NeedFunctionPrototypes
DeleteScore(int j)801 static void DeleteScore(int j)
802 #else
803 static void DeleteScore(j)
804 	int j;
805 #endif
806 {
807 	/* Delete the given score and shift all others up to fill in the hole */
808 	int i;
809 
810 	/* Move all the scores down one notch */
811 	for (i = j; i < NUM_HIGHSCORES-1; i++)
812 	{
813 		/* Shift the scores up one notch */
814 		highScores[i].score 	= highScores[i+1].score;
815 		highScores[i].level 	= highScores[i+1].level;
816 		highScores[i].time 		= highScores[i+1].time;
817 		highScores[i].gameTime 	= highScores[i+1].gameTime;
818 		highScores[i].userId 	= highScores[i+1].userId;
819 		strcpy(highScores[i].name, highScores[i+1].name);
820 	}
821 
822 	highScores[i].score 	= htonl((u_long)0);
823 	highScores[i].level 	= htonl((u_long)1);
824 	highScores[i].gameTime 	= htonl(0);
825 	highScores[i].userId 	= htonl(getuid());
826 	highScores[i].time 		= htonl(time(NULL));
827 	strcpy(highScores[i].name, "To be announced!");
828 }
829 
830 #if NeedFunctionPrototypes
CheckAndAddScoreToHighScore(u_long score,u_long level,time_t gameTime,int type,char * message)831 int CheckAndAddScoreToHighScore(u_long score, u_long level, time_t gameTime,
832 	int type, char *message)
833 #else
834 int CheckAndAddScoreToHighScore(score, level, gameTime, type, message)
835 	u_long score;
836 	u_long level;
837 	time_t gameTime;
838 	int type;
839 	char *message;
840 #endif
841 {
842 	int i;
843 	int id = -1;
844 	char name[80];
845 
846 	/* Lock the file for me only */
847 	if (type == GLOBAL)
848 		id = LockUnlock(LOCK_FILE, -1);
849 
850 	/* Read in the lastest scores */
851 	if (ReadHighScoreTable(type) == False)
852 		InitialiseHighScores();
853 
854 	/* Speed up by obtaining users name */
855 	if (nickName[0] != '\0')
856 		strcpy(name, nickName);
857 	else
858 		strcpy(name, getUsersFullName());
859 
860 	if (type == GLOBAL)
861 	{
862 		/* Go through the highscore table */
863 		for (i = 0; i < NUM_HIGHSCORES; i++)
864 		{
865 			if (ntohl(highScores[i].userId) == getuid())
866 			{
867 				/* Can the last score be added to the highscores */
868 				if (score > ntohl(highScores[i].score))
869 				{
870 					/* Delete and move up all old scores */
871 					DeleteScore(i);
872 
873 					break;
874 				}
875 				else
876 				{
877 					/* Don't add as score is smaller */
878 					if (id != -1)
879 						id = LockUnlock(UNLOCK_FILE, id);
880 					goto doUnlock;
881 				}
882 			}
883 		}	/* for */
884 
885 		/* Now add the new score into the table */
886 		for (i = 0; i < NUM_HIGHSCORES; i++)
887 		{
888 			/* Can the last game be added to the highscores */
889 			if (score > ntohl(highScores[i].score))
890 			{
891 				ShiftScoresDown(i, score, level, gameTime, name);
892 
893 				/* Add the boing master message if on top */
894 				if (i == 0)
895 					SetBoingMasterText(message);
896 
897 				/* Add to the highscore by writing it out */
898 				(void) WriteHighScoreTable(type);
899 
900 				/* Unlock the file now thanks */
901 				if (id != -1)
902 					id = LockUnlock(UNLOCK_FILE, id);
903 
904 				/* Yes - it was placed in the highscore */
905 				return True;
906 			}
907 		}
908 
909 doUnlock:
910 		/* Unlock the file now thanks */
911 		if (id != -1)
912 			id = LockUnlock(UNLOCK_FILE, id);
913 
914 		/* Not even a highscore - loser! */
915 		return False;
916 	}
917 	else	/* Type == PERSONAL */
918 	{
919 		/* Go through the highscore table */
920 		for (i = 0; i < NUM_HIGHSCORES; i++)
921 		{
922 			/* Can the last game be added to the highscores */
923 			if (score > ntohl(highScores[i].score))
924 			{
925 				ShiftScoresDown(i, score, level, gameTime, name);
926 
927 				/* Add to the highscore by writing it out */
928 				(void) WriteHighScoreTable(type);
929 
930 				/* Yes - it was placed in the highscore */
931 				return True;
932 			}
933 		}
934 
935 		/* Not even a highscore - loser! */
936 		return False;
937 	}
938 }
939 
940 #if NeedFunctionPrototypes
SortHighScores(void)941 static void SortHighScores(void)
942 #else
943 static void SortHighScores()
944 #endif
945 {
946 	int i, j;
947 	highScoreEntry tempHighScore;
948 
949 	/*
950 	 * Perform a simple bubble sort of the entries. I haven't used a
951 	 * fancy sorting method as we have only 10 entries to sort.
952 	 */
953 
954 	for (i = 0; i < NUM_HIGHSCORES - 1; ++i)
955 	{
956 		for (j = NUM_HIGHSCORES - 1; j > i; --j)
957 		{
958 			/* Who has the higher score */
959 			if (ntohl(highScores[j-1].score)
960 				< ntohl(highScores[j].score))
961 			{
962 				/* Swap the entries around - use memcpy in future */
963 				tempHighScore.score 		= highScores[j-1].score;
964 				tempHighScore.level 		= highScores[j-1].level;
965 				tempHighScore.gameTime 		= highScores[j-1].gameTime;
966 				tempHighScore.userId 		= highScores[j-1].userId;
967 				strcpy(tempHighScore.name, highScores[j-1].name);
968 
969 				highScores[j-1].score 		= highScores[j].score;
970 				highScores[j-1].level 		= highScores[j].level;
971 				highScores[j-1].gameTime 	= highScores[j].gameTime;
972 				highScores[j-1].userId 		= highScores[j].userId;
973 				strcpy(highScores[j-1].name, highScores[j].name);
974 
975 				highScores[j].score 		= tempHighScore.score;
976 				highScores[j].level 		= tempHighScore.level;
977 				highScores[j].gameTime 		= tempHighScore.gameTime;
978 				highScores[j].userId 		= tempHighScore.userId;
979 				strcpy(highScores[j].name, tempHighScore.name);
980 			}
981 		}
982 	}
983 }
984 
985 #if NeedFunctionPrototypes
InitialiseHighScores(void)986 static void InitialiseHighScores(void)
987 #else
988 static void InitialiseHighScores()
989 #endif
990 {
991 	int i;
992 
993 	/* Setup the header structure */
994 	scoresHeader.version = htonl((u_long) SCORE_VERSION);
995 	SetBoingMasterText("Anyone play this game?");
996 
997 	/* Loop down table clearing everything out */
998 	for (i = 0; i < NUM_HIGHSCORES; i++)
999 	{
1000 		/* Null out all entries */
1001 		highScores[i].score 	= htonl((u_long) 0);
1002 		highScores[i].level 	= htonl((u_long) 1);
1003 		highScores[i].gameTime 	= htonl(0);
1004 		highScores[i].userId 	= htonl(0);
1005 		highScores[i].time 		= htonl(time(NULL));
1006 		strcpy(highScores[i].name, "To be announced!");
1007 	}
1008 }
1009 
1010 #if NeedFunctionPrototypes
ReadHighScoreTable(int type)1011 int ReadHighScoreTable(int type)
1012 #else
1013 int ReadHighScoreTable(type)
1014 	int type;
1015 #endif
1016 {
1017 	/* Read the high score table into memory structure */
1018 	FILE *hsfp;
1019 	int i;
1020 	char filename[MAXPATHLEN];
1021 	char *str;
1022 
1023 	/* Are we going to use the global or personal highscore file */
1024 	if (type == GLOBAL)
1025 	{
1026 		/* Use the environment variable if it exists */
1027 		if ((str = getenv("XBOING_SCORE_FILE")) != NULL)
1028 			strncpy(filename, str, sizeof(filename)-1);
1029 		else
1030 			strcpy(filename, HIGH_SCORE_FILE);
1031 	}
1032 	else
1033 		sprintf(filename, "%s/.xboing-scores", GetHomeDir());
1034 
1035 	/* Open the high score file */
1036     if ((hsfp = fopen(filename, "r")) == NULL)
1037 	{
1038 		/* Cannot open the high score file */
1039 		WarningMessage("Cannot open high score file for reading.");
1040 		return False;
1041 	}
1042 
1043 	/* Write the highscore header */
1044    	if (fread((char*)&scoresHeader, sizeof(highScoreHeader), 1, hsfp) != 1)
1045 	{
1046 		if (fclose(hsfp) < 0)
1047 			WarningMessage("Cannot close high score file.");
1048 		return False;
1049 	}
1050 
1051 	if (ntohl(scoresHeader.version) != (u_long) SCORE_VERSION)
1052 	{
1053 		WarningMessage("Old version of high score files encountered.");
1054 		if (fclose(hsfp) < 0)
1055 			WarningMessage("Cannot close high score file.");
1056 		return False;
1057 	}
1058 
1059 	/* Read all high score entries */
1060 	for (i = 0; i < NUM_HIGHSCORES; i++)
1061 	{
1062 		/* Read the highscore entry */
1063     	if (fread((char*)&highScores[i], sizeof(highScoreEntry), 1, hsfp) != 1)
1064 		{
1065 			if (fclose(hsfp) < 0)
1066 				WarningMessage("Cannot close high score file.");
1067 			return False;
1068 		}
1069 	}
1070 
1071 	/* Close the high score file */
1072 	if (fclose(hsfp) < 0)
1073 		WarningMessage("Cannot close high score file.");
1074 
1075 	return True;
1076 }
1077 
1078 
1079 #if NeedFunctionPrototypes
WriteHighScoreTable(int type)1080 int WriteHighScoreTable(int type)
1081 #else
1082 int WriteHighScoreTable(type)
1083 	int type;
1084 #endif
1085 {
1086 	/* write the high score table to the high score file */
1087 	FILE *hsfp;
1088 	int i;
1089 	char filename[MAXPATHLEN];
1090 	char *str;
1091 
1092 	/* Make sure the table is sorted */
1093 	SortHighScores();
1094 
1095 	/* Are we going to use the global or personal highscore file */
1096 	if (type == GLOBAL)
1097 	{
1098 		/* Use the environment variable if it exists */
1099 		if ((str = getenv("XBOING_SCORE_FILE")) != NULL)
1100 			strncpy(filename, str, sizeof(filename)-1);
1101 		else
1102 			strcpy(filename, HIGH_SCORE_FILE);
1103 	}
1104 	else
1105 		sprintf(filename, "%s/.xboing-scores", GetHomeDir());
1106 
1107 	/* Open the high score file */
1108     if ((hsfp = fopen(filename, "w+")) == NULL)
1109 	{
1110 		/* Cannot open the high score file */
1111 		WarningMessage("Cannot open high score file for writing.");
1112 		return False;
1113 	}
1114 
1115 	/* Setup the header */
1116 	scoresHeader.version = htonl((u_long)SCORE_VERSION);
1117 
1118 	/* Write the highscore header */
1119    	if (fwrite((char*)&scoresHeader, sizeof(highScoreHeader), 1, hsfp) != 1)
1120 	{
1121 		if (fclose(hsfp) < 0)
1122 			WarningMessage("Cannot close high score file.");
1123 		return False;
1124 	}
1125 
1126 	/* Write out all high score entries */
1127 	for (i = 0; i < NUM_HIGHSCORES; i++)
1128 	{
1129 		/* Write the highscore entry */
1130     	if (fwrite((char*)&highScores[i], sizeof(highScoreEntry), 1, hsfp) != 1)
1131 		{
1132 			if (fclose(hsfp) < 0)
1133 				WarningMessage("Cannot close high score file.");
1134 			return False;
1135 		}
1136 	}
1137 
1138 	/* Close the high score file */
1139 	if (fclose(hsfp) < 0)
1140 		WarningMessage("Cannot close high score file.");
1141 
1142 	return True;
1143 }
1144 
1145 #if NeedFunctionPrototypes
RedrawHighScore(Display * display,Window window)1146 void RedrawHighScore(Display *display, Window window)
1147 #else
1148 void RedrawHighScore(display, window)
1149 	Display *display;
1150 	Window window;
1151 #endif
1152 {
1153 	/* Draw the title screen for highscore table */
1154 	DoTitle(display, window);
1155 }
1156 
1157 #if NeedFunctionPrototypes
FreeHighScore(Display * display)1158 void FreeHighScore(Display *display)
1159 #else
1160 void FreeHighScore(display)
1161 	Display *display;
1162 #endif
1163 {
1164 	/* Free up those memory leaks thanks */
1165 	if (titlePixmap) 	XFreePixmap(display, titlePixmap);
1166 	if (titlePixmapM) 	XFreePixmap(display, titlePixmapM);
1167 }
1168 
1169 #if NeedFunctionPrototypes
ResetHighScore(int type)1170 void ResetHighScore(int type)
1171 #else
1172 void ResetHighScore(type)
1173 	int type;
1174 #endif
1175 {
1176 	HighScoreState = HIGHSCORE_TITLE;
1177 	nextFrame = frame + 100;
1178 	endFrame = frame + 4000;
1179 
1180 	/* Reset the sparkles for the names */
1181 	sparkley = 200 + textFont->ascent + 20;
1182 	sindex = 0;
1183 	si = 0;
1184 	scoreType = type;
1185 
1186 	DEBUG("Reset highscore mode.")
1187 }
1188 
1189 #if NeedFunctionPrototypes
LockUnlock(int cmd,int fd)1190 static int LockUnlock(int cmd, int fd)
1191 #else
1192 static int LockUnlock(cmd)
1193 	int cmd, fd;
1194 #endif
1195 {
1196 	static int 	inter = -1;
1197 	char 		filename[1024];
1198 	char 		*str;
1199 	int			theCmd;
1200 
1201 
1202 #ifndef NO_LOCKING
1203 
1204 	/* Suss out what command to use */
1205 	if (cmd == LOCK_FILE)
1206 #ifndef USE_FLOCK
1207 		theCmd = F_LOCK;
1208 #else
1209 		theCmd = LOCK_EX;
1210 #endif
1211 	else
1212 #ifndef USE_FLOCK
1213 		theCmd = F_ULOCK;
1214 #else
1215 		theCmd = LOCK_UN;
1216 #endif
1217 
1218 #endif /* NO_LOCKING */
1219 
1220 
1221 	/* Use the environment variable if it exists */
1222 	if ((str = getenv("XBOING_SCORE_FILE")) != NULL)
1223 		strncpy(filename, str, sizeof(filename)-1);
1224 	else
1225 		strcpy(filename, HIGH_SCORE_FILE);
1226 
1227 	/* Open the highscore file for both read & write */
1228 	if (cmd == LOCK_FILE)
1229 		inter = open(filename, O_CREAT | O_RDWR, 0666);
1230 	else
1231 		/* use old fd to unlock */
1232 		inter = fd;
1233 
1234 #ifndef NO_LOCKING
1235 
1236 	/* Ok - if successful then lock or unlock the file */
1237 	if (inter != -1)
1238 #ifndef USE_FLOCK
1239 		lockf(inter, theCmd, sizeof(highScores));
1240 #else
1241 		flock(inter, theCmd);
1242 #endif
1243 
1244 #endif /* NO_LOCKING */
1245 
1246 
1247 	/* Are we unlocking the file */
1248 	if (cmd == UNLOCK_FILE)
1249 	{
1250 		/* Close the file now thanks */
1251 		close(inter);
1252 		inter = -1;
1253 	}
1254 
1255 	/* Return success status */
1256 	return inter;
1257 }
1258