Codebase list icebreaker / HEAD hiscore.c
HEAD

Tree @HEAD (Download .tar.gz)

hiscore.c @HEADraw · history · blame

/*
* IceBreaker
* Copyright (c) 2000-2020 Matthew Miller <mattdm@mattdm.org> and 
*  Enrico Tassi <gareuselesinge@infinito.it>
*
* <http://www.mattdm.org/icebreaker/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include "icebreaker.h"
#include "globals.h"
#include "hiscore.h"
#include "lock.h"

char hiscorename[HISCORENUM][50];
long hiscoreval[HISCORENUM];

static char temphiscorename[HISCORENUM+1][50]; //used for sorting
static long temphiscoreval[HISCORENUM+1];

// used if we can't get write access to the high score file immediately
static char delayedhiscorename[HISCORENUM+1][50]; 
static long delayedhiscoreval[HISCORENUM+1];

static int cmpscore(int * a, int * b);
static void sorthiscore(char hiname[HISCORENUM][50],long hival[HISCORENUM],char * username, long score);
static void writehiscores(char * username, long thisgamescore);
static void adddelayedhiscore(char* username,long finalscore);

void inithiscores()
{
	int i;
	readhiscores();

	// zero out the just-in-case array
	for (i=0;i<HISCORENUM;i++)
	{
		delayedhiscoreval[i]=0;
	}
}

void readhiscores()
{
	FILE *hiscorefile;
	char linebuf[80];
	char filename[274]; // fix -- use defined OS constant

	int arrayindex[HISCORENUM];
	int i;

	// fill the "helper" array. 
	for (i=0;i<HISCORENUM;i++)
		arrayindex[i]=i;
		

	// make sure all entries are zeroed out to start.
	for (i=0;i<HISCORENUM;i++)
	{
		snprintf(temphiscorename[i],7,"Nobody");
		temphiscoreval[i]=100; //100 is better than 0. :)
	}

	snprintf(filename,274,"%s/%s",homedir,HISCOREFILE);

	hiscorefile=fopen(filename,"r");
	if (hiscorefile==NULL)
	{
		// It's writing we need to worry about, really, so don't
		// complain here.
		//fprintf(stderr,"Can't read high score file; continuing anyway.\nYou may want to ask your sysadmin to make sure this program can access\n " homedir "/" HISCOREFILE "\n");
	}
	else
	{
		for (i=0;i<HISCORENUM;i++)
		{
			if (fgets(linebuf,80,hiscorefile))
			{
				sscanf(linebuf,"%49s %30ld",temphiscorename[i],&temphiscoreval[i]);
			}
		}
		fclose(hiscorefile);
		
		// sort arrayindex based on the corresponding hiscoreval
		// really, the array should already be sorted. but you never know.	
		qsort(arrayindex, HISCORENUM, sizeof(int), (int (*)(const void*,const void*))cmpscore);

	}
	
	// ok, so now, we can copy things over in the proper sorted order
	for (i=0;i<HISCORENUM;i++)
	{
		snprintf(hiscorename[i],50,"%s",temphiscorename[arrayindex[i]]);
		hiscoreval[i]=temphiscoreval[arrayindex[i]];
	}

}


int checkhiscore(long thisgamescore)
{
	// need to re-read from disk in case another user has obtained
	// a better score in the meantime...
	readhiscores();
	// check to see if score is better than the lowest high score
	return (thisgamescore>hiscoreval[HISCORENUM-1]);
}

int addhiscore(char* username, long finalscore, int candelay)
{
	int gotlock=false;
	int rc=-1;
	FILE_DESC filelock;
	char filename[274]; // fix -- use defined OS constant
	
	snprintf(filename,274,"%s/%s",homedir,HISCORELOCKFILE);

	filelock = openlockfile(filename);
	if (filelock == INVALID_FILE_DESC)
	{
		 fprintf(stderr,"Can't access lock file -- this means we can't save the high scores.\n"
	 	                "You may want to ask your sysadmin to make sure this program can write to\n"
		                "<%s>.\n",filename);
	}
	else
	{
		gotlock = lock(filelock);
		if (!gotlock)
		{
			if (checkhiscore(finalscore))
				writehiscores(username,finalscore);
			else
				rc=0;

			gotlock=unlock(filelock);

			if (gotlock)
			{
				fprintf(stderr,"Warning: something very strange has happened. This isn't good.\n"
				               "Can't unlock <%s>!\n",filename);
		        }     
		}
		else if (candelay)
		{
			adddelayedhiscore(username,finalscore);
		}
		else
		{
			fprintf(stderr,"Someone is holding onto the lock file and we can't save high scores, so\n"
			               "despite our best efforts, %s's high score of %ld was dropped.\n",username,finalscore);
		}	

		closelockfile(filelock);
	}

	return rc;

}

void writedelayedhiscores()
{
	int i;

	for (i=0;i<HISCORENUM;i++)
	{
		if(delayedhiscoreval[i]!=0) // fix -- we're done if this happens, right? 
		{
			if(!addhiscore(delayedhiscorename[i],delayedhiscoreval[i],false))
			{
				// I think it's let better to let people figure this
				// out by themselves than to clutter up stderr -- they
				// can just compare and see that the score was beaten.
				//fprintf(stderr,"Sorry: %s %ld was a high score once, but now it's been beaten.\n",delayedhiscorename[i],delayedhiscoreval[i]);
			}
			else
			{
				// pointless at the end of the game, but needed if this function
				// were to be called midgame.
				delayedhiscoreval[i]=0;
			}
		
		}
	}
}

void sorthiscore(char hiname[HISCORENUM][50],long hival[HISCORENUM],char * username, long score)
{
	int arrayindex[HISCORENUM+1]; // note the +1 -- we're including the new score
	int i;

	// make sure the temp array contains the right data
	for (i=0;i<HISCORENUM;i++)
	{
		snprintf(temphiscorename[i],50,"%s",hiname[i]);
		temphiscoreval[i]=hival[i];
	}
	
	// and toss in the new data 
	//(this is why these arrays are size HISCORENUM+1)
	snprintf(temphiscorename[HISCORENUM],50,"%s",username);
	temphiscoreval[HISCORENUM]=score;

	// fill the "helper" array. 
	for (i=0;i<HISCORENUM+1;i++)
		arrayindex[i]=i;

	
	// ok, now sort those 
	qsort(arrayindex, HISCORENUM+1, sizeof(int), (int (*)(const void*,const void*))cmpscore);

	// and take the top ones back.
	for (i=0;i<HISCORENUM;i++)
	{
		snprintf(hiname[i],50,"%s",temphiscorename[arrayindex[i]]); 
		hival[i]=temphiscoreval[arrayindex[i]];
	}
}

void writehiscores(char * username, long thisgamescore)
{
	FILE *hiscorefile;
	char filename[274]; // fix -- use defined OS constant
	int i;

	sorthiscore(hiscorename,hiscoreval,username,thisgamescore);

	snprintf(filename,274,"%s/%s",homedir,HISCOREFILE);
	
	hiscorefile=fopen(filename,"w");
	if (hiscorefile==NULL)
	{
		fprintf(stderr,"Can't save high scores."
		               "You may want to ask your sysadmin to make sure this program can write to\n"
	                       "<%s>.\n",filename);
	}
	else
	{
		// FIX -- make this go so people can have spaces in their names
		//for (i=0;hiscorename[i]!='\0';i++)
		//      if (hiscorename[i]==' ') hiscorename[i]='_';
	
		for (i=0;i<HISCORENUM;i++)
		{
			fprintf(hiscorefile,"%s %ld\n",hiscorename[i],hiscoreval[i]);
		}
			
		fclose(hiscorefile);
	}
}

void adddelayedhiscore(char* username,long finalscore)
{
	// int i;

	sorthiscore(delayedhiscorename,delayedhiscoreval,username,finalscore);

	//Uncomment for debugging
	/*for (i=0;i<HISCORENUM;i++)
	{
		if(delayedhiscoreval[i] != 0)
			printf("%2d : %s %ld\n",i,delayedhiscorename[i],delayedhiscoreval[i]);
	} */             
}

int cmpscore(int * a, int * b)
{
	return(temphiscoreval[*b] - temphiscoreval[*a]);
}