Merge branch 'gles'
[neverball] / ball / set.c
index bef56d3..7d0b813 100644 (file)
@@ -34,21 +34,21 @@ struct set
 {
     char file[PATHMAX];
 
-    char *id;                  /* Internal set identifier    */
-    char *name;                /* Set name                   */
-    char *desc;                /* Set description            */
-    char *shot;                /* Set screen-shot            */
+    char *id;                           /* Internal set identifier           */
+    char *name;                         /* Set name                          */
+    char *desc;                         /* Set description                   */
+    char *shot;                         /* Set screen-shot                   */
 
-    char *user_scores;         /* User high-score file       */
-    char *cheat_scores;        /* Cheat mode score file      */
+    char *user_scores;                  /* User high-score file              */
+    char *cheat_scores;                 /* Cheat mode score file             */
 
-    struct score coin_score;   /* Challenge score            */
-    struct score time_score;   /* Challenge score            */
+    struct score coin_score;            /* Challenge score                   */
+    struct score time_score;            /* Challenge score                   */
 
-    /* Level info */
+    /* Level info                                                            */
 
-    int   count;                /* Number of levels           */
-    char *level_name_v[MAXLVL]; /* List of level file names   */
+    int   count;                        /* Number of levels                  */
+    char *level_name_v[MAXLVL];         /* List of level file names          */
 };
 
 #define SET_GET(a, i) ((struct set *) array_get((a), (i)))
@@ -62,125 +62,118 @@ static struct level level_v[MAXLVL];
 
 static void put_score(fs_file fp, const struct score *s)
 {
-    int j;
+    int i;
 
-    for (j = 0; j < NSCORE; j++)
-        fs_printf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
+    for (i = RANK_HARD; i <= RANK_EASY; i++)
+        fs_printf(fp, "%d %d %s\n", s->timer[i], s->coins[i], s->player[i]);
+}
+
+static int get_score(fs_file fp, struct score *s)
+{
+    char line[MAXSTR];
+    int i;
+
+    for (i = RANK_HARD; i <= RANK_EASY; i++)
+    {
+        int n = -1;
+
+        if (!fs_gets(line, sizeof (line), fp))
+            return 0;
+
+        strip_newline(line);
+
+        if (sscanf(line, "%d %d %n", &s->timer[i], &s->coins[i], &n) < 2)
+            return 0;
+
+        if (n < 0)
+            return 0;
+
+        SAFECPY(s->player[i], line + n);
+    }
+
+    return 1;
 }
 
 void set_store_hs(void)
 {
     const struct set *s = SET_GET(sets, curr);
-    fs_file fout;
-    int i;
-    const struct level *l;
-    char states[MAXLVL + 1];
+    fs_file fp;
 
-    if ((fout = fs_open(config_cheat() ?
-                        s->cheat_scores :
-                        s->user_scores, "w")))
+    if ((fp = fs_open(config_cheat() ? s->cheat_scores : s->user_scores, "w")))
     {
+        const struct level *l;
+
+        char states[MAXLVL + 2] = "";
+        int i;
+
         for (i = 0; i < s->count; i++)
         {
-            if (level_v[i].is_locked)
+            l = &level_v[i];
+
+            if (l->is_locked)
                 states[i] = 'L';
-            else if (level_v[i].is_completed)
+            else if (l->is_completed)
                 states[i] = 'C';
             else
                 states[i] = 'O';
         }
-        states[s->count] = '\0';
-        fs_printf(fout, "%s\n",states);
 
-        put_score(fout, &s->time_score);
-        put_score(fout, &s->coin_score);
+        states[s->count] = '\n';
+
+        fs_puts(states, fp);
+
+        put_score(fp, &s->time_score);
+        put_score(fp, &s->coin_score);
 
         for (i = 0; i < s->count; i++)
         {
-            l = &level_v[i];
+            const struct level *l = &level_v[i];
 
-            put_score(fout, &l->scores[SCORE_BEST_TIMES]);
-            put_score(fout, &l->scores[SCORE_FAST_UNLOCK]);
-            put_score(fout, &l->scores[SCORE_MOST_COINS]);
+            put_score(fp, &l->scores[SCORE_TIME]);
+            put_score(fp, &l->scores[SCORE_GOAL]);
+            put_score(fp, &l->scores[SCORE_COIN]);
         }
 
-        fs_close(fout);
+        fs_close(fp);
     }
 }
 
