1 /*------------------------------------------------------------------.
2 | Copyright 1997, 1998, 2000, 2001 Alexandre Duret-Lutz |
3 | <duret_g@epita.fr> |
4 | |
5 | This file is part of Heroes. |
6 | |
7 | Heroes is free software; you can redistribute it and/or modify it |
8 | under the terms of the GNU General Public License version 2 as |
9 | published by the Free Software Foundation. |
10 | |
11 | Heroes is distributed in the hope that it will be useful, but |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with this program; if not, write to the Free Software |
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
19 | 02111-1307 USA |
20 `------------------------------------------------------------------*/
21
22 #include "system.h"
23 #include "structs.h"
24 #include "scores.h"
25 #include "misc.h"
26 #include "userdir.h"
27 #include "debugmsg.h"
28 #include "rsc_files.h"
29 #include "getshline.h"
30 #include "errors.h"
31 #include "persona.h"
32 #include "filelock.h"
33
34 top_score highs[5][10];
35
36 static char *name = 0;
37 static FILE *fscores = 0;
38
39 void
init_scores(void)40 init_scores (void)
41 {
42 name = sys_persona_if_needed ("score-file", "a+t");
43 dmsg (D_SYSTEM | D_FILE, "opening %s", name);
44 fscores = fopen (name, "a+t");
45 if (!fscores)
46 dperror ("fopen");
47 user_persona ();
48 }
49
50 void
reinit_scores_if_needed(void)51 reinit_scores_if_needed (void)
52 {
53 char *newname = get_non_null_rsc_file ("score-file");
54 if (strcmp (newname, name)) {
55 free_scores ();
56 init_scores ();
57 }
58 free (newname);
59 }
60
61 static int
cmp_scores(const void * r1,const void * r2)62 cmp_scores (const void *r1, const void *r2)
63 {
64 return (((const top_score *) r2)->points - ((const top_score *) r1)->points);
65 }
66
67 void
sort_scores(void)68 sort_scores (void)
69 {
70 int i;
71 for (i = 0; i < 5; i++)
72 qsort (highs[i], 10, sizeof (top_score), cmp_scores);
73 }
74
75 int
find_score_by_gameid(gameid_ptr gid)76 find_score_by_gameid (gameid_ptr gid)
77 {
78 int i;
79 for (i = 0; i < 10; i++)
80 if (equal_gameid (highs[0][i].gid, gid))
81 return (i);
82 return (-1);
83 }
84
85 void
clear_scores(void)86 clear_scores (void)
87 {
88 int i, j;
89 for (i = 0; i < 5; i++)
90 for (j = 0; j < 10; j++) {
91 strncpy (highs[i][j].name, "\0", 8);
92 empty_gameid (highs[i][j].gid);
93 highs[i][j].points = (10 - j) * 250;
94 }
95 }
96
97 void
write_scores_locked(void)98 write_scores_locked (void)
99 {
100 unsigned int i, j;
101
102 if (fscores == 0) {
103 wmsg (_("cannot write %s"), name);
104 } else {
105 fflush (fscores);
106 if (ftruncate (fileno (fscores), 0) != 0)
107 emsg (_("%s: truncate error"), name);
108 }
109
110 if (fscores) {
111 dmsg (D_FILE, "writing scores to %s", name);
112
113 /* Write down scores to disk. */
114 for (i = 0; i < 5; ++i)
115 for (j = 0; j < 10; ++j) {
116 char *gidtxt = gameid_to_text (highs[i][j].gid);
117 fprintf (fscores, "%u %u %u\n %s\n %s\n",
118 i, j, highs[i][j].points, gidtxt, highs[i][j].name);
119 free (gidtxt);
120 }
121 /* We need to seek to the beginning of file before
122 calling file_unlock on some systems. */
123 fseek (fscores, 0L, SEEK_SET);
124 dmsg (D_FILE | D_SYSTEM, "unlocking %s", name);
125 file_unlock (fileno (fscores));
126 }
127 }
128
129 static void
load_scores_seek(bool exclusive)130 load_scores_seek (bool exclusive)
131 {
132 dmsg (D_FILE, "reading scores from %s", name);
133 if (fscores) {
134 fseek (fscores, 0L, SEEK_SET);
135 dmsg (D_FILE | D_SYSTEM, "locking %s", name);
136 file_lock (fileno (fscores), exclusive);
137 }
138 }
139
140 void
write_scores(void)141 write_scores (void)
142 {
143 load_scores_seek (true);
144 write_scores_locked ();
145 }
146
147 static void
load_scores_error(int line)148 load_scores_error (int line)
149 {
150 wmsg (_("%s:%d: parse error. Clearing score table."), name, line);
151 clear_scores ();
152 }
153
154 static void
load_scores_read(void)155 load_scores_read (void)
156 {
157 unsigned int i, j;
158 a_u32 points;
159 int endline = 0;
160 char* buf = 0;
161 size_t bufsize = 0;
162 int firstline = 0;
163
164 clear_scores ();
165
166 if (fscores == 0)
167 return;
168
169 /* Read the scores from disk. */
170 while (getshline_numbered
171 (&firstline, &endline, &buf, &bufsize, fscores) != -1) {
172 if (*buf != ' ') {
173 if (sscanf (buf, "%u %u %u", &i, &j, &points) != 3
174 || i >= 5
175 || j >= 10) {
176 load_scores_error (firstline);
177 return;
178 }
179 highs[i][j].points = points;
180 } else if (buf[1] != ' ') {
181 if (text_to_gameid (buf + 1, highs[i][j].gid)) {
182 load_scores_error (firstline);
183 return;
184 }
185 } else {
186 strncpy (highs[i][j].name, buf + 2, PLAYER_NAME_SIZE + 1);
187 chomp (highs[i][j].name);
188 }
189 }
190 free (buf);
191 }
192
193 void
load_scores(void)194 load_scores (void)
195 {
196 load_scores_seek (false);
197 load_scores_read ();
198 if (fscores) {
199 /* We need to seek to the beginning of file before
200 calling file_unlock on some systems. */
201 fseek (fscores, 0L, SEEK_SET);
202 dmsg (D_FILE | D_SYSTEM, "unlocking %s", name);
203 file_unlock (fileno (fscores));
204 }
205 }
206
207 void
load_scores_and_keep_locked(void)208 load_scores_and_keep_locked (void)
209 {
210 load_scores_seek (true);
211 load_scores_read ();
212 if (fscores)
213 fseek (fscores, 0L, SEEK_SET);
214 }
215
216 void
free_scores(void)217 free_scores (void)
218 {
219 dmsg (D_MISC, "free scores");
220
221 if (name)
222 free (name);
223 name = 0;
224
225 if (fscores) {
226 fclose (fscores);
227 fscores = 0;
228 }
229 }
230
231 /* Insert an score entry in the score table.
232 If NAME is NULL, we just return true or false, whether
233 the score would have been inserted or not (if it's too low). */
234 bool
insert_scores(int gamemode,const char * player_name,a_gameid gid,a_u32 points)235 insert_scores (int gamemode, const char *player_name,
236 a_gameid gid, a_u32 points)
237 {
238 int mag = find_score_by_gameid (gid);
239 /* If we are not goiing to override the score for an existing game,
240 override the last entry of the game... */
241 if (mag == -1)
242 mag = 9;
243 /* ... unless the score is too low, of course. */
244 if (highs[gamemode][mag].points >= points)
245 mag = -1;
246
247 if (mag == -1)
248 return false;
249
250 if (player_name) {
251 copy_gameid (highs[gamemode][mag].gid, gid);
252 highs[gamemode][mag].points = points;
253 strcpy (highs[gamemode][mag].name, player_name);
254 sort_scores ();
255 }
256 return true;
257 }
258