2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
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.
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.
22 #define BDCFF_VERSION "0.5"
24 /* these are used for bdcff loading, storing the sizes of caves */
25 static int cavesize[6], intermissionsize[6];
27 static boolean replay_store_from_bdcff(GdReplay *replay, const char *str)
30 boolean up, down, left, right;
31 boolean fire, suicide;
32 const char *num = NULL;
35 fire = suicide = up = down = left = right = FALSE;
37 for (i = 0; str[i] != 0; i++)
73 /* do nothing, as all other movements are false */
78 /* bdcff 'combined' flags. do nothing. */
82 if (str[i] >= '0' && str[i] <= '9')
90 dir = gd_direction_from_keypress(up, down, left, right);
94 sscanf(num, "%d", &count);
96 for (i = 0; i < count; i++)
97 gd_replay_store_movement(replay, dir, fire, suicide);
102 static boolean attrib_is_valid_for_cave(const char *attrib)
106 /* bdcff engine flag............ */
107 if (strcasecmp(attrib, "Engine") == 0)
110 /* old flags - for compatibility */
111 if (strcasecmp(attrib, "BD1Scheduling") == 0)
114 if (strcasecmp(attrib, "SnapExplosions") == 0)
117 if (strcasecmp(attrib, "AmoebaProperties") == 0)
120 /* search in property database */
121 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
122 if (strcasecmp(gd_cave_properties[i].identifier, attrib) == 0)
128 static boolean attrib_is_valid_for_caveset(const char *attrib)
132 /* search in property database */
133 for (i = 0; gd_caveset_properties[i].identifier != NULL; i++)
134 if (strcasecmp(gd_caveset_properties[i].identifier, attrib) == 0)
140 static boolean struct_set_property(void *str, const GdStructDescriptor *prop_desc,
141 const char *attrib, const char *param, int ratio)
145 boolean identifier_found;
150 params = getSplitStringArray(param, " ", -1);
151 paramcount = getStringArrayLength(params);
152 identifier_found = FALSE;
154 /* check all known tags. do not exit this loop if identifier_found == true...
155 as there are more lines in the array which have the same identifier. */
158 for (i = 0; prop_desc[i].identifier != NULL; i++)
160 if (strcasecmp(prop_desc[i].identifier, attrib) == 0)
162 /* found the identifier */
163 void *value = STRUCT_MEMBER_P(str, prop_desc[i].offset);
165 /* these point to the same, but to avoid the awkward cast syntax */
167 GdElement *evalue = value;
168 GdDirection *dvalue = value;
169 GdScheduling *svalue = value;
170 boolean *bvalue = value;
173 identifier_found = TRUE;
175 if (prop_desc[i].type == GD_TYPE_STRING)
177 /* strings are treated different, as occupy the whole length of the line */
178 gd_strcpy(value, param);
180 /* remember this to skip checking the number of parameters at the end of the function */
186 if (prop_desc[i].type == GD_TYPE_LONGSTRING)
188 char **str = (char **)value;
191 *str = getUnescapedString(param);
193 /* remember this to skip checking the number of parameters at the end of the function */
199 /* not a string, so use scanf calls */
200 /* ALSO, if no more parameters to process, exit loop */
201 for (j = 0; j < prop_desc[i].count && params[paramindex] != NULL; j++)
203 boolean success = FALSE;
206 switch (prop_desc[i].type)
208 case GD_TYPE_LONGSTRING:
216 case GD_TYPE_BOOLEAN:
217 success = sscanf(params[paramindex], "%d", &bvalue[j]) == 1;
220 if (strcasecmp(params[paramindex], "true") == 0 ||
221 strcasecmp(params[paramindex], "on") == 0 ||
222 strcasecmp(params[paramindex], "yes") == 0)
227 else if (strcasecmp(params[paramindex], "false") == 0 ||
228 strcasecmp(params[paramindex], "off") == 0 ||
229 strcasecmp(params[paramindex], "no") == 0)
236 /* if we are processing an array, fill other values with these.
237 if there are other values specified, those will be overwritten. */
239 for (k = j + 1; k < prop_desc[i].count; k++)
240 bvalue[k] = bvalue[j];
245 success = sscanf(params[paramindex], "%d", &ivalue[j]) == 1;
247 /* copy to other if array */
248 for (k = j + 1; k < prop_desc[i].count; k++)
249 ivalue[k] = ivalue[j];
253 case GD_TYPE_PROBABILITY:
254 errno = 0; /* must be reset before calling strtod() to detect overflow/underflow */
255 res = strtod(params[paramindex], NULL);
256 if (errno == 0 && res >= 0 && res <= 1)
258 /* fill all remaining items in array - may be only one */
259 for (k = j; k < prop_desc[i].count; k++)
260 /* probabilities are stored inside as ppm (1E6) */
261 ivalue[k] = res * 1E6 + 0.5;
269 errno = 0; /* must be reset before calling strtod() to detect overflow/underflow */
270 res = strtod (params[paramindex], NULL);
271 if (errno == 0 && res >= 0 && res <= 1)
273 for (k = j; k < prop_desc[i].count; k++)
274 ivalue[k] = (int)(res * ratio + 0.5);
281 case GD_TYPE_ELEMENT:
282 evalue[j] = gd_get_element_from_string(params[paramindex]);
284 /* copy to all remaining elements in array */
285 for (k = j + 1; k < prop_desc[i].count; k++)
286 evalue[k] = evalue[j];
288 /* this shows error message on its own, do treat as always succeeded */
292 case GD_TYPE_DIRECTION:
293 dvalue[j] = gd_direction_from_string(params[paramindex]);
294 /* copy to all remaining items in array */
295 for (k = j + 1; k < prop_desc[i].count; k++)
296 dvalue[k] = dvalue[j];
301 case GD_TYPE_SCHEDULING:
302 svalue[j] = gd_scheduling_from_string(params[paramindex]);
303 /* copy to all remaining items in array */
304 for (k = j + 1; k < prop_desc[i].count; k++)
305 svalue[k] = svalue[j];
307 /* if there was an error, already reported by gd_scheduling_from_string */
313 /* shoud have handled this elsewhere */
318 paramindex++; /* go to next parameter to process */
320 Warn("invalid parameter '%s' for attribute %s", params[paramindex], attrib);
325 /* if we found the identifier, but still could not process all parameters... */
326 /* of course, not for strings, as the whole line is the string */
327 if (identifier_found && !was_string && paramindex < paramcount)
328 Warn("excess parameters for attribute '%s': '%s'", attrib, params[paramindex]);
330 freeStringArray(params);
332 return identifier_found;
335 /********************************************************************************
341 static boolean replay_store_more_from_bdcff(GdReplay *replay, const char *param)
345 boolean result = TRUE;
347 split = getSplitStringArray(param, " ", -1);
349 for (i = 0; split[i] != 0; i++)
350 result = result && replay_store_from_bdcff(replay, split[i]);
352 freeStringArray(split);
357 /* report all remaining tags; called after the above function. */
358 static void replay_report_unknown_tags_func(const char *attrib, const char *param, void *data)
360 Warn("unknown replay tag '%s'", attrib);
363 /* a GHashTable foreach func.
364 keys are attribs; values are params;
365 the user data is the cave the hash table belongs to. */
366 static boolean replay_process_tags_func(const char *attrib, const char *param, GdReplay *replay)
368 boolean identifier_found = FALSE;
371 if (strcasecmp(attrib, "Movements") == 0)
373 identifier_found = TRUE;
374 replay_store_more_from_bdcff(replay, param);
379 /* 0: for ratio types; not used */
380 identifier_found = struct_set_property(replay, gd_replay_properties,
384 /* a ghrfunc should return true if the identifier is to be removed */
385 return identifier_found;
389 static void replay_process_tags(GdReplay *replay, HashTable *tags)
391 /* process all tags */
392 hashtable_foreach_remove(tags, (hashtable_remove_fn)replay_process_tags_func, replay);
395 /* a GHashTable foreach func.
396 keys are attribs; values are params;
397 the user data is the cave the hash table belongs to. */
398 static boolean cave_process_tags_func(const char *attrib, const char *param, GdCave *cave)
402 boolean identifier_found;
404 params = getSplitStringArray(param, " ", -1);
405 paramcount = getStringArrayLength(params);
406 identifier_found = FALSE;
408 if (strcasecmp(attrib, "SnapExplosions") == 0)
410 /* handle compatibility with old snapexplosions flag */
412 identifier_found = TRUE;
414 if (strcasecmp(param, "true") == 0)
416 cave->snap_element = O_EXPLODE_1;
418 else if (strcasecmp(param, "false") == 0)
420 cave->snap_element = O_SPACE;
424 Warn("invalid param for '%s': '%s'", attrib, param);
427 else if (strcasecmp(attrib, "BD1Scheduling") == 0)
429 /* handle compatibility with old bd1scheduling flag */
431 identifier_found = TRUE;
433 if (strcasecmp(param, "true") == 0)
435 if (cave->scheduling == GD_SCHEDULING_PLCK)
436 cave->scheduling = GD_SCHEDULING_BD1;
439 else if (strcasecmp(attrib, "Engine") == 0)
441 /* handle bdcff engine flag */
443 identifier_found = TRUE;
445 GdEngine engine = gd_cave_get_engine_from_string(param);
447 if (engine == GD_ENGINE_INVALID)
448 Warn(_("invalid parameter \"%s\" for attribute %s"), param, attrib);
450 gd_cave_set_engine_defaults(cave, engine);
452 else if (strcasecmp(attrib, "AmoebaProperties") == 0)
454 /* handle compatibility with old AmoebaProperties flag */
456 GdElement elem1 = O_STONE, elem2 = O_DIAMOND;
458 identifier_found = TRUE;
459 elem1 = gd_get_element_from_string(params[0]);
460 elem2 = gd_get_element_from_string(params[1]);
461 cave->amoeba_too_big_effect = elem1;
462 cave->amoeba_enclosed_effect = elem2;
464 else if (strcasecmp(attrib, "Colors") == 0)
466 /* colors attribute is a mess, have to process explicitly */
469 /* Colors = [border background] foreground1 foreground2 foreground3 [amoeba slime] */
470 identifier_found = TRUE;
474 /* only color1,2,3 */
475 cave->colorb = gd_c64_color(0); /* border - black */
476 cave->color0 = gd_c64_color(0); /* background - black */
477 cave->color1 = gd_color_get_from_string(params[0]);
478 cave->color2 = gd_color_get_from_string(params[1]);
479 cave->color3 = gd_color_get_from_string(params[2]);
480 cave->color4 = cave->color3; /* amoeba */
481 cave->color5 = cave->color1; /* slime */
483 else if (paramcount == 5)
485 /* bg,color0,1,2,3 */
486 cave->colorb = gd_color_get_from_string(params[0]);
487 cave->color0 = gd_color_get_from_string(params[1]);
488 cave->color1 = gd_color_get_from_string(params[2]);
489 cave->color2 = gd_color_get_from_string(params[3]);
490 cave->color3 = gd_color_get_from_string(params[4]);
491 cave->color4 = cave->color3; /* amoeba */
492 cave->color5 = cave->color1; /* slime */
494 else if (paramcount == 7)
496 /* bg,color0,1,2,3,amoeba,slime */
497 cave->colorb = gd_color_get_from_string(params[0]);
498 cave->color0 = gd_color_get_from_string(params[1]);
499 cave->color1 = gd_color_get_from_string(params[2]);
500 cave->color2 = gd_color_get_from_string(params[3]);
501 cave->color3 = gd_color_get_from_string(params[4]);
502 cave->color4 = gd_color_get_from_string(params[5]); /* amoeba */
503 cave->color5 = gd_color_get_from_string(params[6]); /* slime */
507 Warn("invalid number of color strings: %s", param);
512 /* now check and maybe make up some new. */
514 gd_color_is_unknown(cave->colorb) ||
515 gd_color_is_unknown(cave->color0) ||
516 gd_color_is_unknown(cave->color1) ||
517 gd_color_is_unknown(cave->color2) ||
518 gd_color_is_unknown(cave->color3) ||
519 gd_color_is_unknown(cave->color4) ||
520 gd_color_is_unknown(cave->color5))
522 Warn("created a new C64 color scheme.");
524 gd_cave_set_random_c64_colors(cave); /* just create some random */
529 identifier_found = struct_set_property(cave, gd_cave_properties, attrib, param, cave->w * cave->h);
532 freeStringArray(params);
534 /* a ghrfunc should return true if the identifier is to be removed */
535 return identifier_found;
538 /* report all remaining tags; called after the above function. */
539 static void cave_report_and_copy_unknown_tags_func(char *attrib, char *param, void *data)
541 GdCave *cave = (GdCave *)data;
543 Warn("unknown tag '%s'", attrib);
545 hashtable_insert(cave->tags, getStringCopy(attrib), getStringCopy(param));
548 /* having read all strings belonging to the cave, process it. */
549 static void cave_process_tags(GdCave *cave, HashTable *tags, List *maplines)
553 /* first check cave name, so we can report errors correctly (saying that GdCave xy: error foobar) */
554 value = hashtable_search(tags, "Name");
556 cave_process_tags_func("Name", value, cave);
558 /* process lame engine tag first so its settings may be overwritten later */
559 value = hashtable_search(tags, "Engine");
562 cave_process_tags_func("Engine", value, cave);
563 hashtable_remove(tags, "Engine");
566 /* check if this is an intermission, so we can set to cavesize or intermissionsize */
567 value = hashtable_search(tags, "Intermission");
570 cave_process_tags_func("Intermission", value, cave);
571 hashtable_remove(tags, "Intermission");
574 if (cave->intermission)
576 /* set to IntermissionSize */
577 cave->w = intermissionsize[0];
578 cave->h = intermissionsize[1];
579 cave->x1 = intermissionsize[2];
580 cave->y1 = intermissionsize[3];
581 cave->x2 = intermissionsize[4];
582 cave->y2 = intermissionsize[5];
586 /* set to CaveSize */
587 cave->w = cavesize[0];
588 cave->h = cavesize[1];
589 cave->x1 = cavesize[2];
590 cave->y1 = cavesize[3];
591 cave->x2 = cavesize[4];
592 cave->y2 = cavesize[5];
595 /* process size at the beginning... as ratio types depend on this. */
596 value = hashtable_search(tags, "Size");
599 cave_process_tags_func("Size", value, cave);
600 hashtable_remove(tags, "Size");
603 /* these are read from the hash table, but also have some implications */
604 /* we do not delete them from the hash table here; as _their values will be processed later_. */
605 /* here we only set their implicite meanings. */
606 /* these also set predictability */
607 if (hashtable_search(tags, "SlimePermeability"))
608 cave->slime_predictable = FALSE;
610 if (hashtable_search(tags, "SlimePermeabilityC64"))
611 cave->slime_predictable = TRUE;
613 /* these set scheduling type. framedelay takes precedence, if there are both; so we check it later. */
614 if (hashtable_search(tags, "CaveDelay"))
616 /* only set scheduling type, when it is not the gdash-default. */
617 /* this allows settings cavescheduling = bd1 in the [game] section, for example. */
618 /* in that case, this one will not overwrite it. */
619 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
620 cave->scheduling = GD_SCHEDULING_PLCK;
623 if (hashtable_search(tags, "FrameTime"))
624 /* but if the cave has a frametime setting, always switch to milliseconds. */
625 cave->scheduling = GD_SCHEDULING_MILLISECONDS;
627 /* process all tags */
628 hashtable_foreach_remove(tags, (hashtable_remove_fn)cave_process_tags_func, cave);
630 /* and at the end, when read all tags (especially the size= tag) */
631 /* process map, if any. */
632 /* only report if map read is bigger than size= specified. */
633 /* some old bdcff files use smaller intermissions than the one specified. */
636 int x, y, length = list_length(maplines);
639 /* create map and fill with initial border, in case that map strings are shorter or somewhat */
640 cave->map = gd_cave_map_new(cave, GdElement);
642 for (y = 0; y < cave->h; y++)
643 for (x = 0; x < cave->w; x++)
644 cave->map[y][x] = cave->initial_border;
646 if (length != cave->h && length != (cave->y2-cave->y1 + 1))
647 Warn("map error: cave height = %d (%d visible), map height = %d",
648 cave->h, cave->y2 - cave->y1 + 1, length);
650 for (iter = maplines, y = 0; y < length && iter != NULL; iter = iter->next, y++)
652 const char *line = iter->data;
653 int slen = strlen(line);
655 if (slen != cave->w && slen != (cave->x2 - cave->x1 + 1))
656 Warn("map error in row %d: cave width = %d (%d visible), map width = %d",
657 y, cave->w, cave->x2 - cave->x1 + 1, slen);
659 /* use number of cells from cave or string, whichever is smaller.
660 so will not overwrite array! */
661 for (x = 0; x < MIN(cave->w, slen); x++)
662 cave->map[y][x] = gd_get_element_from_character (line[x]);
667 /* sets the cavesize array to default values */
668 static void set_cavesize_defaults(void)
678 /* sets the cavesize array to default values */
679 static void set_intermissionsize_defaults(void)
681 intermissionsize[0] = 40;
682 intermissionsize[1] = 22;
683 intermissionsize[2] = 0;
684 intermissionsize[3] = 0;
685 intermissionsize[4] = 19;
686 intermissionsize[5] = 11;
689 boolean gd_caveset_load_from_bdcff(const char *contents)
695 boolean reading_replay = FALSE;
696 boolean reading_map = FALSE;
697 boolean reading_mapcodes = FALSE;
698 boolean reading_highscore = FALSE;
699 boolean reading_objects = FALSE;
700 boolean reading_bdcff_demo = FALSE;
701 /* assume version to be 0.32, also when the file does not specify it explicitly */
702 GdString version_read = "0.32";
703 List *mapstrings = NULL;
705 HashTable *tags, *replay_tags;
706 GdObjectLevels levels = GD_OBJECT_LEVEL_ALL;
707 GdCave *default_cave;
711 set_cavesize_defaults();
712 set_intermissionsize_defaults();
713 gd_create_char_to_element_table();
715 tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
716 replay_tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
718 /* split into lines */
719 lines = getSplitStringArray (contents, "\n", 0);
721 /* attributes read will be set in cave. if no [cave]; they are stored
722 in the default cave; like in a [game] */
723 default_cave = gd_cave_new();
726 linenum = getStringArrayLength(lines);
728 for (lineno = 0; lineno < linenum; lineno++)
730 char *line = lines[lineno];
733 /* remove windows-nightmare \r-s */
734 while((r = strchr(line, '\r')))
737 if (strlen (line) == 0)
738 continue; /* skip empty lines */
740 /* just skip comments. be aware that map lines may start with a semicolon... */
741 if (!reading_map && line[0] == ';')
744 /* STARTING WITH A BRACKET [ IS A SECTION */
747 if (strcasecmp(line, "[cave]") == 0)
752 Warn("incorrect file format: new [cave] section, but already read some map lines");
753 list_free(mapstrings);
757 /* process any pending tags for game ... */
758 cave_process_tags(default_cave, tags, NULL);
760 /* ... to be able to create a copy for a new cave. */
761 cave = gd_cave_new_from_cave(default_cave);
762 gd_caveset = list_append (gd_caveset, cave);
764 else if (strcasecmp(line, "[/cave]") == 0)
766 cave_process_tags(cave, tags, mapstrings);
767 list_free(mapstrings);
770 hashtable_foreach(tags, (hashtable_fn)cave_report_and_copy_unknown_tags_func, cave);
771 hashtable_remove_all(tags);
773 /* set this to point the pseudo-cave which holds default values */
776 else if (strcasecmp(line, "[map]") == 0)
779 if (mapstrings != NULL)
781 Warn("incorrect file format: new [map] section, but already read some map lines");
782 list_free(mapstrings);
786 else if (strcasecmp(line, "[/map]") == 0)
790 else if (strcasecmp(line, "[mapcodes]") == 0)
792 reading_mapcodes = TRUE;
794 else if (strcasecmp(line, "[/mapcodes]") == 0)
796 reading_mapcodes = FALSE;
798 else if (strcasecmp(line, "[highscore]") == 0)
800 reading_highscore = TRUE;
802 else if (strcasecmp(line, "[/highscore]") == 0)
804 reading_highscore = FALSE;
806 else if (strcasecmp(line, "[objects]") == 0)
808 reading_objects = TRUE;
810 else if (strcasecmp(line, "[/objects]") == 0)
812 reading_objects = FALSE;
814 else if (strcasecmp(line, "[demo]") == 0)
818 reading_bdcff_demo = TRUE;
820 if (cave != default_cave)
822 replay = gd_replay_new();
823 replay->saved = TRUE;
824 replay->success = TRUE; /* we think that it is a successful demo */
825 cave->replays = list_append(cave->replays, replay);
826 gd_strcpy(replay->player_name, "???"); /* name not saved */
830 Warn("[demo] section must be in [cave] section!");
833 else if (strcasecmp(line, "[/demo]") == 0)
835 reading_bdcff_demo = FALSE;
837 else if (strcasecmp(line, "[replay]") == 0)
839 reading_replay = TRUE;
841 else if (strcasecmp(line, "[/replay]") == 0)
845 reading_replay = FALSE;
846 replay = gd_replay_new();
848 /* set "saved" flag, so this replay will be written when the caveset is saved again */
849 replay->saved = TRUE;
850 replay_process_tags(replay, replay_tags);
853 /* BDCFF numbers levels from 1 to 5, but internally we number levels from 0 to 4 */
854 if (replay->level > 0)
858 /* report any remaining unknown tags */
859 hashtable_foreach(replay_tags, (hashtable_fn)replay_report_unknown_tags_func, NULL);
860 hashtable_remove_all(replay_tags);
862 if (replay->movements->len != 0)
864 cave->replays = list_append(cave->replays, replay);
868 Warn("no movements in replay!");
869 gd_replay_free(replay);
872 /* GOSH i hate bdcff */
873 else if (strncasecmp(line, "[level=", strlen("[level=")) == 0)
879 /* there IS an equal sign, and we also skip that, so this points to the numbers */
880 nums = strchr(line, '=') + 1;
881 num = sscanf(nums, "%d,%d,%d,%d,%d", l + 0, l + 1, l + 2, l + 3, l + 4);
886 Warn("invalid Levels tag: %s", line);
887 levels = GD_OBJECT_LEVEL_ALL;
893 for (n = 0; n < num; n++)
895 if (l[n] <= 5 && l[n] >= 1)
896 levels |= gd_levels_mask[l[n] - 1];
898 Warn("invalid level number %d", l[n]);
902 else if (strcasecmp(line, "[/level]") == 0)
904 levels = GD_OBJECT_LEVEL_ALL;
906 else if (strcasecmp(line, "[game]") == 0)
909 else if (strcasecmp(line, "[/game]") == 0)
912 else if (strcasecmp(line, "[BDCFF]") == 0)
915 else if (strcasecmp(line, "[/BDCFF]") == 0)
920 Warn("unknown section: \"%s\"", line);
928 /* just append to the mapstrings list. we will process it later */
929 mapstrings = list_append(mapstrings, line);
934 /* strip leading and trailing spaces AFTER checking if we are reading a map.
935 map lines might begin or end with spaces */
938 if (reading_highscore)
942 if (sscanf(line, "%d", &score) != 1 || strchr(line, ' ') == NULL)
943 { /* first word is the score */
944 Warn("highscore format incorrect");
948 if (cave == default_cave)
949 /* if we are reading the [game], add highscore to that one. */
950 /* from first space: the name */
951 gd_add_highscore(gd_caveset_data->highscore, strchr(line, ' ') + 1, score);
953 /* if a cave, add highscore to that. */
954 gd_add_highscore(cave->highscore, strchr(line, ' ') + 1, score);
960 /* read bdcff-style [demo], similar to a complete replay but cannot store like anything */
961 if (reading_bdcff_demo)
966 /* demo must be in [cave] section. we already showed an error message for this. */
967 if (cave == default_cave)
970 iter = list_last(cave->replays);
972 replay = (GdReplay *)iter->data;
973 replay_store_more_from_bdcff(replay, line);
980 GdObject *new_object;
982 new_object = gd_object_new_from_string(line);
985 new_object->levels = levels; /* apply levels to new object */
986 cave->objects = list_append(cave->objects, new_object);
990 Error("invalid object specification: %s", line);
996 /* has an equal sign -> some_attrib = parameters type line. */
997 if (strchr (line, '=') != NULL)
999 char *attrib, *param;
1001 attrib = line; /* attrib is from the first char */
1002 param = strchr(line, '=') + 1; /* param is after equal sign */
1003 *strchr (line, '=') = 0; /* delete equal sign - line is therefore splitted */
1005 /* own tag: not too much thinking :P */
1008 hashtable_insert(replay_tags, getStringCopy(attrib), getStringCopy(param));
1010 else if (reading_mapcodes)
1012 if (strcasecmp("Length", attrib) == 0)
1014 /* we do not support map code width != 1 */
1015 if (strcmp(param, "1") != 0)
1016 Warn(_("Only one-character map codes are currently supported!"));
1020 /* the first character of the attribute is the element code itself */
1021 gd_char_to_element[(int)attrib[0]] = gd_get_element_from_string(param);
1025 else if (strcasecmp("Version", attrib) == 0)
1027 gd_strcpy(version_read, param);
1030 else if (strcasecmp(attrib, "Caves") == 0)
1032 /* BDCFF files sometimes state how many caves they have */
1033 /* we ignore this field. */
1036 else if (strcasecmp(attrib, "Levels") == 0)
1038 /* BDCFF files sometimes state how many levels they have */
1039 /* we ignore this field. */
1041 else if (strcasecmp(attrib, "CaveSize") == 0)
1045 i = sscanf(param, "%d %d %d %d %d %d",
1053 /* allowed: 2 or 6 numbers */
1058 cavesize[4] = cavesize[0]-1;
1059 cavesize[5] = cavesize[1]-1;
1063 set_cavesize_defaults();
1064 Warn("invalid CaveSize tag: %s", line);
1067 else if (strcasecmp(attrib, "IntermissionSize") == 0)
1071 i = sscanf(param, "%d %d %d %d %d %d",
1072 intermissionsize + 0,
1073 intermissionsize + 1,
1074 intermissionsize + 2,
1075 intermissionsize + 3,
1076 intermissionsize + 4,
1077 intermissionsize + 5);
1079 /* allowed: 2 or 6 numbers */
1082 intermissionsize[2] = 0;
1083 intermissionsize[3] = 0;
1084 intermissionsize[4] = intermissionsize[0]-1;
1085 intermissionsize[5] = intermissionsize[1]-1;
1089 set_intermissionsize_defaults();
1090 Warn("invalid IntermissionSize tag: '%s'", line);
1093 else if (strcasecmp(attrib, "Effect") == 0)
1095 /* CHECK IF IT IS AN EFFECT */
1098 params = getSplitStringArray(param, " ", -1);
1100 /* an effect command has two parameters */
1101 if (getStringArrayLength(params) == 2)
1105 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
1107 /* we have to search for this effect */
1108 if (gd_cave_properties[i].type == GD_TYPE_EFFECT &&
1109 strcasecmp(params[0], gd_cave_properties[i].identifier) == 0)
1111 /* found identifier */
1112 void *value = STRUCT_MEMBER_P (cave, gd_cave_properties[i].offset);
1114 *((GdElement *) value) = gd_get_element_from_string (params[1]);
1119 /* if we didn't find first element name */
1120 if (gd_cave_properties[i].identifier == NULL)
1122 /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
1123 if (strcasecmp(params[0], "BOUNCING_BOULDER") == 0)
1124 cave->stone_bouncing_effect = gd_get_element_from_string (params[1]);
1125 else if (strcasecmp(params[0], "EXPLOSION3S") == 0)
1126 cave->explosion_effect = gd_get_element_from_string(params[1]);
1127 /* falling with one l... */
1128 else if (strcasecmp(params[0], "STARTING_FALING_DIAMOND") == 0)
1129 cave->diamond_falling_effect = gd_get_element_from_string (params[1]);
1130 /* dirt lookslike */
1131 else if (strcasecmp(params[0], "DIRT") == 0)
1132 cave->dirt_looks_like = gd_get_element_from_string (params[1]);
1133 else if (strcasecmp(params[0], "HEXPANDING_WALL") == 0 && strcasecmp(params[1], "STEEL_HEXPANDING_WALL") == 0)
1135 cave->expanding_wall_looks_like = O_STEEL;
1138 /* didn't find at all */
1139 Warn("invalid effect name '%s'", params[0]);
1143 Warn("invalid effect specification '%s'", param);
1145 freeStringArray(params);
1149 /* no special handling: this is a normal attribute. */
1151 if (cave == default_cave)
1153 /* we are reading the [game] */
1154 if (attrib_is_valid_for_caveset(attrib))
1156 /* if it is a caveset attrib, process it for the caveset. */
1157 struct_set_property(gd_caveset_data, gd_caveset_properties, attrib, param, 0);
1159 else if (attrib_is_valid_for_cave(attrib))
1161 /* it must be a default setting for all caves. is it a valid identifier? */
1162 /* yes, it is. add to the hash table, which will be copied for all caves. */
1163 hashtable_insert(tags, getStringCopy(attrib), getStringCopy(param));
1167 /* unknown setting - report. */
1168 Warn("invalid attribute for [game] '%s'", attrib);
1173 /* we are reading a [cave] */
1174 /* cave settings are immediately added to cave hash table. */
1175 /* if it is unknown, we have to remember it, and save it again. */
1176 hashtable_insert(tags, getStringCopy(attrib), getStringCopy(param));
1183 Error("cannot parse line: %s", line);
1188 Warn("incorrect file format: end of file, but still have some map lines read");
1189 list_free(mapstrings);
1193 /* the [game] section had some values which are default if not specified in [cave] sections. */
1194 /* these are used only for loading, so forget them now */
1195 if (default_cave->map)
1196 Warn(_("Invalid BDCFF: [game] section has a map"));
1197 if (default_cave->objects)
1198 Warn(_("Invalid BDCFF: [game] section has drawing objects defined"));
1201 freeStringArray(lines);
1202 hashtable_destroy(tags);
1203 hashtable_destroy(replay_tags);
1204 gd_cave_free(default_cave);
1206 /* old bdcff files hack. explanation follows. */
1207 /* there were 40x22 caves in c64 bd, intermissions were also 40x22, but the visible */
1208 /* part was the upper left corner, 20x12. 40x22 caves are needed, as 20x12 caves would */
1209 /* look different (random cave elements needs the correct size.) */
1210 /* also, in older bdcff files, there is no size= tag. caves default to 40x22 and 20x12. */
1211 /* even the explicit drawrect and other drawing instructions, which did set up intermissions */
1212 /* to be 20x12, are deleted. very very bad decision. */
1213 /* here we try to detect and correct this. */
1215 if (strEqual(version_read, "0.32"))
1219 Warn("No BDCFF version, or 0.32. Using unspecified-intermission-size hack.");
1221 for (iter = gd_caveset; iter != NULL; iter = iter->next)
1223 GdCave *cave = (GdCave *)iter->data;
1225 /* only applies to intermissions */
1226 /* not applied to mapped caves, as maps are filled with initial border, if the map read is smaller */
1227 if (cave->intermission && !cave->map)
1229 /* we do not set the cave to 20x12, rather to 40x22 with 20x12 visible. */
1239 /* and cover the invisible area */
1240 object.type = GD_FILLED_RECTANGLE;
1242 object.y1 = 11; /* 11, because this will also be the border */
1245 object.element = cave->initial_border;
1246 object.fill_element = cave->initial_border;
1248 cave->objects = list_prepend(cave->objects, get_memcpy(&object, sizeof(object)));
1251 object.y1 = 0; /* 19, as it is also the border */
1253 cave->objects = list_prepend(cave->objects, get_memcpy(&object, sizeof(object))); /* another */
1258 if (!strEqual(version_read, BDCFF_VERSION))
1259 Warn("BDCFF version %s, loaded caveset may have errors.", version_read);
1261 /* check for replays which are problematic */
1262 for (iter = gd_caveset; iter != NULL; iter = iter->next)
1263 gd_cave_check_replays((GdCave *)iter->data, TRUE, FALSE, FALSE);
1265 /* if there was some error message - return fail XXX */