1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
40 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
44 #define LEVEL_CHUNK_GRP1_SIZE(x) (2 + 8 + (x) * 2)
46 /* file identifier strings */
47 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
48 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
49 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
51 /* values for level file type identifier */
52 #define LEVEL_FILE_TYPE_UNKNOWN 0
53 #define LEVEL_FILE_TYPE_RND 1
54 #define LEVEL_FILE_TYPE_EM 2
56 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
57 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
59 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
60 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
63 /* ========================================================================= */
64 /* level file functions */
65 /* ========================================================================= */
67 void setElementChangePages(struct ElementInfo *ei, int change_pages)
69 int change_page_size = sizeof(struct ElementChangeInfo);
71 ei->num_change_pages = MAX(1, change_pages);
74 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
76 if (ei->current_change_page >= ei->num_change_pages)
77 ei->current_change_page = ei->num_change_pages - 1;
79 ei->change = &ei->change_page[ei->current_change_page];
82 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
86 change->can_change = FALSE;
88 change->events = CE_BITMASK_DEFAULT;
89 change->sides = CH_SIDE_ANY;
91 change->target_element = EL_EMPTY_SPACE;
93 change->delay_fixed = 0;
94 change->delay_random = 0;
95 change->delay_frames = 1;
97 change->trigger_element = EL_EMPTY_SPACE;
99 change->explode = FALSE;
100 change->use_content = FALSE;
101 change->only_complete = FALSE;
102 change->use_random_change = FALSE;
103 change->random = 100;
104 change->power = CP_NON_DESTRUCTIVE;
106 for (x = 0; x < 3; x++)
107 for (y = 0; y < 3; y++)
108 change->content[x][y] = EL_EMPTY_SPACE;
110 change->direct_action = 0;
111 change->other_action = 0;
113 change->pre_change_function = NULL;
114 change->change_function = NULL;
115 change->post_change_function = NULL;
118 static void setLevelInfoToDefaults(struct LevelInfo *level)
122 level->file_version = FILE_VERSION_ACTUAL;
123 level->game_version = GAME_VERSION_ACTUAL;
125 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
126 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
127 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
129 level->fieldx = STD_LEV_FIELDX;
130 level->fieldy = STD_LEV_FIELDY;
132 for (x = 0; x < MAX_LEV_FIELDX; x++)
133 for (y = 0; y < MAX_LEV_FIELDY; y++)
134 level->field[x][y] = EL_SAND;
137 level->gems_needed = 0;
138 level->amoeba_speed = 10;
139 level->time_magic_wall = 10;
140 level->time_wheel = 10;
141 level->time_light = 10;
142 level->time_timegate = 10;
143 level->amoeba_content = EL_DIAMOND;
144 level->double_speed = FALSE;
145 level->initial_gravity = FALSE;
146 level->em_slippery_gems = FALSE;
148 level->use_custom_template = FALSE;
150 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
151 level->name[i] = '\0';
152 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
153 level->author[i] = '\0';
155 strcpy(level->name, NAMELESS_LEVEL_NAME);
156 strcpy(level->author, ANONYMOUS_NAME);
158 for (i = 0; i < 4; i++)
160 level->envelope_text[i][0] = '\0';
161 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
162 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
165 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
166 level->score[i] = 10;
168 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
169 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
170 for (x = 0; x < 3; x++)
171 for (y = 0; y < 3; y++)
172 level->yamyam_content[i][x][y] =
173 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
175 level->field[0][0] = EL_PLAYER_1;
176 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
178 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
180 setElementChangePages(&element_info[i], 1);
181 setElementChangeInfoToDefaults(element_info[i].change);
184 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
186 int element = EL_CUSTOM_START + i;
188 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
189 element_info[element].description[j] = '\0';
190 if (element_info[element].custom_description != NULL)
191 strncpy(element_info[element].description,
192 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
194 strcpy(element_info[element].description,
195 element_info[element].editor_description);
197 element_info[element].use_gfx_element = FALSE;
198 element_info[element].gfx_element = EL_EMPTY_SPACE;
200 element_info[element].collect_score = 10; /* special default */
201 element_info[element].collect_count = 1; /* special default */
203 element_info[element].push_delay_fixed = -1; /* initialize later */
204 element_info[element].push_delay_random = -1; /* initialize later */
205 element_info[element].move_delay_fixed = 0;
206 element_info[element].move_delay_random = 0;
208 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
209 element_info[element].move_direction_initial = MV_NO_MOVING;
210 element_info[element].move_stepsize = TILEX / 8;
212 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
214 for (x = 0; x < 3; x++)
215 for (y = 0; y < 3; y++)
216 element_info[element].content[x][y] = EL_EMPTY_SPACE;
218 element_info[element].access_type = 0;
219 element_info[element].access_layer = 0;
220 element_info[element].walk_to_action = 0;
221 element_info[element].smash_targets = 0;
222 element_info[element].deadliness = 0;
223 element_info[element].consistency = 0;
225 element_info[element].can_explode_by_fire = FALSE;
226 element_info[element].can_explode_smashed = FALSE;
227 element_info[element].can_explode_impact = FALSE;
229 element_info[element].current_change_page = 0;
231 /* start with no properties at all */
232 for (j = 0; j < NUM_EP_BITFIELDS; j++)
233 Properties[element][j] = EP_BITMASK_DEFAULT;
235 element_info[element].modified_settings = FALSE;
238 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
240 int element = EL_GROUP_START + i;
242 if (element_info[element].group == NULL)
243 element_info[element].group =
244 checked_malloc(sizeof(struct ElementGroupInfo));
246 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
247 element_info[element].group->element[j] = EL_EMPTY_SPACE;
249 element_info[element].group->num_elements = 1;
252 BorderElement = EL_STEELWALL;
254 level->no_level_file = FALSE;
256 if (leveldir_current == NULL) /* only when dumping level */
259 /* try to determine better author name than 'anonymous' */
260 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
262 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
263 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
267 switch (LEVELCLASS(leveldir_current))
269 case LEVELCLASS_TUTORIAL:
270 strcpy(level->author, PROGRAM_AUTHOR_STRING);
273 case LEVELCLASS_CONTRIB:
274 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
275 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
278 case LEVELCLASS_PRIVATE:
279 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
280 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
284 /* keep default value */
290 static void ActivateLevelTemplate()
292 /* Currently there is no special action needed to activate the template
293 data, because 'element_info' and 'Properties' overwrite the original
294 level data, while all other variables do not change. */
297 static char *getLevelFilenameFromBasename(char *basename)
299 static char *filename = NULL;
301 checked_free(filename);
303 filename = getPath2(getCurrentLevelDir(), basename);
308 static char *getSingleLevelBasename(int nr, int type)
310 static char basename[MAX_FILENAME_LEN];
314 case LEVEL_FILE_TYPE_RND:
316 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
318 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
321 case LEVEL_FILE_TYPE_EM:
322 sprintf(basename, "%d", nr);
326 strcpy(basename, UNDEFINED_FILENAME);
333 static char *getPackedLevelBasename(int type)
335 static char basename[MAX_FILENAME_LEN];
340 strcpy(basename, UNDEFINED_FILENAME);
347 static char *getSingleLevelFilename(int nr, int type)
349 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
352 static char *getPackedLevelFilename(int type)
354 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
357 char *getDefaultLevelFilename(int nr)
359 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
362 static struct LevelFileInfo *getLevelFileInfo(int nr)
364 static struct LevelFileInfo level_file_info;
366 level_file_info.nr = nr;
368 /* special case: level template */
371 level_file_info.type = LEVEL_FILE_TYPE_RND;
372 level_file_info.filename = getDefaultLevelFilename(nr);
374 return &level_file_info;
377 /* 1st try: check for native Rocks'n'Diamonds level file */
378 level_file_info.type = LEVEL_FILE_TYPE_RND;
379 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
380 if (fileExists(level_file_info.filename))
381 return &level_file_info;
383 /* 2nd try: check for classic Emerald Mine level file */
384 level_file_info.type = LEVEL_FILE_TYPE_EM;
385 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
386 if (fileExists(level_file_info.filename))
387 return &level_file_info;
389 /* no known level file found -- use default values */
390 level_file_info.type = LEVEL_FILE_TYPE_RND;
391 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
393 return &level_file_info;
396 /* ------------------------------------------------------------------------- */
397 /* functions for loading R'n'D level */
398 /* ------------------------------------------------------------------------- */
400 static int checkLevelElement(int element)
402 /* map some (historic, now obsolete) elements */
407 case EL_PLAYER_OBSOLETE:
408 element = EL_PLAYER_1;
411 case EL_KEY_OBSOLETE:
414 case EL_EM_KEY_1_FILE_OBSOLETE:
415 element = EL_EM_KEY_1;
418 case EL_EM_KEY_2_FILE_OBSOLETE:
419 element = EL_EM_KEY_2;
422 case EL_EM_KEY_3_FILE_OBSOLETE:
423 element = EL_EM_KEY_3;
426 case EL_EM_KEY_4_FILE_OBSOLETE:
427 element = EL_EM_KEY_4;
430 case EL_ENVELOPE_OBSOLETE:
431 element = EL_ENVELOPE_1;
439 if (element >= NUM_FILE_ELEMENTS)
441 Error(ERR_WARN, "invalid level element %d", element);
443 element = EL_CHAR_QUESTION;
448 if (element >= NUM_FILE_ELEMENTS)
450 Error(ERR_WARN, "invalid level element %d", element);
452 element = EL_CHAR_QUESTION;
454 else if (element == EL_PLAYER_OBSOLETE)
455 element = EL_PLAYER_1;
456 else if (element == EL_KEY_OBSOLETE)
463 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
465 level->file_version = getFileVersion(file);
466 level->game_version = getFileVersion(file);
471 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
475 level->fieldx = getFile8Bit(file);
476 level->fieldy = getFile8Bit(file);
478 level->time = getFile16BitBE(file);
479 level->gems_needed = getFile16BitBE(file);
481 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
482 level->name[i] = getFile8Bit(file);
483 level->name[MAX_LEVEL_NAME_LEN] = 0;
485 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
486 level->score[i] = getFile8Bit(file);
488 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
489 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
490 for (y = 0; y < 3; y++)
491 for (x = 0; x < 3; x++)
492 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
494 level->amoeba_speed = getFile8Bit(file);
495 level->time_magic_wall = getFile8Bit(file);
496 level->time_wheel = getFile8Bit(file);
497 level->amoeba_content = checkLevelElement(getFile8Bit(file));
498 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
499 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
500 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
501 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
503 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
505 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
510 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
514 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
515 level->author[i] = getFile8Bit(file);
516 level->author[MAX_LEVEL_NAME_LEN] = 0;
521 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
524 int chunk_size_expected = level->fieldx * level->fieldy;
526 /* Note: "chunk_size" was wrong before version 2.0 when elements are
527 stored with 16-bit encoding (and should be twice as big then).
528 Even worse, playfield data was stored 16-bit when only yamyam content
529 contained 16-bit elements and vice versa. */
531 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
532 chunk_size_expected *= 2;
534 if (chunk_size_expected != chunk_size)
536 ReadUnusedBytesFromFile(file, chunk_size);
537 return chunk_size_expected;
540 for (y = 0; y < level->fieldy; y++)
541 for (x = 0; x < level->fieldx; x++)
543 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
548 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
552 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
553 int chunk_size_expected = header_size + content_size;
555 /* Note: "chunk_size" was wrong before version 2.0 when elements are
556 stored with 16-bit encoding (and should be twice as big then).
557 Even worse, playfield data was stored 16-bit when only yamyam content
558 contained 16-bit elements and vice versa. */
560 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
561 chunk_size_expected += content_size;
563 if (chunk_size_expected != chunk_size)
565 ReadUnusedBytesFromFile(file, chunk_size);
566 return chunk_size_expected;
570 level->num_yamyam_contents = getFile8Bit(file);
574 /* correct invalid number of content fields -- should never happen */
575 if (level->num_yamyam_contents < 1 ||
576 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
577 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
579 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
580 for (y = 0; y < 3; y++)
581 for (x = 0; x < 3; x++)
582 level->yamyam_content[i][x][y] =
583 checkLevelElement(level->encoding_16bit_field ?
584 getFile16BitBE(file) : getFile8Bit(file));
588 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
592 int num_contents, content_xsize, content_ysize;
593 int content_array[MAX_ELEMENT_CONTENTS][3][3];
595 element = checkLevelElement(getFile16BitBE(file));
596 num_contents = getFile8Bit(file);
597 content_xsize = getFile8Bit(file);
598 content_ysize = getFile8Bit(file);
600 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
602 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
603 for (y = 0; y < 3; y++)
604 for (x = 0; x < 3; x++)
605 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
607 /* correct invalid number of content fields -- should never happen */
608 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
609 num_contents = STD_ELEMENT_CONTENTS;
611 if (element == EL_YAMYAM)
613 level->num_yamyam_contents = num_contents;
615 for (i = 0; i < num_contents; i++)
616 for (y = 0; y < 3; y++)
617 for (x = 0; x < 3; x++)
618 level->yamyam_content[i][x][y] = content_array[i][x][y];
620 else if (element == EL_BD_AMOEBA)
622 level->amoeba_content = content_array[0][0][0];
626 Error(ERR_WARN, "cannot load content for element '%d'", element);
632 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
638 int chunk_size_expected;
640 element = checkLevelElement(getFile16BitBE(file));
641 if (!IS_ENVELOPE(element))
642 element = EL_ENVELOPE_1;
644 envelope_nr = element - EL_ENVELOPE_1;
646 envelope_len = getFile16BitBE(file);
648 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
649 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
651 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
653 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
655 if (chunk_size_expected != chunk_size)
657 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
658 return chunk_size_expected;
661 for (i = 0; i < envelope_len; i++)
662 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
667 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
669 int num_changed_custom_elements = getFile16BitBE(file);
670 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
673 if (chunk_size_expected != chunk_size)
675 ReadUnusedBytesFromFile(file, chunk_size - 2);
676 return chunk_size_expected;
679 for (i = 0; i < num_changed_custom_elements; i++)
681 int element = getFile16BitBE(file);
682 int properties = getFile32BitBE(file);
684 if (IS_CUSTOM_ELEMENT(element))
685 Properties[element][EP_BITFIELD_BASE] = properties;
687 Error(ERR_WARN, "invalid custom element number %d", element);
693 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
695 int num_changed_custom_elements = getFile16BitBE(file);
696 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
699 if (chunk_size_expected != chunk_size)
701 ReadUnusedBytesFromFile(file, chunk_size - 2);
702 return chunk_size_expected;
705 for (i = 0; i < num_changed_custom_elements; i++)
707 int element = getFile16BitBE(file);
708 int custom_target_element = getFile16BitBE(file);
710 if (IS_CUSTOM_ELEMENT(element))
711 element_info[element].change->target_element = custom_target_element;
713 Error(ERR_WARN, "invalid custom element number %d", element);
719 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
721 int num_changed_custom_elements = getFile16BitBE(file);
722 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
725 if (chunk_size_expected != chunk_size)
727 ReadUnusedBytesFromFile(file, chunk_size - 2);
728 return chunk_size_expected;
731 for (i = 0; i < num_changed_custom_elements; i++)
733 int element = getFile16BitBE(file);
735 if (!IS_CUSTOM_ELEMENT(element))
737 Error(ERR_WARN, "invalid custom element number %d", element);
742 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
743 element_info[element].description[j] = getFile8Bit(file);
744 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
746 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
748 /* some free bytes for future properties and padding */
749 ReadUnusedBytesFromFile(file, 7);
751 element_info[element].use_gfx_element = getFile8Bit(file);
752 element_info[element].gfx_element =
753 checkLevelElement(getFile16BitBE(file));
755 element_info[element].collect_score = getFile8Bit(file);
756 element_info[element].collect_count = getFile8Bit(file);
758 element_info[element].push_delay_fixed = getFile16BitBE(file);
759 element_info[element].push_delay_random = getFile16BitBE(file);
760 element_info[element].move_delay_fixed = getFile16BitBE(file);
761 element_info[element].move_delay_random = getFile16BitBE(file);
763 element_info[element].move_pattern = getFile16BitBE(file);
764 element_info[element].move_direction_initial = getFile8Bit(file);
765 element_info[element].move_stepsize = getFile8Bit(file);
767 for (y = 0; y < 3; y++)
768 for (x = 0; x < 3; x++)
769 element_info[element].content[x][y] =
770 checkLevelElement(getFile16BitBE(file));
772 element_info[element].change->events = getFile32BitBE(file);
774 element_info[element].change->target_element =
775 checkLevelElement(getFile16BitBE(file));
777 element_info[element].change->delay_fixed = getFile16BitBE(file);
778 element_info[element].change->delay_random = getFile16BitBE(file);
779 element_info[element].change->delay_frames = getFile16BitBE(file);
781 element_info[element].change->trigger_element =
782 checkLevelElement(getFile16BitBE(file));
784 element_info[element].change->explode = getFile8Bit(file);
785 element_info[element].change->use_content = getFile8Bit(file);
786 element_info[element].change->only_complete = getFile8Bit(file);
787 element_info[element].change->use_random_change = getFile8Bit(file);
789 element_info[element].change->random = getFile8Bit(file);
790 element_info[element].change->power = getFile8Bit(file);
792 for (y = 0; y < 3; y++)
793 for (x = 0; x < 3; x++)
794 element_info[element].change->content[x][y] =
795 checkLevelElement(getFile16BitBE(file));
797 element_info[element].slippery_type = getFile8Bit(file);
799 /* some free bytes for future properties and padding */
800 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
802 /* mark that this custom element has been modified */
803 element_info[element].modified_settings = TRUE;
809 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
811 struct ElementInfo *ei;
812 int chunk_size_expected;
816 element = getFile16BitBE(file);
818 if (!IS_CUSTOM_ELEMENT(element))
820 Error(ERR_WARN, "invalid custom element number %d", element);
825 ei = &element_info[element];
827 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
828 ei->description[i] = getFile8Bit(file);
829 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
831 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
832 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
834 ei->num_change_pages = getFile8Bit(file);
836 /* some free bytes for future base property values and padding */
837 ReadUnusedBytesFromFile(file, 5);
839 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
840 if (chunk_size_expected != chunk_size)
842 ReadUnusedBytesFromFile(file, chunk_size - 48);
843 return chunk_size_expected;
846 /* read custom property values */
848 ei->use_gfx_element = getFile8Bit(file);
849 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
851 ei->collect_score = getFile8Bit(file);
852 ei->collect_count = getFile8Bit(file);
854 ei->push_delay_fixed = getFile16BitBE(file);
855 ei->push_delay_random = getFile16BitBE(file);
856 ei->move_delay_fixed = getFile16BitBE(file);
857 ei->move_delay_random = getFile16BitBE(file);
859 ei->move_pattern = getFile16BitBE(file);
860 ei->move_direction_initial = getFile8Bit(file);
861 ei->move_stepsize = getFile8Bit(file);
863 ei->slippery_type = getFile8Bit(file);
865 for (y = 0; y < 3; y++)
866 for (x = 0; x < 3; x++)
867 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
869 /* some free bytes for future custom property values and padding */
870 ReadUnusedBytesFromFile(file, 12);
872 /* read change property values */
874 setElementChangePages(ei, ei->num_change_pages);
876 for (i = 0; i < ei->num_change_pages; i++)
878 struct ElementChangeInfo *change = &ei->change_page[i];
880 /* always start with reliable default values */
881 setElementChangeInfoToDefaults(change);
883 change->events = getFile32BitBE(file);
885 change->target_element = checkLevelElement(getFile16BitBE(file));
887 change->delay_fixed = getFile16BitBE(file);
888 change->delay_random = getFile16BitBE(file);
889 change->delay_frames = getFile16BitBE(file);
891 change->trigger_element = checkLevelElement(getFile16BitBE(file));
893 change->explode = getFile8Bit(file);
894 change->use_content = getFile8Bit(file);
895 change->only_complete = getFile8Bit(file);
896 change->use_random_change = getFile8Bit(file);
898 change->random = getFile8Bit(file);
899 change->power = getFile8Bit(file);
901 for (y = 0; y < 3; y++)
902 for (x = 0; x < 3; x++)
903 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
905 change->can_change = getFile8Bit(file);
907 change->sides = getFile8Bit(file);
909 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
910 change->sides = CH_SIDE_ANY;
912 /* some free bytes for future change property values and padding */
913 ReadUnusedBytesFromFile(file, 8);
916 /* mark this custom element as modified */
917 ei->modified_settings = TRUE;
922 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
924 struct ElementGroupInfo *group;
925 int chunk_size_expected;
929 element = getFile16BitBE(file);
931 if (!IS_GROUP_ELEMENT(element))
933 Error(ERR_WARN, "invalid group element number %d", element);
935 ReadUnusedBytesFromFile(file, chunk_size - 2);
939 group = element_info[element].group;
941 group->num_elements = getFile8Bit(file);
943 /* some free bytes for future values and padding */
944 ReadUnusedBytesFromFile(file, 7);
946 chunk_size_expected = LEVEL_CHUNK_GRP1_SIZE(group->num_elements);
947 if (chunk_size_expected != chunk_size)
949 ReadUnusedBytesFromFile(file, chunk_size - 10);
950 return chunk_size_expected;
953 for (i = 0; i < group->num_elements; i++)
954 group->element[i] = checkLevelElement(getFile16BitBE(file));
956 /* mark this group element as modified */
957 element_info[element].modified_settings = TRUE;
962 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
963 struct LevelFileInfo *level_file_info)
965 char *filename = level_file_info->filename;
966 char cookie[MAX_LINE_LEN];
967 char chunk_name[CHUNK_ID_LEN + 1];
971 /* always start with reliable default values */
972 setLevelInfoToDefaults(level);
974 if (!(file = fopen(filename, MODE_READ)))
976 level->no_level_file = TRUE;
978 if (level != &level_template)
979 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
984 getFileChunkBE(file, chunk_name, NULL);
985 if (strcmp(chunk_name, "RND1") == 0)
987 getFile32BitBE(file); /* not used */
989 getFileChunkBE(file, chunk_name, NULL);
990 if (strcmp(chunk_name, "CAVE") != 0)
992 Error(ERR_WARN, "unknown format of level file '%s'", filename);
997 else /* check for pre-2.0 file format with cookie string */
999 strcpy(cookie, chunk_name);
1000 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1001 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1002 cookie[strlen(cookie) - 1] = '\0';
1004 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1006 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1011 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1013 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1018 /* pre-2.0 level files have no game version, so use file version here */
1019 level->game_version = level->file_version;
1022 if (level->file_version < FILE_VERSION_1_2)
1024 /* level files from versions before 1.2.0 without chunk structure */
1025 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1026 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1034 int (*loader)(FILE *, int, struct LevelInfo *);
1038 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1039 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1040 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1041 { "BODY", -1, LoadLevel_BODY },
1042 { "CONT", -1, LoadLevel_CONT },
1043 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1044 { "CNT3", -1, LoadLevel_CNT3 },
1045 { "CUS1", -1, LoadLevel_CUS1 },
1046 { "CUS2", -1, LoadLevel_CUS2 },
1047 { "CUS3", -1, LoadLevel_CUS3 },
1048 { "CUS4", -1, LoadLevel_CUS4 },
1049 { "GRP1", -1, LoadLevel_GRP1 },
1053 while (getFileChunkBE(file, chunk_name, &chunk_size))
1057 while (chunk_info[i].name != NULL &&
1058 strcmp(chunk_name, chunk_info[i].name) != 0)
1061 if (chunk_info[i].name == NULL)
1063 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1064 chunk_name, filename);
1065 ReadUnusedBytesFromFile(file, chunk_size);
1067 else if (chunk_info[i].size != -1 &&
1068 chunk_info[i].size != chunk_size)
1070 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1071 chunk_size, chunk_name, filename);
1072 ReadUnusedBytesFromFile(file, chunk_size);
1076 /* call function to load this level chunk */
1077 int chunk_size_expected =
1078 (chunk_info[i].loader)(file, chunk_size, level);
1080 /* the size of some chunks cannot be checked before reading other
1081 chunks first (like "HEAD" and "BODY") that contain some header
1082 information, so check them here */
1083 if (chunk_size_expected != chunk_size)
1085 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1086 chunk_size, chunk_name, filename);
1095 /* ------------------------------------------------------------------------- */
1096 /* functions for loading EM level */
1097 /* ------------------------------------------------------------------------- */
1099 static int map_em_element_yam(int element)
1103 case 0x00: return EL_EMPTY;
1104 case 0x01: return EL_EMERALD;
1105 case 0x02: return EL_DIAMOND;
1106 case 0x03: return EL_ROCK;
1107 case 0x04: return EL_ROBOT;
1108 case 0x05: return EL_SPACESHIP_UP;
1109 case 0x06: return EL_BOMB;
1110 case 0x07: return EL_BUG_UP;
1111 case 0x08: return EL_AMOEBA_DROP;
1112 case 0x09: return EL_NUT;
1113 case 0x0a: return EL_YAMYAM;
1114 case 0x0b: return EL_QUICKSAND_FULL;
1115 case 0x0c: return EL_SAND;
1116 case 0x0d: return EL_WALL_SLIPPERY;
1117 case 0x0e: return EL_STEELWALL;
1118 case 0x0f: return EL_WALL;
1119 case 0x10: return EL_EM_KEY_1;
1120 case 0x11: return EL_EM_KEY_2;
1121 case 0x12: return EL_EM_KEY_4;
1122 case 0x13: return EL_EM_KEY_3;
1123 case 0x14: return EL_MAGIC_WALL;
1124 case 0x15: return EL_ROBOT_WHEEL;
1125 case 0x16: return EL_DYNAMITE;
1127 case 0x17: return EL_EM_KEY_1; /* EMC */
1128 case 0x18: return EL_BUG_UP; /* EMC */
1129 case 0x1a: return EL_DIAMOND; /* EMC */
1130 case 0x1b: return EL_EMERALD; /* EMC */
1131 case 0x25: return EL_NUT; /* EMC */
1132 case 0x80: return EL_EMPTY; /* EMC */
1133 case 0x85: return EL_EM_KEY_1; /* EMC */
1134 case 0x86: return EL_EM_KEY_2; /* EMC */
1135 case 0x87: return EL_EM_KEY_4; /* EMC */
1136 case 0x88: return EL_EM_KEY_3; /* EMC */
1137 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1138 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1139 case 0xaf: return EL_DYNAMITE; /* EMC */
1140 case 0xbd: return EL_SAND; /* EMC */
1143 Error(ERR_WARN, "invalid level element %d", element);
1144 return EL_CHAR_QUESTION;
1148 static int map_em_element_field(int element)
1150 if (element >= 0xc8 && element <= 0xe1)
1151 return EL_CHAR_A + (element - 0xc8);
1152 else if (element >= 0xe2 && element <= 0xeb)
1153 return EL_CHAR_0 + (element - 0xe2);
1157 case 0x00: return EL_ROCK;
1158 case 0x02: return EL_DIAMOND;
1159 case 0x03: return EL_DIAMOND;
1160 case 0x04: return EL_ROBOT;
1161 case 0x05: return EL_ROBOT; /* EMC */
1162 case 0x08: return EL_SPACESHIP_UP;
1163 case 0x09: return EL_SPACESHIP_RIGHT;
1164 case 0x0a: return EL_SPACESHIP_DOWN;
1165 case 0x0b: return EL_SPACESHIP_LEFT;
1166 case 0x0c: return EL_SPACESHIP_UP;
1167 case 0x0d: return EL_SPACESHIP_RIGHT;
1168 case 0x0e: return EL_SPACESHIP_DOWN;
1169 case 0x0f: return EL_SPACESHIP_LEFT;
1170 case 0x10: return EL_BOMB;
1171 case 0x12: return EL_EMERALD;
1172 case 0x13: return EL_EMERALD;
1173 case 0x14: return EL_BUG_UP;
1174 case 0x15: return EL_BUG_RIGHT;
1175 case 0x16: return EL_BUG_DOWN;
1176 case 0x17: return EL_BUG_LEFT;
1177 case 0x18: return EL_BUG_UP;
1178 case 0x19: return EL_BUG_RIGHT;
1179 case 0x1a: return EL_BUG_DOWN;
1180 case 0x1b: return EL_BUG_LEFT;
1181 case 0x1c: return EL_AMOEBA_DROP;
1182 case 0x20: return EL_ROCK;
1183 case 0x24: return EL_MAGIC_WALL;
1184 case 0x25: return EL_NUT;
1186 /* looks like magic wheel, but is _always_ activated */
1187 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1189 case 0x29: return EL_YAMYAM;
1190 case 0x2a: return EL_YAMYAM;
1191 case 0x2b: return EL_YAMYAM; /* EMC */
1192 case 0x2c: return EL_YAMYAM; /* EMC */
1193 case 0x2d: return EL_QUICKSAND_FULL;
1194 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1195 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1196 case 0x3b: return EL_DYNAMITE_ACTIVE;
1197 case 0x3c: return EL_DYNAMITE_ACTIVE;
1198 case 0x3d: return EL_DYNAMITE_ACTIVE;
1199 case 0x3e: return EL_DYNAMITE_ACTIVE;
1200 case 0x3f: return EL_ACID_POOL_BOTTOM;
1201 case 0x40: return EL_EXIT_OPEN;
1202 case 0x41: return EL_EXIT_OPEN;
1203 case 0x42: return EL_EXIT_OPEN;
1204 case 0x43: return EL_BALLOON;
1205 case 0x4e: return EL_INVISIBLE_WALL;
1206 case 0x65: return EL_ACID; /* EMC */
1207 case 0x73: return EL_SAND; /* EMC */
1208 case 0x74: return EL_STEELWALL;
1209 case 0x7b: return EL_ACID;
1210 case 0x80: return EL_EMPTY;
1211 case 0x81: return EL_WALL_SLIPPERY;
1212 case 0x82: return EL_SAND;
1213 case 0x83: return EL_STEELWALL;
1214 case 0x84: return EL_WALL;
1215 case 0x85: return EL_EM_KEY_1;
1216 case 0x86: return EL_EM_KEY_2;
1217 case 0x87: return EL_EM_KEY_4;
1218 case 0x88: return EL_EM_KEY_3;
1219 case 0x89: return EL_EM_GATE_1;
1220 case 0x8a: return EL_EM_GATE_2;
1221 case 0x8b: return EL_EM_GATE_4;
1222 case 0x8c: return EL_EM_GATE_3;
1223 case 0x8d: return EL_INVISIBLE_WALL; /* EMC */
1224 case 0x8e: return EL_EM_GATE_1_GRAY;
1225 case 0x8f: return EL_EM_GATE_2_GRAY;
1226 case 0x90: return EL_EM_GATE_4_GRAY;
1227 case 0x91: return EL_EM_GATE_3_GRAY;
1228 case 0x92: return EL_MAGIC_WALL;
1229 case 0x94: return EL_QUICKSAND_EMPTY;
1230 case 0x95: return EL_ACID_POOL_TOPLEFT;
1231 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1232 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1233 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1234 case 0x99: return EL_ACID;
1235 case 0x9a: return EL_AMOEBA_DEAD;
1236 case 0x9b: return EL_AMOEBA_DEAD;
1237 case 0x9c: return EL_AMOEBA_DEAD;
1238 case 0x9d: return EL_AMOEBA_DEAD;
1239 case 0x9e: return EL_EXIT_CLOSED;
1240 case 0x9f: return EL_CHAR_LESS; /* EMC */
1241 case 0x93: return EL_ROBOT_WHEEL;
1243 /* looks like normal dust, but behaves like wall */
1244 case 0xa0: return EL_WALL; /* EMC */
1246 case 0xa8: return EL_EMC_WALL_1; /* EMC */
1247 case 0xa9: return EL_EMC_WALL_2; /* EMC */
1248 case 0xaa: return EL_EMC_WALL_3; /* EMC */
1249 case 0xab: return EL_EMC_WALL_7; /* EMC */
1250 case 0xae: return EL_CHAR_MINUS; /* EMC */
1251 case 0xaf: return EL_DYNAMITE;
1252 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */
1253 case 0xb1: return EL_EMC_WALL_8; /* EMC */
1255 /* (exact steel wall) */
1256 case 0xb3: return EL_STEELWALL; /* EMC */
1258 case 0xb4: return EL_WALL_SLIPPERY; /* EMC */
1259 case 0xb5: return EL_EMC_WALL_6; /* EMC */
1260 case 0xb6: return EL_EMC_WALL_5; /* EMC */
1261 case 0xb7: return EL_EMC_WALL_4; /* EMC */
1262 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1263 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1264 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1265 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1266 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1267 case 0xbd: return EL_SAND; /* EMC */
1268 case 0xec: return EL_CHAR_PERIOD;
1269 case 0xed: return EL_CHAR_EXCLAM;
1270 case 0xee: return EL_CHAR_COLON;
1271 case 0xef: return EL_CHAR_QUESTION;
1272 case 0xf0: return EL_CHAR_GREATER;
1273 case 0xf1: return EL_CHAR_COPYRIGHT;
1274 case 0xfe: return EL_PLAYER_1;
1275 case 0xff: return EL_PLAYER_2;
1278 Error(ERR_WARN, "invalid level element %d", element);
1279 return EL_CHAR_QUESTION;
1283 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1284 struct LevelFileInfo *level_file_info)
1286 char *filename = level_file_info->filename;
1288 unsigned char body[40][64];
1289 unsigned char *leveldata = &body[0][0];
1290 unsigned char *header = &leveldata[2048];
1291 unsigned char code0 = 0x65;
1292 unsigned char code1 = 0x11;
1293 boolean level_is_crypted = FALSE;
1294 int nr = level_file_info->nr;
1298 /* always start with reliable default values */
1299 setLevelInfoToDefaults(level);
1301 if (!(file = fopen(filename, MODE_READ)))
1303 level->no_level_file = TRUE;
1305 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1310 for(i = 0; i < 2106; i++)
1311 leveldata[i] = fgetc(file);
1315 /* check if level data is crypted by testing against known starting bytes
1316 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1318 if ((leveldata[0] == 0xf1 ||
1319 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1321 level_is_crypted = TRUE;
1323 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1324 leveldata[0] = 0xf1;
1327 if (level_is_crypted) /* decode crypted level data */
1329 for(i = 0; i < 2106; i++)
1331 leveldata[i] ^= code0;
1332 leveldata[i] -= code1;
1334 code0 = (code0 + 7) & 0xff;
1341 level->time = header[46] * 10;
1342 level->gems_needed = header[47];
1344 /* The original Emerald Mine levels have their level number stored
1345 at the second byte of the level file...
1346 Do not trust this information at other level files, e.g. EMC,
1347 but correct it anyway (normally the first row is completely
1348 steel wall, so the correction does not hurt anyway). */
1350 if (leveldata[1] == nr)
1351 leveldata[1] = leveldata[2]; /* correct level number field */
1353 sprintf(level->name, "Level %d", nr);
1355 level->score[SC_EMERALD] = header[36];
1356 level->score[SC_DIAMOND] = header[37];
1357 level->score[SC_ROBOT] = header[38];
1358 level->score[SC_SPACESHIP] = header[39];
1359 level->score[SC_BUG] = header[40];
1360 level->score[SC_YAMYAM] = header[41];
1361 level->score[SC_NUT] = header[42];
1362 level->score[SC_DYNAMITE] = header[43];
1363 level->score[SC_TIME_BONUS] = header[44];
1365 level->num_yamyam_contents = 4;
1367 for(i = 0; i < level->num_yamyam_contents; i++)
1368 for(y = 0; y < 3; y++)
1369 for(x = 0; x < 3; x++)
1370 level->yamyam_content[i][x][y] =
1371 map_em_element_yam(header[i * 9 + y * 3 + x]);
1373 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1374 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1375 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1376 level->amoeba_content = EL_DIAMOND;
1378 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1380 int new_element = map_em_element_field(body[y][x]);
1382 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1383 new_element = EL_AMOEBA_WET;
1385 level->field[x][y] = new_element;
1388 jx = (header[48] * 256 + header[49]) % 64;
1389 jy = (header[48] * 256 + header[49]) / 64;
1390 level->field[jx][jy] = EL_PLAYER_1;
1392 jx = (header[50] * 256 + header[51]) % 64;
1393 jy = (header[50] * 256 + header[51]) / 64;
1394 level->field[jx][jy] = EL_PLAYER_2;
1397 void LoadLevelFromFileInfo(struct LevelInfo *level,
1398 struct LevelFileInfo *level_file_info)
1400 switch (level_file_info->type)
1402 case LEVEL_FILE_TYPE_RND:
1403 LoadLevelFromFileInfo_RND(level, level_file_info);
1406 case LEVEL_FILE_TYPE_EM:
1407 LoadLevelFromFileInfo_EM(level, level_file_info);
1411 LoadLevelFromFileInfo_RND(level, level_file_info);
1416 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1418 static struct LevelFileInfo level_file_info;
1420 level_file_info.nr = 0; /* unknown */
1421 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1422 level_file_info.filename = filename;
1424 LoadLevelFromFileInfo(level, &level_file_info);
1427 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1429 if (leveldir_current == NULL) /* only when dumping level */
1433 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1436 /* determine correct game engine version of current level */
1438 if (!leveldir_current->latest_engine)
1440 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1441 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1442 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1446 printf("\n::: This level is private or contributed: '%s'\n", filename);
1450 printf("\n::: Use the stored game engine version for this level\n");
1453 /* For all levels which are not forced to use the latest game engine
1454 version (normally user contributed, private and undefined levels),
1455 use the version of the game engine the levels were created for.
1457 Since 2.0.1, the game engine version is now directly stored
1458 in the level file (chunk "VERS"), so there is no need anymore
1459 to set the game version from the file version (except for old,
1460 pre-2.0 levels, where the game version is still taken from the
1461 file format version used to store the level -- see above). */
1463 /* do some special adjustments to support older level versions */
1464 if (level->file_version == FILE_VERSION_1_0)
1466 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1467 Error(ERR_WARN, "using high speed movement for player");
1469 /* player was faster than monsters in (pre-)1.0 levels */
1470 level->double_speed = TRUE;
1473 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1474 if (level->game_version == VERSION_IDENT(2,0,1,0))
1475 level->em_slippery_gems = TRUE;
1480 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1481 leveldir_current->sort_priority, filename);
1485 printf("\n::: Use latest game engine version for this level.\n");
1488 /* For all levels which are forced to use the latest game engine version
1489 (normally all but user contributed, private and undefined levels), set
1490 the game engine version to the actual version; this allows for actual
1491 corrections in the game engine to take effect for existing, converted
1492 levels (from "classic" or other existing games) to make the emulation
1493 of the corresponding game more accurate, while (hopefully) not breaking
1494 existing levels created from other players. */
1497 printf("::: changing engine from %d to %d\n",
1498 level->game_version, GAME_VERSION_ACTUAL);
1501 level->game_version = GAME_VERSION_ACTUAL;
1503 /* Set special EM style gems behaviour: EM style gems slip down from
1504 normal, steel and growing wall. As this is a more fundamental change,
1505 it seems better to set the default behaviour to "off" (as it is more
1506 natural) and make it configurable in the level editor (as a property
1507 of gem style elements). Already existing converted levels (neither
1508 private nor contributed levels) are changed to the new behaviour. */
1510 if (level->file_version < FILE_VERSION_2_0)
1511 level->em_slippery_gems = TRUE;
1515 printf("::: => %d\n", level->game_version);
1519 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1523 /* map custom element change events that have changed in newer versions
1524 (these following values were accidentally changed in version 3.0.1) */
1525 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1529 int element = EL_CUSTOM_START + i;
1531 /* order of checking and copying events to be mapped is important */
1532 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1534 if (HAS_CHANGE_EVENT(element, j - 2))
1536 SET_CHANGE_EVENT(element, j - 2, FALSE);
1537 SET_CHANGE_EVENT(element, j, TRUE);
1541 /* order of checking and copying events to be mapped is important */
1542 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1544 if (HAS_CHANGE_EVENT(element, j - 1))
1546 SET_CHANGE_EVENT(element, j - 1, FALSE);
1547 SET_CHANGE_EVENT(element, j, TRUE);
1553 /* some custom element change events get mapped since version 3.0.3 */
1554 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1556 int element = EL_CUSTOM_START + i;
1558 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1559 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1561 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1562 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1564 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1568 /* initialize "can_change" field for old levels with only one change page */
1569 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1571 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1573 int element = EL_CUSTOM_START + i;
1575 if (CAN_CHANGE(element))
1576 element_info[element].change->can_change = TRUE;
1581 /* set default push delay values (corrected since version 3.0.7-1) */
1582 if (level->game_version < VERSION_IDENT(3,0,7,1))
1584 game.default_push_delay_fixed = 2;
1585 game.default_push_delay_random = 8;
1589 game.default_push_delay_fixed = 8;
1590 game.default_push_delay_random = 8;
1593 /* set uninitialized push delay values of custom elements in older levels */
1594 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1596 int element = EL_CUSTOM_START + i;
1598 if (element_info[element].push_delay_fixed == -1)
1599 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1600 if (element_info[element].push_delay_random == -1)
1601 element_info[element].push_delay_random = game.default_push_delay_random;
1605 /* initialize element properties for level editor etc. */
1606 InitElementPropertiesEngine(level->game_version);
1609 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1613 /* map elements that have changed in newer versions */
1614 for (y = 0; y < level->fieldy; y++)
1616 for (x = 0; x < level->fieldx; x++)
1618 int element = level->field[x][y];
1620 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1622 /* map game font elements */
1623 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1624 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1625 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1626 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1629 if (level->game_version < VERSION_IDENT(3,0,0,0))
1631 /* map Supaplex gravity tube elements */
1632 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1633 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1634 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1635 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1639 level->field[x][y] = element;
1643 /* copy elements to runtime playfield array */
1644 for (x = 0; x < MAX_LEV_FIELDX; x++)
1645 for (y = 0; y < MAX_LEV_FIELDY; y++)
1646 Feld[x][y] = level->field[x][y];
1648 /* initialize level size variables for faster access */
1649 lev_fieldx = level->fieldx;
1650 lev_fieldy = level->fieldy;
1652 /* determine border element for this level */
1656 void LoadLevelTemplate(int nr)
1659 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1660 char *filename = level_file_info->filename;
1662 LoadLevelFromFileInfo(&level_template, level_file_info);
1664 char *filename = getDefaultLevelFilename(nr);
1666 LoadLevelFromFilename_RND(&level_template, filename);
1669 LoadLevel_InitVersion(&level, filename);
1670 LoadLevel_InitElements(&level, filename);
1672 ActivateLevelTemplate();
1675 void LoadLevel(int nr)
1678 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1679 char *filename = level_file_info->filename;
1681 LoadLevelFromFileInfo(&level, level_file_info);
1683 char *filename = getLevelFilename(nr);
1685 LoadLevelFromFilename_RND(&level, filename);
1688 if (level.use_custom_template)
1689 LoadLevelTemplate(-1);
1692 LoadLevel_InitVersion(&level, filename);
1693 LoadLevel_InitElements(&level, filename);
1694 LoadLevel_InitPlayfield(&level, filename);
1696 LoadLevel_InitLevel(&level, filename);
1700 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1702 putFileVersion(file, level->file_version);
1703 putFileVersion(file, level->game_version);
1706 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1710 putFile8Bit(file, level->fieldx);
1711 putFile8Bit(file, level->fieldy);
1713 putFile16BitBE(file, level->time);
1714 putFile16BitBE(file, level->gems_needed);
1716 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1717 putFile8Bit(file, level->name[i]);
1719 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1720 putFile8Bit(file, level->score[i]);
1722 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1723 for (y = 0; y < 3; y++)
1724 for (x = 0; x < 3; x++)
1725 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1726 level->yamyam_content[i][x][y]));
1727 putFile8Bit(file, level->amoeba_speed);
1728 putFile8Bit(file, level->time_magic_wall);
1729 putFile8Bit(file, level->time_wheel);
1730 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1731 level->amoeba_content));
1732 putFile8Bit(file, (level->double_speed ? 1 : 0));
1733 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1734 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1735 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1737 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1739 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1742 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1746 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1747 putFile8Bit(file, level->author[i]);
1750 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1754 for (y = 0; y < level->fieldy; y++)
1755 for (x = 0; x < level->fieldx; x++)
1756 if (level->encoding_16bit_field)
1757 putFile16BitBE(file, level->field[x][y]);
1759 putFile8Bit(file, level->field[x][y]);
1763 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1767 putFile8Bit(file, EL_YAMYAM);
1768 putFile8Bit(file, level->num_yamyam_contents);
1769 putFile8Bit(file, 0);
1770 putFile8Bit(file, 0);
1772 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1773 for (y = 0; y < 3; y++)
1774 for (x = 0; x < 3; x++)
1775 if (level->encoding_16bit_field)
1776 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1778 putFile8Bit(file, level->yamyam_content[i][x][y]);
1782 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1785 int num_contents, content_xsize, content_ysize;
1786 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1788 if (element == EL_YAMYAM)
1790 num_contents = level->num_yamyam_contents;
1794 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1795 for (y = 0; y < 3; y++)
1796 for (x = 0; x < 3; x++)
1797 content_array[i][x][y] = level->yamyam_content[i][x][y];
1799 else if (element == EL_BD_AMOEBA)
1805 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1806 for (y = 0; y < 3; y++)
1807 for (x = 0; x < 3; x++)
1808 content_array[i][x][y] = EL_EMPTY;
1809 content_array[0][0][0] = level->amoeba_content;
1813 /* chunk header already written -- write empty chunk data */
1814 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1816 Error(ERR_WARN, "cannot save content for element '%d'", element);
1820 putFile16BitBE(file, element);
1821 putFile8Bit(file, num_contents);
1822 putFile8Bit(file, content_xsize);
1823 putFile8Bit(file, content_ysize);
1825 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1827 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1828 for (y = 0; y < 3; y++)
1829 for (x = 0; x < 3; x++)
1830 putFile16BitBE(file, content_array[i][x][y]);
1833 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1836 int envelope_nr = element - EL_ENVELOPE_1;
1837 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1839 putFile16BitBE(file, element);
1840 putFile16BitBE(file, envelope_len);
1841 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1842 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1844 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1846 for (i = 0; i < envelope_len; i++)
1847 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1851 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1852 int num_changed_custom_elements)
1856 putFile16BitBE(file, num_changed_custom_elements);
1858 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1860 int element = EL_CUSTOM_START + i;
1862 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1864 if (check < num_changed_custom_elements)
1866 putFile16BitBE(file, element);
1867 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1874 if (check != num_changed_custom_elements) /* should not happen */
1875 Error(ERR_WARN, "inconsistent number of custom element properties");
1880 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1881 int num_changed_custom_elements)
1885 putFile16BitBE(file, num_changed_custom_elements);
1887 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1889 int element = EL_CUSTOM_START + i;
1891 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1893 if (check < num_changed_custom_elements)
1895 putFile16BitBE(file, element);
1896 putFile16BitBE(file, element_info[element].change->target_element);
1903 if (check != num_changed_custom_elements) /* should not happen */
1904 Error(ERR_WARN, "inconsistent number of custom target elements");
1909 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1910 int num_changed_custom_elements)
1912 int i, j, x, y, check = 0;
1914 putFile16BitBE(file, num_changed_custom_elements);
1916 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1918 int element = EL_CUSTOM_START + i;
1920 if (element_info[element].modified_settings)
1922 if (check < num_changed_custom_elements)
1924 putFile16BitBE(file, element);
1926 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1927 putFile8Bit(file, element_info[element].description[j]);
1929 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1931 /* some free bytes for future properties and padding */
1932 WriteUnusedBytesToFile(file, 7);
1934 putFile8Bit(file, element_info[element].use_gfx_element);
1935 putFile16BitBE(file, element_info[element].gfx_element);
1937 putFile8Bit(file, element_info[element].collect_score);
1938 putFile8Bit(file, element_info[element].collect_count);
1940 putFile16BitBE(file, element_info[element].push_delay_fixed);
1941 putFile16BitBE(file, element_info[element].push_delay_random);
1942 putFile16BitBE(file, element_info[element].move_delay_fixed);
1943 putFile16BitBE(file, element_info[element].move_delay_random);
1945 putFile16BitBE(file, element_info[element].move_pattern);
1946 putFile8Bit(file, element_info[element].move_direction_initial);
1947 putFile8Bit(file, element_info[element].move_stepsize);
1949 for (y = 0; y < 3; y++)
1950 for (x = 0; x < 3; x++)
1951 putFile16BitBE(file, element_info[element].content[x][y]);
1953 putFile32BitBE(file, element_info[element].change->events);
1955 putFile16BitBE(file, element_info[element].change->target_element);
1957 putFile16BitBE(file, element_info[element].change->delay_fixed);
1958 putFile16BitBE(file, element_info[element].change->delay_random);
1959 putFile16BitBE(file, element_info[element].change->delay_frames);
1961 putFile16BitBE(file, element_info[element].change->trigger_element);
1963 putFile8Bit(file, element_info[element].change->explode);
1964 putFile8Bit(file, element_info[element].change->use_content);
1965 putFile8Bit(file, element_info[element].change->only_complete);
1966 putFile8Bit(file, element_info[element].change->use_random_change);
1968 putFile8Bit(file, element_info[element].change->random);
1969 putFile8Bit(file, element_info[element].change->power);
1971 for (y = 0; y < 3; y++)
1972 for (x = 0; x < 3; x++)
1973 putFile16BitBE(file, element_info[element].change->content[x][y]);
1975 putFile8Bit(file, element_info[element].slippery_type);
1977 /* some free bytes for future properties and padding */
1978 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1985 if (check != num_changed_custom_elements) /* should not happen */
1986 Error(ERR_WARN, "inconsistent number of custom element properties");
1990 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1992 struct ElementInfo *ei = &element_info[element];
1995 putFile16BitBE(file, element);
1997 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1998 putFile8Bit(file, ei->description[i]);
2000 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2001 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2003 putFile8Bit(file, ei->num_change_pages);
2005 /* some free bytes for future base property values and padding */
2006 WriteUnusedBytesToFile(file, 5);
2008 /* write custom property values */
2010 putFile8Bit(file, ei->use_gfx_element);
2011 putFile16BitBE(file, ei->gfx_element);
2013 putFile8Bit(file, ei->collect_score);
2014 putFile8Bit(file, ei->collect_count);
2016 putFile16BitBE(file, ei->push_delay_fixed);
2017 putFile16BitBE(file, ei->push_delay_random);
2018 putFile16BitBE(file, ei->move_delay_fixed);
2019 putFile16BitBE(file, ei->move_delay_random);
2021 putFile16BitBE(file, ei->move_pattern);
2022 putFile8Bit(file, ei->move_direction_initial);
2023 putFile8Bit(file, ei->move_stepsize);
2025 putFile8Bit(file, ei->slippery_type);
2027 for (y = 0; y < 3; y++)
2028 for (x = 0; x < 3; x++)
2029 putFile16BitBE(file, ei->content[x][y]);
2031 /* some free bytes for future custom property values and padding */
2032 WriteUnusedBytesToFile(file, 12);
2034 /* write change property values */
2036 for (i = 0; i < ei->num_change_pages; i++)
2038 struct ElementChangeInfo *change = &ei->change_page[i];
2040 putFile32BitBE(file, change->events);
2042 putFile16BitBE(file, change->target_element);
2044 putFile16BitBE(file, change->delay_fixed);
2045 putFile16BitBE(file, change->delay_random);
2046 putFile16BitBE(file, change->delay_frames);
2048 putFile16BitBE(file, change->trigger_element);
2050 putFile8Bit(file, change->explode);
2051 putFile8Bit(file, change->use_content);
2052 putFile8Bit(file, change->only_complete);
2053 putFile8Bit(file, change->use_random_change);
2055 putFile8Bit(file, change->random);
2056 putFile8Bit(file, change->power);
2058 for (y = 0; y < 3; y++)
2059 for (x = 0; x < 3; x++)
2060 putFile16BitBE(file, change->content[x][y]);
2062 putFile8Bit(file, change->can_change);
2064 putFile8Bit(file, change->sides);
2066 /* some free bytes for future change property values and padding */
2067 WriteUnusedBytesToFile(file, 8);
2071 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2073 struct ElementGroupInfo *group = element_info[element].group;
2076 putFile16BitBE(file, element);
2078 putFile8Bit(file, group->num_elements);
2080 /* some free bytes for future values and padding */
2081 WriteUnusedBytesToFile(file, 7);
2083 for (i = 0; i < group->num_elements; i++)
2084 putFile16BitBE(file, group->element[i]);
2087 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2089 int body_chunk_size;
2093 if (!(file = fopen(filename, MODE_WRITE)))
2095 Error(ERR_WARN, "cannot save level file '%s'", filename);
2099 level->file_version = FILE_VERSION_ACTUAL;
2100 level->game_version = GAME_VERSION_ACTUAL;
2102 /* check level field for 16-bit elements */
2103 level->encoding_16bit_field = FALSE;
2104 for (y = 0; y < level->fieldy; y++)
2105 for (x = 0; x < level->fieldx; x++)
2106 if (level->field[x][y] > 255)
2107 level->encoding_16bit_field = TRUE;
2109 /* check yamyam content for 16-bit elements */
2110 level->encoding_16bit_yamyam = FALSE;
2111 for (i = 0; i < level->num_yamyam_contents; i++)
2112 for (y = 0; y < 3; y++)
2113 for (x = 0; x < 3; x++)
2114 if (level->yamyam_content[i][x][y] > 255)
2115 level->encoding_16bit_yamyam = TRUE;
2117 /* check amoeba content for 16-bit elements */
2118 level->encoding_16bit_amoeba = FALSE;
2119 if (level->amoeba_content > 255)
2120 level->encoding_16bit_amoeba = TRUE;
2122 /* calculate size of "BODY" chunk */
2124 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2126 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2127 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2129 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2130 SaveLevel_VERS(file, level);
2132 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2133 SaveLevel_HEAD(file, level);
2135 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2136 SaveLevel_AUTH(file, level);
2138 putFileChunkBE(file, "BODY", body_chunk_size);
2139 SaveLevel_BODY(file, level);
2141 if (level->encoding_16bit_yamyam ||
2142 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2144 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2145 SaveLevel_CNT2(file, level, EL_YAMYAM);
2148 if (level->encoding_16bit_amoeba)
2150 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2151 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2154 /* check for envelope content */
2155 for (i = 0; i < 4; i++)
2157 if (strlen(level->envelope_text[i]) > 0)
2159 int envelope_len = strlen(level->envelope_text[i]) + 1;
2161 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
2162 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2166 /* check for non-default custom elements (unless using template level) */
2167 if (!level->use_custom_template)
2169 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2171 int element = EL_CUSTOM_START + i;
2173 if (element_info[element].modified_settings)
2175 int num_change_pages = element_info[element].num_change_pages;
2177 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2178 SaveLevel_CUS4(file, level, element);
2183 /* check for non-default group elements (unless using template level) */
2184 if (!level->use_custom_template)
2186 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2188 int element = EL_GROUP_START + i;
2190 if (element_info[element].modified_settings)
2192 int num_elements = element_info[element].group->num_elements;
2194 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE(num_elements));
2195 SaveLevel_GRP1(file, level, element);
2202 SetFilePermissions(filename, PERMS_PRIVATE);
2205 void SaveLevel(int nr)
2207 char *filename = getDefaultLevelFilename(nr);
2209 SaveLevelFromFilename(&level, filename);
2212 void SaveLevelTemplate()
2214 char *filename = getDefaultLevelFilename(-1);
2216 SaveLevelFromFilename(&level, filename);
2219 void DumpLevel(struct LevelInfo *level)
2221 printf_line("-", 79);
2222 printf("Level xxx (file version %08d, game version %08d)\n",
2223 level->file_version, level->game_version);
2224 printf_line("-", 79);
2226 printf("Level Author: '%s'\n", level->author);
2227 printf("Level Title: '%s'\n", level->name);
2229 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2231 printf("Level Time: %d seconds\n", level->time);
2232 printf("Gems needed: %d\n", level->gems_needed);
2234 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2235 printf("Time for Wheel: %d seconds\n", level->time_wheel);
2236 printf("Time for Light: %d seconds\n", level->time_light);
2237 printf("Time for Timegate: %d seconds\n", level->time_timegate);
2239 printf("Amoeba Speed: %d\n", level->amoeba_speed);
2241 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2242 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
2243 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2245 printf_line("-", 79);
2249 /* ========================================================================= */
2250 /* tape file functions */
2251 /* ========================================================================= */
2253 static void setTapeInfoToDefaults()
2257 /* always start with reliable default values (empty tape) */
2260 /* default values (also for pre-1.2 tapes) with only the first player */
2261 tape.player_participates[0] = TRUE;
2262 for (i = 1; i < MAX_PLAYERS; i++)
2263 tape.player_participates[i] = FALSE;
2265 /* at least one (default: the first) player participates in every tape */
2266 tape.num_participating_players = 1;
2268 tape.level_nr = level_nr;
2270 tape.changed = FALSE;
2272 tape.recording = FALSE;
2273 tape.playing = FALSE;
2274 tape.pausing = FALSE;
2277 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2279 tape->file_version = getFileVersion(file);
2280 tape->game_version = getFileVersion(file);
2285 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2289 tape->random_seed = getFile32BitBE(file);
2290 tape->date = getFile32BitBE(file);
2291 tape->length = getFile32BitBE(file);
2293 /* read header fields that are new since version 1.2 */
2294 if (tape->file_version >= FILE_VERSION_1_2)
2296 byte store_participating_players = getFile8Bit(file);
2299 /* since version 1.2, tapes store which players participate in the tape */
2300 tape->num_participating_players = 0;
2301 for (i = 0; i < MAX_PLAYERS; i++)
2303 tape->player_participates[i] = FALSE;
2305 if (store_participating_players & (1 << i))
2307 tape->player_participates[i] = TRUE;
2308 tape->num_participating_players++;
2312 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2314 engine_version = getFileVersion(file);
2315 if (engine_version > 0)
2316 tape->engine_version = engine_version;
2318 tape->engine_version = tape->game_version;
2324 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2326 int level_identifier_size;
2329 level_identifier_size = getFile16BitBE(file);
2331 tape->level_identifier =
2332 checked_realloc(tape->level_identifier, level_identifier_size);
2334 for (i = 0; i < level_identifier_size; i++)
2335 tape->level_identifier[i] = getFile8Bit(file);
2337 tape->level_nr = getFile16BitBE(file);
2339 chunk_size = 2 + level_identifier_size + 2;
2344 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2347 int chunk_size_expected =
2348 (tape->num_participating_players + 1) * tape->length;
2350 if (chunk_size_expected != chunk_size)
2352 ReadUnusedBytesFromFile(file, chunk_size);
2353 return chunk_size_expected;
2356 for (i = 0; i < tape->length; i++)
2358 if (i >= MAX_TAPELEN)
2361 for (j = 0; j < MAX_PLAYERS; j++)
2363 tape->pos[i].action[j] = MV_NO_MOVING;
2365 if (tape->player_participates[j])
2366 tape->pos[i].action[j] = getFile8Bit(file);
2369 tape->pos[i].delay = getFile8Bit(file);
2371 if (tape->file_version == FILE_VERSION_1_0)
2373 /* eliminate possible diagonal moves in old tapes */
2374 /* this is only for backward compatibility */
2376 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2377 byte action = tape->pos[i].action[0];
2378 int k, num_moves = 0;
2380 for (k = 0; k<4; k++)
2382 if (action & joy_dir[k])
2384 tape->pos[i + num_moves].action[0] = joy_dir[k];
2386 tape->pos[i + num_moves].delay = 0;
2395 tape->length += num_moves;
2398 else if (tape->file_version < FILE_VERSION_2_0)
2400 /* convert pre-2.0 tapes to new tape format */
2402 if (tape->pos[i].delay > 1)
2405 tape->pos[i + 1] = tape->pos[i];
2406 tape->pos[i + 1].delay = 1;
2409 for (j = 0; j < MAX_PLAYERS; j++)
2410 tape->pos[i].action[j] = MV_NO_MOVING;
2411 tape->pos[i].delay--;
2422 if (i != tape->length)
2423 chunk_size = (tape->num_participating_players + 1) * i;
2428 void LoadTapeFromFilename(char *filename)
2430 char cookie[MAX_LINE_LEN];
2431 char chunk_name[CHUNK_ID_LEN + 1];
2435 /* always start with reliable default values */
2436 setTapeInfoToDefaults();
2438 if (!(file = fopen(filename, MODE_READ)))
2441 getFileChunkBE(file, chunk_name, NULL);
2442 if (strcmp(chunk_name, "RND1") == 0)
2444 getFile32BitBE(file); /* not used */
2446 getFileChunkBE(file, chunk_name, NULL);
2447 if (strcmp(chunk_name, "TAPE") != 0)
2449 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2454 else /* check for pre-2.0 file format with cookie string */
2456 strcpy(cookie, chunk_name);
2457 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2458 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2459 cookie[strlen(cookie) - 1] = '\0';
2461 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2463 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2468 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2470 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2475 /* pre-2.0 tape files have no game version, so use file version here */
2476 tape.game_version = tape.file_version;
2479 if (tape.file_version < FILE_VERSION_1_2)
2481 /* tape files from versions before 1.2.0 without chunk structure */
2482 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2483 LoadTape_BODY(file, 2 * tape.length, &tape);
2491 int (*loader)(FILE *, int, struct TapeInfo *);
2495 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2496 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2497 { "INFO", -1, LoadTape_INFO },
2498 { "BODY", -1, LoadTape_BODY },
2502 while (getFileChunkBE(file, chunk_name, &chunk_size))
2506 while (chunk_info[i].name != NULL &&
2507 strcmp(chunk_name, chunk_info[i].name) != 0)
2510 if (chunk_info[i].name == NULL)
2512 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2513 chunk_name, filename);
2514 ReadUnusedBytesFromFile(file, chunk_size);
2516 else if (chunk_info[i].size != -1 &&
2517 chunk_info[i].size != chunk_size)
2519 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2520 chunk_size, chunk_name, filename);
2521 ReadUnusedBytesFromFile(file, chunk_size);
2525 /* call function to load this tape chunk */
2526 int chunk_size_expected =
2527 (chunk_info[i].loader)(file, chunk_size, &tape);
2529 /* the size of some chunks cannot be checked before reading other
2530 chunks first (like "HEAD" and "BODY") that contain some header
2531 information, so check them here */
2532 if (chunk_size_expected != chunk_size)
2534 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2535 chunk_size, chunk_name, filename);
2543 tape.length_seconds = GetTapeLength();
2546 printf("::: tape game version: %d\n", tape.game_version);
2547 printf("::: tape engine version: %d\n", tape.engine_version);
2551 void LoadTape(int nr)
2553 char *filename = getTapeFilename(nr);
2555 LoadTapeFromFilename(filename);
2558 void LoadSolutionTape(int nr)
2560 char *filename = getSolutionTapeFilename(nr);
2562 LoadTapeFromFilename(filename);
2565 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2567 putFileVersion(file, tape->file_version);
2568 putFileVersion(file, tape->game_version);
2571 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2574 byte store_participating_players = 0;
2576 /* set bits for participating players for compact storage */
2577 for (i = 0; i < MAX_PLAYERS; i++)
2578 if (tape->player_participates[i])
2579 store_participating_players |= (1 << i);
2581 putFile32BitBE(file, tape->random_seed);
2582 putFile32BitBE(file, tape->date);
2583 putFile32BitBE(file, tape->length);
2585 putFile8Bit(file, store_participating_players);
2587 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2588 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2590 putFileVersion(file, tape->engine_version);
2593 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2595 int level_identifier_size = strlen(tape->level_identifier) + 1;
2598 putFile16BitBE(file, level_identifier_size);
2600 for (i = 0; i < level_identifier_size; i++)
2601 putFile8Bit(file, tape->level_identifier[i]);
2603 putFile16BitBE(file, tape->level_nr);
2606 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2610 for (i = 0; i < tape->length; i++)
2612 for (j = 0; j < MAX_PLAYERS; j++)
2613 if (tape->player_participates[j])
2614 putFile8Bit(file, tape->pos[i].action[j]);
2616 putFile8Bit(file, tape->pos[i].delay);
2620 void SaveTape(int nr)
2622 char *filename = getTapeFilename(nr);
2624 boolean new_tape = TRUE;
2625 int num_participating_players = 0;
2626 int info_chunk_size;
2627 int body_chunk_size;
2630 InitTapeDirectory(leveldir_current->filename);
2632 /* if a tape still exists, ask to overwrite it */
2633 if (access(filename, F_OK) == 0)
2636 if (!Request("Replace old tape ?", REQ_ASK))
2640 if (!(file = fopen(filename, MODE_WRITE)))
2642 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2646 tape.file_version = FILE_VERSION_ACTUAL;
2647 tape.game_version = GAME_VERSION_ACTUAL;
2649 /* count number of participating players */
2650 for (i = 0; i < MAX_PLAYERS; i++)
2651 if (tape.player_participates[i])
2652 num_participating_players++;
2654 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2655 body_chunk_size = (num_participating_players + 1) * tape.length;
2657 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2658 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2660 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2661 SaveTape_VERS(file, &tape);
2663 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2664 SaveTape_HEAD(file, &tape);
2666 putFileChunkBE(file, "INFO", info_chunk_size);
2667 SaveTape_INFO(file, &tape);
2669 putFileChunkBE(file, "BODY", body_chunk_size);
2670 SaveTape_BODY(file, &tape);
2674 SetFilePermissions(filename, PERMS_PRIVATE);
2676 tape.changed = FALSE;
2679 Request("tape saved !", REQ_CONFIRM);
2682 void DumpTape(struct TapeInfo *tape)
2686 if (TAPE_IS_EMPTY(*tape))
2688 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2692 printf_line("-", 79);
2693 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2694 tape->level_nr, tape->file_version, tape->game_version);
2695 printf("Level series identifier: '%s'\n", tape->level_identifier);
2696 printf_line("-", 79);
2698 for (i = 0; i < tape->length; i++)
2700 if (i >= MAX_TAPELEN)
2703 printf("%03d: ", i);
2705 for (j = 0; j < MAX_PLAYERS; j++)
2707 if (tape->player_participates[j])
2709 int action = tape->pos[i].action[j];
2711 printf("%d:%02x ", j, action);
2712 printf("[%c%c%c%c|%c%c] - ",
2713 (action & JOY_LEFT ? '<' : ' '),
2714 (action & JOY_RIGHT ? '>' : ' '),
2715 (action & JOY_UP ? '^' : ' '),
2716 (action & JOY_DOWN ? 'v' : ' '),
2717 (action & JOY_BUTTON_1 ? '1' : ' '),
2718 (action & JOY_BUTTON_2 ? '2' : ' '));
2722 printf("(%03d)\n", tape->pos[i].delay);
2725 printf_line("-", 79);
2729 /* ========================================================================= */
2730 /* score file functions */
2731 /* ========================================================================= */
2733 void LoadScore(int nr)
2736 char *filename = getScoreFilename(nr);
2737 char cookie[MAX_LINE_LEN];
2738 char line[MAX_LINE_LEN];
2742 /* always start with reliable default values */
2743 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2745 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2746 highscore[i].Score = 0;
2749 if (!(file = fopen(filename, MODE_READ)))
2752 /* check file identifier */
2753 fgets(cookie, MAX_LINE_LEN, file);
2754 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2755 cookie[strlen(cookie) - 1] = '\0';
2757 if (!checkCookieString(cookie, SCORE_COOKIE))
2759 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2764 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2766 fscanf(file, "%d", &highscore[i].Score);
2767 fgets(line, MAX_LINE_LEN, file);
2769 if (line[strlen(line) - 1] == '\n')
2770 line[strlen(line) - 1] = '\0';
2772 for (line_ptr = line; *line_ptr; line_ptr++)
2774 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2776 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2777 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2786 void SaveScore(int nr)
2789 char *filename = getScoreFilename(nr);
2792 InitScoreDirectory(leveldir_current->filename);
2794 if (!(file = fopen(filename, MODE_WRITE)))
2796 Error(ERR_WARN, "cannot save score for level %d", nr);
2800 fprintf(file, "%s\n\n", SCORE_COOKIE);
2802 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2803 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2807 SetFilePermissions(filename, PERMS_PUBLIC);
2811 /* ========================================================================= */
2812 /* setup file functions */
2813 /* ========================================================================= */
2815 #define TOKEN_STR_PLAYER_PREFIX "player_"
2818 #define SETUP_TOKEN_PLAYER_NAME 0
2819 #define SETUP_TOKEN_SOUND 1
2820 #define SETUP_TOKEN_SOUND_LOOPS 2
2821 #define SETUP_TOKEN_SOUND_MUSIC 3
2822 #define SETUP_TOKEN_SOUND_SIMPLE 4
2823 #define SETUP_TOKEN_TOONS 5
2824 #define SETUP_TOKEN_SCROLL_DELAY 6
2825 #define SETUP_TOKEN_SOFT_SCROLLING 7
2826 #define SETUP_TOKEN_FADING 8
2827 #define SETUP_TOKEN_AUTORECORD 9
2828 #define SETUP_TOKEN_QUICK_DOORS 10
2829 #define SETUP_TOKEN_TEAM_MODE 11
2830 #define SETUP_TOKEN_HANDICAP 12
2831 #define SETUP_TOKEN_TIME_LIMIT 13
2832 #define SETUP_TOKEN_FULLSCREEN 14
2833 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2834 #define SETUP_TOKEN_GRAPHICS_SET 16
2835 #define SETUP_TOKEN_SOUNDS_SET 17
2836 #define SETUP_TOKEN_MUSIC_SET 18
2837 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2838 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2839 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2841 #define NUM_GLOBAL_SETUP_TOKENS 22
2844 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2845 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2846 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2847 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2848 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2849 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2850 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2851 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2852 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2853 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2854 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2855 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2857 #define NUM_EDITOR_SETUP_TOKENS 12
2859 /* shortcut setup */
2860 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2861 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2862 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2864 #define NUM_SHORTCUT_SETUP_TOKENS 3
2867 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2868 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2869 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2870 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2871 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2872 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2873 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2874 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2875 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2876 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
2877 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2878 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2879 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2880 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2881 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2882 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
2884 #define NUM_PLAYER_SETUP_TOKENS 16
2887 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2888 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2890 #define NUM_SYSTEM_SETUP_TOKENS 2
2893 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2895 #define NUM_OPTIONS_SETUP_TOKENS 1
2898 static struct SetupInfo si;
2899 static struct SetupEditorInfo sei;
2900 static struct SetupShortcutInfo ssi;
2901 static struct SetupInputInfo sii;
2902 static struct SetupSystemInfo syi;
2903 static struct OptionInfo soi;
2905 static struct TokenInfo global_setup_tokens[] =
2907 { TYPE_STRING, &si.player_name, "player_name" },
2908 { TYPE_SWITCH, &si.sound, "sound" },
2909 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2910 { TYPE_SWITCH, &si.sound_music, "background_music" },
2911 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2912 { TYPE_SWITCH, &si.toons, "toons" },
2913 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2914 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2915 { TYPE_SWITCH, &si.fading, "screen_fading" },
2916 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2917 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2918 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2919 { TYPE_SWITCH, &si.handicap, "handicap" },
2920 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2921 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2922 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2923 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2924 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2925 { TYPE_STRING, &si.music_set, "music_set" },
2926 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2927 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2928 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2931 static struct TokenInfo editor_setup_tokens[] =
2933 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2934 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2935 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2936 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2937 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2938 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2939 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2940 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2941 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2942 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2943 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2944 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2947 static struct TokenInfo shortcut_setup_tokens[] =
2949 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2950 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2951 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2954 static struct TokenInfo player_setup_tokens[] =
2956 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2957 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2958 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2959 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2960 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2961 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2962 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2963 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2964 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2965 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
2966 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2967 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2968 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2969 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2970 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2971 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
2974 static struct TokenInfo system_setup_tokens[] =
2976 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2977 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2980 static struct TokenInfo options_setup_tokens[] =
2982 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2985 static char *get_corrected_login_name(char *login_name)
2987 /* needed because player name must be a fixed length string */
2988 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2990 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2991 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2993 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2994 if (strchr(login_name_new, ' '))
2995 *strchr(login_name_new, ' ') = '\0';
2997 return login_name_new;
3000 static void setSetupInfoToDefaults(struct SetupInfo *si)
3004 si->player_name = get_corrected_login_name(getLoginName());
3007 si->sound_loops = TRUE;
3008 si->sound_music = TRUE;
3009 si->sound_simple = TRUE;
3011 si->double_buffering = TRUE;
3012 si->direct_draw = !si->double_buffering;
3013 si->scroll_delay = TRUE;
3014 si->soft_scrolling = TRUE;
3016 si->autorecord = TRUE;
3017 si->quick_doors = FALSE;
3018 si->team_mode = FALSE;
3019 si->handicap = TRUE;
3020 si->time_limit = TRUE;
3021 si->fullscreen = FALSE;
3022 si->ask_on_escape = TRUE;
3024 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3025 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3026 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3027 si->override_level_graphics = FALSE;
3028 si->override_level_sounds = FALSE;
3029 si->override_level_music = FALSE;
3031 si->editor.el_boulderdash = TRUE;
3032 si->editor.el_emerald_mine = TRUE;
3033 si->editor.el_more = TRUE;
3034 si->editor.el_sokoban = TRUE;
3035 si->editor.el_supaplex = TRUE;
3036 si->editor.el_diamond_caves = TRUE;
3037 si->editor.el_dx_boulderdash = TRUE;
3038 si->editor.el_chars = TRUE;
3039 si->editor.el_custom = TRUE;
3040 si->editor.el_custom_more = FALSE;
3042 si->editor.el_headlines = TRUE;
3043 si->editor.el_user_defined = FALSE;
3045 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3046 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3047 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3049 for (i = 0; i < MAX_PLAYERS; i++)
3051 si->input[i].use_joystick = FALSE;
3052 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3053 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3054 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3055 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3056 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3057 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3058 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3059 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3060 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3061 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3062 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3063 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3064 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3065 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3066 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3069 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3070 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3072 si->options.verbose = FALSE;
3075 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3079 if (!setup_file_hash)
3084 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3085 setSetupInfo(global_setup_tokens, i,
3086 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3091 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3092 setSetupInfo(editor_setup_tokens, i,
3093 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3096 /* shortcut setup */
3097 ssi = setup.shortcut;
3098 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3099 setSetupInfo(shortcut_setup_tokens, i,
3100 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3101 setup.shortcut = ssi;
3104 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3108 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3110 sii = setup.input[pnr];
3111 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3113 char full_token[100];
3115 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3116 setSetupInfo(player_setup_tokens, i,
3117 getHashEntry(setup_file_hash, full_token));
3119 setup.input[pnr] = sii;
3124 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3125 setSetupInfo(system_setup_tokens, i,
3126 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3130 soi = setup.options;
3131 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3132 setSetupInfo(options_setup_tokens, i,
3133 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3134 setup.options = soi;
3139 char *filename = getSetupFilename();
3140 SetupFileHash *setup_file_hash = NULL;
3142 /* always start with reliable default values */
3143 setSetupInfoToDefaults(&setup);
3145 setup_file_hash = loadSetupFileHash(filename);
3147 if (setup_file_hash)
3149 char *player_name_new;
3151 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3152 decodeSetupFileHash(setup_file_hash);
3154 setup.direct_draw = !setup.double_buffering;
3156 freeSetupFileHash(setup_file_hash);
3158 /* needed to work around problems with fixed length strings */
3159 player_name_new = get_corrected_login_name(setup.player_name);
3160 free(setup.player_name);
3161 setup.player_name = player_name_new;
3164 Error(ERR_WARN, "using default setup values");
3169 char *filename = getSetupFilename();
3173 InitUserDataDirectory();
3175 if (!(file = fopen(filename, MODE_WRITE)))
3177 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3181 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3182 getCookie("SETUP")));
3183 fprintf(file, "\n");
3187 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3189 /* just to make things nicer :) */
3190 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3191 i == SETUP_TOKEN_GRAPHICS_SET)
3192 fprintf(file, "\n");
3194 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3199 fprintf(file, "\n");
3200 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3201 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3203 /* shortcut setup */
3204 ssi = setup.shortcut;
3205 fprintf(file, "\n");
3206 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3207 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3210 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3214 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3215 fprintf(file, "\n");
3217 sii = setup.input[pnr];
3218 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3219 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3224 fprintf(file, "\n");
3225 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3226 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3229 soi = setup.options;
3230 fprintf(file, "\n");
3231 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3232 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3236 SetFilePermissions(filename, PERMS_PRIVATE);
3239 void LoadCustomElementDescriptions()
3241 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3242 SetupFileHash *setup_file_hash;
3245 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3247 if (element_info[i].custom_description != NULL)
3249 free(element_info[i].custom_description);
3250 element_info[i].custom_description = NULL;
3254 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3257 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3259 char *token = getStringCat2(element_info[i].token_name, ".name");
3260 char *value = getHashEntry(setup_file_hash, token);
3263 element_info[i].custom_description = getStringCopy(value);
3268 freeSetupFileHash(setup_file_hash);
3271 void LoadSpecialMenuDesignSettings()
3273 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3274 SetupFileHash *setup_file_hash;
3277 /* always start with reliable default values from default config */
3278 for (i = 0; image_config_vars[i].token != NULL; i++)
3279 for (j = 0; image_config[j].token != NULL; j++)
3280 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3281 *image_config_vars[i].value =
3282 get_auto_parameter_value(image_config_vars[i].token,
3283 image_config[j].value);
3285 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3288 /* special case: initialize with default values that may be overwritten */
3289 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3291 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3292 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3293 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3295 if (value_x != NULL)
3296 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3297 if (value_y != NULL)
3298 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3299 if (list_size != NULL)
3300 menu.list_size[i] = get_integer_from_string(list_size);
3303 /* read (and overwrite with) values that may be specified in config file */
3304 for (i = 0; image_config_vars[i].token != NULL; i++)
3306 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3309 *image_config_vars[i].value =
3310 get_auto_parameter_value(image_config_vars[i].token, value);
3313 freeSetupFileHash(setup_file_hash);
3316 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3318 char *filename = getEditorSetupFilename();
3319 SetupFileList *setup_file_list, *list;
3320 SetupFileHash *element_hash;
3321 int num_unknown_tokens = 0;
3324 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3327 element_hash = newSetupFileHash();
3329 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3330 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3332 /* determined size may be larger than needed (due to unknown elements) */
3334 for (list = setup_file_list; list != NULL; list = list->next)
3337 /* add space for up to 3 more elements for padding that may be needed */
3340 *elements = checked_malloc(*num_elements * sizeof(int));
3343 for (list = setup_file_list; list != NULL; list = list->next)
3345 char *value = getHashEntry(element_hash, list->token);
3349 (*elements)[(*num_elements)++] = atoi(value);
3353 if (num_unknown_tokens == 0)
3355 Error(ERR_RETURN_LINE, "-");
3356 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3357 Error(ERR_RETURN, "- config file: '%s'", filename);
3359 num_unknown_tokens++;
3362 Error(ERR_RETURN, "- token: '%s'", list->token);
3366 if (num_unknown_tokens > 0)
3367 Error(ERR_RETURN_LINE, "-");
3369 while (*num_elements % 4) /* pad with empty elements, if needed */
3370 (*elements)[(*num_elements)++] = EL_EMPTY;
3372 freeSetupFileList(setup_file_list);
3373 freeSetupFileHash(element_hash);
3377 for (i = 0; i < *num_elements; i++)
3378 printf("editor: element '%s' [%d]\n",
3379 element_info[(*elements)[i]].token_name, (*elements)[i]);
3383 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3386 SetupFileHash *setup_file_hash = NULL;
3387 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3388 char *filename_music, *filename_prefix, *filename_info;
3394 token_to_value_ptr[] =
3396 { "title_header", &tmp_music_file_info.title_header },
3397 { "artist_header", &tmp_music_file_info.artist_header },
3398 { "album_header", &tmp_music_file_info.album_header },
3399 { "year_header", &tmp_music_file_info.year_header },
3401 { "title", &tmp_music_file_info.title },
3402 { "artist", &tmp_music_file_info.artist },
3403 { "album", &tmp_music_file_info.album },
3404 { "year", &tmp_music_file_info.year },
3410 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3411 getCustomMusicFilename(basename));
3413 if (filename_music == NULL)
3416 /* ---------- try to replace file extension ---------- */
3418 filename_prefix = getStringCopy(filename_music);
3419 if (strrchr(filename_prefix, '.') != NULL)
3420 *strrchr(filename_prefix, '.') = '\0';
3421 filename_info = getStringCat2(filename_prefix, ".txt");
3424 printf("trying to load file '%s'...\n", filename_info);
3427 if (fileExists(filename_info))
3428 setup_file_hash = loadSetupFileHash(filename_info);
3430 free(filename_prefix);
3431 free(filename_info);
3433 if (setup_file_hash == NULL)
3435 /* ---------- try to add file extension ---------- */
3437 filename_prefix = getStringCopy(filename_music);
3438 filename_info = getStringCat2(filename_prefix, ".txt");
3441 printf("trying to load file '%s'...\n", filename_info);
3444 if (fileExists(filename_info))
3445 setup_file_hash = loadSetupFileHash(filename_info);
3447 free(filename_prefix);
3448 free(filename_info);
3451 if (setup_file_hash == NULL)
3454 /* ---------- music file info found ---------- */
3456 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3458 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3460 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3462 *token_to_value_ptr[i].value_ptr =
3463 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3466 tmp_music_file_info.basename = getStringCopy(basename);
3467 tmp_music_file_info.music = music;
3468 tmp_music_file_info.is_sound = is_sound;
3470 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3471 *new_music_file_info = tmp_music_file_info;
3473 return new_music_file_info;
3476 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3478 return get_music_file_info_ext(basename, music, FALSE);
3481 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3483 return get_music_file_info_ext(basename, sound, TRUE);
3486 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3487 char *basename, boolean is_sound)
3489 for (; list != NULL; list = list->next)
3490 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3496 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3498 return music_info_listed_ext(list, basename, FALSE);
3501 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3503 return music_info_listed_ext(list, basename, TRUE);
3506 void LoadMusicInfo()
3508 char *music_directory = getCustomMusicDirectory();
3509 int num_music = getMusicListSize();
3510 int num_music_noconf = 0;
3511 int num_sounds = getSoundListSize();
3513 struct dirent *dir_entry;
3514 struct FileInfo *music, *sound;
3515 struct MusicFileInfo *next, **new;
3518 while (music_file_info != NULL)
3520 next = music_file_info->next;
3522 checked_free(music_file_info->basename);
3524 checked_free(music_file_info->title_header);
3525 checked_free(music_file_info->artist_header);
3526 checked_free(music_file_info->album_header);
3527 checked_free(music_file_info->year_header);
3529 checked_free(music_file_info->title);
3530 checked_free(music_file_info->artist);
3531 checked_free(music_file_info->album);
3532 checked_free(music_file_info->year);
3534 free(music_file_info);
3536 music_file_info = next;
3539 new = &music_file_info;
3542 printf("::: num_music == %d\n", num_music);
3545 for (i = 0; i < num_music; i++)
3547 music = getMusicListEntry(i);
3550 printf("::: %d [%08x]\n", i, music->filename);
3553 if (music->filename == NULL)
3556 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3559 /* a configured file may be not recognized as music */
3560 if (!FileIsMusic(music->filename))
3564 printf("::: -> '%s' (configured)\n", music->filename);
3567 if (!music_info_listed(music_file_info, music->filename))
3569 *new = get_music_file_info(music->filename, i);
3571 new = &(*new)->next;
3575 if ((dir = opendir(music_directory)) == NULL)
3577 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3581 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
3583 char *basename = dir_entry->d_name;
3584 boolean music_already_used = FALSE;
3587 /* skip all music files that are configured in music config file */
3588 for (i = 0; i < num_music; i++)
3590 music = getMusicListEntry(i);
3592 if (music->filename == NULL)
3595 if (strcmp(basename, music->filename) == 0)
3597 music_already_used = TRUE;
3602 if (music_already_used)
3605 if (!FileIsMusic(basename))
3609 printf("::: -> '%s' (found in directory)\n", basename);
3612 if (!music_info_listed(music_file_info, basename))
3614 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3616 new = &(*new)->next;
3624 for (i = 0; i < num_sounds; i++)
3626 sound = getSoundListEntry(i);
3628 if (sound->filename == NULL)
3631 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3634 /* a configured file may be not recognized as sound */
3635 if (!FileIsSound(sound->filename))
3639 printf("::: -> '%s' (configured)\n", sound->filename);
3642 if (!sound_info_listed(music_file_info, sound->filename))
3644 *new = get_sound_file_info(sound->filename, i);
3646 new = &(*new)->next;
3652 for (next = music_file_info; next != NULL; next = next->next)
3653 printf("::: title == '%s'\n", next->title);
3657 void add_helpanim_entry(int element, int action, int direction, int delay,
3658 int *num_list_entries)
3660 struct HelpAnimInfo *new_list_entry;
3661 (*num_list_entries)++;
3664 checked_realloc(helpanim_info,
3665 *num_list_entries * sizeof(struct HelpAnimInfo));
3666 new_list_entry = &helpanim_info[*num_list_entries - 1];
3668 new_list_entry->element = element;
3669 new_list_entry->action = action;
3670 new_list_entry->direction = direction;
3671 new_list_entry->delay = delay;
3674 void print_unknown_token(char *filename, char *token, int token_nr)
3678 Error(ERR_RETURN_LINE, "-");
3679 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3680 Error(ERR_RETURN, "- config file: '%s'", filename);
3683 Error(ERR_RETURN, "- token: '%s'", token);
3686 void print_unknown_token_end(int token_nr)
3689 Error(ERR_RETURN_LINE, "-");
3692 void LoadHelpAnimInfo()
3694 char *filename = getHelpAnimFilename();
3695 SetupFileList *setup_file_list = NULL, *list;
3696 SetupFileHash *element_hash, *action_hash, *direction_hash;
3697 int num_list_entries = 0;
3698 int num_unknown_tokens = 0;
3701 if (fileExists(filename))
3702 setup_file_list = loadSetupFileList(filename);
3704 if (setup_file_list == NULL)
3706 /* use reliable default values from static configuration */
3707 SetupFileList *insert_ptr;
3709 insert_ptr = setup_file_list =
3710 newSetupFileList(helpanim_config[0].token,
3711 helpanim_config[0].value);
3713 for (i = 1; helpanim_config[i].token; i++)
3714 insert_ptr = addListEntry(insert_ptr,
3715 helpanim_config[i].token,
3716 helpanim_config[i].value);
3719 element_hash = newSetupFileHash();
3720 action_hash = newSetupFileHash();
3721 direction_hash = newSetupFileHash();
3723 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3724 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3726 for (i = 0; i < NUM_ACTIONS; i++)
3727 setHashEntry(action_hash, element_action_info[i].suffix,
3728 i_to_a(element_action_info[i].value));
3730 /* do not store direction index (bit) here, but direction value! */
3731 for (i = 0; i < NUM_DIRECTIONS; i++)
3732 setHashEntry(direction_hash, element_direction_info[i].suffix,
3733 i_to_a(1 << element_direction_info[i].value));
3735 for (list = setup_file_list; list != NULL; list = list->next)
3737 char *element_token, *action_token, *direction_token;
3738 char *element_value, *action_value, *direction_value;
3739 int delay = atoi(list->value);
3741 if (strcmp(list->token, "end") == 0)
3743 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3748 /* first try to break element into element/action/direction parts;
3749 if this does not work, also accept combined "element[.act][.dir]"
3750 elements (like "dynamite.active"), which are unique elements */
3752 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
3754 element_value = getHashEntry(element_hash, list->token);
3755 if (element_value != NULL) /* element found */
3756 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3760 /* no further suffixes found -- this is not an element */
3761 print_unknown_token(filename, list->token, num_unknown_tokens++);
3767 /* token has format "<prefix>.<something>" */
3769 action_token = strchr(list->token, '.'); /* suffix may be action ... */
3770 direction_token = action_token; /* ... or direction */
3772 element_token = getStringCopy(list->token);
3773 *strchr(element_token, '.') = '\0';
3775 element_value = getHashEntry(element_hash, element_token);
3777 if (element_value == NULL) /* this is no element */
3779 element_value = getHashEntry(element_hash, list->token);
3780 if (element_value != NULL) /* combined element found */
3781 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3784 print_unknown_token(filename, list->token, num_unknown_tokens++);
3786 free(element_token);
3791 action_value = getHashEntry(action_hash, action_token);
3793 if (action_value != NULL) /* action found */
3795 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3798 free(element_token);
3803 direction_value = getHashEntry(direction_hash, direction_token);
3805 if (direction_value != NULL) /* direction found */
3807 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3810 free(element_token);
3815 if (strchr(action_token + 1, '.') == NULL)
3817 /* no further suffixes found -- this is not an action nor direction */
3819 element_value = getHashEntry(element_hash, list->token);
3820 if (element_value != NULL) /* combined element found */
3821 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3824 print_unknown_token(filename, list->token, num_unknown_tokens++);
3826 free(element_token);
3831 /* token has format "<prefix>.<suffix>.<something>" */
3833 direction_token = strchr(action_token + 1, '.');
3835 action_token = getStringCopy(action_token);
3836 *strchr(action_token + 1, '.') = '\0';
3838 action_value = getHashEntry(action_hash, action_token);
3840 if (action_value == NULL) /* this is no action */
3842 element_value = getHashEntry(element_hash, list->token);
3843 if (element_value != NULL) /* combined element found */
3844 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3847 print_unknown_token(filename, list->token, num_unknown_tokens++);
3849 free(element_token);
3855 direction_value = getHashEntry(direction_hash, direction_token);
3857 if (direction_value != NULL) /* direction found */
3859 add_helpanim_entry(atoi(element_value), atoi(action_value),
3860 atoi(direction_value), delay, &num_list_entries);
3862 free(element_token);
3868 /* this is no direction */
3870 element_value = getHashEntry(element_hash, list->token);
3871 if (element_value != NULL) /* combined element found */
3872 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3875 print_unknown_token(filename, list->token, num_unknown_tokens++);
3877 free(element_token);
3881 print_unknown_token_end(num_unknown_tokens);
3883 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3884 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
3886 freeSetupFileList(setup_file_list);
3887 freeSetupFileHash(element_hash);
3888 freeSetupFileHash(action_hash);
3889 freeSetupFileHash(direction_hash);
3893 for (i = 0; i < num_list_entries; i++)
3894 printf("::: %d, %d, %d => %d\n",
3895 helpanim_info[i].element,
3896 helpanim_info[i].action,
3897 helpanim_info[i].direction,
3898 helpanim_info[i].delay);
3902 void LoadHelpTextInfo()
3904 char *filename = getHelpTextFilename();
3907 if (helptext_info != NULL)
3909 freeSetupFileHash(helptext_info);
3910 helptext_info = NULL;
3913 if (fileExists(filename))
3914 helptext_info = loadSetupFileHash(filename);
3916 if (helptext_info == NULL)
3918 /* use reliable default values from static configuration */
3919 helptext_info = newSetupFileHash();
3921 for (i = 0; helptext_config[i].token; i++)
3922 setHashEntry(helptext_info,
3923 helptext_config[i].token,
3924 helptext_config[i].value);
3929 BEGIN_HASH_ITERATION(helptext_info, itr)
3931 printf("::: '%s' => '%s'\n",
3932 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3934 END_HASH_ITERATION(hash, itr)