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 // must be reset before calling strtod() to detect overflow/underflow
256 res = strtod(params[paramindex], NULL);
257 if (errno == 0 && res >= 0 && res <= 1)
259 // fill all remaining items in array - may be only one
260 for (k = j; k < prop_desc[i].count; k++)
261 // probabilities are stored inside as ppm (1E6)
262 ivalue[k] = res * 1E6 + 0.5;
270 // must be reset before calling strtod() to detect overflow/underflow
272 res = strtod (params[paramindex], NULL);
273 if (errno == 0 && res >= 0 && res <= 1)
275 for (k = j; k < prop_desc[i].count; k++)
276 ivalue[k] = (int)(res * ratio + 0.5);
283 case GD_TYPE_ELEMENT:
284 evalue[j] = gd_get_element_from_string(params[paramindex]);
286 // copy to all remaining elements in array
287 for (k = j + 1; k < prop_desc[i].count; k++)
288 evalue[k] = evalue[j];
290 // this shows error message on its own, do treat as always succeeded
294 case GD_TYPE_DIRECTION:
295 dvalue[j] = gd_direction_from_string(params[paramindex]);
296 // copy to all remaining items in array
297 for (k = j + 1; k < prop_desc[i].count; k++)
298 dvalue[k] = dvalue[j];
303 case GD_TYPE_SCHEDULING:
304 svalue[j] = gd_scheduling_from_string(params[paramindex]);
305 // copy to all remaining items in array
306 for (k = j + 1; k < prop_desc[i].count; k++)
307 svalue[k] = svalue[j];
309 // if there was an error, already reported by gd_scheduling_from_string
315 // shoud have handled this elsewhere
320 paramindex++; // go to next parameter to process
322 Warn("invalid parameter '%s' for attribute %s", params[paramindex], attrib);
327 // if we found the identifier, but still could not process all parameters...
328 // of course, not for strings, as the whole line is the string
329 if (identifier_found && !was_string && paramindex < paramcount)
330 Warn("excess parameters for attribute '%s': '%s'", attrib, params[paramindex]);
332 freeStringArray(params);
334 return identifier_found;
338 // ============================================================================
340 // ============================================================================
342 static boolean replay_store_more_from_bdcff(GdReplay *replay, const char *param)
346 boolean result = TRUE;
348 split = getSplitStringArray(param, " ", -1);
350 for (i = 0; split[i] != 0; i++)
351 result = result && replay_store_from_bdcff(replay, split[i]);
353 freeStringArray(split);
358 // report all remaining tags; called after the above function.
359 static void replay_report_unknown_tags_func(const char *attrib, const char *param, void *data)
361 Warn("unknown replay tag '%s'", attrib);
364 // a GHashTable foreach func.
365 // keys are attribs; values are params;
366 // the user data is the cave the hash table belongs to.
367 static boolean replay_process_tags_func(const char *attrib, const char *param, GdReplay *replay)
369 boolean identifier_found = FALSE;
372 if (strcasecmp(attrib, "Movements") == 0)
374 identifier_found = TRUE;
375 replay_store_more_from_bdcff(replay, param);
380 // 0: for ratio types; not used
381 identifier_found = struct_set_property(replay, gd_replay_properties,
385 // a ghrfunc should return true if the identifier is to be removed
386 return identifier_found;
390 static void replay_process_tags(GdReplay *replay, HashTable *tags)
393 hashtable_foreach_remove(tags, (hashtable_remove_fn)replay_process_tags_func, replay);
396 // a GHashTable foreach func.
397 // keys are attribs; values are params;
398 // the user data is the cave the hash table belongs to.
399 static boolean cave_process_tags_func(const char *attrib, const char *param, GdCave *cave)
403 boolean identifier_found;
405 params = getSplitStringArray(param, " ", -1);
406 paramcount = getStringArrayLength(params);
407 identifier_found = FALSE;
409 if (strcasecmp(attrib, "SnapExplosions") == 0)
411 // handle compatibility with old snapexplosions flag
413 identifier_found = TRUE;
415 if (strcasecmp(param, "true") == 0)
417 cave->snap_element = O_EXPLODE_1;
419 else if (strcasecmp(param, "false") == 0)
421 cave->snap_element = O_SPACE;
425 Warn("invalid param for '%s': '%s'", attrib, param);
428 else if (strcasecmp(attrib, "BD1Scheduling") == 0)
430 // handle compatibility with old bd1scheduling flag
432 identifier_found = TRUE;
434 if (strcasecmp(param, "true") == 0)
436 if (cave->scheduling == GD_SCHEDULING_PLCK)
437 cave->scheduling = GD_SCHEDULING_BD1;
440 else if (strcasecmp(attrib, "Engine") == 0)
442 // handle bdcff engine flag
444 identifier_found = TRUE;
446 GdEngine engine = gd_cave_get_engine_from_string(param);
448 if (engine == GD_ENGINE_INVALID)
449 Warn(_("invalid parameter \"%s\" for attribute %s"), param, attrib);
451 gd_cave_set_engine_defaults(cave, engine);
453 else if (strcasecmp(attrib, "AmoebaProperties") == 0)
455 // handle compatibility with old AmoebaProperties flag
457 GdElement elem1 = O_STONE, elem2 = O_DIAMOND;
459 identifier_found = TRUE;
460 elem1 = gd_get_element_from_string(params[0]);
461 elem2 = gd_get_element_from_string(params[1]);
462 cave->amoeba_too_big_effect = elem1;
463 cave->amoeba_enclosed_effect = elem2;
465 else if (strcasecmp(attrib, "Colors") == 0)
467 // colors attribute is a mess, have to process explicitly
470 // Colors = [border background] foreground1 foreground2 foreground3 [amoeba slime]
471 identifier_found = TRUE;
476 cave->colorb = gd_c64_color(0); // border - black
477 cave->color0 = gd_c64_color(0); // background - black
478 cave->color1 = gd_color_get_from_string(params[0]);
479 cave->color2 = gd_color_get_from_string(params[1]);
480 cave->color3 = gd_color_get_from_string(params[2]);
481 cave->color4 = cave->color3; // amoeba
482 cave->color5 = cave->color1; // slime
484 else if (paramcount == 5)
487 cave->colorb = gd_color_get_from_string(params[0]);
488 cave->color0 = gd_color_get_from_string(params[1]);
489 cave->color1 = gd_color_get_from_string(params[2]);
490 cave->color2 = gd_color_get_from_string(params[3]);
491 cave->color3 = gd_color_get_from_string(params[4]);
492 cave->color4 = cave->color3; // amoeba
493 cave->color5 = cave->color1; // slime
495 else if (paramcount == 7)
497 // bg,color0,1,2,3,amoeba,slime
498 cave->colorb = gd_color_get_from_string(params[0]);
499 cave->color0 = gd_color_get_from_string(params[1]);
500 cave->color1 = gd_color_get_from_string(params[2]);
501 cave->color2 = gd_color_get_from_string(params[3]);
502 cave->color3 = gd_color_get_from_string(params[4]);
503 cave->color4 = gd_color_get_from_string(params[5]); // amoeba
504 cave->color5 = gd_color_get_from_string(params[6]); // slime
508 Warn("invalid number of color strings: %s", param);
513 // now check and maybe make up some new.
515 gd_color_is_unknown(cave->colorb) ||
516 gd_color_is_unknown(cave->color0) ||
517 gd_color_is_unknown(cave->color1) ||
518 gd_color_is_unknown(cave->color2) ||
519 gd_color_is_unknown(cave->color3) ||
520 gd_color_is_unknown(cave->color4) ||
521 gd_color_is_unknown(cave->color5))
523 Warn("created a new C64 color scheme.");
525 gd_cave_set_random_c64_colors(cave); // just create some random
530 identifier_found = struct_set_property(cave, gd_cave_properties, attrib, param, cave->w * cave->h);
533 freeStringArray(params);
535 // a ghrfunc should return true if the identifier is to be removed
536 return identifier_found;
539 // report all remaining tags; called after the above function.
540 static void cave_report_and_copy_unknown_tags_func(char *attrib, char *param, void *data)
542 GdCave *cave = (GdCave *)data;
544 Warn("unknown tag '%s'", attrib);
546 hashtable_insert(cave->tags, getStringCopy(attrib), getStringCopy(param));
549 // having read all strings belonging to the cave, process it.
550 static void cave_process_tags(GdCave *cave, HashTable *tags, List *maplines)
554 // first check cave name, so we can report errors correctly (saying that GdCave xy: error foobar)
555 value = hashtable_search(tags, "Name");
557 cave_process_tags_func("Name", value, cave);
559 // process lame engine tag first so its settings may be overwritten later
560 value = hashtable_search(tags, "Engine");
563 cave_process_tags_func("Engine", value, cave);
564 hashtable_remove(tags, "Engine");
567 // check if this is an intermission, so we can set to cavesize or intermissionsize
568 value = hashtable_search(tags, "Intermission");
571 cave_process_tags_func("Intermission", value, cave);
572 hashtable_remove(tags, "Intermission");
575 if (cave->intermission)
577 // set to IntermissionSize
578 cave->w = intermissionsize[0];
579 cave->h = intermissionsize[1];
580 cave->x1 = intermissionsize[2];
581 cave->y1 = intermissionsize[3];
582 cave->x2 = intermissionsize[4];
583 cave->y2 = intermissionsize[5];
588 cave->w = cavesize[0];
589 cave->h = cavesize[1];
590 cave->x1 = cavesize[2];
591 cave->y1 = cavesize[3];
592 cave->x2 = cavesize[4];
593 cave->y2 = cavesize[5];
596 // process size at the beginning... as ratio types depend on this.
597 value = hashtable_search(tags, "Size");
600 cave_process_tags_func("Size", value, cave);
601 hashtable_remove(tags, "Size");
604 // these are read from the hash table, but also have some implications
605 // we do not delete them from the hash table here; as _their values will be processed later_.
606 // here we only set their implicite meanings.
607 // these also set predictability
608 if (hashtable_search(tags, "SlimePermeability"))
609 cave->slime_predictable = FALSE;
611 if (hashtable_search(tags, "SlimePermeabilityC64"))
612 cave->slime_predictable = TRUE;
614 // these set scheduling type.
615 // framedelay takes precedence, if there are both; so we check it later.
616 if (hashtable_search(tags, "CaveDelay"))
618 // only set scheduling type, when it is not the gdash-default.
619 // this allows settings cavescheduling = bd1 in the [game] section, for example.
620 // in that case, this one will not overwrite it.
621 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
622 cave->scheduling = GD_SCHEDULING_PLCK;
625 // but if the cave has a frametime setting, always switch to milliseconds.
626 if (hashtable_search(tags, "FrameTime"))
627 cave->scheduling = GD_SCHEDULING_MILLISECONDS;
630 hashtable_foreach_remove(tags, (hashtable_remove_fn)cave_process_tags_func, cave);
632 // and at the end, when read all tags (especially the size= tag)
633 // process map, if any.
634 // only report if map read is bigger than size= specified.
635 // some old bdcff files use smaller intermissions than the one specified.
638 int x, y, length = list_length(maplines);
641 // create map and fill with initial border, in case that map strings are shorter or somewhat
642 cave->map = gd_cave_map_new(cave, GdElement);
644 for (y = 0; y < cave->h; y++)
645 for (x = 0; x < cave->w; x++)
646 cave->map[y][x] = cave->initial_border;
648 if (length != cave->h && length != (cave->y2-cave->y1 + 1))
649 Warn("map error: cave height = %d (%d visible), map height = %d",
650 cave->h, cave->y2 - cave->y1 + 1, length);
652 for (iter = maplines, y = 0; y < length && iter != NULL; iter = iter->next, y++)
654 const char *line = iter->data;
655 int slen = strlen(line);
657 if (slen != cave->w && slen != (cave->x2 - cave->x1 + 1))
658 Warn("map error in row %d: cave width = %d (%d visible), map width = %d",
659 y, cave->w, cave->x2 - cave->x1 + 1, slen);
661 // use number of cells from cave or string, whichever is smaller.
662 // so will not overwrite array!
663 for (x = 0; x < MIN(cave->w, slen); x++)
664 cave->map[y][x] = gd_get_element_from_character (line[x]);
669 // sets the cavesize array to default values
670 static void set_cavesize_defaults(void)
680 // sets the cavesize array to default values
681 static void set_intermissionsize_defaults(void)
683 intermissionsize[0] = 40;
684 intermissionsize[1] = 22;
685 intermissionsize[2] = 0;
686 intermissionsize[3] = 0;
687 intermissionsize[4] = 19;
688 intermissionsize[5] = 11;
691 boolean gd_caveset_load_from_bdcff(const char *contents)
697 boolean reading_replay = FALSE;
698 boolean reading_map = FALSE;
699 boolean reading_mapcodes = FALSE;
700 boolean reading_highscore = FALSE;
701 boolean reading_objects = FALSE;
702 boolean reading_bdcff_demo = FALSE;
703 // assume version to be 0.32, also when the file does not specify it explicitly
704 GdString version_read = "0.32";
705 List *mapstrings = NULL;
707 HashTable *tags, *replay_tags;
708 GdObjectLevels levels = GD_OBJECT_LEVEL_ALL;
709 GdCave *default_cave;
713 set_cavesize_defaults();
714 set_intermissionsize_defaults();
715 gd_create_char_to_element_table();
717 tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
718 replay_tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
721 lines = getSplitStringArray (contents, "\n", 0);
723 // attributes read will be set in cave. if no [cave]; they are stored
724 // in the default cave; like in a [game] */
725 default_cave = gd_cave_new();
728 linenum = getStringArrayLength(lines);
730 for (lineno = 0; lineno < linenum; lineno++)
732 char *line = lines[lineno];
735 // remove windows-nightmare \r-s
736 while ((r = strchr(line, '\r')))
740 if (strlen(line) == 0)
743 // just skip comments. be aware that map lines may start with a semicolon...
744 if (!reading_map && line[0] == ';')
747 // STARTING WITH A BRACKET [ IS A SECTION
750 if (strcasecmp(line, "[cave]") == 0)
755 Warn("incorrect file format: new [cave] section, but already read some map lines");
756 list_free(mapstrings);
760 // process any pending tags for game ...
761 cave_process_tags(default_cave, tags, NULL);
763 // ... to be able to create a copy for a new cave.
764 cave = gd_cave_new_from_cave(default_cave);
765 gd_caveset = list_append (gd_caveset, cave);
767 else if (strcasecmp(line, "[/cave]") == 0)
769 cave_process_tags(cave, tags, mapstrings);
770 list_free(mapstrings);
773 hashtable_foreach(tags, (hashtable_fn)cave_report_and_copy_unknown_tags_func, cave);
774 hashtable_remove_all(tags);
776 // set this to point the pseudo-cave which holds default values
779 else if (strcasecmp(line, "[map]") == 0)
782 if (mapstrings != NULL)
784 Warn("incorrect file format: new [map] section, but already read some map lines");
785 list_free(mapstrings);
789 else if (strcasecmp(line, "[/map]") == 0)
793 else if (strcasecmp(line, "[mapcodes]") == 0)
795 reading_mapcodes = TRUE;
797 else if (strcasecmp(line, "[/mapcodes]") == 0)
799 reading_mapcodes = FALSE;
801 else if (strcasecmp(line, "[highscore]") == 0)
803 reading_highscore = TRUE;
805 else if (strcasecmp(line, "[/highscore]") == 0)
807 reading_highscore = FALSE;
809 else if (strcasecmp(line, "[objects]") == 0)
811 reading_objects = TRUE;
813 else if (strcasecmp(line, "[/objects]") == 0)
815 reading_objects = FALSE;
817 else if (strcasecmp(line, "[demo]") == 0)
821 reading_bdcff_demo = TRUE;
823 if (cave != default_cave)
825 replay = gd_replay_new();
826 replay->saved = TRUE;
827 replay->success = TRUE; // we think that it is a successful demo
828 cave->replays = list_append(cave->replays, replay);
829 gd_strcpy(replay->player_name, "???"); // name not saved
833 Warn("[demo] section must be in [cave] section!");
836 else if (strcasecmp(line, "[/demo]") == 0)
838 reading_bdcff_demo = FALSE;
840 else if (strcasecmp(line, "[replay]") == 0)
842 reading_replay = TRUE;
844 else if (strcasecmp(line, "[/replay]") == 0)
848 reading_replay = FALSE;
849 replay = gd_replay_new();
851 // set "saved" flag, so this replay will be written when the caveset is saved again
852 replay->saved = TRUE;
853 replay_process_tags(replay, replay_tags);
856 // BDCFF numbers levels from 1 to 5, but internally we number levels from 0 to 4
857 if (replay->level > 0)
861 // report any remaining unknown tags
862 hashtable_foreach(replay_tags, (hashtable_fn)replay_report_unknown_tags_func, NULL);
863 hashtable_remove_all(replay_tags);
865 if (replay->movements->len != 0)
867 cave->replays = list_append(cave->replays, replay);
871 Warn("no movements in replay!");
872 gd_replay_free(replay);
876 else if (strncasecmp(line, "[level=", strlen("[level=")) == 0)
882 // there IS an equal sign, and we also skip that, so this points to the numbers
883 nums = strchr(line, '=') + 1;
884 num = sscanf(nums, "%d,%d,%d,%d,%d", l + 0, l + 1, l + 2, l + 3, l + 4);
889 Warn("invalid Levels tag: %s", line);
890 levels = GD_OBJECT_LEVEL_ALL;
896 for (n = 0; n < num; n++)
898 if (l[n] <= 5 && l[n] >= 1)
899 levels |= gd_levels_mask[l[n] - 1];
901 Warn("invalid level number %d", l[n]);
905 else if (strcasecmp(line, "[/level]") == 0)
907 levels = GD_OBJECT_LEVEL_ALL;
909 else if (strcasecmp(line, "[game]") == 0)
912 else if (strcasecmp(line, "[/game]") == 0)
915 else if (strcasecmp(line, "[BDCFF]") == 0)
918 else if (strcasecmp(line, "[/BDCFF]") == 0)
923 Warn("unknown section: \"%s\"", line);
931 // just append to the mapstrings list. we will process it later
932 mapstrings = list_append(mapstrings, line);
937 // strip leading and trailing spaces AFTER checking if we are reading a map.
938 // map lines might begin or end with spaces */
941 if (reading_highscore)
945 if (sscanf(line, "%d", &score) != 1 || strchr(line, ' ') == NULL)
947 // first word is the score
948 Warn("highscore format incorrect");
952 if (cave == default_cave)
953 // if we are reading the [game], add highscore to that one.
954 // from first space: the name
955 gd_add_highscore(gd_caveset_data->highscore, strchr(line, ' ') + 1, score);
957 // if a cave, add highscore to that.
958 gd_add_highscore(cave->highscore, strchr(line, ' ') + 1, score);
964 // read bdcff-style [demo], similar to a complete replay but cannot store like anything
965 if (reading_bdcff_demo)
970 // demo must be in [cave] section. we already showed an error message for this.
971 if (cave == default_cave)
974 iter = list_last(cave->replays);
976 replay = (GdReplay *)iter->data;
977 replay_store_more_from_bdcff(replay, line);
984 GdObject *new_object;
986 new_object = gd_object_new_from_string(line);
989 new_object->levels = levels; // apply levels to new object
990 cave->objects = list_append(cave->objects, new_object);
994 Error("invalid object specification: %s", line);
1000 // has an equal sign -> some_attrib = parameters type line.
1001 if (strchr (line, '=') != NULL)
1003 char *attrib, *param;
1005 attrib = line; // attrib is from the first char
1006 param = strchr(line, '=') + 1; // param is after equal sign
1007 *strchr (line, '=') = 0; // delete equal sign - line is therefore splitted
1009 // own tag: not too much thinking :P
1012 hashtable_insert(replay_tags, getStringCopy(attrib), getStringCopy(param));
1014 else if (reading_mapcodes)
1016 if (strcasecmp("Length", attrib) == 0)
1018 // we do not support map code width != 1
1019 if (strcmp(param, "1") != 0)
1020 Warn(_("Only one-character map codes are currently supported!"));
1024 // the first character of the attribute is the element code itself
1025 gd_char_to_element[(int)attrib[0]] = gd_get_element_from_string(param);
1028 else if (strcasecmp("Version", attrib) == 0)
1031 gd_strcpy(version_read, param);
1033 else if (strcasecmp(attrib, "Caves") == 0)
1037 // BDCFF files sometimes state how many caves they have
1038 // we ignore this field.
1040 else if (strcasecmp(attrib, "Levels") == 0)
1044 // BDCFF files sometimes state how many levels they have
1045 // we ignore this field.
1047 else if (strcasecmp(attrib, "CaveSize") == 0)
1051 i = sscanf(param, "%d %d %d %d %d %d",
1059 // allowed: 2 or 6 numbers
1064 cavesize[4] = cavesize[0]-1;
1065 cavesize[5] = cavesize[1]-1;
1069 set_cavesize_defaults();
1070 Warn("invalid CaveSize tag: %s", line);
1073 else if (strcasecmp(attrib, "IntermissionSize") == 0)
1077 i = sscanf(param, "%d %d %d %d %d %d",
1078 intermissionsize + 0,
1079 intermissionsize + 1,
1080 intermissionsize + 2,
1081 intermissionsize + 3,
1082 intermissionsize + 4,
1083 intermissionsize + 5);
1085 // allowed: 2 or 6 numbers
1088 intermissionsize[2] = 0;
1089 intermissionsize[3] = 0;
1090 intermissionsize[4] = intermissionsize[0]-1;
1091 intermissionsize[5] = intermissionsize[1]-1;
1095 set_intermissionsize_defaults();
1096 Warn("invalid IntermissionSize tag: '%s'", line);
1099 else if (strcasecmp(attrib, "Effect") == 0)
1101 // CHECK IF IT IS AN EFFECT
1104 params = getSplitStringArray(param, " ", -1);
1106 // an effect command has two parameters
1107 if (getStringArrayLength(params) == 2)
1111 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
1113 // we have to search for this effect
1114 if (gd_cave_properties[i].type == GD_TYPE_EFFECT &&
1115 strcasecmp(params[0], gd_cave_properties[i].identifier) == 0)
1118 void *value = STRUCT_MEMBER_P (cave, gd_cave_properties[i].offset);
1120 *((GdElement *) value) = gd_get_element_from_string (params[1]);
1125 // if we didn't find first element name
1126 if (gd_cave_properties[i].identifier == NULL)
1128 // for compatibility with tim stridmann's memorydump->bdcff converter... .... ...
1129 if (strcasecmp(params[0], "BOUNCING_BOULDER") == 0)
1130 cave->stone_bouncing_effect = gd_get_element_from_string (params[1]);
1131 else if (strcasecmp(params[0], "EXPLOSION3S") == 0)
1132 cave->explosion_effect = gd_get_element_from_string(params[1]);
1133 // falling with one l...
1134 else if (strcasecmp(params[0], "STARTING_FALING_DIAMOND") == 0)
1135 cave->diamond_falling_effect = gd_get_element_from_string (params[1]);
1137 else if (strcasecmp(params[0], "DIRT") == 0)
1138 cave->dirt_looks_like = gd_get_element_from_string (params[1]);
1139 else if (strcasecmp(params[0], "HEXPANDING_WALL") == 0 &&
1140 strcasecmp(params[1], "STEEL_HEXPANDING_WALL") == 0)
1142 cave->expanding_wall_looks_like = O_STEEL;
1146 // didn't find at all
1147 Warn("invalid effect name '%s'", params[0]);
1153 Warn("invalid effect specification '%s'", param);
1156 freeStringArray(params);
1160 // no special handling: this is a normal attribute.
1162 if (cave == default_cave)
1164 // we are reading the [game]
1165 if (attrib_is_valid_for_caveset(attrib))
1167 // if it is a caveset attrib, process it for the caveset.
1168 struct_set_property(gd_caveset_data, gd_caveset_properties, attrib, param, 0);
1170 else if (attrib_is_valid_for_cave(attrib))
1172 // it must be a default setting for all caves. is it a valid identifier?
1173 // yes, it is. add to the hash table, which will be copied for all caves.
1174 hashtable_insert(tags, getStringCopy(attrib), getStringCopy(param));
1178 // unknown setting - report.
1179 Warn("invalid attribute for [game] '%s'", attrib);
1184 // we are reading a [cave]
1185 // cave settings are immediately added to cave hash table.
1186 // if it is unknown, we have to remember it, and save it again.
1187 hashtable_insert(tags, getStringCopy(attrib), getStringCopy(param));
1194 Error("cannot parse line: %s", line);
1199 Warn("incorrect file format: end of file, but still have some map lines read");
1200 list_free(mapstrings);
1204 // the [game] section had some values which are default if not specified in [cave] sections.
1205 // these are used only for loading, so forget them now
1206 if (default_cave->map)
1207 Warn(_("Invalid BDCFF: [game] section has a map"));
1208 if (default_cave->objects)
1209 Warn(_("Invalid BDCFF: [game] section has drawing objects defined"));
1212 freeStringArray(lines);
1213 hashtable_destroy(tags);
1214 hashtable_destroy(replay_tags);
1215 gd_cave_free(default_cave);
1217 // old bdcff files hack. explanation follows.
1218 // there were 40x22 caves in c64 bd, intermissions were also 40x22, but the visible
1219 // part was the upper left corner, 20x12. 40x22 caves are needed, as 20x12 caves would
1220 // look different (random cave elements needs the correct size.)
1221 // also, in older bdcff files, there is no size= tag. caves default to 40x22 and 20x12.
1222 // even the explicit drawrect and other drawing instructions, which did set up intermissions
1223 // to be 20x12, are deleted. very very bad decision.
1224 // here we try to detect and correct this.
1226 if (strEqual(version_read, "0.32"))
1230 Warn("No BDCFF version, or 0.32. Using unspecified-intermission-size hack.");
1232 for (iter = gd_caveset; iter != NULL; iter = iter->next)
1234 GdCave *cave = (GdCave *)iter->data;
1236 // only applies to intermissions; not applied to mapped caves, as maps are filled with
1237 // initial border, if the map read is smaller
1238 if (cave->intermission && !cave->map)
1240 // we do not set the cave to 20x12, rather to 40x22 with 20x12 visible.
1250 // and cover the invisible area
1251 object.type = GD_FILLED_RECTANGLE;
1253 object.y1 = 11; // 11, because this will also be the border
1256 object.element = cave->initial_border;
1257 object.fill_element = cave->initial_border;
1259 cave->objects = list_prepend(cave->objects, get_memcpy(&object, sizeof(object)));
1262 object.y1 = 0; // 19, as it is also the border
1265 cave->objects = list_prepend(cave->objects, get_memcpy(&object, sizeof(object)));
1270 if (!strEqual(version_read, BDCFF_VERSION))
1271 Warn("BDCFF version %s, loaded caveset may have errors.", version_read);
1273 // check for replays which are problematic
1274 for (iter = gd_caveset; iter != NULL; iter = iter->next)
1275 gd_cave_check_replays((GdCave *)iter->data, TRUE, FALSE, FALSE);
1277 // if there was some error message - return fail XXX
1282 // ============================================================================
1284 // ============================================================================
1286 #define GD_PTR_ARRAY_MINIMUM_INITIAL_SIZE 64
1288 GdPtrArray *gd_ptr_array_sized_new(unsigned int size)
1290 GdPtrArray *array = checked_calloc(sizeof(GdPtrArray));
1292 array->data = checked_calloc(size * sizeof(void *));
1293 array->size_allocated = size;
1294 array->size_initial = size;
1300 GdPtrArray *gd_ptr_array_new(void)
1302 return gd_ptr_array_sized_new(GD_PTR_ARRAY_MINIMUM_INITIAL_SIZE);
1305 void gd_ptr_array_add(GdPtrArray *array, void *data)
1307 if (array->size == array->size_allocated)
1309 array->size_allocated += array->size_initial;
1310 array->data = checked_realloc(array->data, array->size_allocated * sizeof(void *));
1313 array->data[array->size++] = data;
1316 boolean gd_ptr_array_remove(GdPtrArray *array, void *data)
1320 for (i = 0; i < array->size; i++)
1322 if (array->data[i] == data)
1324 checked_free(array->data[i]);
1326 for (j = i; j < array->size - 1; j++)
1327 array->data[j] = array->data[j + 1];
1338 void gd_ptr_array_free(GdPtrArray *array, boolean free_data)
1344 for (i = 0; i < array->size; i++)
1345 checked_free(array->data[i]);
1348 checked_free(array);
1351 // ratio: max cave size for GD_TYPE_RATIO. should be set to cave->w*cave->h when calling
1352 static void save_properties(GdPtrArray *out, void *str, void *str_def,
1353 const GdStructDescriptor *prop_desc, int ratio)
1356 boolean parameter_written = FALSE, should_write = FALSE;
1358 const char *identifier = NULL;
1360 for (i = 0; prop_desc[i].identifier != NULL; i++)
1362 void *value, *default_value;
1364 // used only by the gui
1365 if (prop_desc[i].type == GD_TAB || prop_desc[i].type == GD_LABEL)
1368 // these are handled explicitly
1369 if (prop_desc[i].flags & GD_DONT_SAVE)
1373 // write together with identifier, as one string per line.
1374 if (prop_desc[i].type == GD_TYPE_STRING)
1376 // treat strings as special - do not even write identifier if no string.
1377 char *text = STRUCT_MEMBER_P(str, prop_desc[i].offset);
1379 if (strlen(text) > 0)
1380 gd_ptr_array_add(out, getStringPrint("%s=%s", prop_desc[i].identifier, text));
1385 // dynamic string: need to escape newlines
1386 if (prop_desc[i].type == GD_TYPE_LONGSTRING)
1388 char *string = STRUCT_MEMBER(char *, str, prop_desc[i].offset);
1390 if (string != NULL && strlen(string) > 0)
1392 char *escaped = getEscapedString(string);
1394 gd_ptr_array_add(out, getStringPrint("%s=%s", prop_desc[i].identifier, escaped));
1396 checked_free(escaped);
1402 // if identifier differs from the previous, write out the line collected, and start a new one
1403 if (identifier == NULL || !strEqual(prop_desc[i].identifier, identifier))
1407 // write lines only which carry information other than the default settings
1408 gd_ptr_array_add(out, getStringCopy(line));
1411 if (prop_desc[i].type == GD_TYPE_EFFECT)
1412 setStringPrint(&line, "Effect=");
1414 setStringPrint(&line, "%s=", prop_desc[i].identifier);
1416 parameter_written = FALSE; // no value written yet
1417 should_write = FALSE;
1419 // remember identifier
1420 identifier = prop_desc[i].identifier;
1423 // if we always save this identifier, remember now
1424 if (prop_desc[i].flags & GD_ALWAYS_SAVE)
1425 should_write = TRUE;
1427 value = STRUCT_MEMBER_P(str, prop_desc[i].offset);
1428 default_value = STRUCT_MEMBER_P(str_def, prop_desc[i].offset);
1430 for (j = 0; j < prop_desc[i].count; j++)
1432 // separate values by spaces. of course no space required for the first one
1433 if (parameter_written)
1434 appendStringPrint(&line, " ");
1436 parameter_written = TRUE; // at least one value written, so write space the next time
1438 switch (prop_desc[i].type)
1440 case GD_TYPE_BOOLEAN:
1441 appendStringPrint(&line, "%s", ((boolean *) value)[j] ? "true" : "false");
1442 if (((boolean *) value)[j] != ((boolean *) default_value)[j])
1443 should_write = TRUE;
1447 appendStringPrint(&line, "%d", ((int *) value)[j]);
1448 if (((int *) value)[j] != ((int *) default_value)[j])
1449 should_write = TRUE;
1453 appendStringPrint(&line, "%6.5f", ((int *) value)[j] / (double)ratio);
1454 if (((int *) value)[j] != ((int *) default_value)[j])
1455 should_write = TRUE;
1458 case GD_TYPE_PROBABILITY:
1459 // probabilities are stored as *1E6
1460 appendStringPrint(&line, "%6.5f", ((int *) value)[j] / 1E6);
1462 if (((double *) value)[j] != ((double *) default_value)[j])
1463 should_write = TRUE;
1466 case GD_TYPE_ELEMENT:
1467 appendStringPrint(&line, "%s", gd_elements[((GdElement *) value)[j]].filename);
1468 if (((GdElement *) value)[j] != ((GdElement *) default_value)[j])
1469 should_write = TRUE;
1472 case GD_TYPE_EFFECT:
1473 // for effects, the property identifier is the effect name.
1474 // "Effect=" is hardcoded; see above.
1475 appendStringPrint(&line, "%s %s", prop_desc[i].identifier,
1476 gd_elements[((GdElement *) value)[j]].filename);
1477 if (((GdElement *) value)[j] != ((GdElement *) default_value)[j])
1478 should_write = TRUE;
1482 appendStringPrint(&line, "%s", gd_color_get_string(((GdColor *) value)[j]));
1483 should_write = TRUE;
1486 case GD_TYPE_DIRECTION:
1487 appendStringPrint(&line, "%s", gd_direction_get_filename(((GdDirection *) value)[j]));
1488 if (((GdDirection *) value)[j] != ((GdDirection *) default_value)[j])
1489 should_write = TRUE;
1492 case GD_TYPE_SCHEDULING:
1493 appendStringPrint(&line, "%s", gd_scheduling_get_filename(((GdScheduling *) value)[j]));
1494 if (((GdScheduling *) value)[j] != ((GdScheduling *) default_value)[j])
1495 should_write = TRUE;
1500 // used by the editor ui
1503 case GD_TYPE_STRING:
1504 case GD_TYPE_LONGSTRING:
1511 // write remaining data
1513 gd_ptr_array_add(out, getStringCopy(line));
1518 // remove a line from the list of strings.
1519 // the prefix should be a property; add an equal sign! so properties which have names like
1520 // "slime" and "slimeproperties" won't match each other.
1521 static void cave_properties_remove(GdPtrArray *out, const char *prefix)
1525 if (!strSuffix(prefix, "="))
1526 Warn("string '%s' should have suffix '='", prefix);
1528 // search for strings which match, and set them to NULL.
1530 for (i = 0; i < out->size; i++)
1532 if (strPrefix(gd_ptr_array_index(out, i), prefix))
1534 checked_free(gd_ptr_array_index(out, i));
1535 gd_ptr_array_index(out, i) = NULL;
1539 // remove all "null" occurrences
1540 while (gd_ptr_array_remove(out, NULL))
1544 // output properties of a structure to a file.
1545 // list_foreach func, so "out" is the last parameter!
1546 static void caveset_save_cave_func(GdCave *cave, GdPtrArray *out)
1548 GdCave *default_cave;
1549 GdPtrArray *this_out;
1550 char *line = NULL; // used for various purposes
1553 gd_ptr_array_add(out, getStringCopy(""));
1554 gd_ptr_array_add(out, getStringCopy("[cave]"));
1556 // first add the properties to a local ptr array.
1557 // later, some are deleted (slime permeability, for example) - this is needed because of the
1558 // inconsistencies of the bdcff.
1559 // finally, remaining will be added to the normal "out" array.
1560 this_out = gd_ptr_array_new();
1562 default_cave = gd_cave_new();
1563 save_properties(this_out, cave, default_cave, gd_cave_properties, cave->w * cave->h);
1564 gd_cave_free(default_cave);
1566 // properties which are handled explicitly. these cannot be handled easily above,
1567 // as they have some special meaning. for example, slime_permeability=x sets permeability to
1568 // x, and sets predictable to false. bdcff format is simply inconsistent in these aspects.
1570 // slime permeability is always set explicitly, as it also sets predictability.
1571 if (cave->slime_predictable)
1572 // if slime is predictable, remove permeab. flag, as that would imply unpredictable slime.
1573 cave_properties_remove(this_out, "SlimePermeability=");
1575 // if slime is UNpredictable, remove permeabc64 flag, as that would imply predictable slime.
1576 cave_properties_remove(this_out, "SlimePermeabilityC64=");
1578 // add tags to output, and free local array
1579 for (i = 0; i < this_out->size; i++)
1580 gd_ptr_array_add(out, gd_ptr_array_index(this_out, i));
1582 // do not free data pointers, which were just added to array "out"
1583 gd_ptr_array_free(this_out, FALSE);
1586 // save unknown tags as they are
1592 hashkeys = g_hash_table_get_keys(cave->tags);
1593 for (iter = hashkeys; iter != NULL; iter = iter->next)
1595 gchar *key = (gchar *)iter->data;
1597 gd_ptr_array_add(out, getStringPrint("%s=%s", key, (const char *) g_hash_table_lookup(cave->tags, key)));
1600 list_free(hashkeys);
1609 gd_ptr_array_add(out, getStringCopy(""));
1610 gd_ptr_array_add(out, getStringCopy("[map]"));
1612 line = checked_calloc(cave->w + 1);
1615 for (y = 0; y < cave->h; y++)
1617 for (x = 0; x < cave->w; x++)
1619 // check if character is non-zero;
1620 // the ...save() should have assigned a character to every element
1621 if (gd_elements[cave->map[y][x]].character_new == 0)
1622 Warn("gd_elements[cave->map[y][x]].character_new should be non-zero");
1624 line[x] = gd_elements[cave->map[y][x]].character_new;
1627 gd_ptr_array_add(out, getStringCopy(line));
1630 gd_ptr_array_add(out, getStringCopy("[/map]"));
1633 // save drawing objects
1638 gd_ptr_array_add(out, getStringCopy(""));
1639 gd_ptr_array_add(out, getStringCopy("[objects]"));
1641 for (listiter = cave->objects; listiter; listiter = list_next(listiter))
1643 GdObject *object = listiter->data;
1646 // not for all levels?
1647 if (object->levels != GD_OBJECT_LEVEL_ALL)
1650 boolean once; // true if already written one number
1652 setStringPrint(&line, "[Level=");
1655 for (i = 0; i < 5; i++)
1657 if (object->levels & gd_levels_mask[i])
1659 if (once) // if written at least one number so far, we need a comma
1660 appendStringPrint(&line, ",");
1662 appendStringPrint(&line, "%d", i+1);
1667 appendStringPrint(&line, "]");
1668 gd_ptr_array_add(out, getStringCopy(line));
1671 text = gd_object_get_bdcff(object);
1672 gd_ptr_array_add(out, getStringCopy(text));
1675 if (object->levels != GD_OBJECT_LEVEL_ALL)
1676 gd_ptr_array_add(out, getStringCopy("[/Level]"));
1679 gd_ptr_array_add(out, getStringCopy("[/objects]"));
1682 gd_ptr_array_add(out, getStringCopy("[/cave]"));
1687 // save cave in bdcff format.
1688 // "out" will be added lines of bdcff description.
1689 GdPtrArray *gd_caveset_save_to_bdcff(void)
1691 GdPtrArray *out = gd_ptr_array_sized_new(512);
1692 GdCavesetData *default_caveset;
1693 boolean write_mapcodes = FALSE;
1697 // check if we need an own mapcode table ------
1698 // copy original characters to character_new fields; new elements will be added to that one
1699 for (i = 0; i < O_MAX; i++)
1700 gd_elements[i].character_new = gd_elements[i].character;
1702 // also regenerate this table as we use it
1703 gd_create_char_to_element_table();
1706 for (iter = gd_caveset; iter != NULL; iter = iter->next)
1708 GdCave *cave = (GdCave *)iter->data;
1710 // if they have a map (random elements+object based maps do not need characters)
1715 // check every element of map
1716 for(y = 0; y < cave->h; y++)
1717 for (x = 0; x < cave->w; x++)
1719 GdElement e = cave->map[y][x];
1721 // if no character assigned
1722 if (gd_elements[e].character_new == 0)
1726 // we found at least one, so later we have to write the mapcodes
1727 write_mapcodes = TRUE;
1729 // find a character which is not yet used for any element
1730 for (j = 32; j < 128; j++)
1732 // the string contains the characters which should not be used.
1733 if (strchr("<>&[]/=\\", j) == NULL && gd_char_to_element[j] == O_UNKNOWN)
1737 // if no more space... XXX we should rather report to the user
1739 Warn("variable j should be != 128");
1741 gd_elements[e].character_new = j;
1743 // we also record to this table, as we use it ^^ a few lines above
1744 gd_char_to_element[j] = e;
1750 gd_ptr_array_add(out, getStringCopy("[BDCFF]"));
1751 gd_ptr_array_add(out, getStringPrint("Version=%s", BDCFF_VERSION));
1753 // this flag was set above if we need to write mapcodes
1758 gd_ptr_array_add(out, getStringCopy("[mapcodes]"));
1759 gd_ptr_array_add(out, getStringCopy("Length=1"));
1761 for (i = 0; i < O_MAX; i++)
1763 // if no character assigned by specification BUT (AND) we assigned one
1764 if (gd_elements[i].character == 0 && gd_elements[i].character_new != 0)
1765 gd_ptr_array_add(out, getStringPrint("%c=%s", gd_elements[i].character_new, gd_elements[i].filename));
1768 gd_ptr_array_add(out, getStringCopy("[/mapcodes]"));
1771 gd_ptr_array_add(out, getStringCopy("[game]"));
1773 default_caveset = gd_caveset_data_new();
1774 save_properties(out, gd_caveset_data, default_caveset, gd_caveset_properties, 0);
1775 gd_caveset_data_free(default_caveset);
1776 gd_ptr_array_add(out, getStringCopy("Levels=5"));
1778 list_foreach(gd_caveset, (list_fn)caveset_save_cave_func, out);
1780 gd_ptr_array_add(out, getStringCopy("[/game]"));
1781 gd_ptr_array_add(out, getStringCopy("[/BDCFF]"));
1783 // saved to ptrarray