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 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
239 int element = EL_GROUP_START + i;
241 if (element_info[element].group == NULL)
242 element_info[element].group =
243 checked_malloc(sizeof(struct ElementGroupInfo));
245 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
246 element_info[element].group->element[j] = EL_EMPTY_SPACE;
248 element_info[element].group->num_elements = 1;
251 BorderElement = EL_STEELWALL;
253 level->no_level_file = FALSE;
255 if (leveldir_current == NULL) /* only when dumping level */
258 /* try to determine better author name than 'anonymous' */
259 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
261 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
262 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
266 switch (LEVELCLASS(leveldir_current))
268 case LEVELCLASS_TUTORIAL:
269 strcpy(level->author, PROGRAM_AUTHOR_STRING);
272 case LEVELCLASS_CONTRIB:
273 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
274 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
277 case LEVELCLASS_PRIVATE:
278 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
279 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
283 /* keep default value */
289 static void ActivateLevelTemplate()
291 /* Currently there is no special action needed to activate the template
292 data, because 'element_info' and 'Properties' overwrite the original
293 level data, while all other variables do not change. */
296 static char *getLevelFilenameFromBasename(char *basename)
298 static char *filename = NULL;
300 checked_free(filename);
302 filename = getPath2(getCurrentLevelDir(), basename);
307 static char *getSingleLevelBasename(int nr, int type)
309 static char basename[MAX_FILENAME_LEN];
313 case LEVEL_FILE_TYPE_RND:
315 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
317 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
320 case LEVEL_FILE_TYPE_EM:
321 sprintf(basename, "%d", nr);
325 strcpy(basename, UNDEFINED_FILENAME);
332 static char *getPackedLevelBasename(int type)
334 static char basename[MAX_FILENAME_LEN];
339 strcpy(basename, UNDEFINED_FILENAME);
346 static char *getSingleLevelFilename(int nr, int type)
348 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
351 static char *getPackedLevelFilename(int type)
353 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
356 char *getDefaultLevelFilename(int nr)
358 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
361 static struct LevelFileInfo *getLevelFileInfo(int nr)
363 static struct LevelFileInfo level_file_info;
365 level_file_info.nr = nr;
367 /* special case: level template */
370 level_file_info.type = LEVEL_FILE_TYPE_RND;
371 level_file_info.filename = getDefaultLevelFilename(nr);
373 return &level_file_info;
376 /* 1st try: check for native Rocks'n'Diamonds level file */
377 level_file_info.type = LEVEL_FILE_TYPE_RND;
378 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
379 if (fileExists(level_file_info.filename))
380 return &level_file_info;
382 /* 2nd try: check for classic Emerald Mine level file */
383 level_file_info.type = LEVEL_FILE_TYPE_EM;
384 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
385 if (fileExists(level_file_info.filename))
386 return &level_file_info;
388 /* no known level file found -- use default values */
389 level_file_info.type = LEVEL_FILE_TYPE_RND;
390 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
392 return &level_file_info;
395 /* ------------------------------------------------------------------------- */
396 /* functions for loading R'n'D level */
397 /* ------------------------------------------------------------------------- */
399 static int checkLevelElement(int element)
401 /* map some (historic, now obsolete) elements */
406 case EL_PLAYER_OBSOLETE:
407 element = EL_PLAYER_1;
410 case EL_KEY_OBSOLETE:
413 case EL_EM_KEY_1_FILE_OBSOLETE:
414 element = EL_EM_KEY_1;
417 case EL_EM_KEY_2_FILE_OBSOLETE:
418 element = EL_EM_KEY_2;
421 case EL_EM_KEY_3_FILE_OBSOLETE:
422 element = EL_EM_KEY_3;
425 case EL_EM_KEY_4_FILE_OBSOLETE:
426 element = EL_EM_KEY_4;
429 case EL_ENVELOPE_OBSOLETE:
430 element = EL_ENVELOPE_1;
438 if (element >= NUM_FILE_ELEMENTS)
440 Error(ERR_WARN, "invalid level element %d", element);
442 element = EL_CHAR_QUESTION;
447 if (element >= NUM_FILE_ELEMENTS)
449 Error(ERR_WARN, "invalid level element %d", element);
451 element = EL_CHAR_QUESTION;
453 else if (element == EL_PLAYER_OBSOLETE)
454 element = EL_PLAYER_1;
455 else if (element == EL_KEY_OBSOLETE)
462 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
464 level->file_version = getFileVersion(file);
465 level->game_version = getFileVersion(file);
470 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
474 level->fieldx = getFile8Bit(file);
475 level->fieldy = getFile8Bit(file);
477 level->time = getFile16BitBE(file);
478 level->gems_needed = getFile16BitBE(file);
480 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
481 level->name[i] = getFile8Bit(file);
482 level->name[MAX_LEVEL_NAME_LEN] = 0;
484 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
485 level->score[i] = getFile8Bit(file);
487 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
488 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
489 for (y = 0; y < 3; y++)
490 for (x = 0; x < 3; x++)
491 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
493 level->amoeba_speed = getFile8Bit(file);
494 level->time_magic_wall = getFile8Bit(file);
495 level->time_wheel = getFile8Bit(file);
496 level->amoeba_content = checkLevelElement(getFile8Bit(file));
497 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
498 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
499 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
500 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
502 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
504 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
509 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
513 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
514 level->author[i] = getFile8Bit(file);
515 level->author[MAX_LEVEL_NAME_LEN] = 0;
520 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
523 int chunk_size_expected = level->fieldx * level->fieldy;
525 /* Note: "chunk_size" was wrong before version 2.0 when elements are
526 stored with 16-bit encoding (and should be twice as big then).
527 Even worse, playfield data was stored 16-bit when only yamyam content
528 contained 16-bit elements and vice versa. */
530 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
531 chunk_size_expected *= 2;
533 if (chunk_size_expected != chunk_size)
535 ReadUnusedBytesFromFile(file, chunk_size);
536 return chunk_size_expected;
539 for (y = 0; y < level->fieldy; y++)
540 for (x = 0; x < level->fieldx; x++)
542 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
547 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
551 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
552 int chunk_size_expected = header_size + content_size;
554 /* Note: "chunk_size" was wrong before version 2.0 when elements are
555 stored with 16-bit encoding (and should be twice as big then).
556 Even worse, playfield data was stored 16-bit when only yamyam content
557 contained 16-bit elements and vice versa. */
559 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
560 chunk_size_expected += content_size;
562 if (chunk_size_expected != chunk_size)
564 ReadUnusedBytesFromFile(file, chunk_size);
565 return chunk_size_expected;
569 level->num_yamyam_contents = getFile8Bit(file);
573 /* correct invalid number of content fields -- should never happen */
574 if (level->num_yamyam_contents < 1 ||
575 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
576 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
578 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
579 for (y = 0; y < 3; y++)
580 for (x = 0; x < 3; x++)
581 level->yamyam_content[i][x][y] =
582 checkLevelElement(level->encoding_16bit_field ?
583 getFile16BitBE(file) : getFile8Bit(file));
587 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
591 int num_contents, content_xsize, content_ysize;
592 int content_array[MAX_ELEMENT_CONTENTS][3][3];
594 element = checkLevelElement(getFile16BitBE(file));
595 num_contents = getFile8Bit(file);
596 content_xsize = getFile8Bit(file);
597 content_ysize = getFile8Bit(file);
599 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
601 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
602 for (y = 0; y < 3; y++)
603 for (x = 0; x < 3; x++)
604 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
606 /* correct invalid number of content fields -- should never happen */
607 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
608 num_contents = STD_ELEMENT_CONTENTS;
610 if (element == EL_YAMYAM)
612 level->num_yamyam_contents = num_contents;
614 for (i = 0; i < num_contents; i++)
615 for (y = 0; y < 3; y++)
616 for (x = 0; x < 3; x++)
617 level->yamyam_content[i][x][y] = content_array[i][x][y];
619 else if (element == EL_BD_AMOEBA)
621 level->amoeba_content = content_array[0][0][0];
625 Error(ERR_WARN, "cannot load content for element '%d'", element);
631 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
637 int chunk_size_expected;
639 element = checkLevelElement(getFile16BitBE(file));
640 if (!IS_ENVELOPE(element))
641 element = EL_ENVELOPE_1;
643 envelope_nr = element - EL_ENVELOPE_1;
645 envelope_len = getFile16BitBE(file);
647 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
648 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
650 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
652 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
654 if (chunk_size_expected != chunk_size)
656 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
657 return chunk_size_expected;
660 for (i = 0; i < envelope_len; i++)
661 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
666 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
668 int num_changed_custom_elements = getFile16BitBE(file);
669 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
672 if (chunk_size_expected != chunk_size)
674 ReadUnusedBytesFromFile(file, chunk_size - 2);
675 return chunk_size_expected;
678 for (i = 0; i < num_changed_custom_elements; i++)
680 int element = getFile16BitBE(file);
681 int properties = getFile32BitBE(file);
683 if (IS_CUSTOM_ELEMENT(element))
684 Properties[element][EP_BITFIELD_BASE] = properties;
686 Error(ERR_WARN, "invalid custom element number %d", element);
692 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
694 int num_changed_custom_elements = getFile16BitBE(file);
695 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
698 if (chunk_size_expected != chunk_size)
700 ReadUnusedBytesFromFile(file, chunk_size - 2);
701 return chunk_size_expected;
704 for (i = 0; i < num_changed_custom_elements; i++)
706 int element = getFile16BitBE(file);
707 int custom_target_element = getFile16BitBE(file);
709 if (IS_CUSTOM_ELEMENT(element))
710 element_info[element].change->target_element = custom_target_element;
712 Error(ERR_WARN, "invalid custom element number %d", element);
718 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
720 int num_changed_custom_elements = getFile16BitBE(file);
721 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
724 if (chunk_size_expected != chunk_size)
726 ReadUnusedBytesFromFile(file, chunk_size - 2);
727 return chunk_size_expected;
730 for (i = 0; i < num_changed_custom_elements; i++)
732 int element = getFile16BitBE(file);
734 if (!IS_CUSTOM_ELEMENT(element))
736 Error(ERR_WARN, "invalid custom element number %d", element);
741 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
742 element_info[element].description[j] = getFile8Bit(file);
743 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
745 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
747 /* some free bytes for future properties and padding */
748 ReadUnusedBytesFromFile(file, 7);
750 element_info[element].use_gfx_element = getFile8Bit(file);
751 element_info[element].gfx_element =
752 checkLevelElement(getFile16BitBE(file));
754 element_info[element].collect_score = getFile8Bit(file);
755 element_info[element].collect_count = getFile8Bit(file);
757 element_info[element].push_delay_fixed = getFile16BitBE(file);
758 element_info[element].push_delay_random = getFile16BitBE(file);
759 element_info[element].move_delay_fixed = getFile16BitBE(file);
760 element_info[element].move_delay_random = getFile16BitBE(file);
762 element_info[element].move_pattern = getFile16BitBE(file);
763 element_info[element].move_direction_initial = getFile8Bit(file);
764 element_info[element].move_stepsize = getFile8Bit(file);
766 for (y = 0; y < 3; y++)
767 for (x = 0; x < 3; x++)
768 element_info[element].content[x][y] =
769 checkLevelElement(getFile16BitBE(file));
771 element_info[element].change->events = getFile32BitBE(file);
773 element_info[element].change->target_element =
774 checkLevelElement(getFile16BitBE(file));
776 element_info[element].change->delay_fixed = getFile16BitBE(file);
777 element_info[element].change->delay_random = getFile16BitBE(file);
778 element_info[element].change->delay_frames = getFile16BitBE(file);
780 element_info[element].change->trigger_element =
781 checkLevelElement(getFile16BitBE(file));
783 element_info[element].change->explode = getFile8Bit(file);
784 element_info[element].change->use_content = getFile8Bit(file);
785 element_info[element].change->only_complete = getFile8Bit(file);
786 element_info[element].change->use_random_change = getFile8Bit(file);
788 element_info[element].change->random = getFile8Bit(file);
789 element_info[element].change->power = getFile8Bit(file);
791 for (y = 0; y < 3; y++)
792 for (x = 0; x < 3; x++)
793 element_info[element].change->content[x][y] =
794 checkLevelElement(getFile16BitBE(file));
796 element_info[element].slippery_type = getFile8Bit(file);
798 /* some free bytes for future properties and padding */
799 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
801 /* mark that this custom element has been modified */
802 element_info[element].modified_settings = TRUE;
808 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
810 struct ElementInfo *ei;
811 int chunk_size_expected;
815 element = getFile16BitBE(file);
817 if (!IS_CUSTOM_ELEMENT(element))
819 Error(ERR_WARN, "invalid custom element number %d", element);
824 ei = &element_info[element];
826 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
827 ei->description[i] = getFile8Bit(file);
828 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
830 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
831 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
833 ei->num_change_pages = getFile8Bit(file);
835 /* some free bytes for future base property values and padding */
836 ReadUnusedBytesFromFile(file, 5);
838 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
839 if (chunk_size_expected != chunk_size)
841 ReadUnusedBytesFromFile(file, chunk_size - 48);
842 return chunk_size_expected;
845 /* read custom property values */
847 ei->use_gfx_element = getFile8Bit(file);
848 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
850 ei->collect_score = getFile8Bit(file);
851 ei->collect_count = getFile8Bit(file);
853 ei->push_delay_fixed = getFile16BitBE(file);
854 ei->push_delay_random = getFile16BitBE(file);
855 ei->move_delay_fixed = getFile16BitBE(file);
856 ei->move_delay_random = getFile16BitBE(file);
858 ei->move_pattern = getFile16BitBE(file);
859 ei->move_direction_initial = getFile8Bit(file);
860 ei->move_stepsize = getFile8Bit(file);
862 ei->slippery_type = getFile8Bit(file);
864 for (y = 0; y < 3; y++)
865 for (x = 0; x < 3; x++)
866 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
868 /* some free bytes for future custom property values and padding */
869 ReadUnusedBytesFromFile(file, 12);
871 /* read change property values */
873 setElementChangePages(ei, ei->num_change_pages);
875 for (i = 0; i < ei->num_change_pages; i++)
877 struct ElementChangeInfo *change = &ei->change_page[i];
879 /* always start with reliable default values */
880 setElementChangeInfoToDefaults(change);
882 change->events = getFile32BitBE(file);
884 change->target_element = checkLevelElement(getFile16BitBE(file));
886 change->delay_fixed = getFile16BitBE(file);
887 change->delay_random = getFile16BitBE(file);
888 change->delay_frames = getFile16BitBE(file);
890 change->trigger_element = checkLevelElement(getFile16BitBE(file));
892 change->explode = getFile8Bit(file);
893 change->use_content = getFile8Bit(file);
894 change->only_complete = getFile8Bit(file);
895 change->use_random_change = getFile8Bit(file);
897 change->random = getFile8Bit(file);
898 change->power = getFile8Bit(file);
900 for (y = 0; y < 3; y++)
901 for (x = 0; x < 3; x++)
902 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
904 change->can_change = getFile8Bit(file);
906 change->sides = getFile8Bit(file);
908 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
909 change->sides = CH_SIDE_ANY;
911 /* some free bytes for future change property values and padding */
912 ReadUnusedBytesFromFile(file, 8);
915 /* mark this custom element as modified */
916 ei->modified_settings = TRUE;
921 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
922 struct LevelFileInfo *level_file_info)
924 char *filename = level_file_info->filename;
925 char cookie[MAX_LINE_LEN];
926 char chunk_name[CHUNK_ID_LEN + 1];
930 /* always start with reliable default values */
931 setLevelInfoToDefaults(level);
933 if (!(file = fopen(filename, MODE_READ)))
935 level->no_level_file = TRUE;
937 if (level != &level_template)
938 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
943 getFileChunkBE(file, chunk_name, NULL);
944 if (strcmp(chunk_name, "RND1") == 0)
946 getFile32BitBE(file); /* not used */
948 getFileChunkBE(file, chunk_name, NULL);
949 if (strcmp(chunk_name, "CAVE") != 0)
951 Error(ERR_WARN, "unknown format of level file '%s'", filename);
956 else /* check for pre-2.0 file format with cookie string */
958 strcpy(cookie, chunk_name);
959 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
960 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
961 cookie[strlen(cookie) - 1] = '\0';
963 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
965 Error(ERR_WARN, "unknown format of level file '%s'", filename);
970 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
972 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
977 /* pre-2.0 level files have no game version, so use file version here */
978 level->game_version = level->file_version;
981 if (level->file_version < FILE_VERSION_1_2)
983 /* level files from versions before 1.2.0 without chunk structure */
984 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
985 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
993 int (*loader)(FILE *, int, struct LevelInfo *);
997 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
998 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
999 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1000 { "BODY", -1, LoadLevel_BODY },
1001 { "CONT", -1, LoadLevel_CONT },
1002 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1003 { "CNT3", -1, LoadLevel_CNT3 },
1004 { "CUS1", -1, LoadLevel_CUS1 },
1005 { "CUS2", -1, LoadLevel_CUS2 },
1006 { "CUS3", -1, LoadLevel_CUS3 },
1007 { "CUS4", -1, LoadLevel_CUS4 },
1011 while (getFileChunkBE(file, chunk_name, &chunk_size))
1015 while (chunk_info[i].name != NULL &&
1016 strcmp(chunk_name, chunk_info[i].name) != 0)
1019 if (chunk_info[i].name == NULL)
1021 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1022 chunk_name, filename);
1023 ReadUnusedBytesFromFile(file, chunk_size);
1025 else if (chunk_info[i].size != -1 &&
1026 chunk_info[i].size != chunk_size)
1028 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1029 chunk_size, chunk_name, filename);
1030 ReadUnusedBytesFromFile(file, chunk_size);
1034 /* call function to load this level chunk */
1035 int chunk_size_expected =
1036 (chunk_info[i].loader)(file, chunk_size, level);
1038 /* the size of some chunks cannot be checked before reading other
1039 chunks first (like "HEAD" and "BODY") that contain some header
1040 information, so check them here */
1041 if (chunk_size_expected != chunk_size)
1043 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1044 chunk_size, chunk_name, filename);
1053 /* ------------------------------------------------------------------------- */
1054 /* functions for loading EM level */
1055 /* ------------------------------------------------------------------------- */
1057 static int map_em_element_yam(int element)
1061 case 0x00: return EL_EMPTY;
1062 case 0x01: return EL_EMERALD;
1063 case 0x02: return EL_DIAMOND;
1064 case 0x03: return EL_ROCK;
1065 case 0x04: return EL_ROBOT;
1066 case 0x05: return EL_SPACESHIP_UP;
1067 case 0x06: return EL_BOMB;
1068 case 0x07: return EL_BUG_UP;
1069 case 0x08: return EL_AMOEBA_DROP;
1070 case 0x09: return EL_NUT;
1071 case 0x0a: return EL_YAMYAM;
1072 case 0x0b: return EL_QUICKSAND_FULL;
1073 case 0x0c: return EL_SAND;
1074 case 0x0d: return EL_WALL_SLIPPERY;
1075 case 0x0e: return EL_STEELWALL;
1076 case 0x0f: return EL_WALL;
1077 case 0x10: return EL_EM_KEY_1;
1078 case 0x11: return EL_EM_KEY_2;
1079 case 0x12: return EL_EM_KEY_4;
1080 case 0x13: return EL_EM_KEY_3;
1081 case 0x14: return EL_MAGIC_WALL;
1082 case 0x15: return EL_ROBOT_WHEEL;
1083 case 0x16: return EL_DYNAMITE;
1085 case 0x17: return EL_EM_KEY_1; /* EMC */
1086 case 0x18: return EL_BUG_UP; /* EMC */
1087 case 0x1a: return EL_DIAMOND; /* EMC */
1088 case 0x1b: return EL_EMERALD; /* EMC */
1089 case 0x25: return EL_NUT; /* EMC */
1090 case 0x80: return EL_EMPTY; /* EMC */
1091 case 0x85: return EL_EM_KEY_1; /* EMC */
1092 case 0x86: return EL_EM_KEY_2; /* EMC */
1093 case 0x87: return EL_EM_KEY_4; /* EMC */
1094 case 0x88: return EL_EM_KEY_3; /* EMC */
1095 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1096 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1097 case 0xaf: return EL_DYNAMITE; /* EMC */
1098 case 0xbd: return EL_SAND; /* EMC */
1101 Error(ERR_WARN, "invalid level element %d", element);
1102 return EL_CHAR_QUESTION;
1106 static int map_em_element_field(int element)
1108 if (element >= 0xc8 && element <= 0xe1)
1109 return EL_CHAR_A + (element - 0xc8);
1110 else if (element >= 0xe2 && element <= 0xeb)
1111 return EL_CHAR_0 + (element - 0xe2);
1115 case 0x00: return EL_ROCK;
1116 case 0x02: return EL_DIAMOND;
1117 case 0x03: return EL_DIAMOND;
1118 case 0x04: return EL_ROBOT;
1119 case 0x05: return EL_ROBOT; /* EMC */
1120 case 0x08: return EL_SPACESHIP_UP;
1121 case 0x09: return EL_SPACESHIP_RIGHT;
1122 case 0x0a: return EL_SPACESHIP_DOWN;
1123 case 0x0b: return EL_SPACESHIP_LEFT;
1124 case 0x0c: return EL_SPACESHIP_UP;
1125 case 0x0d: return EL_SPACESHIP_RIGHT;
1126 case 0x0e: return EL_SPACESHIP_DOWN;
1127 case 0x0f: return EL_SPACESHIP_LEFT;
1128 case 0x10: return EL_BOMB;
1129 case 0x12: return EL_EMERALD;
1130 case 0x13: return EL_EMERALD;
1131 case 0x14: return EL_BUG_UP;
1132 case 0x15: return EL_BUG_RIGHT;
1133 case 0x16: return EL_BUG_DOWN;
1134 case 0x17: return EL_BUG_LEFT;
1135 case 0x18: return EL_BUG_UP;
1136 case 0x19: return EL_BUG_RIGHT;
1137 case 0x1a: return EL_BUG_DOWN;
1138 case 0x1b: return EL_BUG_LEFT;
1139 case 0x1c: return EL_AMOEBA_DROP;
1140 case 0x20: return EL_ROCK;
1141 case 0x24: return EL_MAGIC_WALL;
1142 case 0x25: return EL_NUT;
1144 /* looks like magic wheel, but is _always_ activated */
1145 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1147 case 0x29: return EL_YAMYAM;
1148 case 0x2a: return EL_YAMYAM;
1149 case 0x2b: return EL_YAMYAM; /* EMC */
1150 case 0x2c: return EL_YAMYAM; /* EMC */
1151 case 0x2d: return EL_QUICKSAND_FULL;
1152 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1153 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1154 case 0x3b: return EL_DYNAMITE_ACTIVE;
1155 case 0x3c: return EL_DYNAMITE_ACTIVE;
1156 case 0x3d: return EL_DYNAMITE_ACTIVE;
1157 case 0x3e: return EL_DYNAMITE_ACTIVE;
1158 case 0x3f: return EL_ACID_POOL_BOTTOM;
1159 case 0x40: return EL_EXIT_OPEN;
1160 case 0x41: return EL_EXIT_OPEN;
1161 case 0x42: return EL_EXIT_OPEN;
1162 case 0x43: return EL_BALLOON;
1163 case 0x4e: return EL_INVISIBLE_WALL;
1164 case 0x65: return EL_ACID; /* EMC */
1165 case 0x73: return EL_SAND; /* EMC */
1166 case 0x74: return EL_STEELWALL;
1167 case 0x7b: return EL_ACID;
1168 case 0x80: return EL_EMPTY;
1169 case 0x81: return EL_WALL_SLIPPERY;
1170 case 0x82: return EL_SAND;
1171 case 0x83: return EL_STEELWALL;
1172 case 0x84: return EL_WALL;
1173 case 0x85: return EL_EM_KEY_1;
1174 case 0x86: return EL_EM_KEY_2;
1175 case 0x87: return EL_EM_KEY_4;
1176 case 0x88: return EL_EM_KEY_3;
1177 case 0x89: return EL_EM_GATE_1;
1178 case 0x8a: return EL_EM_GATE_2;
1179 case 0x8b: return EL_EM_GATE_4;
1180 case 0x8c: return EL_EM_GATE_3;
1181 case 0x8d: return EL_INVISIBLE_WALL; /* EMC */
1182 case 0x8e: return EL_EM_GATE_1_GRAY;
1183 case 0x8f: return EL_EM_GATE_2_GRAY;
1184 case 0x90: return EL_EM_GATE_4_GRAY;
1185 case 0x91: return EL_EM_GATE_3_GRAY;
1186 case 0x92: return EL_MAGIC_WALL;
1187 case 0x94: return EL_QUICKSAND_EMPTY;
1188 case 0x95: return EL_ACID_POOL_TOPLEFT;
1189 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1190 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1191 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1192 case 0x99: return EL_ACID;
1193 case 0x9a: return EL_AMOEBA_DEAD;
1194 case 0x9b: return EL_AMOEBA_DEAD;
1195 case 0x9c: return EL_AMOEBA_DEAD;
1196 case 0x9d: return EL_AMOEBA_DEAD;
1197 case 0x9e: return EL_EXIT_CLOSED;
1198 case 0x9f: return EL_CHAR_LESS; /* EMC */
1199 case 0x93: return EL_ROBOT_WHEEL;
1201 /* looks like normal dust, but behaves like wall */
1202 case 0xa0: return EL_WALL; /* EMC */
1204 case 0xa8: return EL_EMC_WALL_1; /* EMC */
1205 case 0xa9: return EL_EMC_WALL_2; /* EMC */
1206 case 0xaa: return EL_EMC_WALL_3; /* EMC */
1207 case 0xab: return EL_EMC_WALL_7; /* EMC */
1208 case 0xae: return EL_CHAR_MINUS; /* EMC */
1209 case 0xaf: return EL_DYNAMITE;
1210 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */
1211 case 0xb1: return EL_EMC_WALL_8; /* EMC */
1213 /* (exact steel wall) */
1214 case 0xb3: return EL_STEELWALL; /* EMC */
1216 case 0xb4: return EL_WALL_SLIPPERY; /* EMC */
1217 case 0xb5: return EL_EMC_WALL_6; /* EMC */
1218 case 0xb6: return EL_EMC_WALL_5; /* EMC */
1219 case 0xb7: return EL_EMC_WALL_4; /* EMC */
1220 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1221 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1222 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1223 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1224 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1225 case 0xbd: return EL_SAND; /* EMC */
1226 case 0xec: return EL_CHAR_PERIOD;
1227 case 0xed: return EL_CHAR_EXCLAM;
1228 case 0xee: return EL_CHAR_COLON;
1229 case 0xef: return EL_CHAR_QUESTION;
1230 case 0xf0: return EL_CHAR_GREATER;
1231 case 0xf1: return EL_CHAR_COPYRIGHT;
1232 case 0xfe: return EL_PLAYER_1;
1233 case 0xff: return EL_PLAYER_2;
1236 Error(ERR_WARN, "invalid level element %d", element);
1237 return EL_CHAR_QUESTION;
1241 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1242 struct LevelFileInfo *level_file_info)
1244 char *filename = level_file_info->filename;
1246 unsigned char body[40][64];
1247 unsigned char *leveldata = &body[0][0];
1248 unsigned char *header = &leveldata[2048];
1249 unsigned char code0 = 0x65;
1250 unsigned char code1 = 0x11;
1251 boolean level_is_crypted = FALSE;
1252 int nr = level_file_info->nr;
1256 /* always start with reliable default values */
1257 setLevelInfoToDefaults(level);
1259 if (!(file = fopen(filename, MODE_READ)))
1261 level->no_level_file = TRUE;
1263 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1268 for(i = 0; i < 2106; i++)
1269 leveldata[i] = fgetc(file);
1273 /* check if level data is crypted by testing against known starting bytes
1274 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1276 if ((leveldata[0] == 0xf1 ||
1277 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1279 level_is_crypted = TRUE;
1281 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1282 leveldata[0] = 0xf1;
1285 if (level_is_crypted) /* decode crypted level data */
1287 for(i = 0; i < 2106; i++)
1289 leveldata[i] ^= code0;
1290 leveldata[i] -= code1;
1292 code0 = (code0 + 7) & 0xff;
1299 level->time = header[46] * 10;
1300 level->gems_needed = header[47];
1302 /* The original Emerald Mine levels have their level number stored
1303 at the second byte of the level file...
1304 Do not trust this information at other level files, e.g. EMC,
1305 but correct it anyway (normally the first row is completely
1306 steel wall, so the correction does not hurt anyway). */
1308 if (leveldata[1] == nr)
1309 leveldata[1] = leveldata[2]; /* correct level number field */
1311 sprintf(level->name, "Level %d", nr);
1313 level->score[SC_EMERALD] = header[36];
1314 level->score[SC_DIAMOND] = header[37];
1315 level->score[SC_ROBOT] = header[38];
1316 level->score[SC_SPACESHIP] = header[39];
1317 level->score[SC_BUG] = header[40];
1318 level->score[SC_YAMYAM] = header[41];
1319 level->score[SC_NUT] = header[42];
1320 level->score[SC_DYNAMITE] = header[43];
1321 level->score[SC_TIME_BONUS] = header[44];
1323 level->num_yamyam_contents = 4;
1325 for(i = 0; i < level->num_yamyam_contents; i++)
1326 for(y = 0; y < 3; y++)
1327 for(x = 0; x < 3; x++)
1328 level->yamyam_content[i][x][y] =
1329 map_em_element_yam(header[i * 9 + y * 3 + x]);
1331 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1332 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1333 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1334 level->amoeba_content = EL_DIAMOND;
1336 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1338 int new_element = map_em_element_field(body[y][x]);
1340 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1341 new_element = EL_AMOEBA_WET;
1343 level->field[x][y] = new_element;
1346 jx = (header[48] * 256 + header[49]) % 64;
1347 jy = (header[48] * 256 + header[49]) / 64;
1348 level->field[jx][jy] = EL_PLAYER_1;
1350 jx = (header[50] * 256 + header[51]) % 64;
1351 jy = (header[50] * 256 + header[51]) / 64;
1352 level->field[jx][jy] = EL_PLAYER_2;
1355 void LoadLevelFromFileInfo(struct LevelInfo *level,
1356 struct LevelFileInfo *level_file_info)
1358 switch (level_file_info->type)
1360 case LEVEL_FILE_TYPE_RND:
1361 LoadLevelFromFileInfo_RND(level, level_file_info);
1364 case LEVEL_FILE_TYPE_EM:
1365 LoadLevelFromFileInfo_EM(level, level_file_info);
1369 LoadLevelFromFileInfo_RND(level, level_file_info);
1374 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1376 static struct LevelFileInfo level_file_info;
1378 level_file_info.nr = 0; /* unknown */
1379 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1380 level_file_info.filename = filename;
1382 LoadLevelFromFileInfo(level, &level_file_info);
1385 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1387 if (leveldir_current == NULL) /* only when dumping level */
1391 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1394 /* determine correct game engine version of current level */
1396 if (!leveldir_current->latest_engine)
1398 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1399 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1400 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1404 printf("\n::: This level is private or contributed: '%s'\n", filename);
1408 printf("\n::: Use the stored game engine version for this level\n");
1411 /* For all levels which are not forced to use the latest game engine
1412 version (normally user contributed, private and undefined levels),
1413 use the version of the game engine the levels were created for.
1415 Since 2.0.1, the game engine version is now directly stored
1416 in the level file (chunk "VERS"), so there is no need anymore
1417 to set the game version from the file version (except for old,
1418 pre-2.0 levels, where the game version is still taken from the
1419 file format version used to store the level -- see above). */
1421 /* do some special adjustments to support older level versions */
1422 if (level->file_version == FILE_VERSION_1_0)
1424 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1425 Error(ERR_WARN, "using high speed movement for player");
1427 /* player was faster than monsters in (pre-)1.0 levels */
1428 level->double_speed = TRUE;
1431 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1432 if (level->game_version == VERSION_IDENT(2,0,1,0))
1433 level->em_slippery_gems = TRUE;
1438 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1439 leveldir_current->sort_priority, filename);
1443 printf("\n::: Use latest game engine version for this level.\n");
1446 /* For all levels which are forced to use the latest game engine version
1447 (normally all but user contributed, private and undefined levels), set
1448 the game engine version to the actual version; this allows for actual
1449 corrections in the game engine to take effect for existing, converted
1450 levels (from "classic" or other existing games) to make the emulation
1451 of the corresponding game more accurate, while (hopefully) not breaking
1452 existing levels created from other players. */
1455 printf("::: changing engine from %d to %d\n",
1456 level->game_version, GAME_VERSION_ACTUAL);
1459 level->game_version = GAME_VERSION_ACTUAL;
1461 /* Set special EM style gems behaviour: EM style gems slip down from
1462 normal, steel and growing wall. As this is a more fundamental change,
1463 it seems better to set the default behaviour to "off" (as it is more
1464 natural) and make it configurable in the level editor (as a property
1465 of gem style elements). Already existing converted levels (neither
1466 private nor contributed levels) are changed to the new behaviour. */
1468 if (level->file_version < FILE_VERSION_2_0)
1469 level->em_slippery_gems = TRUE;
1473 printf("::: => %d\n", level->game_version);
1477 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1481 /* map custom element change events that have changed in newer versions
1482 (these following values were accidentally changed in version 3.0.1) */
1483 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1485 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1487 int element = EL_CUSTOM_START + i;
1489 /* order of checking and copying events to be mapped is important */
1490 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1492 if (HAS_CHANGE_EVENT(element, j - 2))
1494 SET_CHANGE_EVENT(element, j - 2, FALSE);
1495 SET_CHANGE_EVENT(element, j, TRUE);
1499 /* order of checking and copying events to be mapped is important */
1500 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1502 if (HAS_CHANGE_EVENT(element, j - 1))
1504 SET_CHANGE_EVENT(element, j - 1, FALSE);
1505 SET_CHANGE_EVENT(element, j, TRUE);
1511 /* some custom element change events get mapped since version 3.0.3 */
1512 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1514 int element = EL_CUSTOM_START + i;
1516 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1517 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1519 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1520 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1522 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1526 /* initialize "can_change" field for old levels with only one change page */
1527 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1529 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1531 int element = EL_CUSTOM_START + i;
1533 if (CAN_CHANGE(element))
1534 element_info[element].change->can_change = TRUE;
1539 /* set default push delay values (corrected since version 3.0.7-1) */
1540 if (level->game_version < VERSION_IDENT(3,0,7,1))
1542 game.default_push_delay_fixed = 2;
1543 game.default_push_delay_random = 8;
1547 game.default_push_delay_fixed = 8;
1548 game.default_push_delay_random = 8;
1551 /* set uninitialized push delay values of custom elements in older levels */
1552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1554 int element = EL_CUSTOM_START + i;
1556 if (element_info[element].push_delay_fixed == -1)
1557 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1558 if (element_info[element].push_delay_random == -1)
1559 element_info[element].push_delay_random = game.default_push_delay_random;
1563 /* initialize element properties for level editor etc. */
1564 InitElementPropertiesEngine(level->game_version);
1567 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1571 /* map elements that have changed in newer versions */
1572 for (y = 0; y < level->fieldy; y++)
1574 for (x = 0; x < level->fieldx; x++)
1576 int element = level->field[x][y];
1578 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1580 /* map game font elements */
1581 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1582 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1583 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1584 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1587 if (level->game_version < VERSION_IDENT(3,0,0,0))
1589 /* map Supaplex gravity tube elements */
1590 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1591 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1592 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1593 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1597 level->field[x][y] = element;
1601 /* copy elements to runtime playfield array */
1602 for (x = 0; x < MAX_LEV_FIELDX; x++)
1603 for (y = 0; y < MAX_LEV_FIELDY; y++)
1604 Feld[x][y] = level->field[x][y];
1606 /* initialize level size variables for faster access */
1607 lev_fieldx = level->fieldx;
1608 lev_fieldy = level->fieldy;
1610 /* determine border element for this level */
1614 void LoadLevelTemplate(int nr)
1617 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1618 char *filename = level_file_info->filename;
1620 LoadLevelFromFileInfo(&level_template, level_file_info);
1622 char *filename = getDefaultLevelFilename(nr);
1624 LoadLevelFromFilename_RND(&level_template, filename);
1627 LoadLevel_InitVersion(&level, filename);
1628 LoadLevel_InitElements(&level, filename);
1630 ActivateLevelTemplate();
1633 void LoadLevel(int nr)
1636 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1637 char *filename = level_file_info->filename;
1639 LoadLevelFromFileInfo(&level, level_file_info);
1641 char *filename = getLevelFilename(nr);
1643 LoadLevelFromFilename_RND(&level, filename);
1646 if (level.use_custom_template)
1647 LoadLevelTemplate(-1);
1650 LoadLevel_InitVersion(&level, filename);
1651 LoadLevel_InitElements(&level, filename);
1652 LoadLevel_InitPlayfield(&level, filename);
1654 LoadLevel_InitLevel(&level, filename);
1658 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1660 putFileVersion(file, level->file_version);
1661 putFileVersion(file, level->game_version);
1664 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1668 putFile8Bit(file, level->fieldx);
1669 putFile8Bit(file, level->fieldy);
1671 putFile16BitBE(file, level->time);
1672 putFile16BitBE(file, level->gems_needed);
1674 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1675 putFile8Bit(file, level->name[i]);
1677 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1678 putFile8Bit(file, level->score[i]);
1680 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1681 for (y = 0; y < 3; y++)
1682 for (x = 0; x < 3; x++)
1683 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1684 level->yamyam_content[i][x][y]));
1685 putFile8Bit(file, level->amoeba_speed);
1686 putFile8Bit(file, level->time_magic_wall);
1687 putFile8Bit(file, level->time_wheel);
1688 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1689 level->amoeba_content));
1690 putFile8Bit(file, (level->double_speed ? 1 : 0));
1691 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1692 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1693 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1695 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1697 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1700 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1704 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1705 putFile8Bit(file, level->author[i]);
1708 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1712 for (y = 0; y < level->fieldy; y++)
1713 for (x = 0; x < level->fieldx; x++)
1714 if (level->encoding_16bit_field)
1715 putFile16BitBE(file, level->field[x][y]);
1717 putFile8Bit(file, level->field[x][y]);
1721 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1725 putFile8Bit(file, EL_YAMYAM);
1726 putFile8Bit(file, level->num_yamyam_contents);
1727 putFile8Bit(file, 0);
1728 putFile8Bit(file, 0);
1730 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1731 for (y = 0; y < 3; y++)
1732 for (x = 0; x < 3; x++)
1733 if (level->encoding_16bit_field)
1734 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1736 putFile8Bit(file, level->yamyam_content[i][x][y]);
1740 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1743 int num_contents, content_xsize, content_ysize;
1744 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1746 if (element == EL_YAMYAM)
1748 num_contents = level->num_yamyam_contents;
1752 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1753 for (y = 0; y < 3; y++)
1754 for (x = 0; x < 3; x++)
1755 content_array[i][x][y] = level->yamyam_content[i][x][y];
1757 else if (element == EL_BD_AMOEBA)
1763 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1764 for (y = 0; y < 3; y++)
1765 for (x = 0; x < 3; x++)
1766 content_array[i][x][y] = EL_EMPTY;
1767 content_array[0][0][0] = level->amoeba_content;
1771 /* chunk header already written -- write empty chunk data */
1772 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1774 Error(ERR_WARN, "cannot save content for element '%d'", element);
1778 putFile16BitBE(file, element);
1779 putFile8Bit(file, num_contents);
1780 putFile8Bit(file, content_xsize);
1781 putFile8Bit(file, content_ysize);
1783 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1785 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1786 for (y = 0; y < 3; y++)
1787 for (x = 0; x < 3; x++)
1788 putFile16BitBE(file, content_array[i][x][y]);
1791 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1794 int envelope_nr = element - EL_ENVELOPE_1;
1795 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1797 putFile16BitBE(file, element);
1798 putFile16BitBE(file, envelope_len);
1799 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1800 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1802 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1804 for (i = 0; i < envelope_len; i++)
1805 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1809 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1810 int num_changed_custom_elements)
1814 putFile16BitBE(file, num_changed_custom_elements);
1816 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1818 int element = EL_CUSTOM_START + i;
1820 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1822 if (check < num_changed_custom_elements)
1824 putFile16BitBE(file, element);
1825 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1832 if (check != num_changed_custom_elements) /* should not happen */
1833 Error(ERR_WARN, "inconsistent number of custom element properties");
1838 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1839 int num_changed_custom_elements)
1843 putFile16BitBE(file, num_changed_custom_elements);
1845 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1847 int element = EL_CUSTOM_START + i;
1849 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1851 if (check < num_changed_custom_elements)
1853 putFile16BitBE(file, element);
1854 putFile16BitBE(file, element_info[element].change->target_element);
1861 if (check != num_changed_custom_elements) /* should not happen */
1862 Error(ERR_WARN, "inconsistent number of custom target elements");
1867 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1868 int num_changed_custom_elements)
1870 int i, j, x, y, check = 0;
1872 putFile16BitBE(file, num_changed_custom_elements);
1874 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1876 int element = EL_CUSTOM_START + i;
1878 if (element_info[element].modified_settings)
1880 if (check < num_changed_custom_elements)
1882 putFile16BitBE(file, element);
1884 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1885 putFile8Bit(file, element_info[element].description[j]);
1887 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1889 /* some free bytes for future properties and padding */
1890 WriteUnusedBytesToFile(file, 7);
1892 putFile8Bit(file, element_info[element].use_gfx_element);
1893 putFile16BitBE(file, element_info[element].gfx_element);
1895 putFile8Bit(file, element_info[element].collect_score);
1896 putFile8Bit(file, element_info[element].collect_count);
1898 putFile16BitBE(file, element_info[element].push_delay_fixed);
1899 putFile16BitBE(file, element_info[element].push_delay_random);
1900 putFile16BitBE(file, element_info[element].move_delay_fixed);
1901 putFile16BitBE(file, element_info[element].move_delay_random);
1903 putFile16BitBE(file, element_info[element].move_pattern);
1904 putFile8Bit(file, element_info[element].move_direction_initial);
1905 putFile8Bit(file, element_info[element].move_stepsize);
1907 for (y = 0; y < 3; y++)
1908 for (x = 0; x < 3; x++)
1909 putFile16BitBE(file, element_info[element].content[x][y]);
1911 putFile32BitBE(file, element_info[element].change->events);
1913 putFile16BitBE(file, element_info[element].change->target_element);
1915 putFile16BitBE(file, element_info[element].change->delay_fixed);
1916 putFile16BitBE(file, element_info[element].change->delay_random);
1917 putFile16BitBE(file, element_info[element].change->delay_frames);
1919 putFile16BitBE(file, element_info[element].change->trigger_element);
1921 putFile8Bit(file, element_info[element].change->explode);
1922 putFile8Bit(file, element_info[element].change->use_content);
1923 putFile8Bit(file, element_info[element].change->only_complete);
1924 putFile8Bit(file, element_info[element].change->use_random_change);
1926 putFile8Bit(file, element_info[element].change->random);
1927 putFile8Bit(file, element_info[element].change->power);
1929 for (y = 0; y < 3; y++)
1930 for (x = 0; x < 3; x++)
1931 putFile16BitBE(file, element_info[element].change->content[x][y]);
1933 putFile8Bit(file, element_info[element].slippery_type);
1935 /* some free bytes for future properties and padding */
1936 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1943 if (check != num_changed_custom_elements) /* should not happen */
1944 Error(ERR_WARN, "inconsistent number of custom element properties");
1948 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1950 struct ElementInfo *ei = &element_info[element];
1953 putFile16BitBE(file, element);
1955 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1956 putFile8Bit(file, ei->description[i]);
1958 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1959 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1961 putFile8Bit(file, ei->num_change_pages);
1963 /* some free bytes for future base property values and padding */
1964 WriteUnusedBytesToFile(file, 5);
1966 /* write custom property values */
1968 putFile8Bit(file, ei->use_gfx_element);
1969 putFile16BitBE(file, ei->gfx_element);
1971 putFile8Bit(file, ei->collect_score);
1972 putFile8Bit(file, ei->collect_count);
1974 putFile16BitBE(file, ei->push_delay_fixed);
1975 putFile16BitBE(file, ei->push_delay_random);
1976 putFile16BitBE(file, ei->move_delay_fixed);
1977 putFile16BitBE(file, ei->move_delay_random);
1979 putFile16BitBE(file, ei->move_pattern);
1980 putFile8Bit(file, ei->move_direction_initial);
1981 putFile8Bit(file, ei->move_stepsize);
1983 putFile8Bit(file, ei->slippery_type);
1985 for (y = 0; y < 3; y++)
1986 for (x = 0; x < 3; x++)
1987 putFile16BitBE(file, ei->content[x][y]);
1989 /* some free bytes for future custom property values and padding */
1990 WriteUnusedBytesToFile(file, 12);
1992 /* write change property values */
1994 for (i = 0; i < ei->num_change_pages; i++)
1996 struct ElementChangeInfo *change = &ei->change_page[i];
1998 putFile32BitBE(file, change->events);
2000 putFile16BitBE(file, change->target_element);
2002 putFile16BitBE(file, change->delay_fixed);
2003 putFile16BitBE(file, change->delay_random);
2004 putFile16BitBE(file, change->delay_frames);
2006 putFile16BitBE(file, change->trigger_element);
2008 putFile8Bit(file, change->explode);
2009 putFile8Bit(file, change->use_content);
2010 putFile8Bit(file, change->only_complete);
2011 putFile8Bit(file, change->use_random_change);
2013 putFile8Bit(file, change->random);
2014 putFile8Bit(file, change->power);
2016 for (y = 0; y < 3; y++)
2017 for (x = 0; x < 3; x++)
2018 putFile16BitBE(file, change->content[x][y]);
2020 putFile8Bit(file, change->can_change);
2022 putFile8Bit(file, change->sides);
2024 /* some free bytes for future change property values and padding */
2025 WriteUnusedBytesToFile(file, 8);
2029 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2031 int body_chunk_size;
2035 if (!(file = fopen(filename, MODE_WRITE)))
2037 Error(ERR_WARN, "cannot save level file '%s'", filename);
2041 level->file_version = FILE_VERSION_ACTUAL;
2042 level->game_version = GAME_VERSION_ACTUAL;
2044 /* check level field for 16-bit elements */
2045 level->encoding_16bit_field = FALSE;
2046 for (y = 0; y < level->fieldy; y++)
2047 for (x = 0; x < level->fieldx; x++)
2048 if (level->field[x][y] > 255)
2049 level->encoding_16bit_field = TRUE;
2051 /* check yamyam content for 16-bit elements */
2052 level->encoding_16bit_yamyam = FALSE;
2053 for (i = 0; i < level->num_yamyam_contents; i++)
2054 for (y = 0; y < 3; y++)
2055 for (x = 0; x < 3; x++)
2056 if (level->yamyam_content[i][x][y] > 255)
2057 level->encoding_16bit_yamyam = TRUE;
2059 /* check amoeba content for 16-bit elements */
2060 level->encoding_16bit_amoeba = FALSE;
2061 if (level->amoeba_content > 255)
2062 level->encoding_16bit_amoeba = TRUE;
2064 /* calculate size of "BODY" chunk */
2066 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2068 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2069 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2071 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2072 SaveLevel_VERS(file, level);
2074 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2075 SaveLevel_HEAD(file, level);
2077 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2078 SaveLevel_AUTH(file, level);
2080 putFileChunkBE(file, "BODY", body_chunk_size);
2081 SaveLevel_BODY(file, level);
2083 if (level->encoding_16bit_yamyam ||
2084 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2086 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2087 SaveLevel_CNT2(file, level, EL_YAMYAM);
2090 if (level->encoding_16bit_amoeba)
2092 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2093 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2096 /* check for envelope content */
2097 for (i = 0; i < 4; i++)
2099 if (strlen(level->envelope_text[i]) > 0)
2101 int envelope_len = strlen(level->envelope_text[i]) + 1;
2103 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
2104 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2108 /* check for non-default custom elements (unless using template level) */
2109 if (!level->use_custom_template)
2111 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2113 int element = EL_CUSTOM_START + i;
2115 if (element_info[element].modified_settings)
2117 int num_change_pages = element_info[element].num_change_pages;
2119 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2120 SaveLevel_CUS4(file, level, element);
2127 SetFilePermissions(filename, PERMS_PRIVATE);
2130 void SaveLevel(int nr)
2132 char *filename = getDefaultLevelFilename(nr);
2134 SaveLevelFromFilename(&level, filename);
2137 void SaveLevelTemplate()
2139 char *filename = getDefaultLevelFilename(-1);
2141 SaveLevelFromFilename(&level, filename);
2144 void DumpLevel(struct LevelInfo *level)
2146 printf_line("-", 79);
2147 printf("Level xxx (file version %08d, game version %08d)\n",
2148 level->file_version, level->game_version);
2149 printf_line("-", 79);
2151 printf("Level Author: '%s'\n", level->author);
2152 printf("Level Title: '%s'\n", level->name);
2154 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2156 printf("Level Time: %d seconds\n", level->time);
2157 printf("Gems needed: %d\n", level->gems_needed);
2159 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2160 printf("Time for Wheel: %d seconds\n", level->time_wheel);
2161 printf("Time for Light: %d seconds\n", level->time_light);
2162 printf("Time for Timegate: %d seconds\n", level->time_timegate);
2164 printf("Amoeba Speed: %d\n", level->amoeba_speed);
2166 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2167 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
2168 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2170 printf_line("-", 79);
2174 /* ========================================================================= */
2175 /* tape file functions */
2176 /* ========================================================================= */
2178 static void setTapeInfoToDefaults()
2182 /* always start with reliable default values (empty tape) */
2185 /* default values (also for pre-1.2 tapes) with only the first player */
2186 tape.player_participates[0] = TRUE;
2187 for (i = 1; i < MAX_PLAYERS; i++)
2188 tape.player_participates[i] = FALSE;
2190 /* at least one (default: the first) player participates in every tape */
2191 tape.num_participating_players = 1;
2193 tape.level_nr = level_nr;
2195 tape.changed = FALSE;
2197 tape.recording = FALSE;
2198 tape.playing = FALSE;
2199 tape.pausing = FALSE;
2202 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2204 tape->file_version = getFileVersion(file);
2205 tape->game_version = getFileVersion(file);
2210 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2214 tape->random_seed = getFile32BitBE(file);
2215 tape->date = getFile32BitBE(file);
2216 tape->length = getFile32BitBE(file);
2218 /* read header fields that are new since version 1.2 */
2219 if (tape->file_version >= FILE_VERSION_1_2)
2221 byte store_participating_players = getFile8Bit(file);
2224 /* since version 1.2, tapes store which players participate in the tape */
2225 tape->num_participating_players = 0;
2226 for (i = 0; i < MAX_PLAYERS; i++)
2228 tape->player_participates[i] = FALSE;
2230 if (store_participating_players & (1 << i))
2232 tape->player_participates[i] = TRUE;
2233 tape->num_participating_players++;
2237 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2239 engine_version = getFileVersion(file);
2240 if (engine_version > 0)
2241 tape->engine_version = engine_version;
2243 tape->engine_version = tape->game_version;
2249 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2251 int level_identifier_size;
2254 level_identifier_size = getFile16BitBE(file);
2256 tape->level_identifier =
2257 checked_realloc(tape->level_identifier, level_identifier_size);
2259 for (i = 0; i < level_identifier_size; i++)
2260 tape->level_identifier[i] = getFile8Bit(file);
2262 tape->level_nr = getFile16BitBE(file);
2264 chunk_size = 2 + level_identifier_size + 2;
2269 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2272 int chunk_size_expected =
2273 (tape->num_participating_players + 1) * tape->length;
2275 if (chunk_size_expected != chunk_size)
2277 ReadUnusedBytesFromFile(file, chunk_size);
2278 return chunk_size_expected;
2281 for (i = 0; i < tape->length; i++)
2283 if (i >= MAX_TAPELEN)
2286 for (j = 0; j < MAX_PLAYERS; j++)
2288 tape->pos[i].action[j] = MV_NO_MOVING;
2290 if (tape->player_participates[j])
2291 tape->pos[i].action[j] = getFile8Bit(file);
2294 tape->pos[i].delay = getFile8Bit(file);
2296 if (tape->file_version == FILE_VERSION_1_0)
2298 /* eliminate possible diagonal moves in old tapes */
2299 /* this is only for backward compatibility */
2301 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2302 byte action = tape->pos[i].action[0];
2303 int k, num_moves = 0;
2305 for (k = 0; k<4; k++)
2307 if (action & joy_dir[k])
2309 tape->pos[i + num_moves].action[0] = joy_dir[k];
2311 tape->pos[i + num_moves].delay = 0;
2320 tape->length += num_moves;
2323 else if (tape->file_version < FILE_VERSION_2_0)
2325 /* convert pre-2.0 tapes to new tape format */
2327 if (tape->pos[i].delay > 1)
2330 tape->pos[i + 1] = tape->pos[i];
2331 tape->pos[i + 1].delay = 1;
2334 for (j = 0; j < MAX_PLAYERS; j++)
2335 tape->pos[i].action[j] = MV_NO_MOVING;
2336 tape->pos[i].delay--;
2347 if (i != tape->length)
2348 chunk_size = (tape->num_participating_players + 1) * i;
2353 void LoadTapeFromFilename(char *filename)
2355 char cookie[MAX_LINE_LEN];
2356 char chunk_name[CHUNK_ID_LEN + 1];
2360 /* always start with reliable default values */
2361 setTapeInfoToDefaults();
2363 if (!(file = fopen(filename, MODE_READ)))
2366 getFileChunkBE(file, chunk_name, NULL);
2367 if (strcmp(chunk_name, "RND1") == 0)
2369 getFile32BitBE(file); /* not used */
2371 getFileChunkBE(file, chunk_name, NULL);
2372 if (strcmp(chunk_name, "TAPE") != 0)
2374 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2379 else /* check for pre-2.0 file format with cookie string */
2381 strcpy(cookie, chunk_name);
2382 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2383 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2384 cookie[strlen(cookie) - 1] = '\0';
2386 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2388 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2393 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2395 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2400 /* pre-2.0 tape files have no game version, so use file version here */
2401 tape.game_version = tape.file_version;
2404 if (tape.file_version < FILE_VERSION_1_2)
2406 /* tape files from versions before 1.2.0 without chunk structure */
2407 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2408 LoadTape_BODY(file, 2 * tape.length, &tape);
2416 int (*loader)(FILE *, int, struct TapeInfo *);
2420 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2421 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2422 { "INFO", -1, LoadTape_INFO },
2423 { "BODY", -1, LoadTape_BODY },
2427 while (getFileChunkBE(file, chunk_name, &chunk_size))
2431 while (chunk_info[i].name != NULL &&
2432 strcmp(chunk_name, chunk_info[i].name) != 0)
2435 if (chunk_info[i].name == NULL)
2437 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2438 chunk_name, filename);
2439 ReadUnusedBytesFromFile(file, chunk_size);
2441 else if (chunk_info[i].size != -1 &&
2442 chunk_info[i].size != chunk_size)
2444 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2445 chunk_size, chunk_name, filename);
2446 ReadUnusedBytesFromFile(file, chunk_size);
2450 /* call function to load this tape chunk */
2451 int chunk_size_expected =
2452 (chunk_info[i].loader)(file, chunk_size, &tape);
2454 /* the size of some chunks cannot be checked before reading other
2455 chunks first (like "HEAD" and "BODY") that contain some header
2456 information, so check them here */
2457 if (chunk_size_expected != chunk_size)
2459 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2460 chunk_size, chunk_name, filename);
2468 tape.length_seconds = GetTapeLength();
2471 printf("::: tape game version: %d\n", tape.game_version);
2472 printf("::: tape engine version: %d\n", tape.engine_version);
2476 void LoadTape(int nr)
2478 char *filename = getTapeFilename(nr);
2480 LoadTapeFromFilename(filename);
2483 void LoadSolutionTape(int nr)
2485 char *filename = getSolutionTapeFilename(nr);
2487 LoadTapeFromFilename(filename);
2490 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2492 putFileVersion(file, tape->file_version);
2493 putFileVersion(file, tape->game_version);
2496 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2499 byte store_participating_players = 0;
2501 /* set bits for participating players for compact storage */
2502 for (i = 0; i < MAX_PLAYERS; i++)
2503 if (tape->player_participates[i])
2504 store_participating_players |= (1 << i);
2506 putFile32BitBE(file, tape->random_seed);
2507 putFile32BitBE(file, tape->date);
2508 putFile32BitBE(file, tape->length);
2510 putFile8Bit(file, store_participating_players);
2512 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2513 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2515 putFileVersion(file, tape->engine_version);
2518 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2520 int level_identifier_size = strlen(tape->level_identifier) + 1;
2523 putFile16BitBE(file, level_identifier_size);
2525 for (i = 0; i < level_identifier_size; i++)
2526 putFile8Bit(file, tape->level_identifier[i]);
2528 putFile16BitBE(file, tape->level_nr);
2531 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2535 for (i = 0; i < tape->length; i++)
2537 for (j = 0; j < MAX_PLAYERS; j++)
2538 if (tape->player_participates[j])
2539 putFile8Bit(file, tape->pos[i].action[j]);
2541 putFile8Bit(file, tape->pos[i].delay);
2545 void SaveTape(int nr)
2547 char *filename = getTapeFilename(nr);
2549 boolean new_tape = TRUE;
2550 int num_participating_players = 0;
2551 int info_chunk_size;
2552 int body_chunk_size;
2555 InitTapeDirectory(leveldir_current->filename);
2557 /* if a tape still exists, ask to overwrite it */
2558 if (access(filename, F_OK) == 0)
2561 if (!Request("Replace old tape ?", REQ_ASK))
2565 if (!(file = fopen(filename, MODE_WRITE)))
2567 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2571 tape.file_version = FILE_VERSION_ACTUAL;
2572 tape.game_version = GAME_VERSION_ACTUAL;
2574 /* count number of participating players */
2575 for (i = 0; i < MAX_PLAYERS; i++)
2576 if (tape.player_participates[i])
2577 num_participating_players++;
2579 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2580 body_chunk_size = (num_participating_players + 1) * tape.length;
2582 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2583 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2585 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2586 SaveTape_VERS(file, &tape);
2588 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2589 SaveTape_HEAD(file, &tape);
2591 putFileChunkBE(file, "INFO", info_chunk_size);
2592 SaveTape_INFO(file, &tape);
2594 putFileChunkBE(file, "BODY", body_chunk_size);
2595 SaveTape_BODY(file, &tape);
2599 SetFilePermissions(filename, PERMS_PRIVATE);
2601 tape.changed = FALSE;
2604 Request("tape saved !", REQ_CONFIRM);
2607 void DumpTape(struct TapeInfo *tape)
2611 if (TAPE_IS_EMPTY(*tape))
2613 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2617 printf_line("-", 79);
2618 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2619 tape->level_nr, tape->file_version, tape->game_version);
2620 printf("Level series identifier: '%s'\n", tape->level_identifier);
2621 printf_line("-", 79);
2623 for (i = 0; i < tape->length; i++)
2625 if (i >= MAX_TAPELEN)
2628 printf("%03d: ", i);
2630 for (j = 0; j < MAX_PLAYERS; j++)
2632 if (tape->player_participates[j])
2634 int action = tape->pos[i].action[j];
2636 printf("%d:%02x ", j, action);
2637 printf("[%c%c%c%c|%c%c] - ",
2638 (action & JOY_LEFT ? '<' : ' '),
2639 (action & JOY_RIGHT ? '>' : ' '),
2640 (action & JOY_UP ? '^' : ' '),
2641 (action & JOY_DOWN ? 'v' : ' '),
2642 (action & JOY_BUTTON_1 ? '1' : ' '),
2643 (action & JOY_BUTTON_2 ? '2' : ' '));
2647 printf("(%03d)\n", tape->pos[i].delay);
2650 printf_line("-", 79);
2654 /* ========================================================================= */
2655 /* score file functions */
2656 /* ========================================================================= */
2658 void LoadScore(int nr)
2661 char *filename = getScoreFilename(nr);
2662 char cookie[MAX_LINE_LEN];
2663 char line[MAX_LINE_LEN];
2667 /* always start with reliable default values */
2668 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2670 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2671 highscore[i].Score = 0;
2674 if (!(file = fopen(filename, MODE_READ)))
2677 /* check file identifier */
2678 fgets(cookie, MAX_LINE_LEN, file);
2679 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2680 cookie[strlen(cookie) - 1] = '\0';
2682 if (!checkCookieString(cookie, SCORE_COOKIE))
2684 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2689 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2691 fscanf(file, "%d", &highscore[i].Score);
2692 fgets(line, MAX_LINE_LEN, file);
2694 if (line[strlen(line) - 1] == '\n')
2695 line[strlen(line) - 1] = '\0';
2697 for (line_ptr = line; *line_ptr; line_ptr++)
2699 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2701 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2702 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2711 void SaveScore(int nr)
2714 char *filename = getScoreFilename(nr);
2717 InitScoreDirectory(leveldir_current->filename);
2719 if (!(file = fopen(filename, MODE_WRITE)))
2721 Error(ERR_WARN, "cannot save score for level %d", nr);
2725 fprintf(file, "%s\n\n", SCORE_COOKIE);
2727 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2728 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2732 SetFilePermissions(filename, PERMS_PUBLIC);
2736 /* ========================================================================= */
2737 /* setup file functions */
2738 /* ========================================================================= */
2740 #define TOKEN_STR_PLAYER_PREFIX "player_"
2743 #define SETUP_TOKEN_PLAYER_NAME 0
2744 #define SETUP_TOKEN_SOUND 1
2745 #define SETUP_TOKEN_SOUND_LOOPS 2
2746 #define SETUP_TOKEN_SOUND_MUSIC 3
2747 #define SETUP_TOKEN_SOUND_SIMPLE 4
2748 #define SETUP_TOKEN_TOONS 5
2749 #define SETUP_TOKEN_SCROLL_DELAY 6
2750 #define SETUP_TOKEN_SOFT_SCROLLING 7
2751 #define SETUP_TOKEN_FADING 8
2752 #define SETUP_TOKEN_AUTORECORD 9
2753 #define SETUP_TOKEN_QUICK_DOORS 10
2754 #define SETUP_TOKEN_TEAM_MODE 11
2755 #define SETUP_TOKEN_HANDICAP 12
2756 #define SETUP_TOKEN_TIME_LIMIT 13
2757 #define SETUP_TOKEN_FULLSCREEN 14
2758 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2759 #define SETUP_TOKEN_GRAPHICS_SET 16
2760 #define SETUP_TOKEN_SOUNDS_SET 17
2761 #define SETUP_TOKEN_MUSIC_SET 18
2762 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2763 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2764 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2766 #define NUM_GLOBAL_SETUP_TOKENS 22
2769 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2770 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2771 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2772 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2773 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2774 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2775 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2776 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2777 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2778 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2779 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2780 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2782 #define NUM_EDITOR_SETUP_TOKENS 12
2784 /* shortcut setup */
2785 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2786 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2787 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2789 #define NUM_SHORTCUT_SETUP_TOKENS 3
2792 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2793 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2794 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2795 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2796 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2797 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2798 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2799 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2800 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2801 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
2802 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2803 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2804 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2805 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2806 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2807 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
2809 #define NUM_PLAYER_SETUP_TOKENS 16
2812 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2813 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2815 #define NUM_SYSTEM_SETUP_TOKENS 2
2818 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2820 #define NUM_OPTIONS_SETUP_TOKENS 1
2823 static struct SetupInfo si;
2824 static struct SetupEditorInfo sei;
2825 static struct SetupShortcutInfo ssi;
2826 static struct SetupInputInfo sii;
2827 static struct SetupSystemInfo syi;
2828 static struct OptionInfo soi;
2830 static struct TokenInfo global_setup_tokens[] =
2832 { TYPE_STRING, &si.player_name, "player_name" },
2833 { TYPE_SWITCH, &si.sound, "sound" },
2834 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2835 { TYPE_SWITCH, &si.sound_music, "background_music" },
2836 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2837 { TYPE_SWITCH, &si.toons, "toons" },
2838 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2839 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2840 { TYPE_SWITCH, &si.fading, "screen_fading" },
2841 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2842 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2843 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2844 { TYPE_SWITCH, &si.handicap, "handicap" },
2845 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2846 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2847 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2848 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2849 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2850 { TYPE_STRING, &si.music_set, "music_set" },
2851 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2852 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2853 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2856 static struct TokenInfo editor_setup_tokens[] =
2858 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2859 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2860 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2861 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2862 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2863 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2864 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2865 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2866 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2867 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2868 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2869 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2872 static struct TokenInfo shortcut_setup_tokens[] =
2874 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2875 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2876 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2879 static struct TokenInfo player_setup_tokens[] =
2881 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2882 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2883 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2884 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2885 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2886 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2887 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2888 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2889 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2890 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
2891 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2892 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2893 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2894 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2895 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2896 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
2899 static struct TokenInfo system_setup_tokens[] =
2901 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2902 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2905 static struct TokenInfo options_setup_tokens[] =
2907 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2910 static char *get_corrected_login_name(char *login_name)
2912 /* needed because player name must be a fixed length string */
2913 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2915 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2916 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2918 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2919 if (strchr(login_name_new, ' '))
2920 *strchr(login_name_new, ' ') = '\0';
2922 return login_name_new;
2925 static void setSetupInfoToDefaults(struct SetupInfo *si)
2929 si->player_name = get_corrected_login_name(getLoginName());
2932 si->sound_loops = TRUE;
2933 si->sound_music = TRUE;
2934 si->sound_simple = TRUE;
2936 si->double_buffering = TRUE;
2937 si->direct_draw = !si->double_buffering;
2938 si->scroll_delay = TRUE;
2939 si->soft_scrolling = TRUE;
2941 si->autorecord = TRUE;
2942 si->quick_doors = FALSE;
2943 si->team_mode = FALSE;
2944 si->handicap = TRUE;
2945 si->time_limit = TRUE;
2946 si->fullscreen = FALSE;
2947 si->ask_on_escape = TRUE;
2949 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2950 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2951 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2952 si->override_level_graphics = FALSE;
2953 si->override_level_sounds = FALSE;
2954 si->override_level_music = FALSE;
2956 si->editor.el_boulderdash = TRUE;
2957 si->editor.el_emerald_mine = TRUE;
2958 si->editor.el_more = TRUE;
2959 si->editor.el_sokoban = TRUE;
2960 si->editor.el_supaplex = TRUE;
2961 si->editor.el_diamond_caves = TRUE;
2962 si->editor.el_dx_boulderdash = TRUE;
2963 si->editor.el_chars = TRUE;
2964 si->editor.el_custom = TRUE;
2965 si->editor.el_custom_more = FALSE;
2967 si->editor.el_headlines = TRUE;
2968 si->editor.el_user_defined = FALSE;
2970 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2971 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2972 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2974 for (i = 0; i < MAX_PLAYERS; i++)
2976 si->input[i].use_joystick = FALSE;
2977 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2978 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2979 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2980 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2981 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2982 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2983 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2984 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2985 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
2986 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2987 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2988 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2989 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2990 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2991 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
2994 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2995 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2997 si->options.verbose = FALSE;
3000 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3004 if (!setup_file_hash)
3009 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3010 setSetupInfo(global_setup_tokens, i,
3011 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3016 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3017 setSetupInfo(editor_setup_tokens, i,
3018 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3021 /* shortcut setup */
3022 ssi = setup.shortcut;
3023 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3024 setSetupInfo(shortcut_setup_tokens, i,
3025 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3026 setup.shortcut = ssi;
3029 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3033 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3035 sii = setup.input[pnr];
3036 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3038 char full_token[100];
3040 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3041 setSetupInfo(player_setup_tokens, i,
3042 getHashEntry(setup_file_hash, full_token));
3044 setup.input[pnr] = sii;
3049 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3050 setSetupInfo(system_setup_tokens, i,
3051 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3055 soi = setup.options;
3056 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3057 setSetupInfo(options_setup_tokens, i,
3058 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3059 setup.options = soi;
3064 char *filename = getSetupFilename();
3065 SetupFileHash *setup_file_hash = NULL;
3067 /* always start with reliable default values */
3068 setSetupInfoToDefaults(&setup);
3070 setup_file_hash = loadSetupFileHash(filename);
3072 if (setup_file_hash)
3074 char *player_name_new;
3076 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3077 decodeSetupFileHash(setup_file_hash);
3079 setup.direct_draw = !setup.double_buffering;
3081 freeSetupFileHash(setup_file_hash);
3083 /* needed to work around problems with fixed length strings */
3084 player_name_new = get_corrected_login_name(setup.player_name);
3085 free(setup.player_name);
3086 setup.player_name = player_name_new;
3089 Error(ERR_WARN, "using default setup values");
3094 char *filename = getSetupFilename();
3098 InitUserDataDirectory();
3100 if (!(file = fopen(filename, MODE_WRITE)))
3102 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3106 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3107 getCookie("SETUP")));
3108 fprintf(file, "\n");
3112 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3114 /* just to make things nicer :) */
3115 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3116 i == SETUP_TOKEN_GRAPHICS_SET)
3117 fprintf(file, "\n");
3119 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3124 fprintf(file, "\n");
3125 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3126 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3128 /* shortcut setup */
3129 ssi = setup.shortcut;
3130 fprintf(file, "\n");
3131 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3132 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3135 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3139 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3140 fprintf(file, "\n");
3142 sii = setup.input[pnr];
3143 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3144 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3149 fprintf(file, "\n");
3150 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3151 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3154 soi = setup.options;
3155 fprintf(file, "\n");
3156 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3157 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3161 SetFilePermissions(filename, PERMS_PRIVATE);
3164 void LoadCustomElementDescriptions()
3166 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3167 SetupFileHash *setup_file_hash;
3170 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3172 if (element_info[i].custom_description != NULL)
3174 free(element_info[i].custom_description);
3175 element_info[i].custom_description = NULL;
3179 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3182 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3184 char *token = getStringCat2(element_info[i].token_name, ".name");
3185 char *value = getHashEntry(setup_file_hash, token);
3188 element_info[i].custom_description = getStringCopy(value);
3193 freeSetupFileHash(setup_file_hash);
3196 void LoadSpecialMenuDesignSettings()
3198 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3199 SetupFileHash *setup_file_hash;
3202 /* always start with reliable default values from default config */
3203 for (i = 0; image_config_vars[i].token != NULL; i++)
3204 for (j = 0; image_config[j].token != NULL; j++)
3205 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3206 *image_config_vars[i].value =
3207 get_auto_parameter_value(image_config_vars[i].token,
3208 image_config[j].value);
3210 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3213 /* special case: initialize with default values that may be overwritten */
3214 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3216 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3217 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3218 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3220 if (value_x != NULL)
3221 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3222 if (value_y != NULL)
3223 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3224 if (list_size != NULL)
3225 menu.list_size[i] = get_integer_from_string(list_size);
3228 /* read (and overwrite with) values that may be specified in config file */
3229 for (i = 0; image_config_vars[i].token != NULL; i++)
3231 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3234 *image_config_vars[i].value =
3235 get_auto_parameter_value(image_config_vars[i].token, value);
3238 freeSetupFileHash(setup_file_hash);
3241 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3243 char *filename = getEditorSetupFilename();
3244 SetupFileList *setup_file_list, *list;
3245 SetupFileHash *element_hash;
3246 int num_unknown_tokens = 0;
3249 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3252 element_hash = newSetupFileHash();
3254 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3255 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3257 /* determined size may be larger than needed (due to unknown elements) */
3259 for (list = setup_file_list; list != NULL; list = list->next)
3262 /* add space for up to 3 more elements for padding that may be needed */
3265 *elements = checked_malloc(*num_elements * sizeof(int));
3268 for (list = setup_file_list; list != NULL; list = list->next)
3270 char *value = getHashEntry(element_hash, list->token);
3274 (*elements)[(*num_elements)++] = atoi(value);
3278 if (num_unknown_tokens == 0)
3280 Error(ERR_RETURN_LINE, "-");
3281 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3282 Error(ERR_RETURN, "- config file: '%s'", filename);
3284 num_unknown_tokens++;
3287 Error(ERR_RETURN, "- token: '%s'", list->token);
3291 if (num_unknown_tokens > 0)
3292 Error(ERR_RETURN_LINE, "-");
3294 while (*num_elements % 4) /* pad with empty elements, if needed */
3295 (*elements)[(*num_elements)++] = EL_EMPTY;
3297 freeSetupFileList(setup_file_list);
3298 freeSetupFileHash(element_hash);
3302 for (i = 0; i < *num_elements; i++)
3303 printf("editor: element '%s' [%d]\n",
3304 element_info[(*elements)[i]].token_name, (*elements)[i]);
3308 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3311 SetupFileHash *setup_file_hash = NULL;
3312 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3313 char *filename_music, *filename_prefix, *filename_info;
3319 token_to_value_ptr[] =
3321 { "title_header", &tmp_music_file_info.title_header },
3322 { "artist_header", &tmp_music_file_info.artist_header },
3323 { "album_header", &tmp_music_file_info.album_header },
3324 { "year_header", &tmp_music_file_info.year_header },
3326 { "title", &tmp_music_file_info.title },
3327 { "artist", &tmp_music_file_info.artist },
3328 { "album", &tmp_music_file_info.album },
3329 { "year", &tmp_music_file_info.year },
3335 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3336 getCustomMusicFilename(basename));
3338 if (filename_music == NULL)
3341 /* ---------- try to replace file extension ---------- */
3343 filename_prefix = getStringCopy(filename_music);
3344 if (strrchr(filename_prefix, '.') != NULL)
3345 *strrchr(filename_prefix, '.') = '\0';
3346 filename_info = getStringCat2(filename_prefix, ".txt");
3349 printf("trying to load file '%s'...\n", filename_info);
3352 if (fileExists(filename_info))
3353 setup_file_hash = loadSetupFileHash(filename_info);
3355 free(filename_prefix);
3356 free(filename_info);
3358 if (setup_file_hash == NULL)
3360 /* ---------- try to add file extension ---------- */
3362 filename_prefix = getStringCopy(filename_music);
3363 filename_info = getStringCat2(filename_prefix, ".txt");
3366 printf("trying to load file '%s'...\n", filename_info);
3369 if (fileExists(filename_info))
3370 setup_file_hash = loadSetupFileHash(filename_info);
3372 free(filename_prefix);
3373 free(filename_info);
3376 if (setup_file_hash == NULL)
3379 /* ---------- music file info found ---------- */
3381 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3383 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3385 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3387 *token_to_value_ptr[i].value_ptr =
3388 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3391 tmp_music_file_info.basename = getStringCopy(basename);
3392 tmp_music_file_info.music = music;
3393 tmp_music_file_info.is_sound = is_sound;
3395 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3396 *new_music_file_info = tmp_music_file_info;
3398 return new_music_file_info;
3401 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3403 return get_music_file_info_ext(basename, music, FALSE);
3406 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3408 return get_music_file_info_ext(basename, sound, TRUE);
3411 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3412 char *basename, boolean is_sound)
3414 for (; list != NULL; list = list->next)
3415 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3421 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3423 return music_info_listed_ext(list, basename, FALSE);
3426 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3428 return music_info_listed_ext(list, basename, TRUE);
3431 void LoadMusicInfo()
3433 char *music_directory = getCustomMusicDirectory();
3434 int num_music = getMusicListSize();
3435 int num_music_noconf = 0;
3436 int num_sounds = getSoundListSize();
3438 struct dirent *dir_entry;
3439 struct FileInfo *music, *sound;
3440 struct MusicFileInfo *next, **new;
3443 while (music_file_info != NULL)
3445 next = music_file_info->next;
3447 checked_free(music_file_info->basename);
3449 checked_free(music_file_info->title_header);
3450 checked_free(music_file_info->artist_header);
3451 checked_free(music_file_info->album_header);
3452 checked_free(music_file_info->year_header);
3454 checked_free(music_file_info->title);
3455 checked_free(music_file_info->artist);
3456 checked_free(music_file_info->album);
3457 checked_free(music_file_info->year);
3459 free(music_file_info);
3461 music_file_info = next;
3464 new = &music_file_info;
3467 printf("::: num_music == %d\n", num_music);
3470 for (i = 0; i < num_music; i++)
3472 music = getMusicListEntry(i);
3475 printf("::: %d [%08x]\n", i, music->filename);
3478 if (music->filename == NULL)
3481 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3484 /* a configured file may be not recognized as music */
3485 if (!FileIsMusic(music->filename))
3489 printf("::: -> '%s' (configured)\n", music->filename);
3492 if (!music_info_listed(music_file_info, music->filename))
3494 *new = get_music_file_info(music->filename, i);
3496 new = &(*new)->next;
3500 if ((dir = opendir(music_directory)) == NULL)
3502 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3506 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
3508 char *basename = dir_entry->d_name;
3509 boolean music_already_used = FALSE;
3512 /* skip all music files that are configured in music config file */
3513 for (i = 0; i < num_music; i++)
3515 music = getMusicListEntry(i);
3517 if (music->filename == NULL)
3520 if (strcmp(basename, music->filename) == 0)
3522 music_already_used = TRUE;
3527 if (music_already_used)
3530 if (!FileIsMusic(basename))
3534 printf("::: -> '%s' (found in directory)\n", basename);
3537 if (!music_info_listed(music_file_info, basename))
3539 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3541 new = &(*new)->next;
3549 for (i = 0; i < num_sounds; i++)
3551 sound = getSoundListEntry(i);
3553 if (sound->filename == NULL)
3556 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3559 /* a configured file may be not recognized as sound */
3560 if (!FileIsSound(sound->filename))
3564 printf("::: -> '%s' (configured)\n", sound->filename);
3567 if (!sound_info_listed(music_file_info, sound->filename))
3569 *new = get_sound_file_info(sound->filename, i);
3571 new = &(*new)->next;
3577 for (next = music_file_info; next != NULL; next = next->next)
3578 printf("::: title == '%s'\n", next->title);
3582 void add_helpanim_entry(int element, int action, int direction, int delay,
3583 int *num_list_entries)
3585 struct HelpAnimInfo *new_list_entry;
3586 (*num_list_entries)++;
3589 checked_realloc(helpanim_info,
3590 *num_list_entries * sizeof(struct HelpAnimInfo));
3591 new_list_entry = &helpanim_info[*num_list_entries - 1];
3593 new_list_entry->element = element;
3594 new_list_entry->action = action;
3595 new_list_entry->direction = direction;
3596 new_list_entry->delay = delay;
3599 void print_unknown_token(char *filename, char *token, int token_nr)
3603 Error(ERR_RETURN_LINE, "-");
3604 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3605 Error(ERR_RETURN, "- config file: '%s'", filename);
3608 Error(ERR_RETURN, "- token: '%s'", token);
3611 void print_unknown_token_end(int token_nr)
3614 Error(ERR_RETURN_LINE, "-");
3617 void LoadHelpAnimInfo()
3619 char *filename = getHelpAnimFilename();
3620 SetupFileList *setup_file_list = NULL, *list;
3621 SetupFileHash *element_hash, *action_hash, *direction_hash;
3622 int num_list_entries = 0;
3623 int num_unknown_tokens = 0;
3626 if (fileExists(filename))
3627 setup_file_list = loadSetupFileList(filename);
3629 if (setup_file_list == NULL)
3631 /* use reliable default values from static configuration */
3632 SetupFileList *insert_ptr;
3634 insert_ptr = setup_file_list =
3635 newSetupFileList(helpanim_config[0].token,
3636 helpanim_config[0].value);
3638 for (i = 1; helpanim_config[i].token; i++)
3639 insert_ptr = addListEntry(insert_ptr,
3640 helpanim_config[i].token,
3641 helpanim_config[i].value);
3644 element_hash = newSetupFileHash();
3645 action_hash = newSetupFileHash();
3646 direction_hash = newSetupFileHash();
3648 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3649 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3651 for (i = 0; i < NUM_ACTIONS; i++)
3652 setHashEntry(action_hash, element_action_info[i].suffix,
3653 i_to_a(element_action_info[i].value));
3655 /* do not store direction index (bit) here, but direction value! */
3656 for (i = 0; i < NUM_DIRECTIONS; i++)
3657 setHashEntry(direction_hash, element_direction_info[i].suffix,
3658 i_to_a(1 << element_direction_info[i].value));
3660 for (list = setup_file_list; list != NULL; list = list->next)
3662 char *element_token, *action_token, *direction_token;
3663 char *element_value, *action_value, *direction_value;
3664 int delay = atoi(list->value);
3666 if (strcmp(list->token, "end") == 0)
3668 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3673 /* first try to break element into element/action/direction parts;
3674 if this does not work, also accept combined "element[.act][.dir]"
3675 elements (like "dynamite.active"), which are unique elements */
3677 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
3679 element_value = getHashEntry(element_hash, list->token);
3680 if (element_value != NULL) /* element found */
3681 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3685 /* no further suffixes found -- this is not an element */
3686 print_unknown_token(filename, list->token, num_unknown_tokens++);
3692 /* token has format "<prefix>.<something>" */
3694 action_token = strchr(list->token, '.'); /* suffix may be action ... */
3695 direction_token = action_token; /* ... or direction */
3697 element_token = getStringCopy(list->token);
3698 *strchr(element_token, '.') = '\0';
3700 element_value = getHashEntry(element_hash, element_token);
3702 if (element_value == NULL) /* this is no element */
3704 element_value = getHashEntry(element_hash, list->token);
3705 if (element_value != NULL) /* combined element found */
3706 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3709 print_unknown_token(filename, list->token, num_unknown_tokens++);
3711 free(element_token);
3716 action_value = getHashEntry(action_hash, action_token);
3718 if (action_value != NULL) /* action found */
3720 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3723 free(element_token);
3728 direction_value = getHashEntry(direction_hash, direction_token);
3730 if (direction_value != NULL) /* direction found */
3732 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3735 free(element_token);
3740 if (strchr(action_token + 1, '.') == NULL)
3742 /* no further suffixes found -- this is not an action nor direction */
3744 element_value = getHashEntry(element_hash, list->token);
3745 if (element_value != NULL) /* combined element found */
3746 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3749 print_unknown_token(filename, list->token, num_unknown_tokens++);
3751 free(element_token);
3756 /* token has format "<prefix>.<suffix>.<something>" */
3758 direction_token = strchr(action_token + 1, '.');
3760 action_token = getStringCopy(action_token);
3761 *strchr(action_token + 1, '.') = '\0';
3763 action_value = getHashEntry(action_hash, action_token);
3765 if (action_value == NULL) /* this is no action */
3767 element_value = getHashEntry(element_hash, list->token);
3768 if (element_value != NULL) /* combined element found */
3769 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3772 print_unknown_token(filename, list->token, num_unknown_tokens++);
3774 free(element_token);
3780 direction_value = getHashEntry(direction_hash, direction_token);
3782 if (direction_value != NULL) /* direction found */
3784 add_helpanim_entry(atoi(element_value), atoi(action_value),
3785 atoi(direction_value), delay, &num_list_entries);
3787 free(element_token);
3793 /* this is no direction */
3795 element_value = getHashEntry(element_hash, list->token);
3796 if (element_value != NULL) /* combined element found */
3797 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3800 print_unknown_token(filename, list->token, num_unknown_tokens++);
3802 free(element_token);
3806 print_unknown_token_end(num_unknown_tokens);
3808 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3809 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
3811 freeSetupFileList(setup_file_list);
3812 freeSetupFileHash(element_hash);
3813 freeSetupFileHash(action_hash);
3814 freeSetupFileHash(direction_hash);
3818 for (i = 0; i < num_list_entries; i++)
3819 printf("::: %d, %d, %d => %d\n",
3820 helpanim_info[i].element,
3821 helpanim_info[i].action,
3822 helpanim_info[i].direction,
3823 helpanim_info[i].delay);
3827 void LoadHelpTextInfo()
3829 char *filename = getHelpTextFilename();
3832 if (helptext_info != NULL)
3834 freeSetupFileHash(helptext_info);
3835 helptext_info = NULL;
3838 if (fileExists(filename))
3839 helptext_info = loadSetupFileHash(filename);
3841 if (helptext_info == NULL)
3843 /* use reliable default values from static configuration */
3844 helptext_info = newSetupFileHash();
3846 for (i = 0; helptext_config[i].token; i++)
3847 setHashEntry(helptext_info,
3848 helptext_config[i].token,
3849 helptext_config[i].value);
3854 BEGIN_HASH_ITERATION(helptext_info, itr)
3856 printf("::: '%s' => '%s'\n",
3857 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3859 END_HASH_ITERATION(hash, itr)