Set GL read buffer to "front" once and for all when setting video mode
[neverball] / ball / set.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <errno.h>
19
20 #include "glext.h"
21 #include "config.h"
22 #include "video.h"
23 #include "image.h"
24 #include "set.h"
25 #include "common.h"
26 #include "fs.h"
27
28 #include "game_server.h"
29 #include "game_client.h"
30 #include "game_proxy.h"
31
32 /*---------------------------------------------------------------------------*/
33
34 struct set
35 {
36     char file[PATHMAX];
37
38     char *id;                  /* Internal set identifier    */
39     char *name;                /* Set name                   */
40     char *desc;                /* Set description            */
41     char *shot;                /* Set screen-shot            */
42
43     char user_scores[PATHMAX]; /* User high-score file       */
44
45     struct score coin_score;   /* Challenge score            */
46     struct score time_score;   /* Challenge score            */
47
48     /* Level info */
49
50     int   count;                /* Number of levels           */
51     char *level_name_v[MAXLVL]; /* List of level file names   */
52 };
53
54 static int set_state = 0;
55
56 static int set;
57 static int count;
58
59 static struct set   set_v[MAXSET];
60 static struct level level_v[MAXLVL];
61
62 /*---------------------------------------------------------------------------*/
63
64 static void put_score(fs_file fp, const struct score *s)
65 {
66     int j;
67
68     for (j = 0; j < NSCORE; j++)
69         fs_printf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
70 }
71
72 void set_store_hs(void)
73 {
74     const struct set *s = &set_v[set];
75     fs_file fout;
76     int i;
77     const struct level *l;
78     char states[MAXLVL + 1];
79
80     if ((fout = fs_open(s->user_scores, "w")))
81     {
82         for (i = 0; i < s->count; i++)
83         {
84             if (level_v[i].is_locked)
85                 states[i] = 'L';
86             else if (level_v[i].is_completed)
87                 states[i] = 'C';
88             else
89                 states[i] = 'O';
90         }
91         states[s->count] = '\0';
92         fs_printf(fout, "%s\n",states);
93
94         put_score(fout, &s->time_score);
95         put_score(fout, &s->coin_score);
96
97         for (i = 0; i < s->count; i++)
98         {
99             l = &level_v[i];
100
101             put_score(fout, &l->score.best_times);
102             put_score(fout, &l->score.fast_unlock);
103             put_score(fout, &l->score.most_coins);
104         }
105
106         fs_close(fout);
107     }
108 }
109
110 static int get_score(fs_file fp, struct score *s)
111 {
112     int j;
113     int res = 1;
114     char line[MAXSTR];
115
116     for (j = 0; j < NSCORE && res; j++)
117     {
118         res = (fs_gets(line, sizeof (line), fp) &&
119                sscanf(line, "%d %d %s\n",
120                       &s->timer[j],
121                       &s->coins[j],
122                       s->player[j]) == 3);
123     }
124     return res;
125 }
126
127 /* Get the score of the set. */
128 static void set_load_hs(void)
129 {
130     struct set *s = &set_v[set];
131     fs_file fin;
132     int i;
133     int res = 0;
134     struct level *l;
135     const char *fn = s->user_scores;
136     char states[MAXLVL + sizeof ("\n")];
137
138     if ((fin = fs_open(fn, "r")))
139     {
140         res = (fs_gets(states, sizeof (states), fin) &&
141                strlen(states) - 1 == s->count);
142
143         for (i = 0; i < s->count && res; i++)
144         {
145             switch (states[i])
146             {
147             case 'L':
148                 level_v[i].is_locked = 1;
149                 level_v[i].is_completed = 0;
150                 break;
151
152             case 'C':
153                 level_v[i].is_locked = 0;
154                 level_v[i].is_completed = 1;
155                 break;
156
157             case 'O':
158                 level_v[i].is_locked = 0;
159                 level_v[i].is_completed = 0;
160                 break;
161
162             default:
163                 res = 0;
164             }
165         }
166
167         res = res &&
168             get_score(fin, &s->time_score) &&
169             get_score(fin, &s->coin_score);
170
171         for (i = 0; i < s->count && res; i++)
172         {
173             l = &level_v[i];
174             res = get_score(fin, &l->score.best_times) &&
175                 get_score(fin, &l->score.fast_unlock) &&
176                 get_score(fin, &l->score.most_coins);
177         }
178
179         fs_close(fin);
180     }
181
182     if (!res && errno != ENOENT)
183     {
184         fprintf(stderr,
185                 L_("Error while loading user high-score file '%s': %s\n"),
186                 fn, errno ? strerror(errno) : L_("Incorrect format"));
187     }
188 }
189
190 /*---------------------------------------------------------------------------*/
191
192 static int set_load(struct set *s, const char *filename)
193 {
194     fs_file fin;
195     char *scores, *level_name;
196
197     /* Skip "Misc" set when not in dev mode. */
198
199     if (strcmp(filename, SET_MISC) == 0 && !config_cheat())
200         return 0;
201
202     fin = fs_open(filename, "r");
203
204     if (!fin)
205     {
206         fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
207                 filename, strerror(errno));
208         return 0;
209     }
210
211     memset(s, 0, sizeof (struct set));
212
213     /* Set some sane values in case the scores are missing. */
214
215     score_init_hs(&s->time_score, 359999, 0);
216     score_init_hs(&s->coin_score, 359999, 0);
217
218     strncpy(s->file, filename, PATHMAX - 1);
219
220     if (read_line(&s->name, fin) &&
221         read_line(&s->desc, fin) &&
222         read_line(&s->id,   fin) &&
223         read_line(&s->shot, fin) &&
224         read_line(&scores,  fin))
225     {
226         sscanf(scores, "%d %d %d %d %d %d",
227                &s->time_score.timer[0],
228                &s->time_score.timer[1],
229                &s->time_score.timer[2],
230                &s->coin_score.coins[0],
231                &s->coin_score.coins[1],
232                &s->coin_score.coins[2]);
233
234         free(scores);
235
236         sprintf(s->user_scores, "Scores/%s.txt", s->id);
237
238         s->count = 0;
239
240         while (s->count < MAXLVL && read_line(&level_name, fin))
241         {
242             s->level_name_v[s->count] = level_name;
243             s->count++;
244         }
245
246         fs_close(fin);
247
248         return 1;
249     }
250
251     free(s->name);
252     free(s->desc);
253     free(s->id);
254     free(s->shot);
255
256     fs_close(fin);
257
258     return 0;
259 }
260
261 static int cmp_dir_items(const void *A, const void *B)
262 {
263     const struct dir_item *a = A, *b = B;
264     return strcmp(a->path, b->path);
265 }
266
267 static int set_is_loaded(const char *path)
268 {
269     int i;
270
271     for (i = 0; i < count; i++)
272         if (strcmp(set_v[i].file, path) == 0)
273             return 1;
274
275     return 0;
276 }
277
278 static int is_unseen_set(struct dir_item *item)
279 {
280     return (strncmp(base_name(item->path, NULL), "set-", 4) == 0 &&
281             !set_is_loaded(item->path));
282 }
283
284 int set_init()
285 {
286     fs_file fin;
287     char *name;
288
289     Array items;
290     int i;
291
292     if (set_state)
293         set_free();
294
295     set   = 0;
296     count = 0;
297
298      /*
299       * First, load the sets listed in the set file, preserving order.
300       */
301
302     if ((fin = fs_open(SET_FILE, "r")))
303     {
304         while (count < MAXSET && read_line(&name, fin))
305         {
306             if (set_load(&set_v[count], name))
307                 count++;
308
309             free(name);
310         }
311         fs_close(fin);
312
313         set_state = 1;
314     }
315
316     /*
317      * Then, scan for any remaining set description files, and add
318      * them after the first group in alphabetic order.
319      */
320
321     if ((items = fs_dir_scan("", is_unseen_set)))
322     {
323         array_sort(items, cmp_dir_items);
324
325         for (i = 0; i < array_len(items) && count < MAXSET; i++)
326             if (set_load(&set_v[count], DIR_ITEM_GET(items, i)->path))
327                 count++;
328
329         fs_dir_free(items);
330
331         set_state = 1;
332     }
333
334     return count;
335 }
336
337 void set_free(void)
338 {
339     int i, j;
340
341     for (i = 0; i < count; i++)
342     {
343         free(set_v[i].name);
344         free(set_v[i].desc);
345         free(set_v[i].id);
346         free(set_v[i].shot);
347
348         for (j = 0; j < set_v[i].count; j++)
349             free(set_v[i].level_name_v[j]);
350     }
351
352     set_state = 0;
353 }
354
355 /*---------------------------------------------------------------------------*/
356
357 int set_exists(int i)
358 {
359     return (0 <= i && i < count);
360 }
361
362 const char *set_id(int i)
363 {
364     return set_exists(i) ? set_v[i].id : NULL;
365 }
366
367 const char *set_name(int i)
368 {
369     return set_exists(i) ? _(set_v[i].name) : NULL;
370 }
371
372 const char *set_desc(int i)
373 {
374     return set_exists(i) ? _(set_v[i].desc) : NULL;
375 }
376
377 const char *set_shot(int i)
378 {
379     return set_exists(i) ? set_v[i].shot : NULL;
380 }
381
382 const struct score *set_time_score(int i)
383 {
384     return set_exists(i) ? &set_v[i].time_score : NULL;
385 }
386
387 const struct score *set_coin_score(int i)
388 {
389     return set_exists(i) ? &set_v[i].coin_score : NULL;
390 }
391
392 /*---------------------------------------------------------------------------*/
393
394 int set_level_exists(int s, int i)
395 {
396     return (i >= 0 && i < set_v[s].count);
397 }
398
399 static void set_load_levels(void)
400 {
401     struct level *l;
402     int nb = 1, bnb = 1;
403
404     int i;
405
406     const char *roman[] = {
407         "",
408         "I",   "II",   "III",   "IV",   "V",
409         "VI",  "VII",  "VIII",  "IX",   "X",
410         "XI",  "XII",  "XIII",  "XIV",  "XV",
411         "XVI", "XVII", "XVIII", "XIX",  "XX",
412         "XXI", "XXII", "XXIII", "XXIV", "XXV"
413     };
414
415     for (i = 0; i < set_v[set].count; i++)
416     {
417         l = &level_v[i];
418
419         level_load(set_v[set].level_name_v[i], l);
420
421         l->set    = &set_v[set];
422         l->number = i;
423
424         if (l->is_bonus)
425             sprintf(l->name, "%s",   roman[bnb++]);
426         else
427             sprintf(l->name, "%02d", nb++);
428
429         l->is_locked    = 1;
430         l->is_completed = 0;
431     }
432
433     /* Unlock first level. */
434
435     level_v[0].is_locked = 0;
436 }
437
438 void set_goto(int i)
439 {
440     set = i;
441
442     set_load_levels();
443     set_load_hs();
444 }
445
446 int curr_set(void)
447 {
448     return set;
449 }
450
451 struct level *get_level(int i)
452 {
453     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
454 }
455
456 /*---------------------------------------------------------------------------*/
457
458 int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
459 {
460     struct set *s = &set_v[set];
461     char player[MAXSTR] = "";
462
463     config_get_s(CONFIG_PLAYER, player, MAXSTR);
464
465     if (score_rank)
466         *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
467
468     if (times_rank)
469         *times_rank = score_time_insert(&s->time_score, player, timer, coins);
470
471     if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
472         return 1;
473     else
474         return 0;
475 }
476
477 void set_rename_player(int score_rank, int times_rank, const char *player)
478 {
479     struct set *s = &set_v[set];
480
481     strncpy(s->coin_score.player[score_rank], player, MAXNAM);
482     strncpy(s->time_score.player[times_rank], player, MAXNAM);
483 }
484
485 /*---------------------------------------------------------------------------*/
486
487 void level_snap(int i, const char *path)
488 {
489     char filename[MAXSTR];
490
491     /* Convert the level name to a PNG filename. */
492
493     sprintf(filename, "%s/%s.png", path, base_name(level_v[i].file, ".sol"));
494
495     /* Initialize the game for a snapshot. */
496
497     if (game_client_init(level_v[i].file))
498     {
499         union cmd cmd;
500
501         cmd.type = CMD_GOAL_OPEN;
502         game_proxy_enq(&cmd);
503
504         /* Render the level and grab the screen. */
505
506         video_clear();
507         game_set_fly(1.f, game_client_file());
508         game_kill_fade();
509         game_client_step(NULL);
510         game_draw(1, 0);
511         SDL_GL_SwapBuffers();
512
513         image_snap(filename);
514     }
515 }
516
517 void set_cheat(void)
518 {
519     int i;
520
521     for (i = 0; i < set_v[set].count; i++)
522     {
523         level_v[i].is_locked    = 0;
524         level_v[i].is_completed = 1;
525     }
526 }
527
528 /*---------------------------------------------------------------------------*/