added game engine support for playing native Boulder Dash levels
[rocksndiamonds.git] / src / game_bd / bd_caveset.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <glib.h>
18 #include <glib/gi18n.h>
19 #include <glib/gstdio.h>
20
21 #include "main_bd.h"
22
23
24 /* this stores the caves. */
25 GList *gd_caveset;
26
27 /* the data of the caveset: name, highscore, max number of lives, etc. */
28 GdCavesetData *gd_caveset_data;
29
30 /* is set to true, when the caveset was edited since the last save. */
31 boolean gd_caveset_edited;
32
33 /* last selected-to-play cave */
34 int gd_caveset_last_selected;
35 int gd_caveset_last_selected_level;
36
37 /* list of possible extensions which can be opened */
38 char *gd_caveset_extensions[] =
39 {
40   "*.gds",
41   "*.bd",
42   "*.bdr",
43   "*.brc",
44
45   NULL
46 };
47
48 #define CAVESET_OFFSET(property) (G_STRUCT_OFFSET(GdCavesetData, property))
49
50 const GdStructDescriptor gd_caveset_properties[] =
51 {
52   /* default data */
53   {"", GD_TAB, 0, N_("Caveset data")},
54   {"Name", GD_TYPE_STRING, 0, N_("Name"), CAVESET_OFFSET(name), 1, N_("Name of the game")},
55   {"Description", GD_TYPE_STRING, 0, N_("Description"), CAVESET_OFFSET(description), 1, N_("Some words about the game")},
56   {"Author", GD_TYPE_STRING, 0, N_("Author"), CAVESET_OFFSET(author), 1, N_("Name of author")},
57   {"Date", GD_TYPE_STRING, 0, N_("Date"), CAVESET_OFFSET(date), 1, N_("Date of creation")},
58   {"WWW", GD_TYPE_STRING, 0, N_("WWW"), CAVESET_OFFSET(www), 1, N_("Web page or e-mail address")},
59   {"Difficulty", GD_TYPE_STRING, 0, N_("Difficulty"), CAVESET_OFFSET(difficulty), 1, N_("Difficulty (informative)")},
60
61   {"Lives", GD_TYPE_INT, 0, N_("Initial lives"), CAVESET_OFFSET(initial_lives), 1, N_("Number of lives you get at game start."), 3, 9},
62   {"Lives", GD_TYPE_INT, 0, N_("Maximum lives"), CAVESET_OFFSET(maximum_lives), 1, N_("Maximum number of lives you can have by collecting bonus points."), 3, 99},
63   {"BonusLife", GD_TYPE_INT, 0, N_("Bonus life score"), CAVESET_OFFSET(bonus_life_score), 1, N_("Number of points to collect for a bonus life."), 100, 5000},
64
65   {"Story", GD_TYPE_LONGSTRING, 0, N_("Story"), CAVESET_OFFSET(story), 1, N_("Long description of the game.")},
66   {"Remark", GD_TYPE_LONGSTRING, 0, N_("Remark"), CAVESET_OFFSET(remark), 1, N_("Remark (informative).")},
67
68   {"TitleScreen", GD_TYPE_LONGSTRING, GD_DONT_SHOW_IN_EDITOR, N_("Title screen"), CAVESET_OFFSET(title_screen), 1, N_("Title screen image")},
69   {"TitleScreenScroll", GD_TYPE_LONGSTRING, GD_DONT_SHOW_IN_EDITOR, N_("Title screen, scrolling"), CAVESET_OFFSET(title_screen_scroll), 1, N_("Scrolling background for title screen image")},
70
71   {NULL},
72 };
73
74 static GdPropertyDefault caveset_defaults[] =
75 {
76   /* default data */
77   {CAVESET_OFFSET(initial_lives), 3},
78   {CAVESET_OFFSET(maximum_lives), 9},
79   {CAVESET_OFFSET(bonus_life_score), 500},
80
81   {-1},
82 };
83
84 GdCavesetData *gd_caveset_data_new(void)
85 {
86   GdCavesetData *data;
87   int i;
88
89   data = checked_calloc(sizeof(GdCavesetData));
90
91   /* create strings */
92   for (i = 0; gd_caveset_properties[i].identifier != NULL; i++)
93     if (gd_caveset_properties[i].type == GD_TYPE_LONGSTRING)
94       G_STRUCT_MEMBER(GString *, data, gd_caveset_properties[i].offset) =
95         g_string_new(NULL);
96
97   gd_struct_set_defaults_from_array(data, gd_caveset_properties, caveset_defaults);
98
99   return data;
100 }
101
102 void gd_caveset_data_free(GdCavesetData *data)
103 {
104   int i;
105
106   /* free strings */
107   for (i = 0; gd_caveset_properties[i].identifier != NULL; i++)
108     if (gd_caveset_properties[i].type == GD_TYPE_LONGSTRING)
109       g_string_free(G_STRUCT_MEMBER(GString *, data, gd_caveset_properties[i].offset), TRUE);
110
111   free(data);
112 }
113
114 /******************************************************************************
115  *
116  * Misc caveset functions
117  *
118  */
119
120 /** Clears all caves in the caveset. also to be called at application start */
121 void gd_caveset_clear(void)
122 {
123   if (gd_caveset)
124   {
125     g_list_foreach(gd_caveset, (GFunc) gd_cave_free, NULL);
126     g_list_free(gd_caveset);
127     gd_caveset = NULL;
128   }
129
130   if (gd_caveset_data)
131   {
132     free(gd_caveset_data);
133     gd_caveset_data = NULL;
134   }
135
136   /* always newly create this */
137   /* create pseudo cave containing default values */
138   gd_caveset_data = gd_caveset_data_new();
139   gd_strcpy(gd_caveset_data->name, _("New caveset"));
140 }
141
142 /* return number of caves currently in memory. */
143 int gd_caveset_count(void)
144 {
145   return g_list_length(gd_caveset);
146 }
147
148 /* return index of first selectable cave */
149 static int caveset_first_selectable_cave_index(void)
150 {
151   GList *iter;
152   int i;
153
154   for (i = 0, iter = gd_caveset; iter != NULL; i++, iter = iter->next)
155   {
156     GdCave *cave = (GdCave *)iter->data;
157
158     if (cave->selectable)
159       return i;
160   }
161
162   Warn("no selectable cave in caveset!");
163
164   /* and return the first one. */
165   return 0;
166 }
167
168 /* return a cave identified by its index */
169 GdCave *gd_return_nth_cave(const int cave)
170 {
171   return g_list_nth_data(gd_caveset, cave);
172 }
173
174 /* get a selected cave from the loaded caveset (original, unmodified cave) */
175 GdCave *gd_get_original_cave_from_caveset(const int cave)
176 {
177   /* get specified cave from caveset already stored in memory */
178   GdCave *original_cave = gd_return_nth_cave(cave);
179
180   return original_cave;
181 }
182
183 /* get a selected cave from the loaded caveset (cave prepared for playing) */
184 GdCave *gd_get_prepared_cave_from_caveset(const int cave, const int level)
185 {
186   /* get specified cave from caveset already stored in memory */
187   GdCave *original_cave = gd_return_nth_cave(cave);
188
189   /* get prepared cave from original cave */
190   GdCave *prepared_cave = gd_get_prepared_cave(original_cave, level);
191
192   return prepared_cave;
193 }
194
195 /* get a cave prepared for playing from a given original, unmodified cave (with seed) */
196 GdCave *gd_get_prepared_cave(const GdCave *original_cave, const int level)
197 {
198   /* get rendered cave using the selected seed for playing */
199   GdCave *prepared_cave = gd_cave_new_rendered(original_cave, level, game_bd.random_seed);
200
201   /* initialize some cave variables (like player position) */
202   gd_cave_setup_for_game(prepared_cave);
203
204   return prepared_cave;
205 }
206
207 /* colors: 4: purple  3: ciklamen 2: orange 1: blue 0: green */
208 static GdElement brc_import_table[] =
209 {
210   /* 0 */
211   O_SPACE, O_DIRT, O_BRICK, O_MAGIC_WALL, O_PRE_OUTBOX, O_OUTBOX, O_UNKNOWN, O_STEEL,
212   O_H_EXPANDING_WALL, O_H_EXPANDING_WALL /* scanned */, O_FIREFLY_1 /* scanned */, O_FIREFLY_1 /* scanned */, O_FIREFLY_1, O_FIREFLY_2, O_FIREFLY_3, O_FIREFLY_4,
213
214   /* 1 */
215   O_BUTTER_1 /* scanned */, O_BUTTER_1 /* scanned */, O_BUTTER_1, O_BUTTER_2, O_BUTTER_3, O_BUTTER_4, O_PLAYER, O_PLAYER /* scanned */,
216   O_STONE, O_STONE /* scanned */, O_STONE_F, O_STONE_F /* scanned */, O_DIAMOND, O_DIAMOND /* scanned */, O_DIAMOND_F, O_DIAMOND_F /* scanned */,
217
218   /* 2 */
219   O_NONE /* WILL_EXPLODE_THING */, O_EXPLODE_1, O_EXPLODE_2, O_EXPLODE_3, O_EXPLODE_4, O_EXPLODE_5, O_NONE /* WILL EXPLODE TO DIAMOND_THING */, O_PRE_DIA_1,
220   O_PRE_DIA_2, O_PRE_DIA_3, O_PRE_DIA_4, O_PRE_DIA_5, O_AMOEBA, O_AMOEBA /* scanned */, O_SLIME, O_NONE,
221
222   /* 3 */
223   O_CLOCK, O_NONE /* clock eaten */, O_INBOX, O_PRE_PL_1, O_PRE_PL_2, O_PRE_PL_3, O_NONE, O_NONE,
224   O_NONE, O_NONE, O_V_EXPANDING_WALL, O_NONE, O_VOODOO, O_UNKNOWN, O_EXPANDING_WALL, O_EXPANDING_WALL /* sc */,
225
226   /* 4 */
227   O_FALLING_WALL, O_FALLING_WALL_F, O_FALLING_WALL_F /* scanned */, O_UNKNOWN, O_ACID, O_ACID /* scanned */, O_NITRO_PACK, O_NITRO_PACK /* scanned */,
228   O_NITRO_PACK_F, O_NITRO_PACK_F /* scanned */, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE,
229
230   /* 5 */
231   O_NONE /* bomb explosion utolso */, O_UNKNOWN, O_NONE /* solid bomb glued */, O_UNKNOWN, O_STONE_GLUED, O_UNKNOWN, O_DIAMOND_GLUED, O_UNKNOWN,
232   O_UNKNOWN, O_UNKNOWN, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE,
233
234   /* 6 */
235   O_ALT_FIREFLY_1 /* scanned */, O_ALT_FIREFLY_1 /* scanned */, O_ALT_FIREFLY_1, O_ALT_FIREFLY_2, O_ALT_FIREFLY_3, O_ALT_FIREFLY_4, O_PLAYER_BOMB, O_PLAYER_BOMB /* scanned */,
236   O_BOMB, O_BOMB_TICK_1, O_BOMB_TICK_2, O_BOMB_TICK_3, O_BOMB_TICK_4, O_BOMB_TICK_5, O_BOMB_TICK_6, O_BOMB_TICK_7,
237
238   /* 7 */
239   O_BOMB_TICK_7, O_BOMB_EXPL_1, O_BOMB_EXPL_2, O_BOMB_EXPL_3, O_BOMB_EXPL_4, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
240   O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
241 };
242
243 static GdElement brc_effect_table[] =
244 {
245   O_STEEL, O_DIRT, O_SPACE, O_STONE, O_STONE_F, O_STONE_GLUED, O_DIAMOND, O_DIAMOND_F, O_DIAMOND_GLUED, O_PRE_DIA_1,
246   O_PLAYER, O_PRE_PL_1, O_PLAYER_BOMB, O_PRE_OUTBOX, O_OUTBOX, O_FIREFLY_1, O_FIREFLY_2, O_FIREFLY_3, O_FIREFLY_4,
247   O_BUTTER_1, O_BUTTER_2, O_BUTTER_3, O_BUTTER_4, O_BRICK, O_MAGIC_WALL, O_H_EXPANDING_WALL, O_V_EXPANDING_WALL, O_EXPANDING_WALL,
248   O_FALLING_WALL, O_FALLING_WALL_F, O_AMOEBA, O_SLIME, O_ACID, O_VOODOO, O_CLOCK, O_BOMB, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
249   O_ALT_FIREFLY_1, O_ALT_FIREFLY_2, O_ALT_FIREFLY_3, O_ALT_FIREFLY_4, O_ALT_BUTTER_1, O_ALT_BUTTER_2, O_ALT_BUTTER_3, O_ALT_BUTTER_4,
250   O_EXPLODE_1, O_BOMB_EXPL_1, O_UNKNOWN,
251 };
252
253 static GdColor brc_color_table[] =
254 {
255   0x518722, 0x3a96fa, 0xdb7618, 0xff3968,
256   0x9b5fff, 0x0ee06c, 0xc25ea6, 0xf54826,
257   0xf1ff26,
258 };
259
260 static GdColor brc_color_table_comp[] =
261 {
262   0x582287, 0xfa9d39, 0x187ddb, 0x38ffd1,
263   0xc1ff5e, 0xe00d81, 0x5dc27a, 0x27d3f5,
264   0x3526ff,
265 };
266
267 static GdElement brc_effect(guint8 byt)
268 {
269   if (byt >= G_N_ELEMENTS(brc_effect_table))
270   {
271     Warn("invalid element identifier for brc effect: %02x", byt);
272
273     return O_UNKNOWN;
274   }
275
276   return brc_effect_table[byt];
277 }
278
279 static void brc_import(guint8 *data)
280 {
281   int x, y;
282   int level;
283
284   /* we import 100 caves, and the put them in the correct order. */
285   GdCave *imported[100];
286   boolean import_effect;
287
288   gd_caveset_clear();
289
290   /* this is some kind of a version number */
291   import_effect = FALSE;
292
293   switch (data[23])
294   {
295     case 0x0:
296       /* nothing to do */
297       break;
298
299     case 0xde:
300       /* import effects */
301       import_effect = TRUE;
302       break;
303
304     default:
305       Warn("unknown brc version %02x", data[23]);
306       break;
307   }
308
309   for (level = 0; level < 5; level++)
310   {
311     int cavenum;
312     int i;
313
314     for (cavenum = 0; cavenum < 20; cavenum++)
315     {
316       GdCave *cave;
317
318       /* 5 levels, 20 caves, 24 bytes - max 40*2 properties for each cave */
319       int c = 5 * 20 * 24;
320
321       int datapos = (cavenum * 5 +level) * 24 + 22;
322       int colind;
323
324       cave = gd_cave_new();
325       imported[level * 20 + cavenum] = cave;
326
327       if (cavenum < 16)
328         g_snprintf(cave->name, sizeof(GdString), "Cave %c/%d", 'A' + cavenum,
329                    level + 1);
330       else
331         g_snprintf(cave->name, sizeof(GdString), "Intermission %d/%d",
332                    cavenum - 15, level + 1);
333
334       /* fixed intermission caves; are smaller. */
335       if (cavenum >= 16)
336       {
337         cave->w = 20;
338         cave->h = 12;
339       }
340
341       cave->map = gd_cave_map_new(cave, GdElement);
342
343       for (y = 0; y < cave->h; y++)
344       {
345         for (x = 0; x < cave->w; x++)
346         {
347           guint8 import;
348
349           import = data[y + level * 24 + cavenum * 24 * 5 + x * 24 * 5 * 20];
350
351           // if (i == printcave) g_print("%2x", import);
352           if (import < G_N_ELEMENTS(brc_import_table))
353             cave->map[y][x] = brc_import_table[import];
354           else
355             cave->map[y][x] = O_UNKNOWN;
356         }
357       }
358
359       for (i = 0; i < 5; i++)
360       {
361         cave->level_time[i]             = data[0 * c + datapos];
362         cave->level_diamonds[i]         = data[1 * c + datapos];
363         cave->level_magic_wall_time[i]  = data[4 * c + datapos];
364         cave->level_amoeba_time[i]      = data[5 * c + datapos];
365         cave->level_amoeba_threshold[i] = data[6 * c + datapos];
366
367         /* bonus time: 100 was added, so it could also be negative */
368         cave->level_bonus_time[i]      = (int)data[11 * c + datapos + 1] - 100;
369         cave->level_hatching_delay_frame[i] = data[10 * c + datapos];
370
371         /* this was not set in boulder remake. */
372         cave->level_speed[i] = 150;
373       }
374
375       cave->diamond_value = data[2 * c + datapos];
376       cave->extra_diamond_value = data[3 * c +datapos];
377
378       /* BRC PROBABILITIES */
379       /* a typical code example:
380          46:if (random(slime*4)<4) and (tab[x,y+2] = 0) then\r
381          Begin tab[x,y]:=0;col[x,y+2]:=col[x,y];tab[x,y+2]:=27;mat[x,y+2]:=9;Voice4:=2;end;\r
382          where slime is the byte loaded from the file as it is.
383          pascal random function generates a random number between 0..limit-1, inclusive, for random(limit).
384
385          so a random number between 0..limit*4-1 is generated.
386          for limit=1, 0..3, which is always < 4, so P=1.
387          for limit=2, 0..7, 0..7 is < 4 in P=50%.
388          for limit=3, 0..11, is < 4 in P=33%.
389          So the probability is exactly 100%/limit.
390          just make sure we do not divide by zero for some broken input.
391       */
392
393       if (data[7 * c + datapos] == 0)
394         Warn("amoeba growth cannot be zero, error at byte %d",
395              data[7 * c + datapos]);
396       else
397         cave->amoeba_growth_prob = 1E6 / data[7 * c + datapos] + 0.5; /* 0.5 for rounding */
398
399       if (data[8 * c + datapos] == 0)
400         Warn("amoeba growth cannot be zero, error at byte %d",
401              data[8 * c + datapos]);
402       else
403         cave->amoeba_fast_growth_prob = 1E6 / data[8 * c + datapos] + 0.5; /* 0.5 for rounding */
404
405       cave->slime_predictable = FALSE;
406
407       for (i = 0; i < 5; i++)
408         cave->level_slime_permeability[i] = 1E6 / data[9 * c + datapos] + 0.5;   /* 0.5 for rounding */
409
410       /* probability -> *1E6 */
411       cave->acid_spread_ratio = 1E6 / data[10 * c + datapos] + 0.5;
412
413       /* br only allowed values 1..8 in here, but works the same way. prob -> *1E6 */
414       cave->pushing_stone_prob = 1E6 / data[11 * c + datapos] + 0.5;
415
416       cave->magic_wall_stops_amoeba = (data[12 * c + datapos + 1] != 0);
417       cave->intermission = (cavenum >= 16 || data[14 * c + datapos + 1] != 0);
418
419       /* colors */
420       colind = data[31 * c + datapos] % G_N_ELEMENTS(brc_color_table);
421       cave->colorb = 0x000000;    /* fixed rgb black */
422       cave->color0 = 0x000000;    /* fixed rgb black */
423       cave->color1 = brc_color_table[colind];
424       cave->color2 = brc_color_table_comp[colind];    /* complement */
425       cave->color3 = 0xffffff;    /* white for brick */
426       cave->color4 = 0xe5ad23;    /* fixed for amoeba */
427       cave->color5 = 0x8af713;    /* fixed for slime */
428
429       if (import_effect)
430       {
431         cave->amoeba_enclosed_effect = brc_effect(data[14 * c + datapos + 1]);
432         cave->amoeba_too_big_effect  = brc_effect(data[15 * c + datapos + 1]);
433         cave->explosion_effect       = brc_effect(data[16 * c + datapos + 1]);
434         cave->bomb_explosion_effect  = brc_effect(data[17 * c + datapos + 1]);
435
436         /* 18 solid bomb explode to */
437         cave->diamond_birth_effect    = brc_effect(data[19 * c + datapos + 1]);
438         cave->stone_bouncing_effect   = brc_effect(data[20 * c + datapos + 1]);
439         cave->diamond_bouncing_effect = brc_effect(data[21 * c + datapos + 1]);
440         cave->magic_diamond_to        = brc_effect(data[22 * c + datapos + 1]);
441         cave->acid_eats_this          = brc_effect(data[23 * c + datapos + 1]);
442
443         /* slime eats:
444            (diamond,boulder,bomb),
445            (diamond,boulder),
446            (diamond,bomb),
447            (boulder,bomb) */
448         cave->amoeba_enclosed_effect = brc_effect(data[14 * c + datapos + 1]);
449       }
450     }
451   }
452
453   /* put them in the caveset - take correct order into consideration. */
454   for (level = 0; level < 5; level++)
455   {
456     int cavenum;
457
458     for (cavenum = 0; cavenum < 20; cavenum++)
459     {
460       static const int reorder[] =
461       {
462         0, 1, 2, 3, 16, 4, 5, 6, 7, 17, 8, 9, 10, 11, 18, 12, 13, 14, 15, 19
463       };
464       GdCave *cave = imported[level * 20 + reorder[cavenum]];
465       boolean only_dirt;
466       int x, y;
467
468       /* check if cave contains only dirt.
469          that is an empty cave, and do not import. */
470       only_dirt = TRUE;
471
472       for (y = 1; y < cave->h - 1 && only_dirt; y++)
473         for (x = 1; x < cave->w - 1 && only_dirt; x++)
474           if (cave->map[y][x] != O_DIRT)
475             only_dirt = FALSE;
476
477       /* append to caveset or forget it. */
478       if (!only_dirt)
479         gd_caveset = g_list_append(gd_caveset, cave);
480       else
481         gd_cave_free(cave);
482     }
483   }
484 }
485
486 static void caveset_name_set_from_filename(const char *filename)
487 {
488   char *name;
489   char *c;
490
491   /* make up a caveset name from the filename. */
492   name = g_path_get_basename(filename);
493   gd_strcpy(gd_caveset_data->name, name);
494   free(name);
495
496   /* convert underscores to spaces */
497   while ((c = strchr (gd_caveset_data->name, '_')) != NULL)
498     *c = ' ';
499
500   /* remove extension */
501   if ((c = strrchr (gd_caveset_data->name, '.')) != NULL)
502     *c = 0;
503 }
504
505 /* Load caveset from file.
506    Loads the caveset from a file.
507
508    File type is autodetected by extension.
509    param filename: Name of file.
510    result: FALSE if failed
511 */
512 boolean gd_caveset_load_from_file(char *filename)
513 {
514   GError *error = NULL;
515   gsize length;
516   char *buf;
517   boolean read;
518   GList *new_caveset;
519   struct stat st;
520
521   if (g_stat(filename, &st) != 0)
522   {
523     Warn("cannot stat() file");
524
525     return FALSE;
526   }
527
528   if (st.st_size > 1048576)
529   {
530     Warn("file bigger than 1MiB, refusing to load");
531
532     return FALSE;
533   }
534
535   read = g_file_get_contents (filename, &buf, &length, &error);
536   if (!read)
537   {
538     Warn("%s", error->message);
539
540     g_error_free(error);
541
542     return FALSE;
543   }
544
545   if (strSuffix(filename, ".brc") ||
546       strSuffix(filename, ".BRC"))
547   {
548     /* loading a boulder remake file */
549     if (length != 96000)
550     {
551       Warn("BRC files must be 96000 bytes long");
552
553       return FALSE;
554     }
555   }
556
557   if (strSuffix(filename, ".brc") ||
558       strSuffix(filename, "*.BRC"))
559   {
560     brc_import((guint8 *) buf);
561     gd_caveset_edited = FALSE;    /* newly loaded cave is not edited */
562     gd_caveset_last_selected = caveset_first_selectable_cave_index();
563     gd_caveset_last_selected_level = 0;
564     free(buf);
565     caveset_name_set_from_filename(filename);
566
567     return TRUE;
568   }
569
570   /* BDCFF */
571   if (gd_caveset_imported_get_format((guint8 *) buf) == GD_FORMAT_UNKNOWN)
572   {
573     /* try to load as bdcff */
574     boolean result;
575
576     /* bdcff: start another function */
577     result = gd_caveset_load_from_bdcff(buf);
578
579     /* newly loaded file is not edited. */
580     gd_caveset_edited = FALSE;
581
582     gd_caveset_last_selected = caveset_first_selectable_cave_index();
583     gd_caveset_last_selected_level = 0;
584     free(buf);
585
586     return result;
587   }
588
589   /* try to load as a binary file, as we know the format */
590   new_caveset = gd_caveset_import_from_buffer ((guint8 *) buf, length);
591   free(buf);
592
593   /* if unable to load, exit here. error was reported by import_from_buffer() */
594   if (!new_caveset)
595     return FALSE;
596
597   /* no serious error :) */
598
599   /* only clear caveset here. if file read was unsuccessful, caveset remains in memory. */
600   gd_caveset_clear();
601
602   gd_caveset = new_caveset;
603
604   /* newly loaded cave is not edited */
605   gd_caveset_edited = FALSE;
606
607   gd_caveset_last_selected = caveset_first_selectable_cave_index();
608   gd_caveset_last_selected_level = 0;
609   caveset_name_set_from_filename(filename);
610
611   return TRUE;
612 }
613
614 int gd_cave_check_replays(GdCave *cave, boolean report, boolean remove, boolean repair)
615 {
616   GList *riter;
617   int wrong = 0;
618
619   riter = cave->replays;
620   while (riter != NULL)
621   {
622     GdReplay *replay = (GdReplay *)riter->data;
623     guint32 checksum;
624     GdCave *rendered;
625     GList *next = riter->next;
626
627     rendered = gd_cave_new_rendered(cave, replay->level, replay->seed);
628     checksum = gd_cave_adler_checksum(rendered);
629     gd_cave_free(rendered);
630
631     replay->wrong_checksum = FALSE;
632
633     /* count wrong ones... the checksum might be changed later to "repair" */
634     if (replay->checksum != 0 && checksum != replay->checksum)
635       wrong++;
636
637     if (replay->checksum == 0 || repair)
638     {
639       /* if no checksum found, add one. or if repair requested, overwrite old one. */
640       replay->checksum = checksum;
641     }
642     else
643     {
644       /* if has a checksum, compare with this one. */
645       if (replay->checksum != checksum)
646       {
647         replay->wrong_checksum = TRUE;
648
649         if (report)
650           Warn("%s: replay played by %s at %s has wrong checksum",
651                cave->name, replay->player_name, replay->date);
652
653         if (remove)
654         {
655           /* may remove */
656           cave->replays = g_list_remove_link(cave->replays, riter);
657           gd_replay_free(replay);
658         }
659       }
660     }
661
662     /* advance to next list item which we remembered. the current one might have been deleted */
663     riter = next;
664   }
665
666   return wrong;
667 }
668
669 boolean gd_caveset_has_replays(void)
670 {
671   GList *citer;
672
673   /* for all caves */
674   for (citer = gd_caveset; citer != NULL; citer = citer->next)
675   {
676     GdCave *cave = (GdCave *)citer->data;
677
678     if (cave->replays)
679       return TRUE;
680   }
681
682   /* if neither of the caves had a replay, */
683   return FALSE;
684 }