added support for BD game engine to Makefile for Android
[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 <sys/stat.h>
18
19 #include "main_bd.h"
20
21
22 // this stores the caves.
23 List *gd_caveset;
24
25 // the data of the caveset: name, highscore, max number of lives, etc.
26 GdCavesetData *gd_caveset_data;
27
28 // is set to true, when the caveset was edited since the last save.
29 boolean gd_caveset_edited;
30
31 // last selected-to-play cave
32 int gd_caveset_last_selected;
33 int gd_caveset_last_selected_level;
34
35 // list of possible extensions which can be opened
36 char *gd_caveset_extensions[] =
37 {
38   "*.gds",
39   "*.bd",
40   "*.bdr",
41   "*.brc",
42
43   NULL
44 };
45
46 #define CAVESET_OFFSET(property) (STRUCT_OFFSET(GdCavesetData, property))
47
48 const GdStructDescriptor gd_caveset_properties[] =
49 {
50   // default data
51   {"", GD_TAB, 0, N_("Caveset data")},
52   {"Name", GD_TYPE_STRING, 0, N_("Name"), CAVESET_OFFSET(name), 1, N_("Name of the game")},
53   {"Description", GD_TYPE_STRING, 0, N_("Description"), CAVESET_OFFSET(description), 1, N_("Some words about the game")},
54   {"Author", GD_TYPE_STRING, 0, N_("Author"), CAVESET_OFFSET(author), 1, N_("Name of author")},
55   {"Date", GD_TYPE_STRING, 0, N_("Date"), CAVESET_OFFSET(date), 1, N_("Date of creation")},
56   {"WWW", GD_TYPE_STRING, 0, N_("WWW"), CAVESET_OFFSET(www), 1, N_("Web page or e-mail address")},
57   {"Difficulty", GD_TYPE_STRING, 0, N_("Difficulty"), CAVESET_OFFSET(difficulty), 1, N_("Difficulty (informative)")},
58
59   {"Lives", GD_TYPE_INT, 0, N_("Initial lives"), CAVESET_OFFSET(initial_lives), 1, N_("Number of lives you get at game start."), 3, 9},
60   {"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},
61   {"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},
62
63   {"Story", GD_TYPE_LONGSTRING, 0, N_("Story"), CAVESET_OFFSET(story), 1, N_("Long description of the game.")},
64   {"Remark", GD_TYPE_LONGSTRING, 0, N_("Remark"), CAVESET_OFFSET(remark), 1, N_("Remark (informative).")},
65
66   {"TitleScreen", GD_TYPE_LONGSTRING, GD_DONT_SHOW_IN_EDITOR, N_("Title screen"), CAVESET_OFFSET(title_screen), 1, N_("Title screen image")},
67   {"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")},
68
69   {NULL},
70 };
71
72 static GdPropertyDefault caveset_defaults[] =
73 {
74   // default data
75   {CAVESET_OFFSET(initial_lives), 3},
76   {CAVESET_OFFSET(maximum_lives), 9},
77   {CAVESET_OFFSET(bonus_life_score), 500},
78
79   {-1},
80 };
81
82 GdCavesetData *gd_caveset_data_new(void)
83 {
84   GdCavesetData *data;
85
86   data = checked_calloc(sizeof(GdCavesetData));
87
88   gd_struct_set_defaults_from_array(data, gd_caveset_properties, caveset_defaults);
89
90   if (leveldir_current != NULL)
91     data->levelset_subdir = getStringCopy(leveldir_current->subdir);
92
93   return data;
94 }
95
96 void gd_caveset_data_free(GdCavesetData *data)
97 {
98   int i;
99
100   // free strings
101   for (i = 0; gd_caveset_properties[i].identifier != NULL; i++)
102     if (gd_caveset_properties[i].type == GD_TYPE_LONGSTRING)
103       checked_free(STRUCT_MEMBER(char *, data, gd_caveset_properties[i].offset));
104
105   checked_free(data->levelset_subdir);
106
107   checked_free(data);
108 }
109
110 // ============================================================================
111 // Misc caveset functions
112 // ============================================================================
113
114 // Clears all caves in the caveset. also to be called at application start
115 void gd_caveset_clear(void)
116 {
117   if (gd_caveset)
118   {
119     list_foreach(gd_caveset, (list_fn) gd_cave_free, NULL);
120     list_free(gd_caveset);
121     gd_caveset = NULL;
122   }
123
124   if (gd_caveset_data)
125   {
126     gd_caveset_data_free(gd_caveset_data);
127     gd_caveset_data = NULL;
128   }
129
130   // always newly create this
131   // create pseudo cave containing default values
132   gd_caveset_data = gd_caveset_data_new();
133
134   if (leveldir_current != NULL)
135     gd_strcpy(gd_caveset_data->name, leveldir_current->name);
136 }
137
138 // return number of caves currently in memory.
139 int gd_caveset_count(void)
140 {
141   return list_length(gd_caveset);
142 }
143
144 // return index of first selectable cave
145 static int caveset_first_selectable_cave_index(void)
146 {
147   List *iter;
148   int i;
149
150   for (i = 0, iter = gd_caveset; iter != NULL; i++, iter = iter->next)
151   {
152     GdCave *cave = (GdCave *)iter->data;
153
154     if (cave->selectable)
155       return i;
156   }
157
158   Warn("no selectable cave in caveset!");
159
160   // and return the first one.
161   return 0;
162 }
163
164 // return a cave identified by its index
165 GdCave *gd_return_nth_cave(const int cave)
166 {
167   return list_nth_data(gd_caveset, cave);
168 }
169
170 // get a selected cave from the loaded caveset (original, unmodified cave)
171 GdCave *gd_get_original_cave_from_caveset(const int cave)
172 {
173   // get specified cave from caveset already stored in memory
174   GdCave *original_cave = gd_return_nth_cave(cave);
175
176   return original_cave;
177 }
178
179 // get a selected cave from the loaded caveset (cave prepared for playing)
180 GdCave *gd_get_prepared_cave_from_caveset(const int cave, const int level)
181 {
182   // get specified cave from caveset already stored in memory
183   GdCave *original_cave = gd_return_nth_cave(cave);
184
185   // get prepared cave from original cave
186   GdCave *prepared_cave = gd_get_prepared_cave(original_cave, level);
187
188   return prepared_cave;
189 }
190
191 // get a cave prepared for playing from a given original, unmodified cave (with seed)
192 GdCave *gd_get_prepared_cave(const GdCave *original_cave, const int level)
193 {
194   // get rendered cave using the selected seed for playing
195   GdCave *prepared_cave = gd_cave_new_rendered(original_cave, level, game_bd.random_seed);
196
197   // initialize some cave variables (like player position)
198   gd_cave_setup_for_game(prepared_cave);
199
200   return prepared_cave;
201 }
202
203 // colors: 4: purple  3: ciklamen 2: orange 1: blue 0: green
204 static GdElement brc_import_table[] =
205 {
206   /* 0 */
207   O_SPACE, O_DIRT, O_BRICK, O_MAGIC_WALL, O_PRE_OUTBOX, O_OUTBOX, O_UNKNOWN, O_STEEL,
208   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,
209
210   /* 1 */
211   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 */,
212   O_STONE, O_STONE /* scanned */, O_STONE_F, O_STONE_F /* scanned */, O_DIAMOND, O_DIAMOND /* scanned */, O_DIAMOND_F, O_DIAMOND_F /* scanned */,
213
214   /* 2 */
215   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,
216   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,
217
218   /* 3 */
219   O_CLOCK, O_NONE /* clock eaten */, O_INBOX, O_PRE_PL_1, O_PRE_PL_2, O_PRE_PL_3, O_NONE, O_NONE,
220   O_NONE, O_NONE, O_V_EXPANDING_WALL, O_NONE, O_VOODOO, O_UNKNOWN, O_EXPANDING_WALL, O_EXPANDING_WALL /* sc */,
221
222   /* 4 */
223   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 */,
224   O_NITRO_PACK_F, O_NITRO_PACK_F /* scanned */, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE,
225
226   /* 5 */
227   O_NONE /* bomb explosion utolso */, O_UNKNOWN, O_NONE /* solid bomb glued */, O_UNKNOWN, O_STONE_GLUED, O_UNKNOWN, O_DIAMOND_GLUED, O_UNKNOWN,
228   O_UNKNOWN, O_UNKNOWN, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE,
229
230   /* 6 */
231   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 */,
232   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,
233
234   /* 7 */
235   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,
236   O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
237 };
238
239 static GdElement brc_effect_table[] =
240 {
241   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,
242   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,
243   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,
244   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,
245   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,
246   O_EXPLODE_1, O_BOMB_EXPL_1, O_UNKNOWN,
247 };
248
249 static GdColor brc_color_table[] =
250 {
251   0x518722, 0x3a96fa, 0xdb7618, 0xff3968,
252   0x9b5fff, 0x0ee06c, 0xc25ea6, 0xf54826,
253   0xf1ff26,
254 };
255
256 static GdColor brc_color_table_comp[] =
257 {
258   0x582287, 0xfa9d39, 0x187ddb, 0x38ffd1,
259   0xc1ff5e, 0xe00d81, 0x5dc27a, 0x27d3f5,
260   0x3526ff,
261 };
262
263 static GdElement brc_effect(byte byt)
264 {
265   if (byt >= ARRAY_SIZE(brc_effect_table))
266   {
267     Warn("invalid element identifier for brc effect: %02x", byt);
268
269     return O_UNKNOWN;
270   }
271
272   return brc_effect_table[byt];
273 }
274
275 static void brc_import(byte *data)
276 {
277   int x, y;
278   int level;
279
280   // we import 100 caves, and the put them in the correct order.
281   GdCave *imported[100];
282   boolean import_effect;
283
284   gd_caveset_clear();
285
286   // this is some kind of a version number
287   import_effect = FALSE;
288
289   switch (data[23])
290   {
291     case 0x0:
292       // nothing to do
293       break;
294
295     case 0xde:
296       // import effects
297       import_effect = TRUE;
298       break;
299
300     default:
301       Warn("unknown brc version %02x", data[23]);
302       break;
303   }
304
305   for (level = 0; level < 5; level++)
306   {
307     int cavenum;
308     int i;
309
310     for (cavenum = 0; cavenum < 20; cavenum++)
311     {
312       GdCave *cave;
313
314       // 5 levels, 20 caves, 24 bytes - max 40*2 properties for each cave
315       int c = 5 * 20 * 24;
316
317       int datapos = (cavenum * 5 +level) * 24 + 22;
318       int colind;
319
320       cave = gd_cave_new();
321       imported[level * 20 + cavenum] = cave;
322
323       if (cavenum < 16)
324         snprintf(cave->name, sizeof(GdString), "Cave %c/%d", 'A' + cavenum,
325                  level + 1);
326       else
327         snprintf(cave->name, sizeof(GdString), "Intermission %d/%d",
328                  cavenum - 15, level + 1);
329
330       // fixed intermission caves; are smaller.
331       if (cavenum >= 16)
332       {
333         cave->w = 20;
334         cave->h = 12;
335       }
336
337       cave->map = gd_cave_map_new(cave, GdElement);
338
339       for (y = 0; y < cave->h; y++)
340       {
341         for (x = 0; x < cave->w; x++)
342         {
343           byte import;
344
345           import = data[y + level * 24 + cavenum * 24 * 5 + x * 24 * 5 * 20];
346
347           // if (i == printcave) g_print("%2x", import);
348           if (import < ARRAY_SIZE(brc_import_table))
349             cave->map[y][x] = brc_import_table[import];
350           else
351             cave->map[y][x] = O_UNKNOWN;
352         }
353       }
354
355       for (i = 0; i < 5; i++)
356       {
357         cave->level_time[i]             = data[0 * c + datapos];
358         cave->level_diamonds[i]         = data[1 * c + datapos];
359         cave->level_magic_wall_time[i]  = data[4 * c + datapos];
360         cave->level_amoeba_time[i]      = data[5 * c + datapos];
361         cave->level_amoeba_threshold[i] = data[6 * c + datapos];
362
363         // bonus time: 100 was added, so it could also be negative
364         cave->level_bonus_time[i]      = (int)data[11 * c + datapos + 1] - 100;
365         cave->level_hatching_delay_frame[i] = data[10 * c + datapos];
366
367         // this was not set in boulder remake.
368         cave->level_speed[i] = 150;
369       }
370
371       cave->diamond_value = data[2 * c + datapos];
372       cave->extra_diamond_value = data[3 * c +datapos];
373
374       // BRC PROBABILITIES
375       /*
376         a typical code example:
377         46:if (random(slime*4)<4) and (tab[x,y+2] = 0) then
378         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;
379         where slime is the byte loaded from the file as it is.
380         pascal random function generates a random number between 0..limit-1,
381         inclusive, for random(limit).
382
383         so a random number between 0..limit*4-1 is generated.
384         for limit=1, 0..3, which is always < 4, so P=1.
385         for limit=2, 0..7, 0..7 is < 4 in P=50%.
386         for limit=3, 0..11, is < 4 in P=33%.
387         So the probability is exactly 100%/limit.
388         just make sure we do not divide by zero for some broken input.
389       */
390
391       if (data[7 * c + datapos] == 0)
392         Warn("amoeba growth cannot be zero, error at byte %d",
393              data[7 * c + datapos]);
394       else
395         cave->amoeba_growth_prob = 1E6 / data[7 * c + datapos] + 0.5; // 0.5 for rounding
396
397       if (data[8 * c + datapos] == 0)
398         Warn("amoeba growth cannot be zero, error at byte %d",
399              data[8 * c + datapos]);
400       else
401         cave->amoeba_fast_growth_prob = 1E6 / data[8 * c + datapos] + 0.5; // 0.5 for rounding
402
403       cave->slime_predictable = FALSE;
404
405       for (i = 0; i < 5; i++)
406         cave->level_slime_permeability[i] = 1E6 / data[9 * c + datapos] + 0.5;   // 0.5 for rounding
407
408       // probability -> *1E6
409       cave->acid_spread_ratio = 1E6 / data[10 * c + datapos] + 0.5;
410
411       // br only allowed values 1..8 in here, but works the same way. prob -> *1E6
412       cave->pushing_stone_prob = 1E6 / data[11 * c + datapos] + 0.5;
413
414       cave->magic_wall_stops_amoeba = (data[12 * c + datapos + 1] != 0);
415       cave->intermission = (cavenum >= 16 || data[14 * c + datapos + 1] != 0);
416
417       // colors
418       colind = data[31 * c + datapos] % ARRAY_SIZE(brc_color_table);
419       cave->colorb = 0x000000;    // fixed rgb black
420       cave->color0 = 0x000000;    // fixed rgb black
421       cave->color1 = brc_color_table[colind];
422       cave->color2 = brc_color_table_comp[colind];    // complement
423       cave->color3 = 0xffffff;    // white for brick
424       cave->color4 = 0xe5ad23;    // fixed for amoeba
425       cave->color5 = 0x8af713;    // fixed for slime
426
427       if (import_effect)
428       {
429         cave->amoeba_enclosed_effect = brc_effect(data[14 * c + datapos + 1]);
430         cave->amoeba_too_big_effect  = brc_effect(data[15 * c + datapos + 1]);
431         cave->explosion_effect       = brc_effect(data[16 * c + datapos + 1]);
432         cave->bomb_explosion_effect  = brc_effect(data[17 * c + datapos + 1]);
433
434         // 18 solid bomb explode to
435         cave->diamond_birth_effect    = brc_effect(data[19 * c + datapos + 1]);
436         cave->stone_bouncing_effect   = brc_effect(data[20 * c + datapos + 1]);
437         cave->diamond_bouncing_effect = brc_effect(data[21 * c + datapos + 1]);
438         cave->magic_diamond_to        = brc_effect(data[22 * c + datapos + 1]);
439         cave->acid_eats_this          = brc_effect(data[23 * c + datapos + 1]);
440
441         /*
442           slime eats:
443           (diamond,boulder,bomb),
444           (diamond,boulder),
445           (diamond,bomb),
446           (boulder,bomb)
447         */
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 = list_append(gd_caveset, cave);
480       else
481         gd_cave_free(cave);
482     }
483   }
484 }
485
486 static void caveset_name_set_from_filename(char *filename)
487 {
488   char *name;
489   char *c;
490
491   // make up a caveset name from the filename.
492   name = getBaseName(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 /*
506   Load caveset from file.
507   Loads the caveset from a file.
508
509   File type is autodetected by extension.
510   param filename: Name of file.
511   result: FALSE if failed
512 */
513 boolean gd_caveset_load_from_file(char *filename)
514 {
515   size_t length;
516   char *buf;
517   List *new_caveset;
518   struct stat st;
519   File *file;
520
521   if (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   if (!(file = openFile(filename, MODE_READ)))
536   {
537     Warn("cannot open file '%s'", filename);
538
539     return FALSE;
540   }
541
542   buf = checked_malloc(st.st_size + 1);
543   length = readFile(file, buf, 1, st.st_size);
544   buf[length] = '\0';
545
546   closeFile(file);
547
548   if (length < st.st_size)
549   {
550     Warn("cannot read file '%s'", filename);
551
552     return FALSE;
553   }
554
555   if (strSuffix(filename, ".brc") ||
556       strSuffix(filename, ".BRC"))
557   {
558     // loading a boulder remake file
559     if (length != 96000)
560     {
561       Warn("BRC files must be 96000 bytes long");
562
563       return FALSE;
564     }
565   }
566
567   if (strSuffix(filename, ".brc") ||
568       strSuffix(filename, ".BRC"))
569   {
570     brc_import((byte *) buf);
571     gd_caveset_edited = FALSE;    // newly loaded cave is not edited
572     gd_caveset_last_selected = caveset_first_selectable_cave_index();
573     gd_caveset_last_selected_level = 0;
574     free(buf);
575     caveset_name_set_from_filename(filename);
576
577     return TRUE;
578   }
579
580   // BDCFF
581   if (gd_caveset_imported_get_format((byte *) buf) == GD_FORMAT_UNKNOWN)
582   {
583     // try to load as bdcff
584     boolean result;
585
586     // bdcff: start another function
587     result = gd_caveset_load_from_bdcff(buf);
588
589     // newly loaded file is not edited.
590     gd_caveset_edited = FALSE;
591
592     gd_caveset_last_selected = caveset_first_selectable_cave_index();
593     gd_caveset_last_selected_level = 0;
594     free(buf);
595
596     return result;
597   }
598
599   // try to load as a binary file, as we know the format
600   new_caveset = gd_caveset_import_from_buffer ((byte *) buf, length);
601   free(buf);
602
603   // if unable to load, exit here. error was reported by import_from_buffer()
604   if (!new_caveset)
605     return FALSE;
606
607   // no serious error :)
608
609   // only clear caveset here. if file read was unsuccessful, caveset remains in memory.
610   gd_caveset_clear();
611
612   gd_caveset = new_caveset;
613
614   // newly loaded cave is not edited
615   gd_caveset_edited = FALSE;
616
617   gd_caveset_last_selected = caveset_first_selectable_cave_index();
618   gd_caveset_last_selected_level = 0;
619   caveset_name_set_from_filename(filename);
620
621   return TRUE;
622 }
623
624 boolean gd_caveset_save_to_file(const char *filename)
625 {
626   GdPtrArray *saved = gd_caveset_save_to_bdcff();
627   boolean success;
628   File *file;
629   int i;
630
631   if ((file = openFile(filename, MODE_WRITE)) != NULL)
632   {
633     for (i = 0; i < saved->size; i++)
634     {
635       writeFile(file, saved->data[i], 1, strlen(saved->data[i]));
636       writeFile(file, "\n", 1, 1);
637     }
638
639     closeFile(file);
640
641     // remember that it is saved
642     gd_caveset_edited = FALSE;
643
644     success = TRUE;
645   }
646   else
647   {
648     Warn("cannot open file '%s'", filename);
649
650     success = FALSE;
651   }
652
653   gd_ptr_array_free(saved, TRUE);
654
655   return success;
656 }
657
658
659 int gd_cave_check_replays(GdCave *cave, boolean report, boolean remove, boolean repair)
660 {
661   List *riter;
662   int wrong = 0;
663
664   riter = cave->replays;
665   while (riter != NULL)
666   {
667     GdReplay *replay = (GdReplay *)riter->data;
668     unsigned int checksum;
669     GdCave *rendered;
670     List *next = riter->next;
671
672     rendered = gd_cave_new_rendered(cave, replay->level, replay->seed);
673     checksum = gd_cave_adler_checksum(rendered);
674     gd_cave_free(rendered);
675
676     replay->wrong_checksum = FALSE;
677
678     // count wrong ones... the checksum might be changed later to "repair"
679     if (replay->checksum != 0 && checksum != replay->checksum)
680       wrong++;
681
682     if (replay->checksum == 0 || repair)
683     {
684       // if no checksum found, add one. or if repair requested, overwrite old one.
685       replay->checksum = checksum;
686     }
687     else
688     {
689       // if has a checksum, compare with this one.
690       if (replay->checksum != checksum)
691       {
692         replay->wrong_checksum = TRUE;
693
694         if (report)
695           Warn("%s: replay played by %s at %s has wrong checksum",
696                cave->name, replay->player_name, replay->date);
697
698         if (remove)
699         {
700           // may remove
701           cave->replays = list_remove_link(cave->replays, riter);
702           gd_replay_free(replay);
703         }
704       }
705     }
706
707     // advance to next list item which we remembered. the current one might have been deleted
708     riter = next;
709   }
710
711   return wrong;
712 }
713
714 boolean gd_caveset_has_replays(void)
715 {
716   List *citer;
717
718   // for all caves
719   for (citer = gd_caveset; citer != NULL; citer = citer->next)
720   {
721     GdCave *cave = (GdCave *)citer->data;
722
723     if (cave->replays)
724       return TRUE;
725   }
726
727   // if neither of the caves had a replay,
728   return FALSE;
729 }