-static int get_score(fs_file fp, struct score *s)
-{
-    int j;
-    int res = 1;
-    char line[MAXSTR];
-
-    for (j = 0; j < NSCORE && res; j++)
-    {
-        res = (fs_gets(line, sizeof (line), fp) &&
-               sscanf(line, "%d %d %s\n",
-                      &s->timer[j],
-                      &s->coins[j],
-                      s->player[j]) == 3);
-    }
-    return res;
-}
-
-/* Get the score of the set. */
 static void set_load_hs(void)
 {
     struct set *s = SET_GET(sets, curr);
-    fs_file fin;
-    int i;
-    int res = 0;
-    struct level *l;
-    const char *fn = config_cheat() ? s->cheat_scores : s->user_scores;
-    char states[MAXLVL + sizeof ("\n")];
+    fs_file fp;
 
-    if ((fin = fs_open(fn, "r")))
+    if ((fp = fs_open(config_cheat() ? s->cheat_scores : s->user_scores, "r")))
     {
-        res = (fs_gets(states, sizeof (states), fin) &&
-               strlen(states) - 1 == s->count);
+        char states[MAXLVL + 2];
 
-        for (i = 0; i < s->count && res; i++)
+        if (fs_gets(states, sizeof (states), fp))
         {
-            switch (states[i])
+            struct level *l;
+            int i, n = MIN(strlen(states) - 1, s->count);
+
+            for (i = 0; i < n; i++)
             {
-            case 'L':
-                level_v[i].is_locked = 1;
-                level_v[i].is_completed = 0;
-                break;
-
-            case 'C':
-                level_v[i].is_locked = 0;
-                level_v[i].is_completed = 1;
-                break;
-
-            case 'O':
-                level_v[i].is_locked = 0;
-                level_v[i].is_completed = 0;
-                break;
-
-            default:
-                res = 0;
+                l = &level_v[i];
+
+                l->is_locked    = (states[i] == 'L');
+                l->is_completed = (states[i] == 'C');
             }
-        }
 
-        res = res &&
-            get_score(fin, &s->time_score) &&
-            get_score(fin, &s->coin_score);
+            get_score(fp, &s->time_score);
+            get_score(fp, &s->coin_score);
 
-        for (i = 0; i < s->count && res; i++)
-        {
-            l = &level_v[i];
-            res = (get_score(fin, &l->scores[SCORE_BEST_TIMES])  &&
-                   get_score(fin, &l->scores[SCORE_FAST_UNLOCK]) &&
-                   get_score(fin, &l->scores[SCORE_MOST_COINS]));
-        }
+            for (i = 0; i < n; i++)
+            {
+                l = &level_v[i];
 
-        fs_close(fin);
+                get_score(fp, &l->scores[SCORE_TIME]);
+                get_score(fp, &l->scores[SCORE_GOAL]);
+                get_score(fp, &l->scores[SCORE_COIN]);
+            }
+        }
 
-        if (!res)
-            fprintf(stderr, L_("Failure to load user score file '%s'\n"), fn);
+        fs_close(fp);
     }
 }
 
@@ -211,7 +204,7 @@ static int set_load(struct set *s, const char *filename)
     score_init_hs(&s->time_score, 359999, 0);
     score_init_hs(&s->coin_score, 359999, 0);
 
-    strncpy(s->file, filename, PATHMAX - 1);
+    SAFECPY(s->file, filename);
 
     if (read_line(&s->name, fin) &&
         read_line(&s->desc, fin) &&
@@ -220,12 +213,12 @@ static int set_load(struct set *s, const char *filename)
         read_line(&scores,  fin))
     {
         sscanf(scores, "%d %d %d %d %d %d",
-               &s->time_score.timer[0],
-               &s->time_score.timer[1],
-               &s->time_score.timer[2],
-               &s->coin_score.coins[0],
-               &s->coin_score.coins[1],
-               &s->coin_score.coins[2]);
+               &s->time_score.timer[RANK_HARD],
+               &s->time_score.timer[RANK_MEDM],
+               &s->time_score.timer[RANK_EASY],
+               &s->coin_score.coins[RANK_HARD],
+               &s->coin_score.coins[RANK_MEDM],
+               &s->coin_score.coins[RANK_EASY]);
 
         free(scores);
 
