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