1 /* Tower Toppler - Nebulus
2  * Copyright (C) 2000-2012  Andreas R�ver
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18 
19 #include "highscore.h"
20 #include "decl.h"
21 #include "screen.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 
30 #ifdef __QNXNTO__
31    #include <strings.h>
32 #endif // __QNXNTO__
33 
34 #define NUMHISCORES 10
35 
36 #define SCOREFNAME "toppler.hsc"
37 
38 /* the group ids of the game */
39 static gid_t UserGroupID, GameGroupID;
40 
41 /* true, if we use the global highscore table, false if not */
42 static bool globalHighscore;
43 
44 /* the name of the highscore table we use the name because the
45  * file might change any time and so it's better to close and reopen
46  * every time we need access
47  */
48 static char highscoreName[200];
49 
50 typedef struct {
51   Uint32 points;
52   char name[SCORENAMELEN+1];
53   Sint16 tower; /* tower reached, -1 = mission finished */
54 } _scores;
55 
56 static _scores scores[NUMHISCORES];
57 
58 /* this is the name of the surrenlty selected mission */
59 static char missionname[100];
60 
61 #ifdef WIN32
62 #define setegid(x)
63 #endif
64 
savescores(FILE * f)65 static void savescores(FILE *f) {
66 
67   unsigned char len;
68   char mname[256];
69 
70   while (!feof(f)) {
71 
72     if ((fread(&len, 1, 1, f) == 1) &&
73         (fread(mname, 1, len, f) == len)) {
74       mname[len] = 0;
75       if (strcasecmp(mname, missionname) == 0) {
76 
77         // this is necessary because some system can not switch
78         // on the fly from reading to writing
79         fseek(f, ftell(f), SEEK_SET);
80 
81         fwrite(scores, sizeof(_scores)*NUMHISCORES, 1, f);
82         return;
83       }
84     } else
85       break;
86 
87     fseek(f, ftell(f) + sizeof(_scores)*NUMHISCORES, SEEK_SET);
88   }
89 
90   unsigned char tmp = strlen(missionname);
91 
92   fwrite(&tmp, 1, 1, f);
93   fwrite(missionname, 1, tmp, f);
94   fwrite(scores, sizeof(_scores)*NUMHISCORES, 1, f);
95 }
96 
loadscores(FILE * f)97 static void loadscores(FILE *f) {
98 
99   unsigned char len;
100   char mname[256];
101 
102   while (f && !feof(f)) {
103 
104     if ((fread(&len, 1, 1, f) == 1) &&
105         (fread(mname, 1, len, f) == len) &&
106         (fread(scores, 1, sizeof(_scores) * NUMHISCORES, f) == sizeof(_scores) * NUMHISCORES)) {
107       mname[len] = 0;
108       if (strcasecmp(mname, missionname) == 0)
109         return;
110     }
111   }
112 
113   for (int t = 0; t < NUMHISCORES; t++) {
114     scores[t].points = 0;
115     scores[t].name[0] = 0;
116   }
117 }
118 
hsc_lock(void)119 static bool hsc_lock(void) {
120 
121 #ifndef WIN32
122 
123   if (globalHighscore) {
124 
125     setegid(GameGroupID);
126     int lockfd;
127 
128     while ((lockfd = open(HISCOREDIR "/" SCOREFNAME ".lck", O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR)) == -1) {
129       dcl_wait();
130       scr_swap();
131     }
132     close(lockfd);
133     setegid(UserGroupID);
134   }
135 
136 #endif
137 
138   if (globalHighscore) setegid(GameGroupID);
139   FILE *f = fopen(highscoreName, "rb");
140   if (globalHighscore) setegid(UserGroupID);
141 
142   loadscores(f);
143 
144   if (f) fclose(f);
145 
146   return true;
147 }
148 
hsc_unlock(void)149 static void hsc_unlock(void) {
150 
151 #ifndef WIN32
152 
153   if (globalHighscore) {
154     setegid(GameGroupID);
155     unlink(HISCOREDIR "/" SCOREFNAME ".lck");
156     setegid(UserGroupID);
157   }
158 
159 #endif
160 }
161 
hsc_init(void)162 void hsc_init(void) {
163 
164   for (int t = 0; t < NUMHISCORES; t++) {
165     scores[t].points = 0;
166     scores[t].name[0] = 0;
167     scores[t].tower = 0;
168   }
169 
170 #ifndef WIN32
171 
172   /* fine at first save the group ids and drom group privileges */
173   UserGroupID = getgid ();
174   GameGroupID = getegid ();
175 
176   setegid(UserGroupID);
177 
178   /* asume we use local highscore table */
179   globalHighscore = false;
180   snprintf(highscoreName, 199, "%s/.toppler/%s", homedir(), SCOREFNAME);
181 
182   /* now check if we have access to a global highscore table */
183 
184 #ifdef HISCOREDIR
185 
186   /* ok the dir is given, we need to be able to do 2 things: */
187 
188   /* 1. get read and write access to the file */
189 
190   char fname[200];
191   snprintf(fname, 200, HISCOREDIR "/" SCOREFNAME);
192 
193   setegid(GameGroupID);
194   FILE * f = fopen(fname, "r+");
195   setegid(UserGroupID);
196 
197   if (f) {
198 
199     fclose(f);
200 
201     /* 2. get write access to the directory to create the lock file
202      * to check this we try to chreate a file with a random name
203      */
204     snprintf(fname, 200, HISCOREDIR "/" SCOREFNAME "%i", rand());
205 
206     setegid(GameGroupID);
207     f = fopen(fname, "w+");
208     setegid(UserGroupID);
209 
210     if (f) {
211 
212       fclose(f);
213       setegid(GameGroupID);
214       unlink(fname);
215       setegid(UserGroupID);
216 
217       /* ok, we've got all the rights we need */
218       snprintf(highscoreName, 200, HISCOREDIR "/" SCOREFNAME);
219       globalHighscore = true;
220     }
221   }
222 
223 #endif
224 
225   /* no dir to the global highscore table -> not global highscore table */
226 
227   if (globalHighscore)
228     debugprintf(2, "using global highscore at %s\n", highscoreName);
229   else
230     debugprintf(2, "using local highscore at %s\n", highscoreName);
231 
232 #else // ifdef WIN32
233 
234   /* for non unix systems we use only local highscore tables */
235   globalHighscore = false;
236   snprintf(highscoreName, 200, SCOREFNAME);
237 
238 #endif
239 
240 }
241 
hsc_select(const char * mission)242 void hsc_select(const char * mission) {
243   strncpy(missionname, mission, 100);
244 
245   if (globalHighscore) setegid(GameGroupID);
246   FILE *f = fopen(highscoreName, "rb");
247   if (globalHighscore) setegid(UserGroupID);
248 
249   loadscores(f);
250 
251   if (f) fclose(f);
252 }
253 
hsc_entries(void)254 Uint8 hsc_entries(void) { return NUMHISCORES; }
255 
hsc_entry(Uint8 nr,char * name,Uint32 * points,Uint8 * tower)256 void hsc_entry(Uint8 nr, char *name, Uint32 *points, Uint8 *tower) {
257 
258   if (nr < NUMHISCORES) {
259     if (name) strncpy(name, scores[nr].name, SCORENAMELEN);
260     if (points) *points = scores[nr].points;
261     if (tower) *tower = scores[nr].tower;
262   } else {
263     if (name) name[0] = 0;
264     if (points) *points = 0;
265     if (tower) *tower = 0;
266   }
267 }
268 
hsc_canEnter(Uint32 points)269 bool hsc_canEnter(Uint32 points) {
270   return points > scores[NUMHISCORES-1].points;
271 }
272 
hsc_enter(Uint32 points,Uint8 tower,char * name)273 Uint8 hsc_enter(Uint32 points, Uint8 tower, char *name) {
274 
275   if (hsc_lock()) {
276 
277     int t = NUMHISCORES;
278 
279     while ((t > 0) && (points > scores[t-1].points)) {
280       if (t < NUMHISCORES)
281         scores[t] = scores[t-1];
282       t--;
283     }
284 
285     if (t < NUMHISCORES) {
286 
287       strncpy(scores[t].name, name, SCORENAMELEN);
288       scores[t].points = points;
289       scores[t].tower = tower;
290 
291       FILE *f;
292 
293       if (globalHighscore) {
294         if (globalHighscore) setegid(GameGroupID);
295         f = fopen(highscoreName, "r+b");
296         if (globalHighscore) setegid(UserGroupID);
297       } else {
298 
299         /* local highscore: this one might require creating the file */
300         fclose(fopen(highscoreName, "a+"));
301         f = fopen(highscoreName, "r+b");
302       }
303 
304       savescores(f);
305       fclose(f);
306 
307       hsc_unlock();
308 
309       return t;
310     }
311 
312     hsc_unlock();
313   }
314 
315   return 0xff;
316 }
317 
318