@@ -367,7 +360,7 @@ void set_quit(void)
 
 int set_exists(int i)
 {
-    return (0 <= i && i < array_len(sets));
+    return sets ? 0 <= i && i < array_len(sets) : 0;
 }
 
 const char *set_id(int i)
@@ -390,59 +383,52 @@ const char *set_shot(int i)
     return set_exists(i) ? SET_GET(sets, i)->shot : NULL;
 }
 
-const struct score *set_time_score(int i)
-{
-    return set_exists(i) ? &SET_GET(sets, i)->time_score : NULL;
-}
-
-const struct score *set_coin_score(int i)
+const struct score *set_score(int i, int s)
 {
-    return set_exists(i) ? &SET_GET(sets, i)->coin_score : NULL;
+    if (set_exists(i))
+    {
+        if (s == SCORE_TIME) return &SET_GET(sets, i)->time_score;
+        if (s == SCORE_COIN) return &SET_GET(sets, i)->coin_score;
+    }
+    return NULL;
 }
 
 /*---------------------------------------------------------------------------*/
 
-int set_level_exists(int i, int l)
-{
-    return (l >= 0 && l < SET_GET(sets, i)->count);
-}
-
 static void set_load_levels(void)
 {
-    struct level *l;
-    int nb = 1, bnb = 1;
-
-    int i;
-
-    const char *roman[] = {
+    static const char *roman[] = {
         "",
-        "I",   "II",   "III",   "IV",   "V",
-        "VI",  "VII",  "VIII",  "IX",   "X",
-        "XI",  "XII",  "XIII",  "XIV",  "XV",
-        "XVI", "XVII", "XVIII", "XIX",  "XX",
+        "I", "II", "III", "IV", "V",
+        "VI", "VII", "VIII", "IX", "X",
+        "XI", "XII", "XIII", "XIV", "XV",
+        "XVI", "XVII", "XVIII", "XIX", "XX",
         "XXI", "XXII", "XXIII", "XXIV", "XXV"
     };
 
-    for (i = 0; i < SET_GET(sets, curr)->count; i++)
+    struct set *s = SET_GET(sets, curr);
+    int regular = 1, bonus = 1;
+    int i;
+
+    for (i = 0; i < s->count; i++)
     {
-        l = &level_v[i];
+        struct level *l = &level_v[i];
 
-        level_load(SET_GET(sets, curr)->level_name_v[i], l);
+        level_load(s->level_name_v[i], l);
 
         l->number = i;
 
         if (l->is_bonus)
-            sprintf(l->name, "%s",   roman[bnb++]);
+            SAFECPY(l->name, roman[bonus++]);
         else
-            sprintf(l->name, "%02d", nb++);
+            sprintf(l->name, "%02d", regular++);
 
-        l->is_locked    = 1;
+        l->is_locked = (i > 0);
         l->is_completed = 0;
-    }
 
-    /* Unlock first level. */
-
-    level_v[0].is_locked = 0;
+        if (i > 0)
+            level_v[i - 1].next = l;
+    }
 }
 
 void set_goto(int i)
@@ -473,7 +459,8 @@ int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
     score_coin_insert(&s->coin_score, score_rank, player, timer, coins);
     score_time_insert(&s->time_score, times_rank, player, timer, coins);
 
-    if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
+    if ((score_rank && *score_rank < RANK_LAST) ||
+        (times_rank && *times_rank < RANK_LAST))
         return 1;
     else
         return 0;
@@ -483,8 +470,8 @@ void set_rename_player(int score_rank, int times_rank, const char *player)
 {
     struct set *s = SET_GET(sets, curr);
 
-    strncpy(s->coin_score.player[score_rank], player, MAXNAM - 1);
-    strncpy(s->time_score.player[times_rank], player, MAXNAM - 1);
+    SAFECPY(s->coin_score.player[score_rank], player);
+    SAFECPY(s->time_score.player[times_rank], player);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -515,10 +502,10 @@ void level_snap(int i, const char *path)
         video_clear();
         game_client_fly(1.0f);
         game_kill_fade();
-        game_draw(POSE_LEVEL, 0);
-        SDL_GL_SwapBuffers();
-
+        game_client_draw(POSE_LEVEL, 0);
         image_snap(filename);
+
+        SDL_GL_SwapBuffers();
     }
 
     free(filename);