1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
40 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
45 /* file identifier strings */
46 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
47 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
48 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
50 /* values for level file type identifier */
51 #define LEVEL_FILE_TYPE_UNKNOWN 0
52 #define LEVEL_FILE_TYPE_RND 1
53 #define LEVEL_FILE_TYPE_EM 2
55 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
56 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
58 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
59 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
62 /* ========================================================================= */
63 /* level file functions */
64 /* ========================================================================= */
66 void setElementChangePages(struct ElementInfo *ei, int change_pages)
68 int change_page_size = sizeof(struct ElementChangeInfo);
70 ei->num_change_pages = MAX(1, change_pages);
73 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
75 if (ei->current_change_page >= ei->num_change_pages)
76 ei->current_change_page = ei->num_change_pages - 1;
78 ei->change = &ei->change_page[ei->current_change_page];
81 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
85 change->can_change = FALSE;
87 change->events = CE_BITMASK_DEFAULT;
88 change->sides = CH_SIDE_ANY;
90 change->target_element = EL_EMPTY_SPACE;
92 change->delay_fixed = 0;
93 change->delay_random = 0;
94 change->delay_frames = 1;
96 change->trigger_element = EL_EMPTY_SPACE;
98 change->explode = FALSE;
99 change->use_content = FALSE;
100 change->only_complete = FALSE;
101 change->use_random_change = FALSE;
102 change->random = 100;
103 change->power = CP_NON_DESTRUCTIVE;
105 for (x = 0; x < 3; x++)
106 for (y = 0; y < 3; y++)
107 change->content[x][y] = EL_EMPTY_SPACE;
109 change->direct_action = 0;
110 change->other_action = 0;
112 change->pre_change_function = NULL;
113 change->change_function = NULL;
114 change->post_change_function = NULL;
117 static void setLevelInfoToDefaults(struct LevelInfo *level)
121 level->file_version = FILE_VERSION_ACTUAL;
122 level->game_version = GAME_VERSION_ACTUAL;
124 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
125 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
126 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
128 level->fieldx = STD_LEV_FIELDX;
129 level->fieldy = STD_LEV_FIELDY;
131 for (x = 0; x < MAX_LEV_FIELDX; x++)
132 for (y = 0; y < MAX_LEV_FIELDY; y++)
133 level->field[x][y] = EL_SAND;
136 level->gems_needed = 0;
137 level->amoeba_speed = 10;
138 level->time_magic_wall = 10;
139 level->time_wheel = 10;
140 level->time_light = 10;
141 level->time_timegate = 10;
142 level->amoeba_content = EL_DIAMOND;
143 level->double_speed = FALSE;
144 level->initial_gravity = FALSE;
145 level->em_slippery_gems = FALSE;
147 level->use_custom_template = FALSE;
149 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
150 level->name[i] = '\0';
151 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
152 level->author[i] = '\0';
154 strcpy(level->name, NAMELESS_LEVEL_NAME);
155 strcpy(level->author, ANONYMOUS_NAME);
157 for (i = 0; i < 4; i++)
159 level->envelope_text[i][0] = '\0';
160 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
161 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
164 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
165 level->score[i] = 10;
167 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
168 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
169 for (x = 0; x < 3; x++)
170 for (y = 0; y < 3; y++)
171 level->yamyam_content[i][x][y] =
172 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
174 level->field[0][0] = EL_PLAYER_1;
175 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
177 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
179 setElementChangePages(&element_info[i], 1);
180 setElementChangeInfoToDefaults(element_info[i].change);
183 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
185 int element = EL_CUSTOM_START + i;
187 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
188 element_info[element].description[j] = '\0';
189 if (element_info[element].custom_description != NULL)
190 strncpy(element_info[element].description,
191 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
193 strcpy(element_info[element].description,
194 element_info[element].editor_description);
196 element_info[element].use_gfx_element = FALSE;
197 element_info[element].gfx_element = EL_EMPTY_SPACE;
199 element_info[element].collect_score = 10; /* special default */
200 element_info[element].collect_count = 1; /* special default */
202 element_info[element].push_delay_fixed = -1; /* initialize later */
203 element_info[element].push_delay_random = -1; /* initialize later */
204 element_info[element].move_delay_fixed = 0;
205 element_info[element].move_delay_random = 0;
207 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
208 element_info[element].move_direction_initial = MV_NO_MOVING;
209 element_info[element].move_stepsize = TILEX / 8;
211 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
213 for (x = 0; x < 3; x++)
214 for (y = 0; y < 3; y++)
215 element_info[element].content[x][y] = EL_EMPTY_SPACE;
217 element_info[element].access_type = 0;
218 element_info[element].access_layer = 0;
219 element_info[element].walk_to_action = 0;
220 element_info[element].smash_targets = 0;
221 element_info[element].deadliness = 0;
222 element_info[element].consistency = 0;
224 element_info[element].can_explode_by_fire = FALSE;
225 element_info[element].can_explode_smashed = FALSE;
226 element_info[element].can_explode_impact = FALSE;
228 element_info[element].current_change_page = 0;
230 /* start with no properties at all */
231 for (j = 0; j < NUM_EP_BITFIELDS; j++)
232 Properties[element][j] = EP_BITMASK_DEFAULT;
234 element_info[element].modified_settings = FALSE;
237 BorderElement = EL_STEELWALL;
239 level->no_level_file = FALSE;
241 if (leveldir_current == NULL) /* only when dumping level */
244 /* try to determine better author name than 'anonymous' */
245 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
247 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
248 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
252 switch (LEVELCLASS(leveldir_current))
254 case LEVELCLASS_TUTORIAL:
255 strcpy(level->author, PROGRAM_AUTHOR_STRING);
258 case LEVELCLASS_CONTRIB:
259 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
260 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
263 case LEVELCLASS_PRIVATE:
264 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
265 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
269 /* keep default value */
275 static void ActivateLevelTemplate()
277 /* Currently there is no special action needed to activate the template
278 data, because 'element_info' and 'Properties' overwrite the original
279 level data, while all other variables do not change. */
282 static char *getLevelFilenameFromBasename(char *basename)
284 static char *filename = NULL;
286 checked_free(filename);
288 filename = getPath2(getCurrentLevelDir(), basename);
293 static char *getSingleLevelBasename(int nr, int type)
295 static char basename[MAX_FILENAME_LEN];
299 case LEVEL_FILE_TYPE_RND:
301 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
303 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
306 case LEVEL_FILE_TYPE_EM:
307 sprintf(basename, "%d", nr);
311 strcpy(basename, UNDEFINED_FILENAME);
318 static char *getPackedLevelBasename(int type)
320 static char basename[MAX_FILENAME_LEN];
325 strcpy(basename, UNDEFINED_FILENAME);
332 static char *getSingleLevelFilename(int nr, int type)
334 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
337 static char *getPackedLevelFilename(int type)
339 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
342 char *getDefaultLevelFilename(int nr)
344 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
347 static struct LevelFileInfo *getLevelFileInfo(int nr)
349 static struct LevelFileInfo level_file_info;
351 level_file_info.nr = nr;
353 /* special case: level template */
356 level_file_info.type = LEVEL_FILE_TYPE_RND;
357 level_file_info.filename = getDefaultLevelFilename(nr);
359 return &level_file_info;
362 /* 1st try: check for native Rocks'n'Diamonds level file */
363 level_file_info.type = LEVEL_FILE_TYPE_RND;
364 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
365 if (fileExists(level_file_info.filename))
366 return &level_file_info;
368 /* 2nd try: check for classic Emerald Mine level file */
369 level_file_info.type = LEVEL_FILE_TYPE_EM;
370 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
371 if (fileExists(level_file_info.filename))
372 return &level_file_info;
374 /* no known level file found -- use default values */
375 level_file_info.type = LEVEL_FILE_TYPE_RND;
376 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
378 return &level_file_info;
381 /* ------------------------------------------------------------------------- */
382 /* functions for loading R'n'D level */
383 /* ------------------------------------------------------------------------- */
385 static int checkLevelElement(int element)
387 /* map some (historic, now obsolete) elements */
392 case EL_PLAYER_OBSOLETE:
393 element = EL_PLAYER_1;
396 case EL_KEY_OBSOLETE:
399 case EL_EM_KEY_1_FILE_OBSOLETE:
400 element = EL_EM_KEY_1;
403 case EL_EM_KEY_2_FILE_OBSOLETE:
404 element = EL_EM_KEY_2;
407 case EL_EM_KEY_3_FILE_OBSOLETE:
408 element = EL_EM_KEY_3;
411 case EL_EM_KEY_4_FILE_OBSOLETE:
412 element = EL_EM_KEY_4;
415 case EL_ENVELOPE_OBSOLETE:
416 element = EL_ENVELOPE_1;
424 if (element >= NUM_FILE_ELEMENTS)
426 Error(ERR_WARN, "invalid level element %d", element);
428 element = EL_CHAR_QUESTION;
433 if (element >= NUM_FILE_ELEMENTS)
435 Error(ERR_WARN, "invalid level element %d", element);
437 element = EL_CHAR_QUESTION;
439 else if (element == EL_PLAYER_OBSOLETE)
440 element = EL_PLAYER_1;
441 else if (element == EL_KEY_OBSOLETE)
448 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
450 level->file_version = getFileVersion(file);
451 level->game_version = getFileVersion(file);
456 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
460 level->fieldx = getFile8Bit(file);
461 level->fieldy = getFile8Bit(file);
463 level->time = getFile16BitBE(file);
464 level->gems_needed = getFile16BitBE(file);
466 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
467 level->name[i] = getFile8Bit(file);
468 level->name[MAX_LEVEL_NAME_LEN] = 0;
470 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
471 level->score[i] = getFile8Bit(file);
473 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
474 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
475 for (y = 0; y < 3; y++)
476 for (x = 0; x < 3; x++)
477 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
479 level->amoeba_speed = getFile8Bit(file);
480 level->time_magic_wall = getFile8Bit(file);
481 level->time_wheel = getFile8Bit(file);
482 level->amoeba_content = checkLevelElement(getFile8Bit(file));
483 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
484 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
485 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
486 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
488 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
490 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
495 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
499 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
500 level->author[i] = getFile8Bit(file);
501 level->author[MAX_LEVEL_NAME_LEN] = 0;
506 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
509 int chunk_size_expected = level->fieldx * level->fieldy;
511 /* Note: "chunk_size" was wrong before version 2.0 when elements are
512 stored with 16-bit encoding (and should be twice as big then).
513 Even worse, playfield data was stored 16-bit when only yamyam content
514 contained 16-bit elements and vice versa. */
516 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
517 chunk_size_expected *= 2;
519 if (chunk_size_expected != chunk_size)
521 ReadUnusedBytesFromFile(file, chunk_size);
522 return chunk_size_expected;
525 for (y = 0; y < level->fieldy; y++)
526 for (x = 0; x < level->fieldx; x++)
528 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
533 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
537 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
538 int chunk_size_expected = header_size + content_size;
540 /* Note: "chunk_size" was wrong before version 2.0 when elements are
541 stored with 16-bit encoding (and should be twice as big then).
542 Even worse, playfield data was stored 16-bit when only yamyam content
543 contained 16-bit elements and vice versa. */
545 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
546 chunk_size_expected += content_size;
548 if (chunk_size_expected != chunk_size)
550 ReadUnusedBytesFromFile(file, chunk_size);
551 return chunk_size_expected;
555 level->num_yamyam_contents = getFile8Bit(file);
559 /* correct invalid number of content fields -- should never happen */
560 if (level->num_yamyam_contents < 1 ||
561 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
562 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
564 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
565 for (y = 0; y < 3; y++)
566 for (x = 0; x < 3; x++)
567 level->yamyam_content[i][x][y] =
568 checkLevelElement(level->encoding_16bit_field ?
569 getFile16BitBE(file) : getFile8Bit(file));
573 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
577 int num_contents, content_xsize, content_ysize;
578 int content_array[MAX_ELEMENT_CONTENTS][3][3];
580 element = checkLevelElement(getFile16BitBE(file));
581 num_contents = getFile8Bit(file);
582 content_xsize = getFile8Bit(file);
583 content_ysize = getFile8Bit(file);
585 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
587 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
588 for (y = 0; y < 3; y++)
589 for (x = 0; x < 3; x++)
590 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
592 /* correct invalid number of content fields -- should never happen */
593 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
594 num_contents = STD_ELEMENT_CONTENTS;
596 if (element == EL_YAMYAM)
598 level->num_yamyam_contents = num_contents;
600 for (i = 0; i < num_contents; i++)
601 for (y = 0; y < 3; y++)
602 for (x = 0; x < 3; x++)
603 level->yamyam_content[i][x][y] = content_array[i][x][y];
605 else if (element == EL_BD_AMOEBA)
607 level->amoeba_content = content_array[0][0][0];
611 Error(ERR_WARN, "cannot load content for element '%d'", element);
617 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
623 int chunk_size_expected;
625 element = checkLevelElement(getFile16BitBE(file));
626 if (!IS_ENVELOPE(element))
627 element = EL_ENVELOPE_1;
629 envelope_nr = element - EL_ENVELOPE_1;
631 envelope_len = getFile16BitBE(file);
633 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
634 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
636 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
638 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
640 if (chunk_size_expected != chunk_size)
642 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
643 return chunk_size_expected;
646 for (i = 0; i < envelope_len; i++)
647 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
652 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
654 int num_changed_custom_elements = getFile16BitBE(file);
655 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
658 if (chunk_size_expected != chunk_size)
660 ReadUnusedBytesFromFile(file, chunk_size - 2);
661 return chunk_size_expected;
664 for (i = 0; i < num_changed_custom_elements; i++)
666 int element = getFile16BitBE(file);
667 int properties = getFile32BitBE(file);
669 if (IS_CUSTOM_ELEMENT(element))
670 Properties[element][EP_BITFIELD_BASE] = properties;
672 Error(ERR_WARN, "invalid custom element number %d", element);
678 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
680 int num_changed_custom_elements = getFile16BitBE(file);
681 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
684 if (chunk_size_expected != chunk_size)
686 ReadUnusedBytesFromFile(file, chunk_size - 2);
687 return chunk_size_expected;
690 for (i = 0; i < num_changed_custom_elements; i++)
692 int element = getFile16BitBE(file);
693 int custom_target_element = getFile16BitBE(file);
695 if (IS_CUSTOM_ELEMENT(element))
696 element_info[element].change->target_element = custom_target_element;
698 Error(ERR_WARN, "invalid custom element number %d", element);
704 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
706 int num_changed_custom_elements = getFile16BitBE(file);
707 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
710 if (chunk_size_expected != chunk_size)
712 ReadUnusedBytesFromFile(file, chunk_size - 2);
713 return chunk_size_expected;
716 for (i = 0; i < num_changed_custom_elements; i++)
718 int element = getFile16BitBE(file);
720 if (!IS_CUSTOM_ELEMENT(element))
722 Error(ERR_WARN, "invalid custom element number %d", element);
727 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
728 element_info[element].description[j] = getFile8Bit(file);
729 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
731 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
733 /* some free bytes for future properties and padding */
734 ReadUnusedBytesFromFile(file, 7);
736 element_info[element].use_gfx_element = getFile8Bit(file);
737 element_info[element].gfx_element =
738 checkLevelElement(getFile16BitBE(file));
740 element_info[element].collect_score = getFile8Bit(file);
741 element_info[element].collect_count = getFile8Bit(file);
743 element_info[element].push_delay_fixed = getFile16BitBE(file);
744 element_info[element].push_delay_random = getFile16BitBE(file);
745 element_info[element].move_delay_fixed = getFile16BitBE(file);
746 element_info[element].move_delay_random = getFile16BitBE(file);
748 element_info[element].move_pattern = getFile16BitBE(file);
749 element_info[element].move_direction_initial = getFile8Bit(file);
750 element_info[element].move_stepsize = getFile8Bit(file);
752 for (y = 0; y < 3; y++)
753 for (x = 0; x < 3; x++)
754 element_info[element].content[x][y] =
755 checkLevelElement(getFile16BitBE(file));
757 element_info[element].change->events = getFile32BitBE(file);
759 element_info[element].change->target_element =
760 checkLevelElement(getFile16BitBE(file));
762 element_info[element].change->delay_fixed = getFile16BitBE(file);
763 element_info[element].change->delay_random = getFile16BitBE(file);
764 element_info[element].change->delay_frames = getFile16BitBE(file);
766 element_info[element].change->trigger_element =
767 checkLevelElement(getFile16BitBE(file));
769 element_info[element].change->explode = getFile8Bit(file);
770 element_info[element].change->use_content = getFile8Bit(file);
771 element_info[element].change->only_complete = getFile8Bit(file);
772 element_info[element].change->use_random_change = getFile8Bit(file);
774 element_info[element].change->random = getFile8Bit(file);
775 element_info[element].change->power = getFile8Bit(file);
777 for (y = 0; y < 3; y++)
778 for (x = 0; x < 3; x++)
779 element_info[element].change->content[x][y] =
780 checkLevelElement(getFile16BitBE(file));
782 element_info[element].slippery_type = getFile8Bit(file);
784 /* some free bytes for future properties and padding */
785 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
787 /* mark that this custom element has been modified */
788 element_info[element].modified_settings = TRUE;
794 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
796 struct ElementInfo *ei;
797 int chunk_size_expected;
801 element = getFile16BitBE(file);
803 if (!IS_CUSTOM_ELEMENT(element))
805 Error(ERR_WARN, "invalid custom element number %d", element);
810 ei = &element_info[element];
812 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
813 ei->description[i] = getFile8Bit(file);
814 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
816 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
817 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
819 ei->num_change_pages = getFile8Bit(file);
821 /* some free bytes for future base property values and padding */
822 ReadUnusedBytesFromFile(file, 5);
824 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
825 if (chunk_size_expected != chunk_size)
827 ReadUnusedBytesFromFile(file, chunk_size - 48);
828 return chunk_size_expected;
831 /* read custom property values */
833 ei->use_gfx_element = getFile8Bit(file);
834 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
836 ei->collect_score = getFile8Bit(file);
837 ei->collect_count = getFile8Bit(file);
839 ei->push_delay_fixed = getFile16BitBE(file);
840 ei->push_delay_random = getFile16BitBE(file);
841 ei->move_delay_fixed = getFile16BitBE(file);
842 ei->move_delay_random = getFile16BitBE(file);
844 ei->move_pattern = getFile16BitBE(file);
845 ei->move_direction_initial = getFile8Bit(file);
846 ei->move_stepsize = getFile8Bit(file);
848 ei->slippery_type = getFile8Bit(file);
850 for (y = 0; y < 3; y++)
851 for (x = 0; x < 3; x++)
852 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
854 /* some free bytes for future custom property values and padding */
855 ReadUnusedBytesFromFile(file, 12);
857 /* read change property values */
859 setElementChangePages(ei, ei->num_change_pages);
861 for (i = 0; i < ei->num_change_pages; i++)
863 struct ElementChangeInfo *change = &ei->change_page[i];
865 /* always start with reliable default values */
866 setElementChangeInfoToDefaults(change);
868 change->events = getFile32BitBE(file);
870 change->target_element = checkLevelElement(getFile16BitBE(file));
872 change->delay_fixed = getFile16BitBE(file);
873 change->delay_random = getFile16BitBE(file);
874 change->delay_frames = getFile16BitBE(file);
876 change->trigger_element = checkLevelElement(getFile16BitBE(file));
878 change->explode = getFile8Bit(file);
879 change->use_content = getFile8Bit(file);
880 change->only_complete = getFile8Bit(file);
881 change->use_random_change = getFile8Bit(file);
883 change->random = getFile8Bit(file);
884 change->power = getFile8Bit(file);
886 for (y = 0; y < 3; y++)
887 for (x = 0; x < 3; x++)
888 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
890 change->can_change = getFile8Bit(file);
892 change->sides = getFile8Bit(file);
894 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
895 change->sides = CH_SIDE_ANY;
897 /* some free bytes for future change property values and padding */
898 ReadUnusedBytesFromFile(file, 8);
901 /* mark this custom element as modified */
902 ei->modified_settings = TRUE;
907 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
908 struct LevelFileInfo *level_file_info)
910 char *filename = level_file_info->filename;
911 char cookie[MAX_LINE_LEN];
912 char chunk_name[CHUNK_ID_LEN + 1];
916 /* always start with reliable default values */
917 setLevelInfoToDefaults(level);
919 if (!(file = fopen(filename, MODE_READ)))
921 level->no_level_file = TRUE;
923 if (level != &level_template)
924 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
929 getFileChunkBE(file, chunk_name, NULL);
930 if (strcmp(chunk_name, "RND1") == 0)
932 getFile32BitBE(file); /* not used */
934 getFileChunkBE(file, chunk_name, NULL);
935 if (strcmp(chunk_name, "CAVE") != 0)
937 Error(ERR_WARN, "unknown format of level file '%s'", filename);
942 else /* check for pre-2.0 file format with cookie string */
944 strcpy(cookie, chunk_name);
945 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
946 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
947 cookie[strlen(cookie) - 1] = '\0';
949 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
951 Error(ERR_WARN, "unknown format of level file '%s'", filename);
956 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
958 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
963 /* pre-2.0 level files have no game version, so use file version here */
964 level->game_version = level->file_version;
967 if (level->file_version < FILE_VERSION_1_2)
969 /* level files from versions before 1.2.0 without chunk structure */
970 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
971 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
979 int (*loader)(FILE *, int, struct LevelInfo *);
983 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
984 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
985 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
986 { "BODY", -1, LoadLevel_BODY },
987 { "CONT", -1, LoadLevel_CONT },
988 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
989 { "CNT3", -1, LoadLevel_CNT3 },
990 { "CUS1", -1, LoadLevel_CUS1 },
991 { "CUS2", -1, LoadLevel_CUS2 },
992 { "CUS3", -1, LoadLevel_CUS3 },
993 { "CUS4", -1, LoadLevel_CUS4 },
997 while (getFileChunkBE(file, chunk_name, &chunk_size))
1001 while (chunk_info[i].name != NULL &&
1002 strcmp(chunk_name, chunk_info[i].name) != 0)
1005 if (chunk_info[i].name == NULL)
1007 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1008 chunk_name, filename);
1009 ReadUnusedBytesFromFile(file, chunk_size);
1011 else if (chunk_info[i].size != -1 &&
1012 chunk_info[i].size != chunk_size)
1014 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1015 chunk_size, chunk_name, filename);
1016 ReadUnusedBytesFromFile(file, chunk_size);
1020 /* call function to load this level chunk */
1021 int chunk_size_expected =
1022 (chunk_info[i].loader)(file, chunk_size, level);
1024 /* the size of some chunks cannot be checked before reading other
1025 chunks first (like "HEAD" and "BODY") that contain some header
1026 information, so check them here */
1027 if (chunk_size_expected != chunk_size)
1029 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1030 chunk_size, chunk_name, filename);
1039 /* ------------------------------------------------------------------------- */
1040 /* functions for loading EM level */
1041 /* ------------------------------------------------------------------------- */
1043 static int map_em_element_yam(int element)
1047 case 0x00: return EL_EMPTY;
1048 case 0x01: return EL_EMERALD;
1049 case 0x02: return EL_DIAMOND;
1050 case 0x03: return EL_ROCK;
1051 case 0x04: return EL_ROBOT;
1052 case 0x05: return EL_SPACESHIP_UP;
1053 case 0x06: return EL_BOMB;
1054 case 0x07: return EL_BUG_UP;
1055 case 0x08: return EL_AMOEBA_DROP;
1056 case 0x09: return EL_NUT;
1057 case 0x0a: return EL_YAMYAM;
1058 case 0x0b: return EL_QUICKSAND_FULL;
1059 case 0x0c: return EL_SAND;
1060 case 0x0d: return EL_WALL_SLIPPERY;
1061 case 0x0e: return EL_STEELWALL;
1062 case 0x0f: return EL_WALL;
1063 case 0x10: return EL_EM_KEY_1;
1064 case 0x11: return EL_EM_KEY_2;
1065 case 0x12: return EL_EM_KEY_4;
1066 case 0x13: return EL_EM_KEY_3;
1067 case 0x14: return EL_MAGIC_WALL;
1068 case 0x15: return EL_ROBOT_WHEEL;
1069 case 0x16: return EL_DYNAMITE;
1071 case 0x17: return EL_EM_KEY_1; /* EMC */
1072 case 0x18: return EL_BUG_UP; /* EMC */
1073 case 0x1a: return EL_DIAMOND; /* EMC */
1074 case 0x1b: return EL_EMERALD; /* EMC */
1075 case 0x25: return EL_NUT; /* EMC */
1076 case 0x80: return EL_EMPTY; /* EMC */
1077 case 0x85: return EL_EM_KEY_1; /* EMC */
1078 case 0x86: return EL_EM_KEY_2; /* EMC */
1079 case 0x87: return EL_EM_KEY_4; /* EMC */
1080 case 0x88: return EL_EM_KEY_3; /* EMC */
1081 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1082 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1083 case 0xaf: return EL_DYNAMITE; /* EMC */
1084 case 0xbd: return EL_SAND; /* EMC */
1087 Error(ERR_WARN, "invalid level element %d", element);
1088 return EL_CHAR_QUESTION;
1092 static int map_em_element_field(int element)
1094 if (element >= 0xc8 && element <= 0xe1)
1095 return EL_CHAR_A + (element - 0xc8);
1096 else if (element >= 0xe2 && element <= 0xeb)
1097 return EL_CHAR_0 + (element - 0xe2);
1101 case 0x00: return EL_ROCK;
1102 case 0x02: return EL_DIAMOND;
1103 case 0x03: return EL_DIAMOND;
1104 case 0x04: return EL_ROBOT;
1105 case 0x05: return EL_ROBOT; /* EMC */
1106 case 0x08: return EL_SPACESHIP_UP;
1107 case 0x09: return EL_SPACESHIP_RIGHT;
1108 case 0x0a: return EL_SPACESHIP_DOWN;
1109 case 0x0b: return EL_SPACESHIP_LEFT;
1110 case 0x0c: return EL_SPACESHIP_UP;
1111 case 0x0d: return EL_SPACESHIP_RIGHT;
1112 case 0x0e: return EL_SPACESHIP_DOWN;
1113 case 0x0f: return EL_SPACESHIP_LEFT;
1114 case 0x10: return EL_BOMB;
1115 case 0x12: return EL_EMERALD;
1116 case 0x13: return EL_EMERALD;
1117 case 0x14: return EL_BUG_UP;
1118 case 0x15: return EL_BUG_RIGHT;
1119 case 0x16: return EL_BUG_DOWN;
1120 case 0x17: return EL_BUG_LEFT;
1121 case 0x18: return EL_BUG_UP;
1122 case 0x19: return EL_BUG_RIGHT;
1123 case 0x1a: return EL_BUG_DOWN;
1124 case 0x1b: return EL_BUG_LEFT;
1125 case 0x1c: return EL_AMOEBA_DROP;
1126 case 0x20: return EL_ROCK;
1127 case 0x24: return EL_MAGIC_WALL;
1128 case 0x25: return EL_NUT;
1130 /* looks like magic wheel, but is _always_ activated */
1131 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1133 case 0x29: return EL_YAMYAM;
1134 case 0x2a: return EL_YAMYAM;
1135 case 0x2b: return EL_YAMYAM; /* EMC */
1136 case 0x2c: return EL_YAMYAM; /* EMC */
1137 case 0x2d: return EL_QUICKSAND_FULL;
1138 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1139 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1140 case 0x3b: return EL_DYNAMITE_ACTIVE;
1141 case 0x3c: return EL_DYNAMITE_ACTIVE;
1142 case 0x3d: return EL_DYNAMITE_ACTIVE;
1143 case 0x3e: return EL_DYNAMITE_ACTIVE;
1144 case 0x3f: return EL_ACID_POOL_BOTTOM;
1145 case 0x40: return EL_EXIT_OPEN;
1146 case 0x41: return EL_EXIT_OPEN;
1147 case 0x42: return EL_EXIT_OPEN;
1148 case 0x43: return EL_BALLOON;
1149 case 0x4e: return EL_INVISIBLE_WALL;
1150 case 0x65: return EL_ACID; /* EMC */
1151 case 0x73: return EL_SAND; /* EMC */
1152 case 0x74: return EL_STEELWALL;
1153 case 0x7b: return EL_ACID;
1154 case 0x80: return EL_EMPTY;
1155 case 0x81: return EL_WALL_SLIPPERY;
1156 case 0x82: return EL_SAND;
1157 case 0x83: return EL_STEELWALL;
1158 case 0x84: return EL_WALL;
1159 case 0x85: return EL_EM_KEY_1;
1160 case 0x86: return EL_EM_KEY_2;
1161 case 0x87: return EL_EM_KEY_4;
1162 case 0x88: return EL_EM_KEY_3;
1163 case 0x89: return EL_EM_GATE_1;
1164 case 0x8a: return EL_EM_GATE_2;
1165 case 0x8b: return EL_EM_GATE_4;
1166 case 0x8c: return EL_EM_GATE_3;
1167 case 0x8d: return EL_INVISIBLE_WALL; /* EMC */
1168 case 0x8e: return EL_EM_GATE_1_GRAY;
1169 case 0x8f: return EL_EM_GATE_2_GRAY;
1170 case 0x90: return EL_EM_GATE_4_GRAY;
1171 case 0x91: return EL_EM_GATE_3_GRAY;
1172 case 0x92: return EL_MAGIC_WALL;
1173 case 0x94: return EL_QUICKSAND_EMPTY;
1174 case 0x95: return EL_ACID_POOL_TOPLEFT;
1175 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1176 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1177 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1178 case 0x99: return EL_ACID;
1179 case 0x9a: return EL_AMOEBA_DEAD;
1180 case 0x9b: return EL_AMOEBA_DEAD;
1181 case 0x9c: return EL_AMOEBA_DEAD;
1182 case 0x9d: return EL_AMOEBA_DEAD;
1183 case 0x9e: return EL_EXIT_CLOSED;
1184 case 0x9f: return EL_CHAR_LESS; /* EMC */
1185 case 0x93: return EL_ROBOT_WHEEL;
1187 /* looks like normal dust, but behaves like wall */
1188 case 0xa0: return EL_WALL; /* EMC */
1190 case 0xa8: return EL_EMC_WALL_1; /* EMC */
1191 case 0xa9: return EL_EMC_WALL_2; /* EMC */
1192 case 0xaa: return EL_EMC_WALL_3; /* EMC */
1193 case 0xab: return EL_EMC_WALL_7; /* EMC */
1194 case 0xae: return EL_CHAR_MINUS; /* EMC */
1195 case 0xaf: return EL_DYNAMITE;
1196 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */
1197 case 0xb1: return EL_EMC_WALL_8; /* EMC */
1199 /* (exact steel wall) */
1200 case 0xb3: return EL_STEELWALL; /* EMC */
1202 case 0xb4: return EL_WALL_SLIPPERY; /* EMC */
1203 case 0xb5: return EL_EMC_WALL_6; /* EMC */
1204 case 0xb6: return EL_EMC_WALL_5; /* EMC */
1205 case 0xb7: return EL_EMC_WALL_4; /* EMC */
1206 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1207 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1208 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1209 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1210 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1211 case 0xbd: return EL_SAND; /* EMC */
1212 case 0xec: return EL_CHAR_PERIOD;
1213 case 0xed: return EL_CHAR_EXCLAM;
1214 case 0xee: return EL_CHAR_COLON;
1215 case 0xef: return EL_CHAR_QUESTION;
1216 case 0xf0: return EL_CHAR_GREATER;
1217 case 0xf1: return EL_CHAR_COPYRIGHT;
1218 case 0xfe: return EL_PLAYER_1;
1219 case 0xff: return EL_PLAYER_2;
1222 Error(ERR_WARN, "invalid level element %d", element);
1223 return EL_CHAR_QUESTION;
1227 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1228 struct LevelFileInfo *level_file_info)
1230 char *filename = level_file_info->filename;
1232 unsigned char body[40][64];
1233 unsigned char *leveldata = &body[0][0];
1234 unsigned char *header = &leveldata[2048];
1235 unsigned char code0 = 0x65;
1236 unsigned char code1 = 0x11;
1237 boolean level_is_crypted = FALSE;
1238 int nr = level_file_info->nr;
1242 /* always start with reliable default values */
1243 setLevelInfoToDefaults(level);
1245 if (!(file = fopen(filename, MODE_READ)))
1247 level->no_level_file = TRUE;
1249 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1254 for(i = 0; i < 2106; i++)
1255 leveldata[i] = fgetc(file);
1259 /* check if level data is crypted by testing against known starting bytes
1260 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1262 if ((leveldata[0] == 0xf1 ||
1263 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1265 level_is_crypted = TRUE;
1267 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1268 leveldata[0] = 0xf1;
1271 if (level_is_crypted) /* decode crypted level data */
1273 for(i = 0; i < 2106; i++)
1275 leveldata[i] ^= code0;
1276 leveldata[i] -= code1;
1278 code0 = (code0 + 7) & 0xff;
1285 level->time = header[46] * 10;
1286 level->gems_needed = header[47];
1288 /* The original Emerald Mine levels have their level number stored
1289 at the second byte of the level file...
1290 Do not trust this information at other level files, e.g. EMC,
1291 but correct it anyway (normally the first row is completely
1292 steel wall, so the correction does not hurt anyway). */
1294 if (leveldata[1] == nr)
1295 leveldata[1] = leveldata[2]; /* correct level number field */
1297 sprintf(level->name, "Level %d", nr);
1299 level->score[SC_EMERALD] = header[36];
1300 level->score[SC_DIAMOND] = header[37];
1301 level->score[SC_ROBOT] = header[38];
1302 level->score[SC_SPACESHIP] = header[39];
1303 level->score[SC_BUG] = header[40];
1304 level->score[SC_YAMYAM] = header[41];
1305 level->score[SC_NUT] = header[42];
1306 level->score[SC_DYNAMITE] = header[43];
1307 level->score[SC_TIME_BONUS] = header[44];
1309 level->num_yamyam_contents = 4;
1311 for(i = 0; i < level->num_yamyam_contents; i++)
1312 for(y = 0; y < 3; y++)
1313 for(x = 0; x < 3; x++)
1314 level->yamyam_content[i][x][y] =
1315 map_em_element_yam(header[i * 9 + y * 3 + x]);
1317 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1318 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1319 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1320 level->amoeba_content = EL_DIAMOND;
1322 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1324 int new_element = map_em_element_field(body[y][x]);
1326 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1327 new_element = EL_AMOEBA_WET;
1329 level->field[x][y] = new_element;
1332 jx = (header[48] * 256 + header[49]) % 64;
1333 jy = (header[48] * 256 + header[49]) / 64;
1334 level->field[jx][jy] = EL_PLAYER_1;
1336 jx = (header[50] * 256 + header[51]) % 64;
1337 jy = (header[50] * 256 + header[51]) / 64;
1338 level->field[jx][jy] = EL_PLAYER_2;
1341 void LoadLevelFromFileInfo(struct LevelInfo *level,
1342 struct LevelFileInfo *level_file_info)
1344 switch (level_file_info->type)
1346 case LEVEL_FILE_TYPE_RND:
1347 LoadLevelFromFileInfo_RND(level, level_file_info);
1350 case LEVEL_FILE_TYPE_EM:
1351 LoadLevelFromFileInfo_EM(level, level_file_info);
1355 LoadLevelFromFileInfo_RND(level, level_file_info);
1360 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1362 static struct LevelFileInfo level_file_info;
1364 level_file_info.nr = 0; /* unknown */
1365 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1366 level_file_info.filename = filename;
1368 LoadLevelFromFileInfo(level, &level_file_info);
1371 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1373 if (leveldir_current == NULL) /* only when dumping level */
1377 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1380 /* determine correct game engine version of current level */
1382 if (!leveldir_current->latest_engine)
1384 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1385 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1386 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1390 printf("\n::: This level is private or contributed: '%s'\n", filename);
1394 printf("\n::: Use the stored game engine version for this level\n");
1397 /* For all levels which are not forced to use the latest game engine
1398 version (normally user contributed, private and undefined levels),
1399 use the version of the game engine the levels were created for.
1401 Since 2.0.1, the game engine version is now directly stored
1402 in the level file (chunk "VERS"), so there is no need anymore
1403 to set the game version from the file version (except for old,
1404 pre-2.0 levels, where the game version is still taken from the
1405 file format version used to store the level -- see above). */
1407 /* do some special adjustments to support older level versions */
1408 if (level->file_version == FILE_VERSION_1_0)
1410 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1411 Error(ERR_WARN, "using high speed movement for player");
1413 /* player was faster than monsters in (pre-)1.0 levels */
1414 level->double_speed = TRUE;
1417 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1418 if (level->game_version == VERSION_IDENT(2,0,1,0))
1419 level->em_slippery_gems = TRUE;
1424 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1425 leveldir_current->sort_priority, filename);
1429 printf("\n::: Use latest game engine version for this level.\n");
1432 /* For all levels which are forced to use the latest game engine version
1433 (normally all but user contributed, private and undefined levels), set
1434 the game engine version to the actual version; this allows for actual
1435 corrections in the game engine to take effect for existing, converted
1436 levels (from "classic" or other existing games) to make the emulation
1437 of the corresponding game more accurate, while (hopefully) not breaking
1438 existing levels created from other players. */
1441 printf("::: changing engine from %d to %d\n",
1442 level->game_version, GAME_VERSION_ACTUAL);
1445 level->game_version = GAME_VERSION_ACTUAL;
1447 /* Set special EM style gems behaviour: EM style gems slip down from
1448 normal, steel and growing wall. As this is a more fundamental change,
1449 it seems better to set the default behaviour to "off" (as it is more
1450 natural) and make it configurable in the level editor (as a property
1451 of gem style elements). Already existing converted levels (neither
1452 private nor contributed levels) are changed to the new behaviour. */
1454 if (level->file_version < FILE_VERSION_2_0)
1455 level->em_slippery_gems = TRUE;
1459 printf("::: => %d\n", level->game_version);
1463 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1467 /* map custom element change events that have changed in newer versions
1468 (these following values were accidentally changed in version 3.0.1) */
1469 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1471 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1473 int element = EL_CUSTOM_START + i;
1475 /* order of checking and copying events to be mapped is important */
1476 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1478 if (HAS_CHANGE_EVENT(element, j - 2))
1480 SET_CHANGE_EVENT(element, j - 2, FALSE);
1481 SET_CHANGE_EVENT(element, j, TRUE);
1485 /* order of checking and copying events to be mapped is important */
1486 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1488 if (HAS_CHANGE_EVENT(element, j - 1))
1490 SET_CHANGE_EVENT(element, j - 1, FALSE);
1491 SET_CHANGE_EVENT(element, j, TRUE);
1497 /* some custom element change events get mapped since version 3.0.3 */
1498 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1500 int element = EL_CUSTOM_START + i;
1502 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1503 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1505 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1506 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1508 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1512 /* initialize "can_change" field for old levels with only one change page */
1513 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1515 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1517 int element = EL_CUSTOM_START + i;
1519 if (CAN_CHANGE(element))
1520 element_info[element].change->can_change = TRUE;
1525 /* set default push delay values (corrected since version 3.0.7-1) */
1526 if (level->game_version < VERSION_IDENT(3,0,7,1))
1528 game.default_push_delay_fixed = 2;
1529 game.default_push_delay_random = 8;
1533 game.default_push_delay_fixed = 8;
1534 game.default_push_delay_random = 8;
1537 /* set uninitialized push delay values of custom elements in older levels */
1538 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1540 int element = EL_CUSTOM_START + i;
1542 if (element_info[element].push_delay_fixed == -1)
1543 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1544 if (element_info[element].push_delay_random == -1)
1545 element_info[element].push_delay_random = game.default_push_delay_random;
1549 /* initialize element properties for level editor etc. */
1550 InitElementPropertiesEngine(level->game_version);
1553 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1557 /* map elements that have changed in newer versions */
1558 for (y = 0; y < level->fieldy; y++)
1560 for (x = 0; x < level->fieldx; x++)
1562 int element = level->field[x][y];
1564 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1566 /* map game font elements */
1567 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1568 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1569 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1570 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1573 if (level->game_version < VERSION_IDENT(3,0,0,0))
1575 /* map Supaplex gravity tube elements */
1576 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1577 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1578 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1579 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1583 level->field[x][y] = element;
1587 /* copy elements to runtime playfield array */
1588 for (x = 0; x < MAX_LEV_FIELDX; x++)
1589 for (y = 0; y < MAX_LEV_FIELDY; y++)
1590 Feld[x][y] = level->field[x][y];
1592 /* initialize level size variables for faster access */
1593 lev_fieldx = level->fieldx;
1594 lev_fieldy = level->fieldy;
1596 /* determine border element for this level */
1600 void LoadLevelTemplate(int nr)
1603 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1604 char *filename = level_file_info->filename;
1606 LoadLevelFromFileInfo(&level_template, level_file_info);
1608 char *filename = getDefaultLevelFilename(nr);
1610 LoadLevelFromFilename_RND(&level_template, filename);
1613 LoadLevel_InitVersion(&level, filename);
1614 LoadLevel_InitElements(&level, filename);
1616 ActivateLevelTemplate();
1619 void LoadLevel(int nr)
1622 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1623 char *filename = level_file_info->filename;
1625 LoadLevelFromFileInfo(&level, level_file_info);
1627 char *filename = getLevelFilename(nr);
1629 LoadLevelFromFilename_RND(&level, filename);
1632 if (level.use_custom_template)
1633 LoadLevelTemplate(-1);
1636 LoadLevel_InitVersion(&level, filename);
1637 LoadLevel_InitElements(&level, filename);
1638 LoadLevel_InitPlayfield(&level, filename);
1640 LoadLevel_InitLevel(&level, filename);
1644 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1646 putFileVersion(file, level->file_version);
1647 putFileVersion(file, level->game_version);
1650 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1654 putFile8Bit(file, level->fieldx);
1655 putFile8Bit(file, level->fieldy);
1657 putFile16BitBE(file, level->time);
1658 putFile16BitBE(file, level->gems_needed);
1660 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1661 putFile8Bit(file, level->name[i]);
1663 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1664 putFile8Bit(file, level->score[i]);
1666 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1667 for (y = 0; y < 3; y++)
1668 for (x = 0; x < 3; x++)
1669 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1670 level->yamyam_content[i][x][y]));
1671 putFile8Bit(file, level->amoeba_speed);
1672 putFile8Bit(file, level->time_magic_wall);
1673 putFile8Bit(file, level->time_wheel);
1674 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1675 level->amoeba_content));
1676 putFile8Bit(file, (level->double_speed ? 1 : 0));
1677 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1678 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1679 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1681 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1683 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1686 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1690 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1691 putFile8Bit(file, level->author[i]);
1694 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1698 for (y = 0; y < level->fieldy; y++)
1699 for (x = 0; x < level->fieldx; x++)
1700 if (level->encoding_16bit_field)
1701 putFile16BitBE(file, level->field[x][y]);
1703 putFile8Bit(file, level->field[x][y]);
1707 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1711 putFile8Bit(file, EL_YAMYAM);
1712 putFile8Bit(file, level->num_yamyam_contents);
1713 putFile8Bit(file, 0);
1714 putFile8Bit(file, 0);
1716 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1717 for (y = 0; y < 3; y++)
1718 for (x = 0; x < 3; x++)
1719 if (level->encoding_16bit_field)
1720 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1722 putFile8Bit(file, level->yamyam_content[i][x][y]);
1726 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1729 int num_contents, content_xsize, content_ysize;
1730 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1732 if (element == EL_YAMYAM)
1734 num_contents = level->num_yamyam_contents;
1738 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1739 for (y = 0; y < 3; y++)
1740 for (x = 0; x < 3; x++)
1741 content_array[i][x][y] = level->yamyam_content[i][x][y];
1743 else if (element == EL_BD_AMOEBA)
1749 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1750 for (y = 0; y < 3; y++)
1751 for (x = 0; x < 3; x++)
1752 content_array[i][x][y] = EL_EMPTY;
1753 content_array[0][0][0] = level->amoeba_content;
1757 /* chunk header already written -- write empty chunk data */
1758 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1760 Error(ERR_WARN, "cannot save content for element '%d'", element);
1764 putFile16BitBE(file, element);
1765 putFile8Bit(file, num_contents);
1766 putFile8Bit(file, content_xsize);
1767 putFile8Bit(file, content_ysize);
1769 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1771 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1772 for (y = 0; y < 3; y++)
1773 for (x = 0; x < 3; x++)
1774 putFile16BitBE(file, content_array[i][x][y]);
1777 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1780 int envelope_nr = element - EL_ENVELOPE_1;
1781 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1783 putFile16BitBE(file, element);
1784 putFile16BitBE(file, envelope_len);
1785 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1786 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1788 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1790 for (i = 0; i < envelope_len; i++)
1791 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1795 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1796 int num_changed_custom_elements)
1800 putFile16BitBE(file, num_changed_custom_elements);
1802 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1804 int element = EL_CUSTOM_START + i;
1806 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1808 if (check < num_changed_custom_elements)
1810 putFile16BitBE(file, element);
1811 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1818 if (check != num_changed_custom_elements) /* should not happen */
1819 Error(ERR_WARN, "inconsistent number of custom element properties");
1824 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1825 int num_changed_custom_elements)
1829 putFile16BitBE(file, num_changed_custom_elements);
1831 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1833 int element = EL_CUSTOM_START + i;
1835 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1837 if (check < num_changed_custom_elements)
1839 putFile16BitBE(file, element);
1840 putFile16BitBE(file, element_info[element].change->target_element);
1847 if (check != num_changed_custom_elements) /* should not happen */
1848 Error(ERR_WARN, "inconsistent number of custom target elements");
1853 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1854 int num_changed_custom_elements)
1856 int i, j, x, y, check = 0;
1858 putFile16BitBE(file, num_changed_custom_elements);
1860 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1862 int element = EL_CUSTOM_START + i;
1864 if (element_info[element].modified_settings)
1866 if (check < num_changed_custom_elements)
1868 putFile16BitBE(file, element);
1870 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1871 putFile8Bit(file, element_info[element].description[j]);
1873 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1875 /* some free bytes for future properties and padding */
1876 WriteUnusedBytesToFile(file, 7);
1878 putFile8Bit(file, element_info[element].use_gfx_element);
1879 putFile16BitBE(file, element_info[element].gfx_element);
1881 putFile8Bit(file, element_info[element].collect_score);
1882 putFile8Bit(file, element_info[element].collect_count);
1884 putFile16BitBE(file, element_info[element].push_delay_fixed);
1885 putFile16BitBE(file, element_info[element].push_delay_random);
1886 putFile16BitBE(file, element_info[element].move_delay_fixed);
1887 putFile16BitBE(file, element_info[element].move_delay_random);
1889 putFile16BitBE(file, element_info[element].move_pattern);
1890 putFile8Bit(file, element_info[element].move_direction_initial);
1891 putFile8Bit(file, element_info[element].move_stepsize);
1893 for (y = 0; y < 3; y++)
1894 for (x = 0; x < 3; x++)
1895 putFile16BitBE(file, element_info[element].content[x][y]);
1897 putFile32BitBE(file, element_info[element].change->events);
1899 putFile16BitBE(file, element_info[element].change->target_element);
1901 putFile16BitBE(file, element_info[element].change->delay_fixed);
1902 putFile16BitBE(file, element_info[element].change->delay_random);
1903 putFile16BitBE(file, element_info[element].change->delay_frames);
1905 putFile16BitBE(file, element_info[element].change->trigger_element);
1907 putFile8Bit(file, element_info[element].change->explode);
1908 putFile8Bit(file, element_info[element].change->use_content);
1909 putFile8Bit(file, element_info[element].change->only_complete);
1910 putFile8Bit(file, element_info[element].change->use_random_change);
1912 putFile8Bit(file, element_info[element].change->random);
1913 putFile8Bit(file, element_info[element].change->power);
1915 for (y = 0; y < 3; y++)
1916 for (x = 0; x < 3; x++)
1917 putFile16BitBE(file, element_info[element].change->content[x][y]);
1919 putFile8Bit(file, element_info[element].slippery_type);
1921 /* some free bytes for future properties and padding */
1922 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1929 if (check != num_changed_custom_elements) /* should not happen */
1930 Error(ERR_WARN, "inconsistent number of custom element properties");
1934 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1936 struct ElementInfo *ei = &element_info[element];
1939 putFile16BitBE(file, element);
1941 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1942 putFile8Bit(file, ei->description[i]);
1944 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1945 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1947 putFile8Bit(file, ei->num_change_pages);
1949 /* some free bytes for future base property values and padding */
1950 WriteUnusedBytesToFile(file, 5);
1952 /* write custom property values */
1954 putFile8Bit(file, ei->use_gfx_element);
1955 putFile16BitBE(file, ei->gfx_element);
1957 putFile8Bit(file, ei->collect_score);
1958 putFile8Bit(file, ei->collect_count);
1960 putFile16BitBE(file, ei->push_delay_fixed);
1961 putFile16BitBE(file, ei->push_delay_random);
1962 putFile16BitBE(file, ei->move_delay_fixed);
1963 putFile16BitBE(file, ei->move_delay_random);
1965 putFile16BitBE(file, ei->move_pattern);
1966 putFile8Bit(file, ei->move_direction_initial);
1967 putFile8Bit(file, ei->move_stepsize);
1969 putFile8Bit(file, ei->slippery_type);
1971 for (y = 0; y < 3; y++)
1972 for (x = 0; x < 3; x++)
1973 putFile16BitBE(file, ei->content[x][y]);
1975 /* some free bytes for future custom property values and padding */
1976 WriteUnusedBytesToFile(file, 12);
1978 /* write change property values */
1980 for (i = 0; i < ei->num_change_pages; i++)
1982 struct ElementChangeInfo *change = &ei->change_page[i];
1984 putFile32BitBE(file, change->events);
1986 putFile16BitBE(file, change->target_element);
1988 putFile16BitBE(file, change->delay_fixed);
1989 putFile16BitBE(file, change->delay_random);
1990 putFile16BitBE(file, change->delay_frames);
1992 putFile16BitBE(file, change->trigger_element);
1994 putFile8Bit(file, change->explode);
1995 putFile8Bit(file, change->use_content);
1996 putFile8Bit(file, change->only_complete);
1997 putFile8Bit(file, change->use_random_change);
1999 putFile8Bit(file, change->random);
2000 putFile8Bit(file, change->power);
2002 for (y = 0; y < 3; y++)
2003 for (x = 0; x < 3; x++)
2004 putFile16BitBE(file, change->content[x][y]);
2006 putFile8Bit(file, change->can_change);
2008 putFile8Bit(file, change->sides);
2010 /* some free bytes for future change property values and padding */
2011 WriteUnusedBytesToFile(file, 8);
2015 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2017 int body_chunk_size;
2021 if (!(file = fopen(filename, MODE_WRITE)))
2023 Error(ERR_WARN, "cannot save level file '%s'", filename);
2027 level->file_version = FILE_VERSION_ACTUAL;
2028 level->game_version = GAME_VERSION_ACTUAL;
2030 /* check level field for 16-bit elements */
2031 level->encoding_16bit_field = FALSE;
2032 for (y = 0; y < level->fieldy; y++)
2033 for (x = 0; x < level->fieldx; x++)
2034 if (level->field[x][y] > 255)
2035 level->encoding_16bit_field = TRUE;
2037 /* check yamyam content for 16-bit elements */
2038 level->encoding_16bit_yamyam = FALSE;
2039 for (i = 0; i < level->num_yamyam_contents; i++)
2040 for (y = 0; y < 3; y++)
2041 for (x = 0; x < 3; x++)
2042 if (level->yamyam_content[i][x][y] > 255)
2043 level->encoding_16bit_yamyam = TRUE;
2045 /* check amoeba content for 16-bit elements */
2046 level->encoding_16bit_amoeba = FALSE;
2047 if (level->amoeba_content > 255)
2048 level->encoding_16bit_amoeba = TRUE;
2050 /* calculate size of "BODY" chunk */
2052 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2054 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2055 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2057 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2058 SaveLevel_VERS(file, level);
2060 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2061 SaveLevel_HEAD(file, level);
2063 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2064 SaveLevel_AUTH(file, level);
2066 putFileChunkBE(file, "BODY", body_chunk_size);
2067 SaveLevel_BODY(file, level);
2069 if (level->encoding_16bit_yamyam ||
2070 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2072 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2073 SaveLevel_CNT2(file, level, EL_YAMYAM);
2076 if (level->encoding_16bit_amoeba)
2078 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2079 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2082 /* check for envelope content */
2083 for (i = 0; i < 4; i++)
2085 if (strlen(level->envelope_text[i]) > 0)
2087 int envelope_len = strlen(level->envelope_text[i]) + 1;
2089 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
2090 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2094 /* check for non-default custom elements (unless using template level) */
2095 if (!level->use_custom_template)
2097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2099 int element = EL_CUSTOM_START + i;
2101 if (element_info[element].modified_settings)
2103 int num_change_pages = element_info[element].num_change_pages;
2105 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2106 SaveLevel_CUS4(file, level, element);
2113 SetFilePermissions(filename, PERMS_PRIVATE);
2116 void SaveLevel(int nr)
2118 char *filename = getDefaultLevelFilename(nr);
2120 SaveLevelFromFilename(&level, filename);
2123 void SaveLevelTemplate()
2125 char *filename = getDefaultLevelFilename(-1);
2127 SaveLevelFromFilename(&level, filename);
2130 void DumpLevel(struct LevelInfo *level)
2132 printf_line("-", 79);
2133 printf("Level xxx (file version %08d, game version %08d)\n",
2134 level->file_version, level->game_version);
2135 printf_line("-", 79);
2137 printf("Level Author: '%s'\n", level->author);
2138 printf("Level Title: '%s'\n", level->name);
2140 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2142 printf("Level Time: %d seconds\n", level->time);
2143 printf("Gems needed: %d\n", level->gems_needed);
2145 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2146 printf("Time for Wheel: %d seconds\n", level->time_wheel);
2147 printf("Time for Light: %d seconds\n", level->time_light);
2148 printf("Time for Timegate: %d seconds\n", level->time_timegate);
2150 printf("Amoeba Speed: %d\n", level->amoeba_speed);
2152 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2153 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
2154 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2156 printf_line("-", 79);
2160 /* ========================================================================= */
2161 /* tape file functions */
2162 /* ========================================================================= */
2164 static void setTapeInfoToDefaults()
2168 /* always start with reliable default values (empty tape) */
2171 /* default values (also for pre-1.2 tapes) with only the first player */
2172 tape.player_participates[0] = TRUE;
2173 for (i = 1; i < MAX_PLAYERS; i++)
2174 tape.player_participates[i] = FALSE;
2176 /* at least one (default: the first) player participates in every tape */
2177 tape.num_participating_players = 1;
2179 tape.level_nr = level_nr;
2181 tape.changed = FALSE;
2183 tape.recording = FALSE;
2184 tape.playing = FALSE;
2185 tape.pausing = FALSE;
2188 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2190 tape->file_version = getFileVersion(file);
2191 tape->game_version = getFileVersion(file);
2196 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2200 tape->random_seed = getFile32BitBE(file);
2201 tape->date = getFile32BitBE(file);
2202 tape->length = getFile32BitBE(file);
2204 /* read header fields that are new since version 1.2 */
2205 if (tape->file_version >= FILE_VERSION_1_2)
2207 byte store_participating_players = getFile8Bit(file);
2210 /* since version 1.2, tapes store which players participate in the tape */
2211 tape->num_participating_players = 0;
2212 for (i = 0; i < MAX_PLAYERS; i++)
2214 tape->player_participates[i] = FALSE;
2216 if (store_participating_players & (1 << i))
2218 tape->player_participates[i] = TRUE;
2219 tape->num_participating_players++;
2223 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2225 engine_version = getFileVersion(file);
2226 if (engine_version > 0)
2227 tape->engine_version = engine_version;
2229 tape->engine_version = tape->game_version;
2235 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2237 int level_identifier_size;
2240 level_identifier_size = getFile16BitBE(file);
2242 tape->level_identifier =
2243 checked_realloc(tape->level_identifier, level_identifier_size);
2245 for (i = 0; i < level_identifier_size; i++)
2246 tape->level_identifier[i] = getFile8Bit(file);
2248 tape->level_nr = getFile16BitBE(file);
2250 chunk_size = 2 + level_identifier_size + 2;
2255 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2258 int chunk_size_expected =
2259 (tape->num_participating_players + 1) * tape->length;
2261 if (chunk_size_expected != chunk_size)
2263 ReadUnusedBytesFromFile(file, chunk_size);
2264 return chunk_size_expected;
2267 for (i = 0; i < tape->length; i++)
2269 if (i >= MAX_TAPELEN)
2272 for (j = 0; j < MAX_PLAYERS; j++)
2274 tape->pos[i].action[j] = MV_NO_MOVING;
2276 if (tape->player_participates[j])
2277 tape->pos[i].action[j] = getFile8Bit(file);
2280 tape->pos[i].delay = getFile8Bit(file);
2282 if (tape->file_version == FILE_VERSION_1_0)
2284 /* eliminate possible diagonal moves in old tapes */
2285 /* this is only for backward compatibility */
2287 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2288 byte action = tape->pos[i].action[0];
2289 int k, num_moves = 0;
2291 for (k = 0; k<4; k++)
2293 if (action & joy_dir[k])
2295 tape->pos[i + num_moves].action[0] = joy_dir[k];
2297 tape->pos[i + num_moves].delay = 0;
2306 tape->length += num_moves;
2309 else if (tape->file_version < FILE_VERSION_2_0)
2311 /* convert pre-2.0 tapes to new tape format */
2313 if (tape->pos[i].delay > 1)
2316 tape->pos[i + 1] = tape->pos[i];
2317 tape->pos[i + 1].delay = 1;
2320 for (j = 0; j < MAX_PLAYERS; j++)
2321 tape->pos[i].action[j] = MV_NO_MOVING;
2322 tape->pos[i].delay--;
2333 if (i != tape->length)
2334 chunk_size = (tape->num_participating_players + 1) * i;
2339 void LoadTapeFromFilename(char *filename)
2341 char cookie[MAX_LINE_LEN];
2342 char chunk_name[CHUNK_ID_LEN + 1];
2346 /* always start with reliable default values */
2347 setTapeInfoToDefaults();
2349 if (!(file = fopen(filename, MODE_READ)))
2352 getFileChunkBE(file, chunk_name, NULL);
2353 if (strcmp(chunk_name, "RND1") == 0)
2355 getFile32BitBE(file); /* not used */
2357 getFileChunkBE(file, chunk_name, NULL);
2358 if (strcmp(chunk_name, "TAPE") != 0)
2360 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2365 else /* check for pre-2.0 file format with cookie string */
2367 strcpy(cookie, chunk_name);
2368 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2369 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2370 cookie[strlen(cookie) - 1] = '\0';
2372 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2374 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2379 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2381 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2386 /* pre-2.0 tape files have no game version, so use file version here */
2387 tape.game_version = tape.file_version;
2390 if (tape.file_version < FILE_VERSION_1_2)
2392 /* tape files from versions before 1.2.0 without chunk structure */
2393 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2394 LoadTape_BODY(file, 2 * tape.length, &tape);
2402 int (*loader)(FILE *, int, struct TapeInfo *);
2406 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2407 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2408 { "INFO", -1, LoadTape_INFO },
2409 { "BODY", -1, LoadTape_BODY },
2413 while (getFileChunkBE(file, chunk_name, &chunk_size))
2417 while (chunk_info[i].name != NULL &&
2418 strcmp(chunk_name, chunk_info[i].name) != 0)
2421 if (chunk_info[i].name == NULL)
2423 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2424 chunk_name, filename);
2425 ReadUnusedBytesFromFile(file, chunk_size);
2427 else if (chunk_info[i].size != -1 &&
2428 chunk_info[i].size != chunk_size)
2430 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2431 chunk_size, chunk_name, filename);
2432 ReadUnusedBytesFromFile(file, chunk_size);
2436 /* call function to load this tape chunk */
2437 int chunk_size_expected =
2438 (chunk_info[i].loader)(file, chunk_size, &tape);
2440 /* the size of some chunks cannot be checked before reading other
2441 chunks first (like "HEAD" and "BODY") that contain some header
2442 information, so check them here */
2443 if (chunk_size_expected != chunk_size)
2445 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2446 chunk_size, chunk_name, filename);
2454 tape.length_seconds = GetTapeLength();
2457 printf("::: tape game version: %d\n", tape.game_version);
2458 printf("::: tape engine version: %d\n", tape.engine_version);
2462 void LoadTape(int nr)
2464 char *filename = getTapeFilename(nr);
2466 LoadTapeFromFilename(filename);
2469 void LoadSolutionTape(int nr)
2471 char *filename = getSolutionTapeFilename(nr);
2473 LoadTapeFromFilename(filename);
2476 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2478 putFileVersion(file, tape->file_version);
2479 putFileVersion(file, tape->game_version);
2482 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2485 byte store_participating_players = 0;
2487 /* set bits for participating players for compact storage */
2488 for (i = 0; i < MAX_PLAYERS; i++)
2489 if (tape->player_participates[i])
2490 store_participating_players |= (1 << i);
2492 putFile32BitBE(file, tape->random_seed);
2493 putFile32BitBE(file, tape->date);
2494 putFile32BitBE(file, tape->length);
2496 putFile8Bit(file, store_participating_players);
2498 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2499 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2501 putFileVersion(file, tape->engine_version);
2504 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2506 int level_identifier_size = strlen(tape->level_identifier) + 1;
2509 putFile16BitBE(file, level_identifier_size);
2511 for (i = 0; i < level_identifier_size; i++)
2512 putFile8Bit(file, tape->level_identifier[i]);
2514 putFile16BitBE(file, tape->level_nr);
2517 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2521 for (i = 0; i < tape->length; i++)
2523 for (j = 0; j < MAX_PLAYERS; j++)
2524 if (tape->player_participates[j])
2525 putFile8Bit(file, tape->pos[i].action[j]);
2527 putFile8Bit(file, tape->pos[i].delay);
2531 void SaveTape(int nr)
2533 char *filename = getTapeFilename(nr);
2535 boolean new_tape = TRUE;
2536 int num_participating_players = 0;
2537 int info_chunk_size;
2538 int body_chunk_size;
2541 InitTapeDirectory(leveldir_current->filename);
2543 /* if a tape still exists, ask to overwrite it */
2544 if (access(filename, F_OK) == 0)
2547 if (!Request("Replace old tape ?", REQ_ASK))
2551 if (!(file = fopen(filename, MODE_WRITE)))
2553 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2557 tape.file_version = FILE_VERSION_ACTUAL;
2558 tape.game_version = GAME_VERSION_ACTUAL;
2560 /* count number of participating players */
2561 for (i = 0; i < MAX_PLAYERS; i++)
2562 if (tape.player_participates[i])
2563 num_participating_players++;
2565 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2566 body_chunk_size = (num_participating_players + 1) * tape.length;
2568 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2569 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2571 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2572 SaveTape_VERS(file, &tape);
2574 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2575 SaveTape_HEAD(file, &tape);
2577 putFileChunkBE(file, "INFO", info_chunk_size);
2578 SaveTape_INFO(file, &tape);
2580 putFileChunkBE(file, "BODY", body_chunk_size);
2581 SaveTape_BODY(file, &tape);
2585 SetFilePermissions(filename, PERMS_PRIVATE);
2587 tape.changed = FALSE;
2590 Request("tape saved !", REQ_CONFIRM);
2593 void DumpTape(struct TapeInfo *tape)
2597 if (TAPE_IS_EMPTY(*tape))
2599 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2603 printf_line("-", 79);
2604 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2605 tape->level_nr, tape->file_version, tape->game_version);
2606 printf("Level series identifier: '%s'\n", tape->level_identifier);
2607 printf_line("-", 79);
2609 for (i = 0; i < tape->length; i++)
2611 if (i >= MAX_TAPELEN)
2614 printf("%03d: ", i);
2616 for (j = 0; j < MAX_PLAYERS; j++)
2618 if (tape->player_participates[j])
2620 int action = tape->pos[i].action[j];
2622 printf("%d:%02x ", j, action);
2623 printf("[%c%c%c%c|%c%c] - ",
2624 (action & JOY_LEFT ? '<' : ' '),
2625 (action & JOY_RIGHT ? '>' : ' '),
2626 (action & JOY_UP ? '^' : ' '),
2627 (action & JOY_DOWN ? 'v' : ' '),
2628 (action & JOY_BUTTON_1 ? '1' : ' '),
2629 (action & JOY_BUTTON_2 ? '2' : ' '));
2633 printf("(%03d)\n", tape->pos[i].delay);
2636 printf_line("-", 79);
2640 /* ========================================================================= */
2641 /* score file functions */
2642 /* ========================================================================= */
2644 void LoadScore(int nr)
2647 char *filename = getScoreFilename(nr);
2648 char cookie[MAX_LINE_LEN];
2649 char line[MAX_LINE_LEN];
2653 /* always start with reliable default values */
2654 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2656 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2657 highscore[i].Score = 0;
2660 if (!(file = fopen(filename, MODE_READ)))
2663 /* check file identifier */
2664 fgets(cookie, MAX_LINE_LEN, file);
2665 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2666 cookie[strlen(cookie) - 1] = '\0';
2668 if (!checkCookieString(cookie, SCORE_COOKIE))
2670 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2675 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2677 fscanf(file, "%d", &highscore[i].Score);
2678 fgets(line, MAX_LINE_LEN, file);
2680 if (line[strlen(line) - 1] == '\n')
2681 line[strlen(line) - 1] = '\0';
2683 for (line_ptr = line; *line_ptr; line_ptr++)
2685 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2687 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2688 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2697 void SaveScore(int nr)
2700 char *filename = getScoreFilename(nr);
2703 InitScoreDirectory(leveldir_current->filename);
2705 if (!(file = fopen(filename, MODE_WRITE)))
2707 Error(ERR_WARN, "cannot save score for level %d", nr);
2711 fprintf(file, "%s\n\n", SCORE_COOKIE);
2713 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2714 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2718 SetFilePermissions(filename, PERMS_PUBLIC);
2722 /* ========================================================================= */
2723 /* setup file functions */
2724 /* ========================================================================= */
2726 #define TOKEN_STR_PLAYER_PREFIX "player_"
2729 #define SETUP_TOKEN_PLAYER_NAME 0
2730 #define SETUP_TOKEN_SOUND 1
2731 #define SETUP_TOKEN_SOUND_LOOPS 2
2732 #define SETUP_TOKEN_SOUND_MUSIC 3
2733 #define SETUP_TOKEN_SOUND_SIMPLE 4
2734 #define SETUP_TOKEN_TOONS 5
2735 #define SETUP_TOKEN_SCROLL_DELAY 6
2736 #define SETUP_TOKEN_SOFT_SCROLLING 7
2737 #define SETUP_TOKEN_FADING 8
2738 #define SETUP_TOKEN_AUTORECORD 9
2739 #define SETUP_TOKEN_QUICK_DOORS 10
2740 #define SETUP_TOKEN_TEAM_MODE 11
2741 #define SETUP_TOKEN_HANDICAP 12
2742 #define SETUP_TOKEN_TIME_LIMIT 13
2743 #define SETUP_TOKEN_FULLSCREEN 14
2744 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2745 #define SETUP_TOKEN_GRAPHICS_SET 16
2746 #define SETUP_TOKEN_SOUNDS_SET 17
2747 #define SETUP_TOKEN_MUSIC_SET 18
2748 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2749 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2750 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2752 #define NUM_GLOBAL_SETUP_TOKENS 22
2755 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2756 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2757 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2758 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2759 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2760 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2761 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2762 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2763 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2764 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2765 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2766 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2768 #define NUM_EDITOR_SETUP_TOKENS 12
2770 /* shortcut setup */
2771 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2772 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2773 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2775 #define NUM_SHORTCUT_SETUP_TOKENS 3
2778 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2779 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2780 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2781 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2782 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2783 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2784 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2785 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2786 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2787 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
2788 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2789 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2790 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2791 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2792 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2793 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
2795 #define NUM_PLAYER_SETUP_TOKENS 16
2798 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2799 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2801 #define NUM_SYSTEM_SETUP_TOKENS 2
2804 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2806 #define NUM_OPTIONS_SETUP_TOKENS 1
2809 static struct SetupInfo si;
2810 static struct SetupEditorInfo sei;
2811 static struct SetupShortcutInfo ssi;
2812 static struct SetupInputInfo sii;
2813 static struct SetupSystemInfo syi;
2814 static struct OptionInfo soi;
2816 static struct TokenInfo global_setup_tokens[] =
2818 { TYPE_STRING, &si.player_name, "player_name" },
2819 { TYPE_SWITCH, &si.sound, "sound" },
2820 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2821 { TYPE_SWITCH, &si.sound_music, "background_music" },
2822 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2823 { TYPE_SWITCH, &si.toons, "toons" },
2824 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2825 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2826 { TYPE_SWITCH, &si.fading, "screen_fading" },
2827 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2828 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2829 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2830 { TYPE_SWITCH, &si.handicap, "handicap" },
2831 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2832 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2833 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2834 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2835 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2836 { TYPE_STRING, &si.music_set, "music_set" },
2837 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2838 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2839 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2842 static struct TokenInfo editor_setup_tokens[] =
2844 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2845 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2846 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2847 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2848 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2849 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2850 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2851 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2852 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2853 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2854 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2855 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2858 static struct TokenInfo shortcut_setup_tokens[] =
2860 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2861 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2862 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2865 static struct TokenInfo player_setup_tokens[] =
2867 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2868 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2869 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2870 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2871 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2872 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2873 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2874 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2875 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2876 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
2877 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2878 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2879 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2880 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2881 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2882 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
2885 static struct TokenInfo system_setup_tokens[] =
2887 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2888 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2891 static struct TokenInfo options_setup_tokens[] =
2893 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2896 static char *get_corrected_login_name(char *login_name)
2898 /* needed because player name must be a fixed length string */
2899 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2901 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2902 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2904 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2905 if (strchr(login_name_new, ' '))
2906 *strchr(login_name_new, ' ') = '\0';
2908 return login_name_new;
2911 static void setSetupInfoToDefaults(struct SetupInfo *si)
2915 si->player_name = get_corrected_login_name(getLoginName());
2918 si->sound_loops = TRUE;
2919 si->sound_music = TRUE;
2920 si->sound_simple = TRUE;
2922 si->double_buffering = TRUE;
2923 si->direct_draw = !si->double_buffering;
2924 si->scroll_delay = TRUE;
2925 si->soft_scrolling = TRUE;
2927 si->autorecord = TRUE;
2928 si->quick_doors = FALSE;
2929 si->team_mode = FALSE;
2930 si->handicap = TRUE;
2931 si->time_limit = TRUE;
2932 si->fullscreen = FALSE;
2933 si->ask_on_escape = TRUE;
2935 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2936 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2937 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2938 si->override_level_graphics = FALSE;
2939 si->override_level_sounds = FALSE;
2940 si->override_level_music = FALSE;
2942 si->editor.el_boulderdash = TRUE;
2943 si->editor.el_emerald_mine = TRUE;
2944 si->editor.el_more = TRUE;
2945 si->editor.el_sokoban = TRUE;
2946 si->editor.el_supaplex = TRUE;
2947 si->editor.el_diamond_caves = TRUE;
2948 si->editor.el_dx_boulderdash = TRUE;
2949 si->editor.el_chars = TRUE;
2950 si->editor.el_custom = TRUE;
2951 si->editor.el_custom_more = FALSE;
2953 si->editor.el_headlines = TRUE;
2954 si->editor.el_user_defined = FALSE;
2956 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2957 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2958 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2960 for (i = 0; i < MAX_PLAYERS; i++)
2962 si->input[i].use_joystick = FALSE;
2963 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2964 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2965 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2966 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2967 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2968 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2969 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2970 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2971 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
2972 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2973 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2974 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2975 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2976 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2977 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
2980 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2981 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2983 si->options.verbose = FALSE;
2986 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2990 if (!setup_file_hash)
2995 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
2996 setSetupInfo(global_setup_tokens, i,
2997 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3002 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3003 setSetupInfo(editor_setup_tokens, i,
3004 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3007 /* shortcut setup */
3008 ssi = setup.shortcut;
3009 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3010 setSetupInfo(shortcut_setup_tokens, i,
3011 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3012 setup.shortcut = ssi;
3015 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3019 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3021 sii = setup.input[pnr];
3022 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3024 char full_token[100];
3026 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3027 setSetupInfo(player_setup_tokens, i,
3028 getHashEntry(setup_file_hash, full_token));
3030 setup.input[pnr] = sii;
3035 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3036 setSetupInfo(system_setup_tokens, i,
3037 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3041 soi = setup.options;
3042 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3043 setSetupInfo(options_setup_tokens, i,
3044 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3045 setup.options = soi;
3050 char *filename = getSetupFilename();
3051 SetupFileHash *setup_file_hash = NULL;
3053 /* always start with reliable default values */
3054 setSetupInfoToDefaults(&setup);
3056 setup_file_hash = loadSetupFileHash(filename);
3058 if (setup_file_hash)
3060 char *player_name_new;
3062 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3063 decodeSetupFileHash(setup_file_hash);
3065 setup.direct_draw = !setup.double_buffering;
3067 freeSetupFileHash(setup_file_hash);
3069 /* needed to work around problems with fixed length strings */
3070 player_name_new = get_corrected_login_name(setup.player_name);
3071 free(setup.player_name);
3072 setup.player_name = player_name_new;
3075 Error(ERR_WARN, "using default setup values");
3080 char *filename = getSetupFilename();
3084 InitUserDataDirectory();
3086 if (!(file = fopen(filename, MODE_WRITE)))
3088 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3092 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3093 getCookie("SETUP")));
3094 fprintf(file, "\n");
3098 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3100 /* just to make things nicer :) */
3101 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3102 i == SETUP_TOKEN_GRAPHICS_SET)
3103 fprintf(file, "\n");
3105 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3110 fprintf(file, "\n");
3111 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3112 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3114 /* shortcut setup */
3115 ssi = setup.shortcut;
3116 fprintf(file, "\n");
3117 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3118 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3121 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3125 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3126 fprintf(file, "\n");
3128 sii = setup.input[pnr];
3129 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3130 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3135 fprintf(file, "\n");
3136 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3137 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3140 soi = setup.options;
3141 fprintf(file, "\n");
3142 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3143 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3147 SetFilePermissions(filename, PERMS_PRIVATE);
3150 void LoadCustomElementDescriptions()
3152 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3153 SetupFileHash *setup_file_hash;
3156 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3158 if (element_info[i].custom_description != NULL)
3160 free(element_info[i].custom_description);
3161 element_info[i].custom_description = NULL;
3165 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3168 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3170 char *token = getStringCat2(element_info[i].token_name, ".name");
3171 char *value = getHashEntry(setup_file_hash, token);
3174 element_info[i].custom_description = getStringCopy(value);
3179 freeSetupFileHash(setup_file_hash);
3182 void LoadSpecialMenuDesignSettings()
3184 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3185 SetupFileHash *setup_file_hash;
3188 /* always start with reliable default values from default config */
3189 for (i = 0; image_config_vars[i].token != NULL; i++)
3190 for (j = 0; image_config[j].token != NULL; j++)
3191 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3192 *image_config_vars[i].value =
3193 get_auto_parameter_value(image_config_vars[i].token,
3194 image_config[j].value);
3196 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3199 /* special case: initialize with default values that may be overwritten */
3200 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3202 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3203 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3204 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3206 if (value_x != NULL)
3207 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3208 if (value_y != NULL)
3209 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3210 if (list_size != NULL)
3211 menu.list_size[i] = get_integer_from_string(list_size);
3214 /* read (and overwrite with) values that may be specified in config file */
3215 for (i = 0; image_config_vars[i].token != NULL; i++)
3217 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3220 *image_config_vars[i].value =
3221 get_auto_parameter_value(image_config_vars[i].token, value);
3224 freeSetupFileHash(setup_file_hash);
3227 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3229 char *filename = getEditorSetupFilename();
3230 SetupFileList *setup_file_list, *list;
3231 SetupFileHash *element_hash;
3232 int num_unknown_tokens = 0;
3235 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3238 element_hash = newSetupFileHash();
3240 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3241 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3243 /* determined size may be larger than needed (due to unknown elements) */
3245 for (list = setup_file_list; list != NULL; list = list->next)
3248 /* add space for up to 3 more elements for padding that may be needed */
3251 *elements = checked_malloc(*num_elements * sizeof(int));
3254 for (list = setup_file_list; list != NULL; list = list->next)
3256 char *value = getHashEntry(element_hash, list->token);
3260 (*elements)[(*num_elements)++] = atoi(value);
3264 if (num_unknown_tokens == 0)
3266 Error(ERR_RETURN_LINE, "-");
3267 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3268 Error(ERR_RETURN, "- config file: '%s'", filename);
3270 num_unknown_tokens++;
3273 Error(ERR_RETURN, "- token: '%s'", list->token);
3277 if (num_unknown_tokens > 0)
3278 Error(ERR_RETURN_LINE, "-");
3280 while (*num_elements % 4) /* pad with empty elements, if needed */
3281 (*elements)[(*num_elements)++] = EL_EMPTY;
3283 freeSetupFileList(setup_file_list);
3284 freeSetupFileHash(element_hash);
3288 for (i = 0; i < *num_elements; i++)
3289 printf("editor: element '%s' [%d]\n",
3290 element_info[(*elements)[i]].token_name, (*elements)[i]);
3294 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3297 SetupFileHash *setup_file_hash = NULL;
3298 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3299 char *filename_music, *filename_prefix, *filename_info;
3305 token_to_value_ptr[] =
3307 { "title_header", &tmp_music_file_info.title_header },
3308 { "artist_header", &tmp_music_file_info.artist_header },
3309 { "album_header", &tmp_music_file_info.album_header },
3310 { "year_header", &tmp_music_file_info.year_header },
3312 { "title", &tmp_music_file_info.title },
3313 { "artist", &tmp_music_file_info.artist },
3314 { "album", &tmp_music_file_info.album },
3315 { "year", &tmp_music_file_info.year },
3321 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3322 getCustomMusicFilename(basename));
3324 if (filename_music == NULL)
3327 /* ---------- try to replace file extension ---------- */
3329 filename_prefix = getStringCopy(filename_music);
3330 if (strrchr(filename_prefix, '.') != NULL)
3331 *strrchr(filename_prefix, '.') = '\0';
3332 filename_info = getStringCat2(filename_prefix, ".txt");
3335 printf("trying to load file '%s'...\n", filename_info);
3338 if (fileExists(filename_info))
3339 setup_file_hash = loadSetupFileHash(filename_info);
3341 free(filename_prefix);
3342 free(filename_info);
3344 if (setup_file_hash == NULL)
3346 /* ---------- try to add file extension ---------- */
3348 filename_prefix = getStringCopy(filename_music);
3349 filename_info = getStringCat2(filename_prefix, ".txt");
3352 printf("trying to load file '%s'...\n", filename_info);
3355 if (fileExists(filename_info))
3356 setup_file_hash = loadSetupFileHash(filename_info);
3358 free(filename_prefix);
3359 free(filename_info);
3362 if (setup_file_hash == NULL)
3365 /* ---------- music file info found ---------- */
3367 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3369 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3371 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3373 *token_to_value_ptr[i].value_ptr =
3374 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3377 tmp_music_file_info.basename = getStringCopy(basename);
3378 tmp_music_file_info.music = music;
3379 tmp_music_file_info.is_sound = is_sound;
3381 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3382 *new_music_file_info = tmp_music_file_info;
3384 return new_music_file_info;
3387 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3389 return get_music_file_info_ext(basename, music, FALSE);
3392 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3394 return get_music_file_info_ext(basename, sound, TRUE);
3397 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3398 char *basename, boolean is_sound)
3400 for (; list != NULL; list = list->next)
3401 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3407 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3409 return music_info_listed_ext(list, basename, FALSE);
3412 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3414 return music_info_listed_ext(list, basename, TRUE);
3417 void LoadMusicInfo()
3419 char *music_directory = getCustomMusicDirectory();
3420 int num_music = getMusicListSize();
3421 int num_music_noconf = 0;
3422 int num_sounds = getSoundListSize();
3424 struct dirent *dir_entry;
3425 struct FileInfo *music, *sound;
3426 struct MusicFileInfo *next, **new;
3429 while (music_file_info != NULL)
3431 next = music_file_info->next;
3433 checked_free(music_file_info->basename);
3435 checked_free(music_file_info->title_header);
3436 checked_free(music_file_info->artist_header);
3437 checked_free(music_file_info->album_header);
3438 checked_free(music_file_info->year_header);
3440 checked_free(music_file_info->title);
3441 checked_free(music_file_info->artist);
3442 checked_free(music_file_info->album);
3443 checked_free(music_file_info->year);
3445 free(music_file_info);
3447 music_file_info = next;
3450 new = &music_file_info;
3453 printf("::: num_music == %d\n", num_music);
3456 for (i = 0; i < num_music; i++)
3458 music = getMusicListEntry(i);
3461 printf("::: %d [%08x]\n", i, music->filename);
3464 if (music->filename == NULL)
3467 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3470 /* a configured file may be not recognized as music */
3471 if (!FileIsMusic(music->filename))
3475 printf("::: -> '%s' (configured)\n", music->filename);
3478 if (!music_info_listed(music_file_info, music->filename))
3480 *new = get_music_file_info(music->filename, i);
3482 new = &(*new)->next;
3486 if ((dir = opendir(music_directory)) == NULL)
3488 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3492 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
3494 char *basename = dir_entry->d_name;
3495 boolean music_already_used = FALSE;
3498 /* skip all music files that are configured in music config file */
3499 for (i = 0; i < num_music; i++)
3501 music = getMusicListEntry(i);
3503 if (music->filename == NULL)
3506 if (strcmp(basename, music->filename) == 0)
3508 music_already_used = TRUE;
3513 if (music_already_used)
3516 if (!FileIsMusic(basename))
3520 printf("::: -> '%s' (found in directory)\n", basename);
3523 if (!music_info_listed(music_file_info, basename))
3525 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3527 new = &(*new)->next;
3535 for (i = 0; i < num_sounds; i++)
3537 sound = getSoundListEntry(i);
3539 if (sound->filename == NULL)
3542 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3545 /* a configured file may be not recognized as sound */
3546 if (!FileIsSound(sound->filename))
3550 printf("::: -> '%s' (configured)\n", sound->filename);
3553 if (!sound_info_listed(music_file_info, sound->filename))
3555 *new = get_sound_file_info(sound->filename, i);
3557 new = &(*new)->next;
3563 for (next = music_file_info; next != NULL; next = next->next)
3564 printf("::: title == '%s'\n", next->title);
3568 void add_helpanim_entry(int element, int action, int direction, int delay,
3569 int *num_list_entries)
3571 struct HelpAnimInfo *new_list_entry;
3572 (*num_list_entries)++;
3575 checked_realloc(helpanim_info,
3576 *num_list_entries * sizeof(struct HelpAnimInfo));
3577 new_list_entry = &helpanim_info[*num_list_entries - 1];
3579 new_list_entry->element = element;
3580 new_list_entry->action = action;
3581 new_list_entry->direction = direction;
3582 new_list_entry->delay = delay;
3585 void print_unknown_token(char *filename, char *token, int token_nr)
3589 Error(ERR_RETURN_LINE, "-");
3590 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3591 Error(ERR_RETURN, "- config file: '%s'", filename);
3594 Error(ERR_RETURN, "- token: '%s'", token);
3597 void print_unknown_token_end(int token_nr)
3600 Error(ERR_RETURN_LINE, "-");
3603 void LoadHelpAnimInfo()
3605 char *filename = getHelpAnimFilename();
3606 SetupFileList *setup_file_list = NULL, *list;
3607 SetupFileHash *element_hash, *action_hash, *direction_hash;
3608 int num_list_entries = 0;
3609 int num_unknown_tokens = 0;
3612 if (fileExists(filename))
3613 setup_file_list = loadSetupFileList(filename);
3615 if (setup_file_list == NULL)
3617 /* use reliable default values from static configuration */
3618 SetupFileList *insert_ptr;
3620 insert_ptr = setup_file_list =
3621 newSetupFileList(helpanim_config[0].token,
3622 helpanim_config[0].value);
3624 for (i = 1; helpanim_config[i].token; i++)
3625 insert_ptr = addListEntry(insert_ptr,
3626 helpanim_config[i].token,
3627 helpanim_config[i].value);
3630 element_hash = newSetupFileHash();
3631 action_hash = newSetupFileHash();
3632 direction_hash = newSetupFileHash();
3634 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3635 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3637 for (i = 0; i < NUM_ACTIONS; i++)
3638 setHashEntry(action_hash, element_action_info[i].suffix,
3639 i_to_a(element_action_info[i].value));
3641 /* do not store direction index (bit) here, but direction value! */
3642 for (i = 0; i < NUM_DIRECTIONS; i++)
3643 setHashEntry(direction_hash, element_direction_info[i].suffix,
3644 i_to_a(1 << element_direction_info[i].value));
3646 for (list = setup_file_list; list != NULL; list = list->next)
3648 char *element_token, *action_token, *direction_token;
3649 char *element_value, *action_value, *direction_value;
3650 int delay = atoi(list->value);
3652 if (strcmp(list->token, "end") == 0)
3654 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3659 /* first try to break element into element/action/direction parts;
3660 if this does not work, also accept combined "element[.act][.dir]"
3661 elements (like "dynamite.active"), which are unique elements */
3663 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
3665 element_value = getHashEntry(element_hash, list->token);
3666 if (element_value != NULL) /* element found */
3667 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3671 /* no further suffixes found -- this is not an element */
3672 print_unknown_token(filename, list->token, num_unknown_tokens++);
3678 /* token has format "<prefix>.<something>" */
3680 action_token = strchr(list->token, '.'); /* suffix may be action ... */
3681 direction_token = action_token; /* ... or direction */
3683 element_token = getStringCopy(list->token);
3684 *strchr(element_token, '.') = '\0';
3686 element_value = getHashEntry(element_hash, element_token);
3688 if (element_value == NULL) /* this is no element */
3690 element_value = getHashEntry(element_hash, list->token);
3691 if (element_value != NULL) /* combined element found */
3692 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3695 print_unknown_token(filename, list->token, num_unknown_tokens++);
3697 free(element_token);
3702 action_value = getHashEntry(action_hash, action_token);
3704 if (action_value != NULL) /* action found */
3706 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3709 free(element_token);
3714 direction_value = getHashEntry(direction_hash, direction_token);
3716 if (direction_value != NULL) /* direction found */
3718 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3721 free(element_token);
3726 if (strchr(action_token + 1, '.') == NULL)
3728 /* no further suffixes found -- this is not an action nor direction */
3730 element_value = getHashEntry(element_hash, list->token);
3731 if (element_value != NULL) /* combined element found */
3732 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3735 print_unknown_token(filename, list->token, num_unknown_tokens++);
3737 free(element_token);
3742 /* token has format "<prefix>.<suffix>.<something>" */
3744 direction_token = strchr(action_token + 1, '.');
3746 action_token = getStringCopy(action_token);
3747 *strchr(action_token + 1, '.') = '\0';
3749 action_value = getHashEntry(action_hash, action_token);
3751 if (action_value == NULL) /* this is no action */
3753 element_value = getHashEntry(element_hash, list->token);
3754 if (element_value != NULL) /* combined element found */
3755 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3758 print_unknown_token(filename, list->token, num_unknown_tokens++);
3760 free(element_token);
3766 direction_value = getHashEntry(direction_hash, direction_token);
3768 if (direction_value != NULL) /* direction found */
3770 add_helpanim_entry(atoi(element_value), atoi(action_value),
3771 atoi(direction_value), delay, &num_list_entries);
3773 free(element_token);
3779 /* this is no direction */
3781 element_value = getHashEntry(element_hash, list->token);
3782 if (element_value != NULL) /* combined element found */
3783 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3786 print_unknown_token(filename, list->token, num_unknown_tokens++);
3788 free(element_token);
3792 print_unknown_token_end(num_unknown_tokens);
3794 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3795 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
3797 freeSetupFileList(setup_file_list);
3798 freeSetupFileHash(element_hash);
3799 freeSetupFileHash(action_hash);
3800 freeSetupFileHash(direction_hash);
3804 for (i = 0; i < num_list_entries; i++)
3805 printf("::: %d, %d, %d => %d\n",
3806 helpanim_info[i].element,
3807 helpanim_info[i].action,
3808 helpanim_info[i].direction,
3809 helpanim_info[i].delay);
3813 void LoadHelpTextInfo()
3815 char *filename = getHelpTextFilename();
3818 if (helptext_info != NULL)
3820 freeSetupFileHash(helptext_info);
3821 helptext_info = NULL;
3824 if (fileExists(filename))
3825 helptext_info = loadSetupFileHash(filename);
3827 if (helptext_info == NULL)
3829 /* use reliable default values from static configuration */
3830 helptext_info = newSetupFileHash();
3832 for (i = 0; helptext_config[i].token; i++)
3833 setHashEntry(helptext_info,
3834 helptext_config[i].token,
3835 helptext_config[i].value);
3840 BEGIN_HASH_ITERATION(helptext_info, itr)
3842 printf("::: '%s' => '%s'\n",
3843 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3845 END_HASH_ITERATION(hash, itr)