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 LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
40 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
41 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
52 /* values for level file type identifier */
53 #define LEVEL_FILE_TYPE_UNKNOWN 0
54 #define LEVEL_FILE_TYPE_RND 1
55 #define LEVEL_FILE_TYPE_EM 2
57 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
58 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
60 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
61 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
64 /* ========================================================================= */
65 /* level file functions */
66 /* ========================================================================= */
68 void setElementChangePages(struct ElementInfo *ei, int change_pages)
70 int change_page_size = sizeof(struct ElementChangeInfo);
72 ei->num_change_pages = MAX(1, change_pages);
75 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
77 if (ei->current_change_page >= ei->num_change_pages)
78 ei->current_change_page = ei->num_change_pages - 1;
80 ei->change = &ei->change_page[ei->current_change_page];
83 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
87 change->can_change = FALSE;
89 change->events = CE_BITMASK_DEFAULT;
90 change->sides = CH_SIDE_ANY;
92 change->target_element = EL_EMPTY_SPACE;
94 change->delay_fixed = 0;
95 change->delay_random = 0;
96 change->delay_frames = 1;
98 change->trigger_element = EL_EMPTY_SPACE;
100 change->explode = FALSE;
101 change->use_content = FALSE;
102 change->only_complete = FALSE;
103 change->use_random_change = FALSE;
104 change->random = 100;
105 change->power = CP_NON_DESTRUCTIVE;
107 for (x = 0; x < 3; x++)
108 for (y = 0; y < 3; y++)
109 change->content[x][y] = EL_EMPTY_SPACE;
111 change->direct_action = 0;
112 change->other_action = 0;
114 change->pre_change_function = NULL;
115 change->change_function = NULL;
116 change->post_change_function = NULL;
119 static void setLevelInfoToDefaults(struct LevelInfo *level)
123 level->file_version = FILE_VERSION_ACTUAL;
124 level->game_version = GAME_VERSION_ACTUAL;
126 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
127 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
128 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
130 level->fieldx = STD_LEV_FIELDX;
131 level->fieldy = STD_LEV_FIELDY;
133 for (x = 0; x < MAX_LEV_FIELDX; x++)
134 for (y = 0; y < MAX_LEV_FIELDY; y++)
135 level->field[x][y] = EL_SAND;
138 level->gems_needed = 0;
139 level->amoeba_speed = 10;
140 level->time_magic_wall = 10;
141 level->time_wheel = 10;
142 level->time_light = 10;
143 level->time_timegate = 10;
144 level->amoeba_content = EL_DIAMOND;
145 level->double_speed = FALSE;
146 level->initial_gravity = FALSE;
147 level->em_slippery_gems = FALSE;
149 level->use_custom_template = FALSE;
151 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
152 level->name[i] = '\0';
153 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
154 level->author[i] = '\0';
156 strcpy(level->name, NAMELESS_LEVEL_NAME);
157 strcpy(level->author, ANONYMOUS_NAME);
159 for (i = 0; i < 4; i++)
161 level->envelope_text[i][0] = '\0';
162 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
163 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
166 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
167 level->score[i] = 10;
169 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
170 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
171 for (x = 0; x < 3; x++)
172 for (y = 0; y < 3; y++)
173 level->yamyam_content[i][x][y] =
174 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
176 level->field[0][0] = EL_PLAYER_1;
177 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
179 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
183 setElementChangePages(&element_info[element], 1);
184 setElementChangeInfoToDefaults(element_info[element].change);
186 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
188 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
189 element_info[element].description[j] = '\0';
191 if (element_info[element].custom_description != NULL)
192 strncpy(element_info[element].description,
193 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
195 strcpy(element_info[element].description,
196 element_info[element].editor_description);
198 element_info[element].use_gfx_element = FALSE;
199 element_info[element].gfx_element = EL_EMPTY_SPACE;
202 if (IS_CUSTOM_ELEMENT(element))
204 element_info[element].collect_score = 10; /* special default */
205 element_info[element].collect_count = 1; /* special default */
207 element_info[element].push_delay_fixed = -1; /* initialize later */
208 element_info[element].push_delay_random = -1; /* initialize later */
209 element_info[element].move_delay_fixed = 0;
210 element_info[element].move_delay_random = 0;
212 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
213 element_info[element].move_direction_initial = MV_NO_MOVING;
214 element_info[element].move_stepsize = TILEX / 8;
215 element_info[element].move_enter_element = EL_EMPTY_SPACE;
216 element_info[element].move_leave_element = EL_EMPTY_SPACE;
218 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
220 for (x = 0; x < 3; x++)
221 for (y = 0; y < 3; y++)
222 element_info[element].content[x][y] = EL_EMPTY_SPACE;
224 element_info[element].access_type = 0;
225 element_info[element].access_layer = 0;
226 element_info[element].walk_to_action = 0;
227 element_info[element].smash_targets = 0;
228 element_info[element].deadliness = 0;
229 element_info[element].consistency = 0;
231 element_info[element].can_explode_by_fire = FALSE;
232 element_info[element].can_explode_smashed = FALSE;
233 element_info[element].can_explode_impact = FALSE;
235 element_info[element].current_change_page = 0;
237 /* start with no properties at all */
238 for (j = 0; j < NUM_EP_BITFIELDS; j++)
239 Properties[element][j] = EP_BITMASK_DEFAULT;
241 element_info[element].modified_settings = FALSE;
243 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
245 /* initialize memory for list of elements in group */
246 if (element_info[element].group == NULL)
247 element_info[element].group =
248 checked_malloc(sizeof(struct ElementGroupInfo));
250 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
251 element_info[element].group->element[j] = EL_EMPTY_SPACE;
253 /* default: only one element in group */
254 element_info[element].group->num_elements = 1;
258 BorderElement = EL_STEELWALL;
260 level->no_level_file = FALSE;
262 if (leveldir_current == NULL) /* only when dumping level */
265 /* try to determine better author name than 'anonymous' */
266 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
268 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
269 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
273 switch (LEVELCLASS(leveldir_current))
275 case LEVELCLASS_TUTORIAL:
276 strcpy(level->author, PROGRAM_AUTHOR_STRING);
279 case LEVELCLASS_CONTRIB:
280 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
281 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
284 case LEVELCLASS_PRIVATE:
285 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
286 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
290 /* keep default value */
296 static void ActivateLevelTemplate()
298 /* Currently there is no special action needed to activate the template
299 data, because 'element_info' and 'Properties' overwrite the original
300 level data, while all other variables do not change. */
303 static char *getLevelFilenameFromBasename(char *basename)
305 static char *filename = NULL;
307 checked_free(filename);
309 filename = getPath2(getCurrentLevelDir(), basename);
314 static char *getSingleLevelBasename(int nr, int type)
316 static char basename[MAX_FILENAME_LEN];
320 case LEVEL_FILE_TYPE_RND:
322 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
324 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
327 case LEVEL_FILE_TYPE_EM:
328 sprintf(basename, "%d", nr);
332 strcpy(basename, UNDEFINED_FILENAME);
339 static char *getPackedLevelBasename(int type)
341 static char basename[MAX_FILENAME_LEN];
346 strcpy(basename, UNDEFINED_FILENAME);
353 static char *getSingleLevelFilename(int nr, int type)
355 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
358 static char *getPackedLevelFilename(int type)
360 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
363 char *getDefaultLevelFilename(int nr)
365 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
368 static struct LevelFileInfo *getLevelFileInfo(int nr)
370 static struct LevelFileInfo level_file_info;
372 level_file_info.nr = nr;
374 /* special case: level template */
377 level_file_info.type = LEVEL_FILE_TYPE_RND;
378 level_file_info.filename = getDefaultLevelFilename(nr);
380 return &level_file_info;
383 /* 1st try: check for native Rocks'n'Diamonds level file */
384 level_file_info.type = LEVEL_FILE_TYPE_RND;
385 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
386 if (fileExists(level_file_info.filename))
387 return &level_file_info;
389 /* 2nd try: check for classic Emerald Mine level file */
390 level_file_info.type = LEVEL_FILE_TYPE_EM;
391 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
392 if (fileExists(level_file_info.filename))
393 return &level_file_info;
395 /* no known level file found -- use default values */
396 level_file_info.type = LEVEL_FILE_TYPE_RND;
397 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
399 return &level_file_info;
402 /* ------------------------------------------------------------------------- */
403 /* functions for loading R'n'D level */
404 /* ------------------------------------------------------------------------- */
406 static int checkLevelElement(int element)
408 /* map some (historic, now obsolete) elements */
413 case EL_PLAYER_OBSOLETE:
414 element = EL_PLAYER_1;
417 case EL_KEY_OBSOLETE:
420 case EL_EM_KEY_1_FILE_OBSOLETE:
421 element = EL_EM_KEY_1;
424 case EL_EM_KEY_2_FILE_OBSOLETE:
425 element = EL_EM_KEY_2;
428 case EL_EM_KEY_3_FILE_OBSOLETE:
429 element = EL_EM_KEY_3;
432 case EL_EM_KEY_4_FILE_OBSOLETE:
433 element = EL_EM_KEY_4;
436 case EL_ENVELOPE_OBSOLETE:
437 element = EL_ENVELOPE_1;
445 if (element >= NUM_FILE_ELEMENTS)
447 Error(ERR_WARN, "invalid level element %d", element);
449 element = EL_CHAR_QUESTION;
454 if (element >= NUM_FILE_ELEMENTS)
456 Error(ERR_WARN, "invalid level element %d", element);
458 element = EL_CHAR_QUESTION;
460 else if (element == EL_PLAYER_OBSOLETE)
461 element = EL_PLAYER_1;
462 else if (element == EL_KEY_OBSOLETE)
469 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
471 level->file_version = getFileVersion(file);
472 level->game_version = getFileVersion(file);
477 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
481 level->fieldx = getFile8Bit(file);
482 level->fieldy = getFile8Bit(file);
484 level->time = getFile16BitBE(file);
485 level->gems_needed = getFile16BitBE(file);
487 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
488 level->name[i] = getFile8Bit(file);
489 level->name[MAX_LEVEL_NAME_LEN] = 0;
491 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
492 level->score[i] = getFile8Bit(file);
494 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
495 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
496 for (y = 0; y < 3; y++)
497 for (x = 0; x < 3; x++)
498 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
500 level->amoeba_speed = getFile8Bit(file);
501 level->time_magic_wall = getFile8Bit(file);
502 level->time_wheel = getFile8Bit(file);
503 level->amoeba_content = checkLevelElement(getFile8Bit(file));
504 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
505 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
506 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
507 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
509 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
511 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
516 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
520 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
521 level->author[i] = getFile8Bit(file);
522 level->author[MAX_LEVEL_NAME_LEN] = 0;
527 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
530 int chunk_size_expected = level->fieldx * level->fieldy;
532 /* Note: "chunk_size" was wrong before version 2.0 when elements are
533 stored with 16-bit encoding (and should be twice as big then).
534 Even worse, playfield data was stored 16-bit when only yamyam content
535 contained 16-bit elements and vice versa. */
537 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
538 chunk_size_expected *= 2;
540 if (chunk_size_expected != chunk_size)
542 ReadUnusedBytesFromFile(file, chunk_size);
543 return chunk_size_expected;
546 for (y = 0; y < level->fieldy; y++)
547 for (x = 0; x < level->fieldx; x++)
549 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
554 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
558 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
559 int chunk_size_expected = header_size + content_size;
561 /* Note: "chunk_size" was wrong before version 2.0 when elements are
562 stored with 16-bit encoding (and should be twice as big then).
563 Even worse, playfield data was stored 16-bit when only yamyam content
564 contained 16-bit elements and vice versa. */
566 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
567 chunk_size_expected += content_size;
569 if (chunk_size_expected != chunk_size)
571 ReadUnusedBytesFromFile(file, chunk_size);
572 return chunk_size_expected;
576 level->num_yamyam_contents = getFile8Bit(file);
580 /* correct invalid number of content fields -- should never happen */
581 if (level->num_yamyam_contents < 1 ||
582 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
583 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
585 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
586 for (y = 0; y < 3; y++)
587 for (x = 0; x < 3; x++)
588 level->yamyam_content[i][x][y] =
589 checkLevelElement(level->encoding_16bit_field ?
590 getFile16BitBE(file) : getFile8Bit(file));
594 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
598 int num_contents, content_xsize, content_ysize;
599 int content_array[MAX_ELEMENT_CONTENTS][3][3];
601 element = checkLevelElement(getFile16BitBE(file));
602 num_contents = getFile8Bit(file);
603 content_xsize = getFile8Bit(file);
604 content_ysize = getFile8Bit(file);
606 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
608 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
609 for (y = 0; y < 3; y++)
610 for (x = 0; x < 3; x++)
611 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
613 /* correct invalid number of content fields -- should never happen */
614 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
615 num_contents = STD_ELEMENT_CONTENTS;
617 if (element == EL_YAMYAM)
619 level->num_yamyam_contents = num_contents;
621 for (i = 0; i < num_contents; i++)
622 for (y = 0; y < 3; y++)
623 for (x = 0; x < 3; x++)
624 level->yamyam_content[i][x][y] = content_array[i][x][y];
626 else if (element == EL_BD_AMOEBA)
628 level->amoeba_content = content_array[0][0][0];
632 Error(ERR_WARN, "cannot load content for element '%d'", element);
638 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
644 int chunk_size_expected;
646 element = checkLevelElement(getFile16BitBE(file));
647 if (!IS_ENVELOPE(element))
648 element = EL_ENVELOPE_1;
650 envelope_nr = element - EL_ENVELOPE_1;
652 envelope_len = getFile16BitBE(file);
654 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
655 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
657 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
659 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
660 if (chunk_size_expected != chunk_size)
662 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
663 return chunk_size_expected;
666 for (i = 0; i < envelope_len; i++)
667 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
672 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
674 int num_changed_custom_elements = getFile16BitBE(file);
675 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
678 if (chunk_size_expected != chunk_size)
680 ReadUnusedBytesFromFile(file, chunk_size - 2);
681 return chunk_size_expected;
684 for (i = 0; i < num_changed_custom_elements; i++)
686 int element = getFile16BitBE(file);
687 int properties = getFile32BitBE(file);
689 if (IS_CUSTOM_ELEMENT(element))
690 Properties[element][EP_BITFIELD_BASE] = properties;
692 Error(ERR_WARN, "invalid custom element number %d", element);
698 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
700 int num_changed_custom_elements = getFile16BitBE(file);
701 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
704 if (chunk_size_expected != chunk_size)
706 ReadUnusedBytesFromFile(file, chunk_size - 2);
707 return chunk_size_expected;
710 for (i = 0; i < num_changed_custom_elements; i++)
712 int element = getFile16BitBE(file);
713 int custom_target_element = getFile16BitBE(file);
715 if (IS_CUSTOM_ELEMENT(element))
716 element_info[element].change->target_element = custom_target_element;
718 Error(ERR_WARN, "invalid custom element number %d", element);
724 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
726 int num_changed_custom_elements = getFile16BitBE(file);
727 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
730 if (chunk_size_expected != chunk_size)
732 ReadUnusedBytesFromFile(file, chunk_size - 2);
733 return chunk_size_expected;
736 for (i = 0; i < num_changed_custom_elements; i++)
738 int element = getFile16BitBE(file);
740 if (!IS_CUSTOM_ELEMENT(element))
742 Error(ERR_WARN, "invalid custom element number %d", element);
744 element = EL_INTERNAL_DUMMY;
747 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
748 element_info[element].description[j] = getFile8Bit(file);
749 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
751 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
753 /* some free bytes for future properties and padding */
754 ReadUnusedBytesFromFile(file, 7);
756 element_info[element].use_gfx_element = getFile8Bit(file);
757 element_info[element].gfx_element =
758 checkLevelElement(getFile16BitBE(file));
760 element_info[element].collect_score = getFile8Bit(file);
761 element_info[element].collect_count = getFile8Bit(file);
763 element_info[element].push_delay_fixed = getFile16BitBE(file);
764 element_info[element].push_delay_random = getFile16BitBE(file);
765 element_info[element].move_delay_fixed = getFile16BitBE(file);
766 element_info[element].move_delay_random = getFile16BitBE(file);
768 element_info[element].move_pattern = getFile16BitBE(file);
769 element_info[element].move_direction_initial = getFile8Bit(file);
770 element_info[element].move_stepsize = getFile8Bit(file);
772 for (y = 0; y < 3; y++)
773 for (x = 0; x < 3; x++)
774 element_info[element].content[x][y] =
775 checkLevelElement(getFile16BitBE(file));
777 element_info[element].change->events = getFile32BitBE(file);
779 element_info[element].change->target_element =
780 checkLevelElement(getFile16BitBE(file));
782 element_info[element].change->delay_fixed = getFile16BitBE(file);
783 element_info[element].change->delay_random = getFile16BitBE(file);
784 element_info[element].change->delay_frames = getFile16BitBE(file);
786 element_info[element].change->trigger_element =
787 checkLevelElement(getFile16BitBE(file));
789 element_info[element].change->explode = getFile8Bit(file);
790 element_info[element].change->use_content = getFile8Bit(file);
791 element_info[element].change->only_complete = getFile8Bit(file);
792 element_info[element].change->use_random_change = getFile8Bit(file);
794 element_info[element].change->random = getFile8Bit(file);
795 element_info[element].change->power = getFile8Bit(file);
797 for (y = 0; y < 3; y++)
798 for (x = 0; x < 3; x++)
799 element_info[element].change->content[x][y] =
800 checkLevelElement(getFile16BitBE(file));
802 element_info[element].slippery_type = getFile8Bit(file);
804 /* some free bytes for future properties and padding */
805 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
807 /* mark that this custom element has been modified */
808 element_info[element].modified_settings = TRUE;
814 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
816 struct ElementInfo *ei;
817 int chunk_size_expected;
821 element = getFile16BitBE(file);
823 if (!IS_CUSTOM_ELEMENT(element))
825 Error(ERR_WARN, "invalid custom element number %d", element);
827 ReadUnusedBytesFromFile(file, chunk_size - 2);
831 ei = &element_info[element];
833 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
834 ei->description[i] = getFile8Bit(file);
835 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
837 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
838 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
840 ei->num_change_pages = getFile8Bit(file);
842 /* some free bytes for future base property values and padding */
843 ReadUnusedBytesFromFile(file, 5);
845 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
846 if (chunk_size_expected != chunk_size)
848 ReadUnusedBytesFromFile(file, chunk_size - 48);
849 return chunk_size_expected;
852 /* read custom property values */
854 ei->use_gfx_element = getFile8Bit(file);
855 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
857 ei->collect_score = getFile8Bit(file);
858 ei->collect_count = getFile8Bit(file);
860 ei->push_delay_fixed = getFile16BitBE(file);
861 ei->push_delay_random = getFile16BitBE(file);
862 ei->move_delay_fixed = getFile16BitBE(file);
863 ei->move_delay_random = getFile16BitBE(file);
865 ei->move_pattern = getFile16BitBE(file);
866 ei->move_direction_initial = getFile8Bit(file);
867 ei->move_stepsize = getFile8Bit(file);
869 ei->slippery_type = getFile8Bit(file);
871 for (y = 0; y < 3; y++)
872 for (x = 0; x < 3; x++)
873 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
875 ei->move_enter_element = checkLevelElement(getFile16BitBE(file));
876 ei->move_leave_element = checkLevelElement(getFile16BitBE(file));
878 /* some free bytes for future custom property values and padding */
879 ReadUnusedBytesFromFile(file, 8);
881 /* read change property values */
883 setElementChangePages(ei, ei->num_change_pages);
885 for (i = 0; i < ei->num_change_pages; i++)
887 struct ElementChangeInfo *change = &ei->change_page[i];
889 /* always start with reliable default values */
890 setElementChangeInfoToDefaults(change);
892 change->events = getFile32BitBE(file);
894 change->target_element = checkLevelElement(getFile16BitBE(file));
896 change->delay_fixed = getFile16BitBE(file);
897 change->delay_random = getFile16BitBE(file);
898 change->delay_frames = getFile16BitBE(file);
900 change->trigger_element = checkLevelElement(getFile16BitBE(file));
902 change->explode = getFile8Bit(file);
903 change->use_content = getFile8Bit(file);
904 change->only_complete = getFile8Bit(file);
905 change->use_random_change = getFile8Bit(file);
907 change->random = getFile8Bit(file);
908 change->power = getFile8Bit(file);
910 for (y = 0; y < 3; y++)
911 for (x = 0; x < 3; x++)
912 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
914 change->can_change = getFile8Bit(file);
916 change->sides = getFile8Bit(file);
918 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
919 change->sides = CH_SIDE_ANY;
921 /* some free bytes for future change property values and padding */
922 ReadUnusedBytesFromFile(file, 8);
925 /* mark this custom element as modified */
926 ei->modified_settings = TRUE;
931 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
933 struct ElementInfo *ei;
934 struct ElementGroupInfo *group;
938 element = getFile16BitBE(file);
940 if (!IS_GROUP_ELEMENT(element))
942 Error(ERR_WARN, "invalid group element number %d", element);
944 ReadUnusedBytesFromFile(file, chunk_size - 2);
948 ei = &element_info[element];
950 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
951 ei->description[i] = getFile8Bit(file);
952 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
954 group = element_info[element].group;
956 group->num_elements = getFile8Bit(file);
958 ei->use_gfx_element = getFile8Bit(file);
959 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
961 /* some free bytes for future values and padding */
962 ReadUnusedBytesFromFile(file, 4);
964 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
965 group->element[i] = checkLevelElement(getFile16BitBE(file));
967 /* mark this group element as modified */
968 element_info[element].modified_settings = TRUE;
973 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
974 struct LevelFileInfo *level_file_info)
976 char *filename = level_file_info->filename;
977 char cookie[MAX_LINE_LEN];
978 char chunk_name[CHUNK_ID_LEN + 1];
982 /* always start with reliable default values */
983 setLevelInfoToDefaults(level);
985 if (!(file = fopen(filename, MODE_READ)))
987 level->no_level_file = TRUE;
989 if (level != &level_template)
990 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
995 getFileChunkBE(file, chunk_name, NULL);
996 if (strcmp(chunk_name, "RND1") == 0)
998 getFile32BitBE(file); /* not used */
1000 getFileChunkBE(file, chunk_name, NULL);
1001 if (strcmp(chunk_name, "CAVE") != 0)
1003 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1008 else /* check for pre-2.0 file format with cookie string */
1010 strcpy(cookie, chunk_name);
1011 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1012 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1013 cookie[strlen(cookie) - 1] = '\0';
1015 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1017 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1022 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1024 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1029 /* pre-2.0 level files have no game version, so use file version here */
1030 level->game_version = level->file_version;
1033 if (level->file_version < FILE_VERSION_1_2)
1035 /* level files from versions before 1.2.0 without chunk structure */
1036 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1037 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1045 int (*loader)(FILE *, int, struct LevelInfo *);
1049 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1050 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1051 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1052 { "BODY", -1, LoadLevel_BODY },
1053 { "CONT", -1, LoadLevel_CONT },
1054 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1055 { "CNT3", -1, LoadLevel_CNT3 },
1056 { "CUS1", -1, LoadLevel_CUS1 },
1057 { "CUS2", -1, LoadLevel_CUS2 },
1058 { "CUS3", -1, LoadLevel_CUS3 },
1059 { "CUS4", -1, LoadLevel_CUS4 },
1060 { "GRP1", -1, LoadLevel_GRP1 },
1064 while (getFileChunkBE(file, chunk_name, &chunk_size))
1068 while (chunk_info[i].name != NULL &&
1069 strcmp(chunk_name, chunk_info[i].name) != 0)
1072 if (chunk_info[i].name == NULL)
1074 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1075 chunk_name, filename);
1076 ReadUnusedBytesFromFile(file, chunk_size);
1078 else if (chunk_info[i].size != -1 &&
1079 chunk_info[i].size != chunk_size)
1081 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1082 chunk_size, chunk_name, filename);
1083 ReadUnusedBytesFromFile(file, chunk_size);
1087 /* call function to load this level chunk */
1088 int chunk_size_expected =
1089 (chunk_info[i].loader)(file, chunk_size, level);
1091 /* the size of some chunks cannot be checked before reading other
1092 chunks first (like "HEAD" and "BODY") that contain some header
1093 information, so check them here */
1094 if (chunk_size_expected != chunk_size)
1096 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1097 chunk_size, chunk_name, filename);
1106 /* ------------------------------------------------------------------------- */
1107 /* functions for loading EM level */
1108 /* ------------------------------------------------------------------------- */
1110 static int map_em_element_yam(int element)
1114 case 0x00: return EL_EMPTY;
1115 case 0x01: return EL_EMERALD;
1116 case 0x02: return EL_DIAMOND;
1117 case 0x03: return EL_ROCK;
1118 case 0x04: return EL_ROBOT;
1119 case 0x05: return EL_SPACESHIP_UP;
1120 case 0x06: return EL_BOMB;
1121 case 0x07: return EL_BUG_UP;
1122 case 0x08: return EL_AMOEBA_DROP;
1123 case 0x09: return EL_NUT;
1124 case 0x0a: return EL_YAMYAM;
1125 case 0x0b: return EL_QUICKSAND_FULL;
1126 case 0x0c: return EL_SAND;
1127 case 0x0d: return EL_WALL_SLIPPERY;
1128 case 0x0e: return EL_STEELWALL;
1129 case 0x0f: return EL_WALL;
1130 case 0x10: return EL_EM_KEY_1;
1131 case 0x11: return EL_EM_KEY_2;
1132 case 0x12: return EL_EM_KEY_4;
1133 case 0x13: return EL_EM_KEY_3;
1134 case 0x14: return EL_MAGIC_WALL;
1135 case 0x15: return EL_ROBOT_WHEEL;
1136 case 0x16: return EL_DYNAMITE;
1138 case 0x17: return EL_EM_KEY_1; /* EMC */
1139 case 0x18: return EL_BUG_UP; /* EMC */
1140 case 0x1a: return EL_DIAMOND; /* EMC */
1141 case 0x1b: return EL_EMERALD; /* EMC */
1142 case 0x25: return EL_NUT; /* EMC */
1143 case 0x80: return EL_EMPTY; /* EMC */
1144 case 0x85: return EL_EM_KEY_1; /* EMC */
1145 case 0x86: return EL_EM_KEY_2; /* EMC */
1146 case 0x87: return EL_EM_KEY_4; /* EMC */
1147 case 0x88: return EL_EM_KEY_3; /* EMC */
1148 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1149 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1150 case 0xaf: return EL_DYNAMITE; /* EMC */
1151 case 0xbd: return EL_SAND; /* EMC */
1154 Error(ERR_WARN, "invalid level element %d", element);
1155 return EL_CHAR_QUESTION;
1159 static int map_em_element_field(int element)
1161 if (element >= 0xc8 && element <= 0xe1)
1162 return EL_CHAR_A + (element - 0xc8);
1163 else if (element >= 0xe2 && element <= 0xeb)
1164 return EL_CHAR_0 + (element - 0xe2);
1168 case 0x00: return EL_ROCK;
1169 case 0x02: return EL_DIAMOND;
1170 case 0x03: return EL_DIAMOND;
1171 case 0x04: return EL_ROBOT;
1172 case 0x05: return EL_ROBOT; /* EMC */
1173 case 0x08: return EL_SPACESHIP_UP;
1174 case 0x09: return EL_SPACESHIP_RIGHT;
1175 case 0x0a: return EL_SPACESHIP_DOWN;
1176 case 0x0b: return EL_SPACESHIP_LEFT;
1177 case 0x0c: return EL_SPACESHIP_UP;
1178 case 0x0d: return EL_SPACESHIP_RIGHT;
1179 case 0x0e: return EL_SPACESHIP_DOWN;
1180 case 0x0f: return EL_SPACESHIP_LEFT;
1181 case 0x10: return EL_BOMB;
1182 case 0x12: return EL_EMERALD;
1183 case 0x13: return EL_EMERALD;
1184 case 0x14: return EL_BUG_UP;
1185 case 0x15: return EL_BUG_RIGHT;
1186 case 0x16: return EL_BUG_DOWN;
1187 case 0x17: return EL_BUG_LEFT;
1188 case 0x18: return EL_BUG_UP;
1189 case 0x19: return EL_BUG_RIGHT;
1190 case 0x1a: return EL_BUG_DOWN;
1191 case 0x1b: return EL_BUG_LEFT;
1192 case 0x1c: return EL_AMOEBA_DROP;
1193 case 0x20: return EL_ROCK;
1194 case 0x24: return EL_MAGIC_WALL;
1195 case 0x25: return EL_NUT;
1197 /* looks like magic wheel, but is _always_ activated */
1198 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1200 case 0x29: return EL_YAMYAM;
1201 case 0x2a: return EL_YAMYAM;
1202 case 0x2b: return EL_YAMYAM; /* EMC */
1203 case 0x2c: return EL_YAMYAM; /* EMC */
1204 case 0x2d: return EL_QUICKSAND_FULL;
1205 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1206 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1207 case 0x3b: return EL_DYNAMITE_ACTIVE;
1208 case 0x3c: return EL_DYNAMITE_ACTIVE;
1209 case 0x3d: return EL_DYNAMITE_ACTIVE;
1210 case 0x3e: return EL_DYNAMITE_ACTIVE;
1211 case 0x3f: return EL_ACID_POOL_BOTTOM;
1212 case 0x40: return EL_EXIT_OPEN;
1213 case 0x41: return EL_EXIT_OPEN;
1214 case 0x42: return EL_EXIT_OPEN;
1215 case 0x43: return EL_BALLOON;
1216 case 0x4e: return EL_INVISIBLE_WALL;
1217 case 0x65: return EL_ACID; /* EMC */
1218 case 0x73: return EL_SAND; /* EMC */
1219 case 0x74: return EL_STEELWALL;
1220 case 0x7b: return EL_ACID;
1221 case 0x80: return EL_EMPTY;
1222 case 0x81: return EL_WALL_SLIPPERY;
1223 case 0x82: return EL_SAND;
1224 case 0x83: return EL_STEELWALL;
1225 case 0x84: return EL_WALL;
1226 case 0x85: return EL_EM_KEY_1;
1227 case 0x86: return EL_EM_KEY_2;
1228 case 0x87: return EL_EM_KEY_4;
1229 case 0x88: return EL_EM_KEY_3;
1230 case 0x89: return EL_EM_GATE_1;
1231 case 0x8a: return EL_EM_GATE_2;
1232 case 0x8b: return EL_EM_GATE_4;
1233 case 0x8c: return EL_EM_GATE_3;
1234 case 0x8d: return EL_INVISIBLE_WALL; /* EMC */
1235 case 0x8e: return EL_EM_GATE_1_GRAY;
1236 case 0x8f: return EL_EM_GATE_2_GRAY;
1237 case 0x90: return EL_EM_GATE_4_GRAY;
1238 case 0x91: return EL_EM_GATE_3_GRAY;
1239 case 0x92: return EL_MAGIC_WALL;
1240 case 0x94: return EL_QUICKSAND_EMPTY;
1241 case 0x95: return EL_ACID_POOL_TOPLEFT;
1242 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1243 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1244 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1245 case 0x99: return EL_ACID;
1246 case 0x9a: return EL_AMOEBA_DEAD;
1247 case 0x9b: return EL_AMOEBA_DEAD;
1248 case 0x9c: return EL_AMOEBA_DEAD;
1249 case 0x9d: return EL_AMOEBA_DEAD;
1250 case 0x9e: return EL_EXIT_CLOSED;
1251 case 0x9f: return EL_CHAR_LESS; /* EMC */
1252 case 0x93: return EL_ROBOT_WHEEL;
1254 /* looks like normal dust, but behaves like wall */
1255 case 0xa0: return EL_WALL; /* EMC */
1257 case 0xa8: return EL_EMC_WALL_1; /* EMC */
1258 case 0xa9: return EL_EMC_WALL_2; /* EMC */
1259 case 0xaa: return EL_EMC_WALL_3; /* EMC */
1260 case 0xab: return EL_EMC_WALL_7; /* EMC */
1261 case 0xae: return EL_CHAR_MINUS; /* EMC */
1262 case 0xaf: return EL_DYNAMITE;
1263 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */
1264 case 0xb1: return EL_EMC_WALL_8; /* EMC */
1266 /* (exact steel wall) */
1267 case 0xb3: return EL_STEELWALL; /* EMC */
1269 case 0xb4: return EL_WALL_SLIPPERY; /* EMC */
1270 case 0xb5: return EL_EMC_WALL_6; /* EMC */
1271 case 0xb6: return EL_EMC_WALL_5; /* EMC */
1272 case 0xb7: return EL_EMC_WALL_4; /* EMC */
1273 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1274 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1275 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1276 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1277 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1278 case 0xbd: return EL_SAND; /* EMC */
1279 case 0xec: return EL_CHAR_PERIOD;
1280 case 0xed: return EL_CHAR_EXCLAM;
1281 case 0xee: return EL_CHAR_COLON;
1282 case 0xef: return EL_CHAR_QUESTION;
1283 case 0xf0: return EL_CHAR_GREATER;
1284 case 0xf1: return EL_CHAR_COPYRIGHT;
1285 case 0xfe: return EL_PLAYER_1;
1286 case 0xff: return EL_PLAYER_2;
1289 Error(ERR_WARN, "invalid level element %d", element);
1290 return EL_CHAR_QUESTION;
1294 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1295 struct LevelFileInfo *level_file_info)
1297 char *filename = level_file_info->filename;
1299 unsigned char body[40][64];
1300 unsigned char *leveldata = &body[0][0];
1301 unsigned char *header = &leveldata[2048];
1302 unsigned char code0 = 0x65;
1303 unsigned char code1 = 0x11;
1304 boolean level_is_crypted = FALSE;
1305 int nr = level_file_info->nr;
1309 /* always start with reliable default values */
1310 setLevelInfoToDefaults(level);
1312 if (!(file = fopen(filename, MODE_READ)))
1314 level->no_level_file = TRUE;
1316 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1321 for(i = 0; i < 2106; i++)
1322 leveldata[i] = fgetc(file);
1326 /* check if level data is crypted by testing against known starting bytes
1327 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1329 if ((leveldata[0] == 0xf1 ||
1330 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1332 level_is_crypted = TRUE;
1334 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1335 leveldata[0] = 0xf1;
1338 if (level_is_crypted) /* decode crypted level data */
1340 for(i = 0; i < 2106; i++)
1342 leveldata[i] ^= code0;
1343 leveldata[i] -= code1;
1345 code0 = (code0 + 7) & 0xff;
1352 level->time = header[46] * 10;
1353 level->gems_needed = header[47];
1355 /* The original Emerald Mine levels have their level number stored
1356 at the second byte of the level file...
1357 Do not trust this information at other level files, e.g. EMC,
1358 but correct it anyway (normally the first row is completely
1359 steel wall, so the correction does not hurt anyway). */
1361 if (leveldata[1] == nr)
1362 leveldata[1] = leveldata[2]; /* correct level number field */
1364 sprintf(level->name, "Level %d", nr);
1366 level->score[SC_EMERALD] = header[36];
1367 level->score[SC_DIAMOND] = header[37];
1368 level->score[SC_ROBOT] = header[38];
1369 level->score[SC_SPACESHIP] = header[39];
1370 level->score[SC_BUG] = header[40];
1371 level->score[SC_YAMYAM] = header[41];
1372 level->score[SC_NUT] = header[42];
1373 level->score[SC_DYNAMITE] = header[43];
1374 level->score[SC_TIME_BONUS] = header[44];
1376 level->num_yamyam_contents = 4;
1378 for(i = 0; i < level->num_yamyam_contents; i++)
1379 for(y = 0; y < 3; y++)
1380 for(x = 0; x < 3; x++)
1381 level->yamyam_content[i][x][y] =
1382 map_em_element_yam(header[i * 9 + y * 3 + x]);
1384 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1385 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1386 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1387 level->amoeba_content = EL_DIAMOND;
1389 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1391 int new_element = map_em_element_field(body[y][x]);
1393 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1394 new_element = EL_AMOEBA_WET;
1396 level->field[x][y] = new_element;
1399 jx = (header[48] * 256 + header[49]) % 64;
1400 jy = (header[48] * 256 + header[49]) / 64;
1401 level->field[jx][jy] = EL_PLAYER_1;
1403 jx = (header[50] * 256 + header[51]) % 64;
1404 jy = (header[50] * 256 + header[51]) / 64;
1405 level->field[jx][jy] = EL_PLAYER_2;
1408 void LoadLevelFromFileInfo(struct LevelInfo *level,
1409 struct LevelFileInfo *level_file_info)
1411 switch (level_file_info->type)
1413 case LEVEL_FILE_TYPE_RND:
1414 LoadLevelFromFileInfo_RND(level, level_file_info);
1417 case LEVEL_FILE_TYPE_EM:
1418 LoadLevelFromFileInfo_EM(level, level_file_info);
1422 LoadLevelFromFileInfo_RND(level, level_file_info);
1427 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1429 static struct LevelFileInfo level_file_info;
1431 level_file_info.nr = 0; /* unknown */
1432 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1433 level_file_info.filename = filename;
1435 LoadLevelFromFileInfo(level, &level_file_info);
1438 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1440 if (leveldir_current == NULL) /* only when dumping level */
1444 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1447 /* determine correct game engine version of current level */
1449 if (!leveldir_current->latest_engine)
1451 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1452 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1453 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1457 printf("\n::: This level is private or contributed: '%s'\n", filename);
1461 printf("\n::: Use the stored game engine version for this level\n");
1464 /* For all levels which are not forced to use the latest game engine
1465 version (normally user contributed, private and undefined levels),
1466 use the version of the game engine the levels were created for.
1468 Since 2.0.1, the game engine version is now directly stored
1469 in the level file (chunk "VERS"), so there is no need anymore
1470 to set the game version from the file version (except for old,
1471 pre-2.0 levels, where the game version is still taken from the
1472 file format version used to store the level -- see above). */
1474 /* do some special adjustments to support older level versions */
1475 if (level->file_version == FILE_VERSION_1_0)
1477 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1478 Error(ERR_WARN, "using high speed movement for player");
1480 /* player was faster than monsters in (pre-)1.0 levels */
1481 level->double_speed = TRUE;
1484 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1485 if (level->game_version == VERSION_IDENT(2,0,1,0))
1486 level->em_slippery_gems = TRUE;
1491 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1492 leveldir_current->sort_priority, filename);
1496 printf("\n::: Use latest game engine version for this level.\n");
1499 /* For all levels which are forced to use the latest game engine version
1500 (normally all but user contributed, private and undefined levels), set
1501 the game engine version to the actual version; this allows for actual
1502 corrections in the game engine to take effect for existing, converted
1503 levels (from "classic" or other existing games) to make the emulation
1504 of the corresponding game more accurate, while (hopefully) not breaking
1505 existing levels created from other players. */
1508 printf("::: changing engine from %d to %d\n",
1509 level->game_version, GAME_VERSION_ACTUAL);
1512 level->game_version = GAME_VERSION_ACTUAL;
1514 /* Set special EM style gems behaviour: EM style gems slip down from
1515 normal, steel and growing wall. As this is a more fundamental change,
1516 it seems better to set the default behaviour to "off" (as it is more
1517 natural) and make it configurable in the level editor (as a property
1518 of gem style elements). Already existing converted levels (neither
1519 private nor contributed levels) are changed to the new behaviour. */
1521 if (level->file_version < FILE_VERSION_2_0)
1522 level->em_slippery_gems = TRUE;
1526 printf("::: => %d\n", level->game_version);
1530 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1534 /* map custom element change events that have changed in newer versions
1535 (these following values were accidentally changed in version 3.0.1) */
1536 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1538 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1540 int element = EL_CUSTOM_START + i;
1542 /* order of checking and copying events to be mapped is important */
1543 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1545 if (HAS_CHANGE_EVENT(element, j - 2))
1547 SET_CHANGE_EVENT(element, j - 2, FALSE);
1548 SET_CHANGE_EVENT(element, j, TRUE);
1552 /* order of checking and copying events to be mapped is important */
1553 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1555 if (HAS_CHANGE_EVENT(element, j - 1))
1557 SET_CHANGE_EVENT(element, j - 1, FALSE);
1558 SET_CHANGE_EVENT(element, j, TRUE);
1564 /* some custom element change events get mapped since version 3.0.3 */
1565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1567 int element = EL_CUSTOM_START + i;
1569 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1570 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1572 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1573 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1575 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1579 /* initialize "can_change" field for old levels with only one change page */
1580 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1584 int element = EL_CUSTOM_START + i;
1586 if (CAN_CHANGE(element))
1587 element_info[element].change->can_change = TRUE;
1592 /* set default push delay values (corrected since version 3.0.7-1) */
1593 if (level->game_version < VERSION_IDENT(3,0,7,1))
1595 game.default_push_delay_fixed = 2;
1596 game.default_push_delay_random = 8;
1600 game.default_push_delay_fixed = 8;
1601 game.default_push_delay_random = 8;
1604 /* set uninitialized push delay values of custom elements in older levels */
1605 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1607 int element = EL_CUSTOM_START + i;
1609 if (element_info[element].push_delay_fixed == -1)
1610 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1611 if (element_info[element].push_delay_random == -1)
1612 element_info[element].push_delay_random = game.default_push_delay_random;
1616 /* initialize element properties for level editor etc. */
1617 InitElementPropertiesEngine(level->game_version);
1620 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1624 /* map elements that have changed in newer versions */
1625 for (y = 0; y < level->fieldy; y++)
1627 for (x = 0; x < level->fieldx; x++)
1629 int element = level->field[x][y];
1631 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1633 /* map game font elements */
1634 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1635 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1636 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1637 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1640 if (level->game_version < VERSION_IDENT(3,0,0,0))
1642 /* map Supaplex gravity tube elements */
1643 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1644 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1645 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1646 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1650 level->field[x][y] = element;
1654 /* copy elements to runtime playfield array */
1655 for (x = 0; x < MAX_LEV_FIELDX; x++)
1656 for (y = 0; y < MAX_LEV_FIELDY; y++)
1657 Feld[x][y] = level->field[x][y];
1659 /* initialize level size variables for faster access */
1660 lev_fieldx = level->fieldx;
1661 lev_fieldy = level->fieldy;
1663 /* determine border element for this level */
1667 void LoadLevelTemplate(int nr)
1670 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1671 char *filename = level_file_info->filename;
1673 LoadLevelFromFileInfo(&level_template, level_file_info);
1675 char *filename = getDefaultLevelFilename(nr);
1677 LoadLevelFromFilename_RND(&level_template, filename);
1680 LoadLevel_InitVersion(&level, filename);
1681 LoadLevel_InitElements(&level, filename);
1683 ActivateLevelTemplate();
1686 void LoadLevel(int nr)
1689 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1690 char *filename = level_file_info->filename;
1692 LoadLevelFromFileInfo(&level, level_file_info);
1694 char *filename = getLevelFilename(nr);
1696 LoadLevelFromFilename_RND(&level, filename);
1699 if (level.use_custom_template)
1700 LoadLevelTemplate(-1);
1703 LoadLevel_InitVersion(&level, filename);
1704 LoadLevel_InitElements(&level, filename);
1705 LoadLevel_InitPlayfield(&level, filename);
1707 LoadLevel_InitLevel(&level, filename);
1711 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1713 putFileVersion(file, level->file_version);
1714 putFileVersion(file, level->game_version);
1717 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1721 putFile8Bit(file, level->fieldx);
1722 putFile8Bit(file, level->fieldy);
1724 putFile16BitBE(file, level->time);
1725 putFile16BitBE(file, level->gems_needed);
1727 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1728 putFile8Bit(file, level->name[i]);
1730 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1731 putFile8Bit(file, level->score[i]);
1733 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1734 for (y = 0; y < 3; y++)
1735 for (x = 0; x < 3; x++)
1736 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1737 level->yamyam_content[i][x][y]));
1738 putFile8Bit(file, level->amoeba_speed);
1739 putFile8Bit(file, level->time_magic_wall);
1740 putFile8Bit(file, level->time_wheel);
1741 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1742 level->amoeba_content));
1743 putFile8Bit(file, (level->double_speed ? 1 : 0));
1744 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1745 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1746 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1748 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1750 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1753 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1757 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1758 putFile8Bit(file, level->author[i]);
1761 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1765 for (y = 0; y < level->fieldy; y++)
1766 for (x = 0; x < level->fieldx; x++)
1767 if (level->encoding_16bit_field)
1768 putFile16BitBE(file, level->field[x][y]);
1770 putFile8Bit(file, level->field[x][y]);
1774 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1778 putFile8Bit(file, EL_YAMYAM);
1779 putFile8Bit(file, level->num_yamyam_contents);
1780 putFile8Bit(file, 0);
1781 putFile8Bit(file, 0);
1783 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1784 for (y = 0; y < 3; y++)
1785 for (x = 0; x < 3; x++)
1786 if (level->encoding_16bit_field)
1787 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1789 putFile8Bit(file, level->yamyam_content[i][x][y]);
1793 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1796 int num_contents, content_xsize, content_ysize;
1797 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1799 if (element == EL_YAMYAM)
1801 num_contents = level->num_yamyam_contents;
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] = level->yamyam_content[i][x][y];
1810 else if (element == EL_BD_AMOEBA)
1816 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1817 for (y = 0; y < 3; y++)
1818 for (x = 0; x < 3; x++)
1819 content_array[i][x][y] = EL_EMPTY;
1820 content_array[0][0][0] = level->amoeba_content;
1824 /* chunk header already written -- write empty chunk data */
1825 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1827 Error(ERR_WARN, "cannot save content for element '%d'", element);
1831 putFile16BitBE(file, element);
1832 putFile8Bit(file, num_contents);
1833 putFile8Bit(file, content_xsize);
1834 putFile8Bit(file, content_ysize);
1836 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1838 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1839 for (y = 0; y < 3; y++)
1840 for (x = 0; x < 3; x++)
1841 putFile16BitBE(file, content_array[i][x][y]);
1844 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1847 int envelope_nr = element - EL_ENVELOPE_1;
1848 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1850 putFile16BitBE(file, element);
1851 putFile16BitBE(file, envelope_len);
1852 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1853 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1855 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1857 for (i = 0; i < envelope_len; i++)
1858 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1862 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1863 int num_changed_custom_elements)
1867 putFile16BitBE(file, num_changed_custom_elements);
1869 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1871 int element = EL_CUSTOM_START + i;
1873 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1875 if (check < num_changed_custom_elements)
1877 putFile16BitBE(file, element);
1878 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1885 if (check != num_changed_custom_elements) /* should not happen */
1886 Error(ERR_WARN, "inconsistent number of custom element properties");
1891 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1892 int num_changed_custom_elements)
1896 putFile16BitBE(file, num_changed_custom_elements);
1898 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1900 int element = EL_CUSTOM_START + i;
1902 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1904 if (check < num_changed_custom_elements)
1906 putFile16BitBE(file, element);
1907 putFile16BitBE(file, element_info[element].change->target_element);
1914 if (check != num_changed_custom_elements) /* should not happen */
1915 Error(ERR_WARN, "inconsistent number of custom target elements");
1920 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1921 int num_changed_custom_elements)
1923 int i, j, x, y, check = 0;
1925 putFile16BitBE(file, num_changed_custom_elements);
1927 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1929 int element = EL_CUSTOM_START + i;
1931 if (element_info[element].modified_settings)
1933 if (check < num_changed_custom_elements)
1935 putFile16BitBE(file, element);
1937 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1938 putFile8Bit(file, element_info[element].description[j]);
1940 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1942 /* some free bytes for future properties and padding */
1943 WriteUnusedBytesToFile(file, 7);
1945 putFile8Bit(file, element_info[element].use_gfx_element);
1946 putFile16BitBE(file, element_info[element].gfx_element);
1948 putFile8Bit(file, element_info[element].collect_score);
1949 putFile8Bit(file, element_info[element].collect_count);
1951 putFile16BitBE(file, element_info[element].push_delay_fixed);
1952 putFile16BitBE(file, element_info[element].push_delay_random);
1953 putFile16BitBE(file, element_info[element].move_delay_fixed);
1954 putFile16BitBE(file, element_info[element].move_delay_random);
1956 putFile16BitBE(file, element_info[element].move_pattern);
1957 putFile8Bit(file, element_info[element].move_direction_initial);
1958 putFile8Bit(file, element_info[element].move_stepsize);
1960 for (y = 0; y < 3; y++)
1961 for (x = 0; x < 3; x++)
1962 putFile16BitBE(file, element_info[element].content[x][y]);
1964 putFile32BitBE(file, element_info[element].change->events);
1966 putFile16BitBE(file, element_info[element].change->target_element);
1968 putFile16BitBE(file, element_info[element].change->delay_fixed);
1969 putFile16BitBE(file, element_info[element].change->delay_random);
1970 putFile16BitBE(file, element_info[element].change->delay_frames);
1972 putFile16BitBE(file, element_info[element].change->trigger_element);
1974 putFile8Bit(file, element_info[element].change->explode);
1975 putFile8Bit(file, element_info[element].change->use_content);
1976 putFile8Bit(file, element_info[element].change->only_complete);
1977 putFile8Bit(file, element_info[element].change->use_random_change);
1979 putFile8Bit(file, element_info[element].change->random);
1980 putFile8Bit(file, element_info[element].change->power);
1982 for (y = 0; y < 3; y++)
1983 for (x = 0; x < 3; x++)
1984 putFile16BitBE(file, element_info[element].change->content[x][y]);
1986 putFile8Bit(file, element_info[element].slippery_type);
1988 /* some free bytes for future properties and padding */
1989 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1996 if (check != num_changed_custom_elements) /* should not happen */
1997 Error(ERR_WARN, "inconsistent number of custom element properties");
2001 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2003 struct ElementInfo *ei = &element_info[element];
2006 putFile16BitBE(file, element);
2008 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2009 putFile8Bit(file, ei->description[i]);
2011 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2012 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2014 putFile8Bit(file, ei->num_change_pages);
2016 /* some free bytes for future base property values and padding */
2017 WriteUnusedBytesToFile(file, 5);
2019 /* write custom property values */
2021 putFile8Bit(file, ei->use_gfx_element);
2022 putFile16BitBE(file, ei->gfx_element);
2024 putFile8Bit(file, ei->collect_score);
2025 putFile8Bit(file, ei->collect_count);
2027 putFile16BitBE(file, ei->push_delay_fixed);
2028 putFile16BitBE(file, ei->push_delay_random);
2029 putFile16BitBE(file, ei->move_delay_fixed);
2030 putFile16BitBE(file, ei->move_delay_random);
2032 putFile16BitBE(file, ei->move_pattern);
2033 putFile8Bit(file, ei->move_direction_initial);
2034 putFile8Bit(file, ei->move_stepsize);
2036 putFile8Bit(file, ei->slippery_type);
2038 for (y = 0; y < 3; y++)
2039 for (x = 0; x < 3; x++)
2040 putFile16BitBE(file, ei->content[x][y]);
2042 putFile16BitBE(file, ei->move_enter_element);
2043 putFile16BitBE(file, ei->move_leave_element);
2045 /* some free bytes for future custom property values and padding */
2046 WriteUnusedBytesToFile(file, 8);
2048 /* write change property values */
2050 for (i = 0; i < ei->num_change_pages; i++)
2052 struct ElementChangeInfo *change = &ei->change_page[i];
2054 putFile32BitBE(file, change->events);
2056 putFile16BitBE(file, change->target_element);
2058 putFile16BitBE(file, change->delay_fixed);
2059 putFile16BitBE(file, change->delay_random);
2060 putFile16BitBE(file, change->delay_frames);
2062 putFile16BitBE(file, change->trigger_element);
2064 putFile8Bit(file, change->explode);
2065 putFile8Bit(file, change->use_content);
2066 putFile8Bit(file, change->only_complete);
2067 putFile8Bit(file, change->use_random_change);
2069 putFile8Bit(file, change->random);
2070 putFile8Bit(file, change->power);
2072 for (y = 0; y < 3; y++)
2073 for (x = 0; x < 3; x++)
2074 putFile16BitBE(file, change->content[x][y]);
2076 putFile8Bit(file, change->can_change);
2078 putFile8Bit(file, change->sides);
2080 /* some free bytes for future change property values and padding */
2081 WriteUnusedBytesToFile(file, 8);
2085 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2087 struct ElementInfo *ei = &element_info[element];
2088 struct ElementGroupInfo *group = ei->group;
2091 putFile16BitBE(file, element);
2093 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2094 putFile8Bit(file, ei->description[i]);
2096 putFile8Bit(file, group->num_elements);
2098 putFile8Bit(file, ei->use_gfx_element);
2099 putFile16BitBE(file, ei->gfx_element);
2101 /* some free bytes for future values and padding */
2102 WriteUnusedBytesToFile(file, 4);
2104 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2105 putFile16BitBE(file, group->element[i]);
2108 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2110 int body_chunk_size;
2114 if (!(file = fopen(filename, MODE_WRITE)))
2116 Error(ERR_WARN, "cannot save level file '%s'", filename);
2120 level->file_version = FILE_VERSION_ACTUAL;
2121 level->game_version = GAME_VERSION_ACTUAL;
2123 /* check level field for 16-bit elements */
2124 level->encoding_16bit_field = FALSE;
2125 for (y = 0; y < level->fieldy; y++)
2126 for (x = 0; x < level->fieldx; x++)
2127 if (level->field[x][y] > 255)
2128 level->encoding_16bit_field = TRUE;
2130 /* check yamyam content for 16-bit elements */
2131 level->encoding_16bit_yamyam = FALSE;
2132 for (i = 0; i < level->num_yamyam_contents; i++)
2133 for (y = 0; y < 3; y++)
2134 for (x = 0; x < 3; x++)
2135 if (level->yamyam_content[i][x][y] > 255)
2136 level->encoding_16bit_yamyam = TRUE;
2138 /* check amoeba content for 16-bit elements */
2139 level->encoding_16bit_amoeba = FALSE;
2140 if (level->amoeba_content > 255)
2141 level->encoding_16bit_amoeba = TRUE;
2143 /* calculate size of "BODY" chunk */
2145 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2147 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2148 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2150 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2151 SaveLevel_VERS(file, level);
2153 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2154 SaveLevel_HEAD(file, level);
2156 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2157 SaveLevel_AUTH(file, level);
2159 putFileChunkBE(file, "BODY", body_chunk_size);
2160 SaveLevel_BODY(file, level);
2162 if (level->encoding_16bit_yamyam ||
2163 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2165 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2166 SaveLevel_CNT2(file, level, EL_YAMYAM);
2169 if (level->encoding_16bit_amoeba)
2171 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2172 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2175 /* check for envelope content */
2176 for (i = 0; i < 4; i++)
2178 if (strlen(level->envelope_text[i]) > 0)
2180 int envelope_len = strlen(level->envelope_text[i]) + 1;
2182 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2183 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2187 /* check for non-default custom elements (unless using template level) */
2188 if (!level->use_custom_template)
2190 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2192 int element = EL_CUSTOM_START + i;
2194 if (element_info[element].modified_settings)
2196 int num_change_pages = element_info[element].num_change_pages;
2198 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2199 SaveLevel_CUS4(file, level, element);
2204 /* check for non-default group elements (unless using template level) */
2205 if (!level->use_custom_template)
2207 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2209 int element = EL_GROUP_START + i;
2211 if (element_info[element].modified_settings)
2213 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2214 SaveLevel_GRP1(file, level, element);
2221 SetFilePermissions(filename, PERMS_PRIVATE);
2224 void SaveLevel(int nr)
2226 char *filename = getDefaultLevelFilename(nr);
2228 SaveLevelFromFilename(&level, filename);
2231 void SaveLevelTemplate()
2233 char *filename = getDefaultLevelFilename(-1);
2235 SaveLevelFromFilename(&level, filename);
2238 void DumpLevel(struct LevelInfo *level)
2240 printf_line("-", 79);
2241 printf("Level xxx (file version %08d, game version %08d)\n",
2242 level->file_version, level->game_version);
2243 printf_line("-", 79);
2245 printf("Level Author: '%s'\n", level->author);
2246 printf("Level Title: '%s'\n", level->name);
2248 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2250 printf("Level Time: %d seconds\n", level->time);
2251 printf("Gems needed: %d\n", level->gems_needed);
2253 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2254 printf("Time for Wheel: %d seconds\n", level->time_wheel);
2255 printf("Time for Light: %d seconds\n", level->time_light);
2256 printf("Time for Timegate: %d seconds\n", level->time_timegate);
2258 printf("Amoeba Speed: %d\n", level->amoeba_speed);
2260 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2261 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
2262 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2264 printf_line("-", 79);
2268 /* ========================================================================= */
2269 /* tape file functions */
2270 /* ========================================================================= */
2272 static void setTapeInfoToDefaults()
2276 /* always start with reliable default values (empty tape) */
2279 /* default values (also for pre-1.2 tapes) with only the first player */
2280 tape.player_participates[0] = TRUE;
2281 for (i = 1; i < MAX_PLAYERS; i++)
2282 tape.player_participates[i] = FALSE;
2284 /* at least one (default: the first) player participates in every tape */
2285 tape.num_participating_players = 1;
2287 tape.level_nr = level_nr;
2289 tape.changed = FALSE;
2291 tape.recording = FALSE;
2292 tape.playing = FALSE;
2293 tape.pausing = FALSE;
2296 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2298 tape->file_version = getFileVersion(file);
2299 tape->game_version = getFileVersion(file);
2304 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2308 tape->random_seed = getFile32BitBE(file);
2309 tape->date = getFile32BitBE(file);
2310 tape->length = getFile32BitBE(file);
2312 /* read header fields that are new since version 1.2 */
2313 if (tape->file_version >= FILE_VERSION_1_2)
2315 byte store_participating_players = getFile8Bit(file);
2318 /* since version 1.2, tapes store which players participate in the tape */
2319 tape->num_participating_players = 0;
2320 for (i = 0; i < MAX_PLAYERS; i++)
2322 tape->player_participates[i] = FALSE;
2324 if (store_participating_players & (1 << i))
2326 tape->player_participates[i] = TRUE;
2327 tape->num_participating_players++;
2331 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2333 engine_version = getFileVersion(file);
2334 if (engine_version > 0)
2335 tape->engine_version = engine_version;
2337 tape->engine_version = tape->game_version;
2343 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2345 int level_identifier_size;
2348 level_identifier_size = getFile16BitBE(file);
2350 tape->level_identifier =
2351 checked_realloc(tape->level_identifier, level_identifier_size);
2353 for (i = 0; i < level_identifier_size; i++)
2354 tape->level_identifier[i] = getFile8Bit(file);
2356 tape->level_nr = getFile16BitBE(file);
2358 chunk_size = 2 + level_identifier_size + 2;
2363 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2366 int chunk_size_expected =
2367 (tape->num_participating_players + 1) * tape->length;
2369 if (chunk_size_expected != chunk_size)
2371 ReadUnusedBytesFromFile(file, chunk_size);
2372 return chunk_size_expected;
2375 for (i = 0; i < tape->length; i++)
2377 if (i >= MAX_TAPELEN)
2380 for (j = 0; j < MAX_PLAYERS; j++)
2382 tape->pos[i].action[j] = MV_NO_MOVING;
2384 if (tape->player_participates[j])
2385 tape->pos[i].action[j] = getFile8Bit(file);
2388 tape->pos[i].delay = getFile8Bit(file);
2390 if (tape->file_version == FILE_VERSION_1_0)
2392 /* eliminate possible diagonal moves in old tapes */
2393 /* this is only for backward compatibility */
2395 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2396 byte action = tape->pos[i].action[0];
2397 int k, num_moves = 0;
2399 for (k = 0; k<4; k++)
2401 if (action & joy_dir[k])
2403 tape->pos[i + num_moves].action[0] = joy_dir[k];
2405 tape->pos[i + num_moves].delay = 0;
2414 tape->length += num_moves;
2417 else if (tape->file_version < FILE_VERSION_2_0)
2419 /* convert pre-2.0 tapes to new tape format */
2421 if (tape->pos[i].delay > 1)
2424 tape->pos[i + 1] = tape->pos[i];
2425 tape->pos[i + 1].delay = 1;
2428 for (j = 0; j < MAX_PLAYERS; j++)
2429 tape->pos[i].action[j] = MV_NO_MOVING;
2430 tape->pos[i].delay--;
2441 if (i != tape->length)
2442 chunk_size = (tape->num_participating_players + 1) * i;
2447 void LoadTapeFromFilename(char *filename)
2449 char cookie[MAX_LINE_LEN];
2450 char chunk_name[CHUNK_ID_LEN + 1];
2454 /* always start with reliable default values */
2455 setTapeInfoToDefaults();
2457 if (!(file = fopen(filename, MODE_READ)))
2460 getFileChunkBE(file, chunk_name, NULL);
2461 if (strcmp(chunk_name, "RND1") == 0)
2463 getFile32BitBE(file); /* not used */
2465 getFileChunkBE(file, chunk_name, NULL);
2466 if (strcmp(chunk_name, "TAPE") != 0)
2468 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2473 else /* check for pre-2.0 file format with cookie string */
2475 strcpy(cookie, chunk_name);
2476 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2477 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2478 cookie[strlen(cookie) - 1] = '\0';
2480 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2482 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2487 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2489 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2494 /* pre-2.0 tape files have no game version, so use file version here */
2495 tape.game_version = tape.file_version;
2498 if (tape.file_version < FILE_VERSION_1_2)
2500 /* tape files from versions before 1.2.0 without chunk structure */
2501 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2502 LoadTape_BODY(file, 2 * tape.length, &tape);
2510 int (*loader)(FILE *, int, struct TapeInfo *);
2514 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2515 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2516 { "INFO", -1, LoadTape_INFO },
2517 { "BODY", -1, LoadTape_BODY },
2521 while (getFileChunkBE(file, chunk_name, &chunk_size))
2525 while (chunk_info[i].name != NULL &&
2526 strcmp(chunk_name, chunk_info[i].name) != 0)
2529 if (chunk_info[i].name == NULL)
2531 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2532 chunk_name, filename);
2533 ReadUnusedBytesFromFile(file, chunk_size);
2535 else if (chunk_info[i].size != -1 &&
2536 chunk_info[i].size != chunk_size)
2538 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2539 chunk_size, chunk_name, filename);
2540 ReadUnusedBytesFromFile(file, chunk_size);
2544 /* call function to load this tape chunk */
2545 int chunk_size_expected =
2546 (chunk_info[i].loader)(file, chunk_size, &tape);
2548 /* the size of some chunks cannot be checked before reading other
2549 chunks first (like "HEAD" and "BODY") that contain some header
2550 information, so check them here */
2551 if (chunk_size_expected != chunk_size)
2553 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2554 chunk_size, chunk_name, filename);
2562 tape.length_seconds = GetTapeLength();
2565 printf("::: tape game version: %d\n", tape.game_version);
2566 printf("::: tape engine version: %d\n", tape.engine_version);
2570 void LoadTape(int nr)
2572 char *filename = getTapeFilename(nr);
2574 LoadTapeFromFilename(filename);
2577 void LoadSolutionTape(int nr)
2579 char *filename = getSolutionTapeFilename(nr);
2581 LoadTapeFromFilename(filename);
2584 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2586 putFileVersion(file, tape->file_version);
2587 putFileVersion(file, tape->game_version);
2590 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2593 byte store_participating_players = 0;
2595 /* set bits for participating players for compact storage */
2596 for (i = 0; i < MAX_PLAYERS; i++)
2597 if (tape->player_participates[i])
2598 store_participating_players |= (1 << i);
2600 putFile32BitBE(file, tape->random_seed);
2601 putFile32BitBE(file, tape->date);
2602 putFile32BitBE(file, tape->length);
2604 putFile8Bit(file, store_participating_players);
2606 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2607 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2609 putFileVersion(file, tape->engine_version);
2612 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2614 int level_identifier_size = strlen(tape->level_identifier) + 1;
2617 putFile16BitBE(file, level_identifier_size);
2619 for (i = 0; i < level_identifier_size; i++)
2620 putFile8Bit(file, tape->level_identifier[i]);
2622 putFile16BitBE(file, tape->level_nr);
2625 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2629 for (i = 0; i < tape->length; i++)
2631 for (j = 0; j < MAX_PLAYERS; j++)
2632 if (tape->player_participates[j])
2633 putFile8Bit(file, tape->pos[i].action[j]);
2635 putFile8Bit(file, tape->pos[i].delay);
2639 void SaveTape(int nr)
2641 char *filename = getTapeFilename(nr);
2643 boolean new_tape = TRUE;
2644 int num_participating_players = 0;
2645 int info_chunk_size;
2646 int body_chunk_size;
2649 InitTapeDirectory(leveldir_current->filename);
2651 /* if a tape still exists, ask to overwrite it */
2652 if (access(filename, F_OK) == 0)
2655 if (!Request("Replace old tape ?", REQ_ASK))
2659 if (!(file = fopen(filename, MODE_WRITE)))
2661 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2665 tape.file_version = FILE_VERSION_ACTUAL;
2666 tape.game_version = GAME_VERSION_ACTUAL;
2668 /* count number of participating players */
2669 for (i = 0; i < MAX_PLAYERS; i++)
2670 if (tape.player_participates[i])
2671 num_participating_players++;
2673 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2674 body_chunk_size = (num_participating_players + 1) * tape.length;
2676 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2677 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2679 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2680 SaveTape_VERS(file, &tape);
2682 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2683 SaveTape_HEAD(file, &tape);
2685 putFileChunkBE(file, "INFO", info_chunk_size);
2686 SaveTape_INFO(file, &tape);
2688 putFileChunkBE(file, "BODY", body_chunk_size);
2689 SaveTape_BODY(file, &tape);
2693 SetFilePermissions(filename, PERMS_PRIVATE);
2695 tape.changed = FALSE;
2698 Request("tape saved !", REQ_CONFIRM);
2701 void DumpTape(struct TapeInfo *tape)
2705 if (TAPE_IS_EMPTY(*tape))
2707 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2711 printf_line("-", 79);
2712 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2713 tape->level_nr, tape->file_version, tape->game_version);
2714 printf("Level series identifier: '%s'\n", tape->level_identifier);
2715 printf_line("-", 79);
2717 for (i = 0; i < tape->length; i++)
2719 if (i >= MAX_TAPELEN)
2722 printf("%03d: ", i);
2724 for (j = 0; j < MAX_PLAYERS; j++)
2726 if (tape->player_participates[j])
2728 int action = tape->pos[i].action[j];
2730 printf("%d:%02x ", j, action);
2731 printf("[%c%c%c%c|%c%c] - ",
2732 (action & JOY_LEFT ? '<' : ' '),
2733 (action & JOY_RIGHT ? '>' : ' '),
2734 (action & JOY_UP ? '^' : ' '),
2735 (action & JOY_DOWN ? 'v' : ' '),
2736 (action & JOY_BUTTON_1 ? '1' : ' '),
2737 (action & JOY_BUTTON_2 ? '2' : ' '));
2741 printf("(%03d)\n", tape->pos[i].delay);
2744 printf_line("-", 79);
2748 /* ========================================================================= */
2749 /* score file functions */
2750 /* ========================================================================= */
2752 void LoadScore(int nr)
2755 char *filename = getScoreFilename(nr);
2756 char cookie[MAX_LINE_LEN];
2757 char line[MAX_LINE_LEN];
2761 /* always start with reliable default values */
2762 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2764 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2765 highscore[i].Score = 0;
2768 if (!(file = fopen(filename, MODE_READ)))
2771 /* check file identifier */
2772 fgets(cookie, MAX_LINE_LEN, file);
2773 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2774 cookie[strlen(cookie) - 1] = '\0';
2776 if (!checkCookieString(cookie, SCORE_COOKIE))
2778 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2783 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2785 fscanf(file, "%d", &highscore[i].Score);
2786 fgets(line, MAX_LINE_LEN, file);
2788 if (line[strlen(line) - 1] == '\n')
2789 line[strlen(line) - 1] = '\0';
2791 for (line_ptr = line; *line_ptr; line_ptr++)
2793 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2795 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2796 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2805 void SaveScore(int nr)
2808 char *filename = getScoreFilename(nr);
2811 InitScoreDirectory(leveldir_current->filename);
2813 if (!(file = fopen(filename, MODE_WRITE)))
2815 Error(ERR_WARN, "cannot save score for level %d", nr);
2819 fprintf(file, "%s\n\n", SCORE_COOKIE);
2821 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2822 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2826 SetFilePermissions(filename, PERMS_PUBLIC);
2830 /* ========================================================================= */
2831 /* setup file functions */
2832 /* ========================================================================= */
2834 #define TOKEN_STR_PLAYER_PREFIX "player_"
2837 #define SETUP_TOKEN_PLAYER_NAME 0
2838 #define SETUP_TOKEN_SOUND 1
2839 #define SETUP_TOKEN_SOUND_LOOPS 2
2840 #define SETUP_TOKEN_SOUND_MUSIC 3
2841 #define SETUP_TOKEN_SOUND_SIMPLE 4
2842 #define SETUP_TOKEN_TOONS 5
2843 #define SETUP_TOKEN_SCROLL_DELAY 6
2844 #define SETUP_TOKEN_SOFT_SCROLLING 7
2845 #define SETUP_TOKEN_FADING 8
2846 #define SETUP_TOKEN_AUTORECORD 9
2847 #define SETUP_TOKEN_QUICK_DOORS 10
2848 #define SETUP_TOKEN_TEAM_MODE 11
2849 #define SETUP_TOKEN_HANDICAP 12
2850 #define SETUP_TOKEN_TIME_LIMIT 13
2851 #define SETUP_TOKEN_FULLSCREEN 14
2852 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2853 #define SETUP_TOKEN_GRAPHICS_SET 16
2854 #define SETUP_TOKEN_SOUNDS_SET 17
2855 #define SETUP_TOKEN_MUSIC_SET 18
2856 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2857 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2858 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2860 #define NUM_GLOBAL_SETUP_TOKENS 22
2863 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2864 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2865 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2866 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2867 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2868 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2869 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2870 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2871 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2872 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2873 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2874 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2876 #define NUM_EDITOR_SETUP_TOKENS 12
2878 /* shortcut setup */
2879 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2880 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2881 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2883 #define NUM_SHORTCUT_SETUP_TOKENS 3
2886 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2887 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2888 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2889 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2890 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2891 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2892 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2893 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2894 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2895 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
2896 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2897 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2898 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2899 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2900 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2901 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
2903 #define NUM_PLAYER_SETUP_TOKENS 16
2906 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2907 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2909 #define NUM_SYSTEM_SETUP_TOKENS 2
2912 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2914 #define NUM_OPTIONS_SETUP_TOKENS 1
2917 static struct SetupInfo si;
2918 static struct SetupEditorInfo sei;
2919 static struct SetupShortcutInfo ssi;
2920 static struct SetupInputInfo sii;
2921 static struct SetupSystemInfo syi;
2922 static struct OptionInfo soi;
2924 static struct TokenInfo global_setup_tokens[] =
2926 { TYPE_STRING, &si.player_name, "player_name" },
2927 { TYPE_SWITCH, &si.sound, "sound" },
2928 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2929 { TYPE_SWITCH, &si.sound_music, "background_music" },
2930 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2931 { TYPE_SWITCH, &si.toons, "toons" },
2932 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2933 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2934 { TYPE_SWITCH, &si.fading, "screen_fading" },
2935 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2936 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2937 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2938 { TYPE_SWITCH, &si.handicap, "handicap" },
2939 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2940 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2941 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2942 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2943 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2944 { TYPE_STRING, &si.music_set, "music_set" },
2945 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2946 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2947 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2950 static struct TokenInfo editor_setup_tokens[] =
2952 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2953 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2954 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2955 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2956 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2957 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2958 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2959 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2960 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2961 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2962 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2963 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2966 static struct TokenInfo shortcut_setup_tokens[] =
2968 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2969 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2970 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2973 static struct TokenInfo player_setup_tokens[] =
2975 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2976 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2977 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2978 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2979 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2980 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2981 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2982 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2983 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2984 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
2985 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2986 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2987 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2988 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2989 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2990 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
2993 static struct TokenInfo system_setup_tokens[] =
2995 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2996 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2999 static struct TokenInfo options_setup_tokens[] =
3001 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3004 static char *get_corrected_login_name(char *login_name)
3006 /* needed because player name must be a fixed length string */
3007 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3009 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3010 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3012 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3013 if (strchr(login_name_new, ' '))
3014 *strchr(login_name_new, ' ') = '\0';
3016 return login_name_new;
3019 static void setSetupInfoToDefaults(struct SetupInfo *si)
3023 si->player_name = get_corrected_login_name(getLoginName());
3026 si->sound_loops = TRUE;
3027 si->sound_music = TRUE;
3028 si->sound_simple = TRUE;
3030 si->double_buffering = TRUE;
3031 si->direct_draw = !si->double_buffering;
3032 si->scroll_delay = TRUE;
3033 si->soft_scrolling = TRUE;
3035 si->autorecord = TRUE;
3036 si->quick_doors = FALSE;
3037 si->team_mode = FALSE;
3038 si->handicap = TRUE;
3039 si->time_limit = TRUE;
3040 si->fullscreen = FALSE;
3041 si->ask_on_escape = TRUE;
3043 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3044 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3045 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3046 si->override_level_graphics = FALSE;
3047 si->override_level_sounds = FALSE;
3048 si->override_level_music = FALSE;
3050 si->editor.el_boulderdash = TRUE;
3051 si->editor.el_emerald_mine = TRUE;
3052 si->editor.el_more = TRUE;
3053 si->editor.el_sokoban = TRUE;
3054 si->editor.el_supaplex = TRUE;
3055 si->editor.el_diamond_caves = TRUE;
3056 si->editor.el_dx_boulderdash = TRUE;
3057 si->editor.el_chars = TRUE;
3058 si->editor.el_custom = TRUE;
3059 si->editor.el_custom_more = FALSE;
3061 si->editor.el_headlines = TRUE;
3062 si->editor.el_user_defined = FALSE;
3064 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3065 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3066 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3068 for (i = 0; i < MAX_PLAYERS; i++)
3070 si->input[i].use_joystick = FALSE;
3071 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3072 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3073 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3074 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3075 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3076 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3077 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3078 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3079 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3080 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3081 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3082 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3083 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3084 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3085 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3088 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3089 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3091 si->options.verbose = FALSE;
3094 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3098 if (!setup_file_hash)
3103 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3104 setSetupInfo(global_setup_tokens, i,
3105 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3110 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3111 setSetupInfo(editor_setup_tokens, i,
3112 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3115 /* shortcut setup */
3116 ssi = setup.shortcut;
3117 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3118 setSetupInfo(shortcut_setup_tokens, i,
3119 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3120 setup.shortcut = ssi;
3123 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3127 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3129 sii = setup.input[pnr];
3130 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3132 char full_token[100];
3134 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3135 setSetupInfo(player_setup_tokens, i,
3136 getHashEntry(setup_file_hash, full_token));
3138 setup.input[pnr] = sii;
3143 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3144 setSetupInfo(system_setup_tokens, i,
3145 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3149 soi = setup.options;
3150 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3151 setSetupInfo(options_setup_tokens, i,
3152 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3153 setup.options = soi;
3158 char *filename = getSetupFilename();
3159 SetupFileHash *setup_file_hash = NULL;
3161 /* always start with reliable default values */
3162 setSetupInfoToDefaults(&setup);
3164 setup_file_hash = loadSetupFileHash(filename);
3166 if (setup_file_hash)
3168 char *player_name_new;
3170 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3171 decodeSetupFileHash(setup_file_hash);
3173 setup.direct_draw = !setup.double_buffering;
3175 freeSetupFileHash(setup_file_hash);
3177 /* needed to work around problems with fixed length strings */
3178 player_name_new = get_corrected_login_name(setup.player_name);
3179 free(setup.player_name);
3180 setup.player_name = player_name_new;
3183 Error(ERR_WARN, "using default setup values");
3188 char *filename = getSetupFilename();
3192 InitUserDataDirectory();
3194 if (!(file = fopen(filename, MODE_WRITE)))
3196 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3200 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3201 getCookie("SETUP")));
3202 fprintf(file, "\n");
3206 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3208 /* just to make things nicer :) */
3209 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3210 i == SETUP_TOKEN_GRAPHICS_SET)
3211 fprintf(file, "\n");
3213 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3218 fprintf(file, "\n");
3219 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3220 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3222 /* shortcut setup */
3223 ssi = setup.shortcut;
3224 fprintf(file, "\n");
3225 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3226 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3229 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3233 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3234 fprintf(file, "\n");
3236 sii = setup.input[pnr];
3237 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3238 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3243 fprintf(file, "\n");
3244 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3245 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3248 soi = setup.options;
3249 fprintf(file, "\n");
3250 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3251 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3255 SetFilePermissions(filename, PERMS_PRIVATE);
3258 void LoadCustomElementDescriptions()
3260 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3261 SetupFileHash *setup_file_hash;
3264 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3266 if (element_info[i].custom_description != NULL)
3268 free(element_info[i].custom_description);
3269 element_info[i].custom_description = NULL;
3273 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3276 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3278 char *token = getStringCat2(element_info[i].token_name, ".name");
3279 char *value = getHashEntry(setup_file_hash, token);
3282 element_info[i].custom_description = getStringCopy(value);
3287 freeSetupFileHash(setup_file_hash);
3290 void LoadSpecialMenuDesignSettings()
3292 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3293 SetupFileHash *setup_file_hash;
3296 /* always start with reliable default values from default config */
3297 for (i = 0; image_config_vars[i].token != NULL; i++)
3298 for (j = 0; image_config[j].token != NULL; j++)
3299 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3300 *image_config_vars[i].value =
3301 get_auto_parameter_value(image_config_vars[i].token,
3302 image_config[j].value);
3304 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3307 /* special case: initialize with default values that may be overwritten */
3308 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3310 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3311 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3312 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3314 if (value_x != NULL)
3315 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3316 if (value_y != NULL)
3317 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3318 if (list_size != NULL)
3319 menu.list_size[i] = get_integer_from_string(list_size);
3322 /* read (and overwrite with) values that may be specified in config file */
3323 for (i = 0; image_config_vars[i].token != NULL; i++)
3325 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3328 *image_config_vars[i].value =
3329 get_auto_parameter_value(image_config_vars[i].token, value);
3332 freeSetupFileHash(setup_file_hash);
3335 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3337 char *filename = getEditorSetupFilename();
3338 SetupFileList *setup_file_list, *list;
3339 SetupFileHash *element_hash;
3340 int num_unknown_tokens = 0;
3343 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3346 element_hash = newSetupFileHash();
3348 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3349 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3351 /* determined size may be larger than needed (due to unknown elements) */
3353 for (list = setup_file_list; list != NULL; list = list->next)
3356 /* add space for up to 3 more elements for padding that may be needed */
3359 *elements = checked_malloc(*num_elements * sizeof(int));
3362 for (list = setup_file_list; list != NULL; list = list->next)
3364 char *value = getHashEntry(element_hash, list->token);
3368 (*elements)[(*num_elements)++] = atoi(value);
3372 if (num_unknown_tokens == 0)
3374 Error(ERR_RETURN_LINE, "-");
3375 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3376 Error(ERR_RETURN, "- config file: '%s'", filename);
3378 num_unknown_tokens++;
3381 Error(ERR_RETURN, "- token: '%s'", list->token);
3385 if (num_unknown_tokens > 0)
3386 Error(ERR_RETURN_LINE, "-");
3388 while (*num_elements % 4) /* pad with empty elements, if needed */
3389 (*elements)[(*num_elements)++] = EL_EMPTY;
3391 freeSetupFileList(setup_file_list);
3392 freeSetupFileHash(element_hash);
3396 for (i = 0; i < *num_elements; i++)
3397 printf("editor: element '%s' [%d]\n",
3398 element_info[(*elements)[i]].token_name, (*elements)[i]);
3402 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3405 SetupFileHash *setup_file_hash = NULL;
3406 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3407 char *filename_music, *filename_prefix, *filename_info;
3413 token_to_value_ptr[] =
3415 { "title_header", &tmp_music_file_info.title_header },
3416 { "artist_header", &tmp_music_file_info.artist_header },
3417 { "album_header", &tmp_music_file_info.album_header },
3418 { "year_header", &tmp_music_file_info.year_header },
3420 { "title", &tmp_music_file_info.title },
3421 { "artist", &tmp_music_file_info.artist },
3422 { "album", &tmp_music_file_info.album },
3423 { "year", &tmp_music_file_info.year },
3429 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3430 getCustomMusicFilename(basename));
3432 if (filename_music == NULL)
3435 /* ---------- try to replace file extension ---------- */
3437 filename_prefix = getStringCopy(filename_music);
3438 if (strrchr(filename_prefix, '.') != NULL)
3439 *strrchr(filename_prefix, '.') = '\0';
3440 filename_info = getStringCat2(filename_prefix, ".txt");
3443 printf("trying to load file '%s'...\n", filename_info);
3446 if (fileExists(filename_info))
3447 setup_file_hash = loadSetupFileHash(filename_info);
3449 free(filename_prefix);
3450 free(filename_info);
3452 if (setup_file_hash == NULL)
3454 /* ---------- try to add file extension ---------- */
3456 filename_prefix = getStringCopy(filename_music);
3457 filename_info = getStringCat2(filename_prefix, ".txt");
3460 printf("trying to load file '%s'...\n", filename_info);
3463 if (fileExists(filename_info))
3464 setup_file_hash = loadSetupFileHash(filename_info);
3466 free(filename_prefix);
3467 free(filename_info);
3470 if (setup_file_hash == NULL)
3473 /* ---------- music file info found ---------- */
3475 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3477 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3479 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3481 *token_to_value_ptr[i].value_ptr =
3482 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3485 tmp_music_file_info.basename = getStringCopy(basename);
3486 tmp_music_file_info.music = music;
3487 tmp_music_file_info.is_sound = is_sound;
3489 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3490 *new_music_file_info = tmp_music_file_info;
3492 return new_music_file_info;
3495 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3497 return get_music_file_info_ext(basename, music, FALSE);
3500 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3502 return get_music_file_info_ext(basename, sound, TRUE);
3505 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3506 char *basename, boolean is_sound)
3508 for (; list != NULL; list = list->next)
3509 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3515 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3517 return music_info_listed_ext(list, basename, FALSE);
3520 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3522 return music_info_listed_ext(list, basename, TRUE);
3525 void LoadMusicInfo()
3527 char *music_directory = getCustomMusicDirectory();
3528 int num_music = getMusicListSize();
3529 int num_music_noconf = 0;
3530 int num_sounds = getSoundListSize();
3532 struct dirent *dir_entry;
3533 struct FileInfo *music, *sound;
3534 struct MusicFileInfo *next, **new;
3537 while (music_file_info != NULL)
3539 next = music_file_info->next;
3541 checked_free(music_file_info->basename);
3543 checked_free(music_file_info->title_header);
3544 checked_free(music_file_info->artist_header);
3545 checked_free(music_file_info->album_header);
3546 checked_free(music_file_info->year_header);
3548 checked_free(music_file_info->title);
3549 checked_free(music_file_info->artist);
3550 checked_free(music_file_info->album);
3551 checked_free(music_file_info->year);
3553 free(music_file_info);
3555 music_file_info = next;
3558 new = &music_file_info;
3561 printf("::: num_music == %d\n", num_music);
3564 for (i = 0; i < num_music; i++)
3566 music = getMusicListEntry(i);
3569 printf("::: %d [%08x]\n", i, music->filename);
3572 if (music->filename == NULL)
3575 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3578 /* a configured file may be not recognized as music */
3579 if (!FileIsMusic(music->filename))
3583 printf("::: -> '%s' (configured)\n", music->filename);
3586 if (!music_info_listed(music_file_info, music->filename))
3588 *new = get_music_file_info(music->filename, i);
3590 new = &(*new)->next;
3594 if ((dir = opendir(music_directory)) == NULL)
3596 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3600 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
3602 char *basename = dir_entry->d_name;
3603 boolean music_already_used = FALSE;
3606 /* skip all music files that are configured in music config file */
3607 for (i = 0; i < num_music; i++)
3609 music = getMusicListEntry(i);
3611 if (music->filename == NULL)
3614 if (strcmp(basename, music->filename) == 0)
3616 music_already_used = TRUE;
3621 if (music_already_used)
3624 if (!FileIsMusic(basename))
3628 printf("::: -> '%s' (found in directory)\n", basename);
3631 if (!music_info_listed(music_file_info, basename))
3633 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3635 new = &(*new)->next;
3643 for (i = 0; i < num_sounds; i++)
3645 sound = getSoundListEntry(i);
3647 if (sound->filename == NULL)
3650 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3653 /* a configured file may be not recognized as sound */
3654 if (!FileIsSound(sound->filename))
3658 printf("::: -> '%s' (configured)\n", sound->filename);
3661 if (!sound_info_listed(music_file_info, sound->filename))
3663 *new = get_sound_file_info(sound->filename, i);
3665 new = &(*new)->next;
3671 for (next = music_file_info; next != NULL; next = next->next)
3672 printf("::: title == '%s'\n", next->title);
3676 void add_helpanim_entry(int element, int action, int direction, int delay,
3677 int *num_list_entries)
3679 struct HelpAnimInfo *new_list_entry;
3680 (*num_list_entries)++;
3683 checked_realloc(helpanim_info,
3684 *num_list_entries * sizeof(struct HelpAnimInfo));
3685 new_list_entry = &helpanim_info[*num_list_entries - 1];
3687 new_list_entry->element = element;
3688 new_list_entry->action = action;
3689 new_list_entry->direction = direction;
3690 new_list_entry->delay = delay;
3693 void print_unknown_token(char *filename, char *token, int token_nr)
3697 Error(ERR_RETURN_LINE, "-");
3698 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3699 Error(ERR_RETURN, "- config file: '%s'", filename);
3702 Error(ERR_RETURN, "- token: '%s'", token);
3705 void print_unknown_token_end(int token_nr)
3708 Error(ERR_RETURN_LINE, "-");
3711 void LoadHelpAnimInfo()
3713 char *filename = getHelpAnimFilename();
3714 SetupFileList *setup_file_list = NULL, *list;
3715 SetupFileHash *element_hash, *action_hash, *direction_hash;
3716 int num_list_entries = 0;
3717 int num_unknown_tokens = 0;
3720 if (fileExists(filename))
3721 setup_file_list = loadSetupFileList(filename);
3723 if (setup_file_list == NULL)
3725 /* use reliable default values from static configuration */
3726 SetupFileList *insert_ptr;
3728 insert_ptr = setup_file_list =
3729 newSetupFileList(helpanim_config[0].token,
3730 helpanim_config[0].value);
3732 for (i = 1; helpanim_config[i].token; i++)
3733 insert_ptr = addListEntry(insert_ptr,
3734 helpanim_config[i].token,
3735 helpanim_config[i].value);
3738 element_hash = newSetupFileHash();
3739 action_hash = newSetupFileHash();
3740 direction_hash = newSetupFileHash();
3742 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3743 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3745 for (i = 0; i < NUM_ACTIONS; i++)
3746 setHashEntry(action_hash, element_action_info[i].suffix,
3747 i_to_a(element_action_info[i].value));
3749 /* do not store direction index (bit) here, but direction value! */
3750 for (i = 0; i < NUM_DIRECTIONS; i++)
3751 setHashEntry(direction_hash, element_direction_info[i].suffix,
3752 i_to_a(1 << element_direction_info[i].value));
3754 for (list = setup_file_list; list != NULL; list = list->next)
3756 char *element_token, *action_token, *direction_token;
3757 char *element_value, *action_value, *direction_value;
3758 int delay = atoi(list->value);
3760 if (strcmp(list->token, "end") == 0)
3762 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3767 /* first try to break element into element/action/direction parts;
3768 if this does not work, also accept combined "element[.act][.dir]"
3769 elements (like "dynamite.active"), which are unique elements */
3771 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
3773 element_value = getHashEntry(element_hash, list->token);
3774 if (element_value != NULL) /* element found */
3775 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3779 /* no further suffixes found -- this is not an element */
3780 print_unknown_token(filename, list->token, num_unknown_tokens++);
3786 /* token has format "<prefix>.<something>" */
3788 action_token = strchr(list->token, '.'); /* suffix may be action ... */
3789 direction_token = action_token; /* ... or direction */
3791 element_token = getStringCopy(list->token);
3792 *strchr(element_token, '.') = '\0';
3794 element_value = getHashEntry(element_hash, element_token);
3796 if (element_value == NULL) /* this is no element */
3798 element_value = getHashEntry(element_hash, list->token);
3799 if (element_value != NULL) /* combined element found */
3800 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3803 print_unknown_token(filename, list->token, num_unknown_tokens++);
3805 free(element_token);
3810 action_value = getHashEntry(action_hash, action_token);
3812 if (action_value != NULL) /* action found */
3814 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3817 free(element_token);
3822 direction_value = getHashEntry(direction_hash, direction_token);
3824 if (direction_value != NULL) /* direction found */
3826 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3829 free(element_token);
3834 if (strchr(action_token + 1, '.') == NULL)
3836 /* no further suffixes found -- this is not an action nor direction */
3838 element_value = getHashEntry(element_hash, list->token);
3839 if (element_value != NULL) /* combined element found */
3840 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3843 print_unknown_token(filename, list->token, num_unknown_tokens++);
3845 free(element_token);
3850 /* token has format "<prefix>.<suffix>.<something>" */
3852 direction_token = strchr(action_token + 1, '.');
3854 action_token = getStringCopy(action_token);
3855 *strchr(action_token + 1, '.') = '\0';
3857 action_value = getHashEntry(action_hash, action_token);
3859 if (action_value == NULL) /* this is no action */
3861 element_value = getHashEntry(element_hash, list->token);
3862 if (element_value != NULL) /* combined element found */
3863 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3866 print_unknown_token(filename, list->token, num_unknown_tokens++);
3868 free(element_token);
3874 direction_value = getHashEntry(direction_hash, direction_token);
3876 if (direction_value != NULL) /* direction found */
3878 add_helpanim_entry(atoi(element_value), atoi(action_value),
3879 atoi(direction_value), delay, &num_list_entries);
3881 free(element_token);
3887 /* this is no direction */
3889 element_value = getHashEntry(element_hash, list->token);
3890 if (element_value != NULL) /* combined element found */
3891 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3894 print_unknown_token(filename, list->token, num_unknown_tokens++);
3896 free(element_token);
3900 print_unknown_token_end(num_unknown_tokens);
3902 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3903 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
3905 freeSetupFileList(setup_file_list);
3906 freeSetupFileHash(element_hash);
3907 freeSetupFileHash(action_hash);
3908 freeSetupFileHash(direction_hash);
3912 for (i = 0; i < num_list_entries; i++)
3913 printf("::: %d, %d, %d => %d\n",
3914 helpanim_info[i].element,
3915 helpanim_info[i].action,
3916 helpanim_info[i].direction,
3917 helpanim_info[i].delay);
3921 void LoadHelpTextInfo()
3923 char *filename = getHelpTextFilename();
3926 if (helptext_info != NULL)
3928 freeSetupFileHash(helptext_info);
3929 helptext_info = NULL;
3932 if (fileExists(filename))
3933 helptext_info = loadSetupFileHash(filename);
3935 if (helptext_info == NULL)
3937 /* use reliable default values from static configuration */
3938 helptext_info = newSetupFileHash();
3940 for (i = 0; helptext_config[i].token; i++)
3941 setHashEntry(helptext_info,
3942 helptext_config[i].token,
3943 helptext_config[i].value);
3948 BEGIN_HASH_ITERATION(helptext_info, itr)
3950 printf("::: '%s' => '%s'\n",
3951 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3953 END_HASH_ITERATION(hash, itr)