1 /*
2 * Copyright (c) 2000 Mark B. Allan. All rights reserved.
3 *
4 * "Chromium B.S.U." is free software; you can redistribute
5 * it and/or use it and/or modify it under the terms of the
6 * "Clarified Artistic License"
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include <chromium-bsu-config.h>
11 #endif
12
13 #include "gettext.h"
14
15 #include "HiScore.h"
16 #include "extern.h"
17
18 #include "Config.h"
19
20 #include "HeroAircraft.h"
21
22 //====================================================================
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26 #include <ctime>
27
28 #ifdef HAVE_LOCALE_H
29 #include <locale.h>
30 #endif
31
32 HiScore *HiScore::instance = 0;
33
34 static const char* header = "# Chromium B.S.U. high scores file: skill rank score date time(UTC) name (do not remove this comment line)\n";
35
36 /**
37 * initialize variables, then read high score file
38 */
39 //----------------------------------------------------------
HiScore()40 HiScore::HiScore()
41 {
42 int i,j;
43 for(i = 0; i < 10; i++)
44 {
45 for(j = 0; j < HI_SCORE_HIST; j++)
46 {
47 //-- default high scores
48 switch(j)
49 {
50 case 0: hiScore[i][j] = 250000.0; break;
51 case 1: hiScore[i][j] = 200000.0; break;
52 case 2: hiScore[i][j] = 150000.0; break;
53 case 3: hiScore[i][j] = 100000.0; break;
54 case 4: hiScore[i][j] = 50000.0; break;
55 default: hiScore[i][j] = 99.0; break;
56 }
57 //-- default player
58 sprintf(hiScoreName[i][j], _("nobody"));
59 //-- default date (01/01/2000);
60 hiScoreDate[i][j] = 946713600;
61 }
62 }
63 readFile();
64 }
65
~HiScore()66 HiScore::~HiScore()
67 {
68 }
69
70 /**
71 * create single HiScore object
72 * @returns HiScore::instance
73 */
74 //----------------------------------------------------------
init()75 HiScore *HiScore::init()
76 {
77 if(!HiScore::instance)
78 {
79 HiScore::instance = new HiScore;
80 }
81 else
82 {
83 fprintf(stderr, _("WARNING: HiScore::init() has already been called.\n"));
84 }
85 return HiScore::instance;
86 }
87
88 /**
89 * @returns HiScore::instance
90 */
91 //----------------------------------------------------------
getInstance()92 HiScore *HiScore::getInstance()
93 {
94 if(!instance)
95 {
96 return HiScore::init();
97 }
98 else
99 return HiScore::instance;
100 }
101
102 /**
103 * deletes singleton instance and sets HiScore::instance to 0.
104 */
105 //----------------------------------------------------------
destroy()106 void HiScore::destroy()
107 {
108 delete HiScore::instance;
109 HiScore::instance = 0;
110 }
111
112 /**
113 * return score for given level and index.
114 * @returns score, or -1 if skill,index is out of range
115 */
116 //----------------------------------------------------------
getScore(int skill,int index)117 double HiScore::getScore(int skill, int index)
118 {
119 double retVal = -1.0;
120 if(skill > 0 && skill < 10)
121 if(index >= 0 && index < HI_SCORE_HIST)
122 retVal = hiScore[skill][index];
123 return retVal;
124 }
125
126 /**
127 * return high scorer's name for given level and index.
128 * @returns name, or "OUT_OF_RANGE" if skill,index is out of range
129 */
130 //----------------------------------------------------------
getName(int skill,int index)131 const char *HiScore::getName(int skill, int index)
132 {
133 const char *retVal = "OUT_OF_RANGE";
134 if(skill > 0 && skill < 10)
135 if(index >= 0 && index < HI_SCORE_HIST)
136 retVal = hiScoreName[skill][index];
137 return retVal;
138 }
139
140 /**
141 * return date of high score for given level and index.
142 * @returns date (int time_t format), or 0 if skill,index is out of range
143 */
144 //----------------------------------------------------------
getDate(int skill,int index)145 time_t HiScore::getDate(int skill, int index)
146 {
147 int retVal = 0;
148 if(skill > 0 && skill < 10)
149 if(index >= 0 && index < HI_SCORE_HIST)
150 retVal = hiScoreDate[skill][index];
151 return retVal;
152 }
153
154 /**
155 * If CHROMIUM_BSU_SCORE environment variable is set, that
156 * filename will be used. Otherwise, the default score file.
157 * @returns name of score file
158 */
159 //----------------------------------------------------------
getFileName()160 const char *HiScore::getFileName()
161 {
162 static char configFilename[256];
163 const char *envFile = getenv("CHROMIUM_BSU_SCORE");
164 if(envFile && strlen(envFile) < 256)
165 {
166 strcpy(configFilename, envFile);
167 }
168 else
169 {
170 const char *homeDir = getenv("HOME");
171 if(!homeDir)
172 homeDir = "./";
173 sprintf(configFilename, "%s/%s", homeDir, CONFIG_SCORE_FILE);
174 alterPathForPlatform(configFilename);
175 }
176 return configFilename;
177 }
178
179 /**
180 * Prints the name of the old score file
181 * @returns name of oldscore file
182 */
183 //----------------------------------------------------------
getOldFileName()184 const char *HiScore::getOldFileName()
185 {
186 static char configFilename[256];
187 const char *homeDir = getenv("HOME");
188 if(!homeDir)
189 homeDir = "./";
190 sprintf(configFilename, "%s/.chromium-score" CONFIG_EXT, homeDir);
191 alterPathForPlatform(configFilename);
192 return configFilename;
193 }
194
195 /**
196 * Save high score file.
197 * @returns success
198 */
199 //----------------------------------------------------------
saveFile()200 bool HiScore::saveFile()
201 {
202 bool retVal = true;
203 FILE *file;
204
205 file = fopen(getFileName(), "w");
206 if(file)
207 {
208 #ifdef HAVE_LOCALE_H
209 char* locale = setlocale(LC_NUMERIC,"C");
210 #endif
211 struct tm* time;
212 int i,j;
213 fprintf(file, "%s", header);
214 for(i = 0; i < 10; i++)
215 {
216 for(j = 0; j < HI_SCORE_HIST; j++)
217 {
218 time = gmtime(&hiScoreDate[i][j]);
219 if( time != NULL ){
220 fprintf(file,
221 "%d %d %f %04d-%02d-%02d %02d:%02d:%02d %s\n",
222 i, j,
223 hiScore[i][j],
224 1900+time->tm_year,
225 1+time->tm_mon,
226 time->tm_mday,
227 time->tm_hour,
228 time->tm_min,
229 time->tm_sec,
230 hiScoreName[i][j]);
231 }
232 }
233 }
234 fclose(file);
235 #ifdef HAVE_LOCALE_H
236 setlocale(LC_NUMERIC,locale);
237 #endif
238 }
239 else
240 {
241 fprintf(stderr, _("WARNING: could not write score file (%s)\n"), getFileName());
242 retVal = false;
243 }
244 return retVal;
245 }
246
247
248 /**
249 * Read high score file.
250 * @returns success
251 */
252 //----------------------------------------------------------
readFile()253 bool HiScore::readFile()
254 {
255 bool retVal = true;
256 FILE *file;
257
258 const char* fileName = getFileName();
259 file = fopen(fileName, "r");
260 if(file)
261 {
262 int chr = fgetc(file);
263 if( EOF != chr ){
264 fseek(file, 0L, SEEK_SET);
265 if( '#' == chr ){
266 // Save and reset locale/timezone info
267 #ifdef HAVE_LOCALE_H
268 char* locale = setlocale(LC_NUMERIC,"C");
269 #endif
270 char *tz = getenv("TZ");
271 setenv("TZ", "", 1);
272 tzset();
273
274 // Discard the comment line
275 if( fscanf(file,"%*[^\n]") == EOF )
276 fprintf(stderr, _("WARNING: error reading score file (%s)\n"), getFileName());
277
278 char name[100];
279 struct tm time;
280 int i, j;
281 double score;
282 int fields;
283 do{
284 i = j = -1;
285 memset(&time,0,sizeof(time));
286 fields = fscanf(file,
287 "%d %d %lf %d-%d-%d %d:%d:%d %99s%*[^\n]",
288 &i, &j,
289 &score,
290 &time.tm_year,
291 &time.tm_mon,
292 &time.tm_mday,
293 &time.tm_hour,
294 &time.tm_min,
295 &time.tm_sec,
296 name);
297 if( fields == 10 && i >=0 && i < 10 && j >= 0 && j < HI_SCORE_HIST ){
298 hiScore[i][j] = score;
299 time.tm_year -= 1900;
300 time.tm_mon--;
301 hiScoreDate[i][j] = mktime(&time);
302 strncpy(hiScoreName[i][j], name, 99);
303 hiScoreName[i][j][99] = '\0';
304 }
305 } while( fields != EOF );
306 fclose(file);
307
308 // Reset locale/timezone info
309 #ifdef HAVE_LOCALE_H
310 setlocale(LC_NUMERIC,locale);
311 #endif
312 if (tz) setenv("TZ", tz, 1);
313 else unsetenv("TZ");
314 tzset();
315
316 } else {
317 // Nasty old memory dump format
318 if( fread(hiScore, sizeof(double), 10*HI_SCORE_HIST, file) != 10*HI_SCORE_HIST )
319 fprintf(stderr, _("WARNING: error reading old score file (%s)\n"), getFileName());
320 if( fread(hiScoreName, 64*sizeof(char), 10*HI_SCORE_HIST, file) != 10*HI_SCORE_HIST )
321 fprintf(stderr, _("WARNING: error reading old score file (%s)\n"), getFileName());
322 if( fread(hiScoreDate, sizeof(time_t), 10*HI_SCORE_HIST, file) != 10*HI_SCORE_HIST )
323 fprintf(stderr, _("WARNING: error reading old score file (%s)\n"), getFileName());
324 fclose(file);
325 // Resave the file in plain text format
326 saveFile();
327 }
328 }
329 else
330 {
331 Config* config = Config::instance();
332 if( config->debug() ) fprintf(stderr, _("WARNING: empty score file (%s)\n"), getFileName());
333 retVal = false;
334 }
335 }
336 else
337 {
338 fileName = getOldFileName();
339 file = fopen(fileName, "r");
340 if(file)
341 {
342 if( fread(hiScore, sizeof(double), 10*HI_SCORE_HIST, file) != 10*HI_SCORE_HIST )
343 fprintf(stderr, _("WARNING: error reading old score file (%s)\n"), getFileName());
344 if( fread(hiScoreName, 64*sizeof(char), 10*HI_SCORE_HIST, file) != 10*HI_SCORE_HIST )
345 fprintf(stderr, _("WARNING: error reading old score file (%s)\n"), getFileName());
346 if( fread(hiScoreDate, sizeof(time_t), 10*HI_SCORE_HIST, file) != 10*HI_SCORE_HIST )
347 fprintf(stderr, _("WARNING: error reading old score file (%s)\n"), getFileName());
348 fclose(file);
349
350 // Try to save the new file and delete the old one if successful
351 if( saveFile() )
352 remove(fileName);
353 }
354 else
355 {
356 Config* config = Config::instance();
357 if( config->debug() ) fprintf(stderr, _("WARNING: could not read score file (%s)\n"), getFileName());
358 retVal = false;
359 }
360 }
361
362 return retVal;
363 }
364
365
366 //----------------------------------------------------------
insertScore(int skill,int rank,float score)367 void HiScore::insertScore(int skill, int rank, float score)
368 {
369 int i;
370 i = HI_SCORE_HIST-2;
371 while(i >= rank)
372 {
373 hiScore[skill][i+1] = hiScore[skill][i];
374 strcpy(hiScoreName[skill][i+1], hiScoreName[skill][i]);
375 memcpy(&(hiScoreDate[skill][i+1]), &(hiScoreDate[skill][i]), sizeof(time_t));
376 i--;
377 }
378 hiScore[skill][rank] = score;
379 char *name = getenv("USER");
380 if(name)
381 strcpy(hiScoreName[skill][rank], name);
382 else
383 strcpy(hiScoreName[skill][rank], "player");
384 time(&hiScoreDate[skill][rank]);
385 }
386
387 /**
388 * reads high score file, inserts current score (if appropriate), then saves
389 * high score file. If multiple users are sharing a common high score file,
390 * we want to keep it as current as possible.
391 */
392 //----------------------------------------------------------
set(int skill,float score)393 int HiScore::set(int skill, float score)
394 {
395 int retVal = 0;
396 if(skill > 0 && skill < 10)
397 {
398 readFile();
399 int i;
400 int rank = -1;
401 for(i = HI_SCORE_HIST-1; i >= 0; i--)
402 {
403 if(score > hiScore[skill][i])
404 rank = i;
405 }
406 if(rank > -1)
407 {
408 insertScore(skill, rank, score);
409 saveFile();
410 retVal = rank+1;
411 }
412 }
413
414 return retVal;
415 }
416
417 /**
418 * check whether score qualifies as high score
419 * returns rank of player {1..HI_SCORE_HIST}, or 0
420 */
421 //----------------------------------------------------------
check(int skill,float score)422 int HiScore::check(int skill, float score)
423 {
424 int retVal = 0;
425 if(skill > 0 && skill < 10)
426 {
427 int i;
428 int rank = -1;
429 for(i = HI_SCORE_HIST-1; i >= 0; i--)
430 {
431 if(score > hiScore[skill][i])
432 rank = i;
433 }
434 if(rank > -1)
435 {
436 retVal = rank+1;
437 }
438 }
439 return retVal;
440 }
441
442 /**
443 * print high scores to stderr
444 */
445 //----------------------------------------------------------
print(int skill)446 void HiScore::print(int skill)
447 {
448 struct tm *tmptr;
449 fprintf(stderr, _("high scores:\n"));
450 for(int j = 0; j < HI_SCORE_HIST; j++)
451 {
452 tmptr = localtime(&hiScoreDate[skill][j]);
453 if (!tmptr)
454 break;
455 fprintf(stderr, _("%02d/%02d/%04d %16s %d\n"), 1+tmptr->tm_mon, tmptr->tm_mday, 1900+tmptr->tm_year,
456 hiScoreName[skill][j], (int)(hiScore[skill][j]));
457 }
458 }
459