1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
40 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
41 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
52 /* values for level file type identifier */
53 #define LEVEL_FILE_TYPE_UNKNOWN 0
54 #define LEVEL_FILE_TYPE_RND 1
55 #define LEVEL_FILE_TYPE_EM 2
57 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
58 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
60 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
61 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
64 /* ========================================================================= */
65 /* level file functions */
66 /* ========================================================================= */
68 void setElementChangePages(struct ElementInfo *ei, int change_pages)
70 int change_page_size = sizeof(struct ElementChangeInfo);
72 ei->num_change_pages = MAX(1, change_pages);
75 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
77 if (ei->current_change_page >= ei->num_change_pages)
78 ei->current_change_page = ei->num_change_pages - 1;
80 ei->change = &ei->change_page[ei->current_change_page];
83 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
87 change->can_change = FALSE;
89 change->events = CE_BITMASK_DEFAULT;
90 change->sides = CH_SIDE_ANY;
92 change->target_element = EL_EMPTY_SPACE;
94 change->delay_fixed = 0;
95 change->delay_random = 0;
96 change->delay_frames = 1;
98 change->trigger_element = EL_EMPTY_SPACE;
100 change->explode = FALSE;
101 change->use_content = FALSE;
102 change->only_complete = FALSE;
103 change->use_random_change = FALSE;
104 change->random = 100;
105 change->power = CP_NON_DESTRUCTIVE;
107 for (x = 0; x < 3; x++)
108 for (y = 0; y < 3; y++)
109 change->content[x][y] = EL_EMPTY_SPACE;
111 change->direct_action = 0;
112 change->other_action = 0;
114 change->pre_change_function = NULL;
115 change->change_function = NULL;
116 change->post_change_function = NULL;
119 static void setLevelInfoToDefaults(struct LevelInfo *level)
123 level->file_version = FILE_VERSION_ACTUAL;
124 level->game_version = GAME_VERSION_ACTUAL;
126 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
127 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
128 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
130 level->fieldx = STD_LEV_FIELDX;
131 level->fieldy = STD_LEV_FIELDY;
133 for (x = 0; x < MAX_LEV_FIELDX; x++)
134 for (y = 0; y < MAX_LEV_FIELDY; y++)
135 level->field[x][y] = EL_SAND;
138 level->gems_needed = 0;
139 level->amoeba_speed = 10;
140 level->time_magic_wall = 10;
141 level->time_wheel = 10;
142 level->time_light = 10;
143 level->time_timegate = 10;
144 level->amoeba_content = EL_DIAMOND;
145 level->double_speed = FALSE;
146 level->initial_gravity = FALSE;
147 level->em_slippery_gems = FALSE;
149 level->use_custom_template = FALSE;
151 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
152 level->name[i] = '\0';
153 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
154 level->author[i] = '\0';
156 strcpy(level->name, NAMELESS_LEVEL_NAME);
157 strcpy(level->author, ANONYMOUS_NAME);
159 for (i = 0; i < 4; i++)
161 level->envelope_text[i][0] = '\0';
162 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
163 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
166 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
167 level->score[i] = 10;
169 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
170 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
171 for (x = 0; x < 3; x++)
172 for (y = 0; y < 3; y++)
173 level->yamyam_content[i][x][y] =
174 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
176 level->field[0][0] = EL_PLAYER_1;
177 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
179 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
183 setElementChangePages(&element_info[element], 1);
184 setElementChangeInfoToDefaults(element_info[element].change);
186 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
188 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
189 element_info[element].description[j] = '\0';
191 if (element_info[element].custom_description != NULL)
192 strncpy(element_info[element].description,
193 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
195 strcpy(element_info[element].description,
196 element_info[element].editor_description);
198 element_info[element].use_gfx_element = FALSE;
199 element_info[element].gfx_element = EL_EMPTY_SPACE;
202 if (IS_CUSTOM_ELEMENT(element))
204 element_info[element].collect_score = 10; /* special default */
205 element_info[element].collect_count = 1; /* special default */
207 element_info[element].push_delay_fixed = -1; /* initialize later */
208 element_info[element].push_delay_random = -1; /* initialize later */
209 element_info[element].move_delay_fixed = 0;
210 element_info[element].move_delay_random = 0;
212 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
213 element_info[element].move_direction_initial = MV_NO_MOVING;
214 element_info[element].move_stepsize = TILEX / 8;
216 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
218 for (x = 0; x < 3; x++)
219 for (y = 0; y < 3; y++)
220 element_info[element].content[x][y] = EL_EMPTY_SPACE;
222 element_info[element].access_type = 0;
223 element_info[element].access_layer = 0;
224 element_info[element].walk_to_action = 0;
225 element_info[element].smash_targets = 0;
226 element_info[element].deadliness = 0;
227 element_info[element].consistency = 0;
229 element_info[element].can_explode_by_fire = FALSE;
230 element_info[element].can_explode_smashed = FALSE;
231 element_info[element].can_explode_impact = FALSE;
233 element_info[element].current_change_page = 0;
235 /* start with no properties at all */
236 for (j = 0; j < NUM_EP_BITFIELDS; j++)
237 Properties[element][j] = EP_BITMASK_DEFAULT;
239 element_info[element].modified_settings = FALSE;
241 else if (IS_GROUP_ELEMENT(element))
243 /* initialize memory for list of elements in group */
244 if (element_info[element].group == NULL)
245 element_info[element].group =
246 checked_malloc(sizeof(struct ElementGroupInfo));
248 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
249 element_info[element].group->element[j] = EL_EMPTY_SPACE;
251 /* default: only one element in group */
252 element_info[element].group->num_elements = 1;
256 BorderElement = EL_STEELWALL;
258 level->no_level_file = FALSE;
260 if (leveldir_current == NULL) /* only when dumping level */
263 /* try to determine better author name than 'anonymous' */
264 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
266 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
267 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
271 switch (LEVELCLASS(leveldir_current))
273 case LEVELCLASS_TUTORIAL:
274 strcpy(level->author, PROGRAM_AUTHOR_STRING);
277 case LEVELCLASS_CONTRIB:
278 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
279 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
282 case LEVELCLASS_PRIVATE:
283 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
284 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
288 /* keep default value */
294 static void ActivateLevelTemplate()
296 /* Currently there is no special action needed to activate the template
297 data, because 'element_info' and 'Properties' overwrite the original
298 level data, while all other variables do not change. */
301 static char *getLevelFilenameFromBasename(char *basename)
303 static char *filename = NULL;
305 checked_free(filename);
307 filename = getPath2(getCurrentLevelDir(), basename);
312 static char *getSingleLevelBasename(int nr, int type)
314 static char basename[MAX_FILENAME_LEN];
318 case LEVEL_FILE_TYPE_RND:
320 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
322 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
325 case LEVEL_FILE_TYPE_EM:
326 sprintf(basename, "%d", nr);
330 strcpy(basename, UNDEFINED_FILENAME);
337 static char *getPackedLevelBasename(int type)
339 static char basename[MAX_FILENAME_LEN];
344 strcpy(basename, UNDEFINED_FILENAME);
351 static char *getSingleLevelFilename(int nr, int type)
353 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
356 static char *getPackedLevelFilename(int type)
358 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
361 char *getDefaultLevelFilename(int nr)
363 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
366 static struct LevelFileInfo *getLevelFileInfo(int nr)
368 static struct LevelFileInfo level_file_info;
370 level_file_info.nr = nr;
372 /* special case: level template */
375 level_file_info.type = LEVEL_FILE_TYPE_RND;
376 level_file_info.filename = getDefaultLevelFilename(nr);
378 return &level_file_info;
381 /* 1st try: check for native Rocks'n'Diamonds level file */
382 level_file_info.type = LEVEL_FILE_TYPE_RND;
383 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
384 if (fileExists(level_file_info.filename))
385 return &level_file_info;
387 /* 2nd try: check for classic Emerald Mine level file */
388 level_file_info.type = LEVEL_FILE_TYPE_EM;
389 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
390 if (fileExists(level_file_info.filename))
391 return &level_file_info;
393 /* no known level file found -- use default values */
394 level_file_info.type = LEVEL_FILE_TYPE_RND;
395 level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
397 return &level_file_info;
400 /* ------------------------------------------------------------------------- */
401 /* functions for loading R'n'D level */
402 /* ------------------------------------------------------------------------- */
404 static int checkLevelElement(int element)
406 /* map some (historic, now obsolete) elements */
411 case EL_PLAYER_OBSOLETE:
412 element = EL_PLAYER_1;
415 case EL_KEY_OBSOLETE:
418 case EL_EM_KEY_1_FILE_OBSOLETE:
419 element = EL_EM_KEY_1;
422 case EL_EM_KEY_2_FILE_OBSOLETE:
423 element = EL_EM_KEY_2;
426 case EL_EM_KEY_3_FILE_OBSOLETE:
427 element = EL_EM_KEY_3;
430 case EL_EM_KEY_4_FILE_OBSOLETE:
431 element = EL_EM_KEY_4;
434 case EL_ENVELOPE_OBSOLETE:
435 element = EL_ENVELOPE_1;
443 if (element >= NUM_FILE_ELEMENTS)
445 Error(ERR_WARN, "invalid level element %d", element);
447 element = EL_CHAR_QUESTION;
452 if (element >= NUM_FILE_ELEMENTS)
454 Error(ERR_WARN, "invalid level element %d", element);
456 element = EL_CHAR_QUESTION;
458 else if (element == EL_PLAYER_OBSOLETE)
459 element = EL_PLAYER_1;
460 else if (element == EL_KEY_OBSOLETE)
467 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
469 level->file_version = getFileVersion(file);
470 level->game_version = getFileVersion(file);
475 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
479 level->fieldx = getFile8Bit(file);
480 level->fieldy = getFile8Bit(file);
482 level->time = getFile16BitBE(file);
483 level->gems_needed = getFile16BitBE(file);
485 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
486 level->name[i] = getFile8Bit(file);
487 level->name[MAX_LEVEL_NAME_LEN] = 0;
489 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
490 level->score[i] = getFile8Bit(file);
492 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
493 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
494 for (y = 0; y < 3; y++)
495 for (x = 0; x < 3; x++)
496 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
498 level->amoeba_speed = getFile8Bit(file);
499 level->time_magic_wall = getFile8Bit(file);
500 level->time_wheel = getFile8Bit(file);
501 level->amoeba_content = checkLevelElement(getFile8Bit(file));
502 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
503 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
504 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
505 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
507 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
509 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
514 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
518 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
519 level->author[i] = getFile8Bit(file);
520 level->author[MAX_LEVEL_NAME_LEN] = 0;
525 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
528 int chunk_size_expected = level->fieldx * level->fieldy;
530 /* Note: "chunk_size" was wrong before version 2.0 when elements are
531 stored with 16-bit encoding (and should be twice as big then).
532 Even worse, playfield data was stored 16-bit when only yamyam content
533 contained 16-bit elements and vice versa. */
535 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
536 chunk_size_expected *= 2;
538 if (chunk_size_expected != chunk_size)
540 ReadUnusedBytesFromFile(file, chunk_size);
541 return chunk_size_expected;
544 for (y = 0; y < level->fieldy; y++)
545 for (x = 0; x < level->fieldx; x++)
547 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
552 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
556 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
557 int chunk_size_expected = header_size + content_size;
559 /* Note: "chunk_size" was wrong before version 2.0 when elements are
560 stored with 16-bit encoding (and should be twice as big then).
561 Even worse, playfield data was stored 16-bit when only yamyam content
562 contained 16-bit elements and vice versa. */
564 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
565 chunk_size_expected += content_size;
567 if (chunk_size_expected != chunk_size)
569 ReadUnusedBytesFromFile(file, chunk_size);
570 return chunk_size_expected;
574 level->num_yamyam_contents = getFile8Bit(file);
578 /* correct invalid number of content fields -- should never happen */
579 if (level->num_yamyam_contents < 1 ||
580 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
581 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
583 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
584 for (y = 0; y < 3; y++)
585 for (x = 0; x < 3; x++)
586 level->yamyam_content[i][x][y] =
587 checkLevelElement(level->encoding_16bit_field ?
588 getFile16BitBE(file) : getFile8Bit(file));
592 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
596 int num_contents, content_xsize, content_ysize;
597 int content_array[MAX_ELEMENT_CONTENTS][3][3];
599 element = checkLevelElement(getFile16BitBE(file));
600 num_contents = getFile8Bit(file);
601 content_xsize = getFile8Bit(file);
602 content_ysize = getFile8Bit(file);
604 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
606 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
607 for (y = 0; y < 3; y++)
608 for (x = 0; x < 3; x++)
609 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
611 /* correct invalid number of content fields -- should never happen */
612 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
613 num_contents = STD_ELEMENT_CONTENTS;
615 if (element == EL_YAMYAM)
617 level->num_yamyam_contents = num_contents;
619 for (i = 0; i < num_contents; i++)
620 for (y = 0; y < 3; y++)
621 for (x = 0; x < 3; x++)
622 level->yamyam_content[i][x][y] = content_array[i][x][y];
624 else if (element == EL_BD_AMOEBA)
626 level->amoeba_content = content_array[0][0][0];
630 Error(ERR_WARN, "cannot load content for element '%d'", element);
636 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
642 int chunk_size_expected;
644 element = checkLevelElement(getFile16BitBE(file));
645 if (!IS_ENVELOPE(element))
646 element = EL_ENVELOPE_1;
648 envelope_nr = element - EL_ENVELOPE_1;
650 envelope_len = getFile16BitBE(file);
652 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
653 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
655 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
657 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
658 if (chunk_size_expected != chunk_size)
660 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
661 return chunk_size_expected;
664 for (i = 0; i < envelope_len; i++)
665 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
670 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
672 int num_changed_custom_elements = getFile16BitBE(file);
673 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
676 if (chunk_size_expected != chunk_size)
678 ReadUnusedBytesFromFile(file, chunk_size - 2);
679 return chunk_size_expected;
682 for (i = 0; i < num_changed_custom_elements; i++)
684 int element = getFile16BitBE(file);
685 int properties = getFile32BitBE(file);
687 if (IS_CUSTOM_ELEMENT(element))
688 Properties[element][EP_BITFIELD_BASE] = properties;
690 Error(ERR_WARN, "invalid custom element number %d", element);
696 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
698 int num_changed_custom_elements = getFile16BitBE(file);
699 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
702 if (chunk_size_expected != chunk_size)
704 ReadUnusedBytesFromFile(file, chunk_size - 2);
705 return chunk_size_expected;
708 for (i = 0; i < num_changed_custom_elements; i++)
710 int element = getFile16BitBE(file);
711 int custom_target_element = getFile16BitBE(file);
713 if (IS_CUSTOM_ELEMENT(element))
714 element_info[element].change->target_element = custom_target_element;
716 Error(ERR_WARN, "invalid custom element number %d", element);
722 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
724 int num_changed_custom_elements = getFile16BitBE(file);
725 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
728 if (chunk_size_expected != chunk_size)
730 ReadUnusedBytesFromFile(file, chunk_size - 2);
731 return chunk_size_expected;
734 for (i = 0; i < num_changed_custom_elements; i++)
736 int element = getFile16BitBE(file);
738 if (!IS_CUSTOM_ELEMENT(element))
740 Error(ERR_WARN, "invalid custom element number %d", element);
745 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
746 element_info[element].description[j] = getFile8Bit(file);
747 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
749 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
751 /* some free bytes for future properties and padding */
752 ReadUnusedBytesFromFile(file, 7);
754 element_info[element].use_gfx_element = getFile8Bit(file);
755 element_info[element].gfx_element =
756 checkLevelElement(getFile16BitBE(file));
758 element_info[element].collect_score = getFile8Bit(file);
759 element_info[element].collect_count = getFile8Bit(file);
761 element_info[element].push_delay_fixed = getFile16BitBE(file);
762 element_info[element].push_delay_random = getFile16BitBE(file);
763 element_info[element].move_delay_fixed = getFile16BitBE(file);
764 element_info[element].move_delay_random = getFile16BitBE(file);
766 element_info[element].move_pattern = getFile16BitBE(file);
767 element_info[element].move_direction_initial = getFile8Bit(file);
768 element_info[element].move_stepsize = getFile8Bit(file);
770 for (y = 0; y < 3; y++)
771 for (x = 0; x < 3; x++)
772 element_info[element].content[x][y] =
773 checkLevelElement(getFile16BitBE(file));
775 element_info[element].change->events = getFile32BitBE(file);
777 element_info[element].change->target_element =
778 checkLevelElement(getFile16BitBE(file));
780 element_info[element].change->delay_fixed = getFile16BitBE(file);
781 element_info[element].change->delay_random = getFile16BitBE(file);
782 element_info[element].change->delay_frames = getFile16BitBE(file);
784 element_info[element].change->trigger_element =
785 checkLevelElement(getFile16BitBE(file));
787 element_info[element].change->explode = getFile8Bit(file);
788 element_info[element].change->use_content = getFile8Bit(file);
789 element_info[element].change->only_complete = getFile8Bit(file);
790 element_info[element].change->use_random_change = getFile8Bit(file);
792 element_info[element].change->random = getFile8Bit(file);
793 element_info[element].change->power = getFile8Bit(file);
795 for (y = 0; y < 3; y++)
796 for (x = 0; x < 3; x++)
797 element_info[element].change->content[x][y] =
798 checkLevelElement(getFile16BitBE(file));
800 element_info[element].slippery_type = getFile8Bit(file);
802 /* some free bytes for future properties and padding */
803 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
805 /* mark that this custom element has been modified */
806 element_info[element].modified_settings = TRUE;
812 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
814 struct ElementInfo *ei;
815 int chunk_size_expected;
819 element = getFile16BitBE(file);
821 if (!IS_CUSTOM_ELEMENT(element))
823 Error(ERR_WARN, "invalid custom element number %d", element);
825 ReadUnusedBytesFromFile(file, chunk_size - 2);
829 ei = &element_info[element];
831 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
832 ei->description[i] = getFile8Bit(file);
833 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
835 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
836 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
838 ei->num_change_pages = getFile8Bit(file);
840 /* some free bytes for future base property values and padding */
841 ReadUnusedBytesFromFile(file, 5);
843 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
844 if (chunk_size_expected != chunk_size)
846 ReadUnusedBytesFromFile(file, chunk_size - 48);
847 return chunk_size_expected;
850 /* read custom property values */
852 ei->use_gfx_element = getFile8Bit(file);
853 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
855 ei->collect_score = getFile8Bit(file);
856 ei->collect_count = getFile8Bit(file);
858 ei->push_delay_fixed = getFile16BitBE(file);
859 ei->push_delay_random = getFile16BitBE(file);
860 ei->move_delay_fixed = getFile16BitBE(file);
861 ei->move_delay_random = getFile16BitBE(file);
863 ei->move_pattern = getFile16BitBE(file);
864 ei->move_direction_initial = getFile8Bit(file);
865 ei->move_stepsize = getFile8Bit(file);
867 ei->slippery_type = getFile8Bit(file);
869 for (y = 0; y < 3; y++)
870 for (x = 0; x < 3; x++)
871 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
873 /* some free bytes for future custom property values and padding */
874 ReadUnusedBytesFromFile(file, 12);
876 /* read change property values */
878 setElementChangePages(ei, ei->num_change_pages);
880 for (i = 0; i < ei->num_change_pages; i++)
882 struct ElementChangeInfo *change = &ei->change_page[i];
884 /* always start with reliable default values */
885 setElementChangeInfoToDefaults(change);
887 change->events = getFile32BitBE(file);
889 change->target_element = checkLevelElement(getFile16BitBE(file));
891 change->delay_fixed = getFile16BitBE(file);
892 change->delay_random = getFile16BitBE(file);
893 change->delay_frames = getFile16BitBE(file);
895 change->trigger_element = checkLevelElement(getFile16BitBE(file));
897 change->explode = getFile8Bit(file);
898 change->use_content = getFile8Bit(file);
899 change->only_complete = getFile8Bit(file);
900 change->use_random_change = getFile8Bit(file);
902 change->random = getFile8Bit(file);
903 change->power = getFile8Bit(file);
905 for (y = 0; y < 3; y++)
906 for (x = 0; x < 3; x++)
907 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
909 change->can_change = getFile8Bit(file);
911 change->sides = getFile8Bit(file);
913 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
914 change->sides = CH_SIDE_ANY;
916 /* some free bytes for future change property values and padding */
917 ReadUnusedBytesFromFile(file, 8);
920 /* mark this custom element as modified */
921 ei->modified_settings = TRUE;
926 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
928 struct ElementInfo *ei;
929 struct ElementGroupInfo *group;
933 element = getFile16BitBE(file);
935 if (!IS_GROUP_ELEMENT(element))
937 Error(ERR_WARN, "invalid group element number %d", element);
939 ReadUnusedBytesFromFile(file, chunk_size - 2);
943 ei = &element_info[element];
945 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
946 ei->description[i] = getFile8Bit(file);
947 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
949 group = element_info[element].group;
951 group->num_elements = getFile8Bit(file);
953 ei->use_gfx_element = getFile8Bit(file);
954 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
956 /* some free bytes for future values and padding */
957 ReadUnusedBytesFromFile(file, 4);
959 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
960 group->element[i] = checkLevelElement(getFile16BitBE(file));
962 /* mark this group element as modified */
963 element_info[element].modified_settings = TRUE;
968 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
969 struct LevelFileInfo *level_file_info)
971 char *filename = level_file_info->filename;
972 char cookie[MAX_LINE_LEN];
973 char chunk_name[CHUNK_ID_LEN + 1];
977 /* always start with reliable default values */
978 setLevelInfoToDefaults(level);
980 if (!(file = fopen(filename, MODE_READ)))
982 level->no_level_file = TRUE;
984 if (level != &level_template)
985 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
990 getFileChunkBE(file, chunk_name, NULL);
991 if (strcmp(chunk_name, "RND1") == 0)
993 getFile32BitBE(file); /* not used */
995 getFileChunkBE(file, chunk_name, NULL);
996 if (strcmp(chunk_name, "CAVE") != 0)
998 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1003 else /* check for pre-2.0 file format with cookie string */
1005 strcpy(cookie, chunk_name);
1006 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1007 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1008 cookie[strlen(cookie) - 1] = '\0';
1010 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1012 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1017 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1019 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1024 /* pre-2.0 level files have no game version, so use file version here */
1025 level->game_version = level->file_version;
1028 if (level->file_version < FILE_VERSION_1_2)
1030 /* level files from versions before 1.2.0 without chunk structure */
1031 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1032 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1040 int (*loader)(FILE *, int, struct LevelInfo *);
1044 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1045 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1046 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1047 { "BODY", -1, LoadLevel_BODY },
1048 { "CONT", -1, LoadLevel_CONT },
1049 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1050 { "CNT3", -1, LoadLevel_CNT3 },
1051 { "CUS1", -1, LoadLevel_CUS1 },
1052 { "CUS2", -1, LoadLevel_CUS2 },
1053 { "CUS3", -1, LoadLevel_CUS3 },
1054 { "CUS4", -1, LoadLevel_CUS4 },
1055 { "GRP1", -1, LoadLevel_GRP1 },
1059 while (getFileChunkBE(file, chunk_name, &chunk_size))
1063 while (chunk_info[i].name != NULL &&
1064 strcmp(chunk_name, chunk_info[i].name) != 0)
1067 if (chunk_info[i].name == NULL)
1069 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1070 chunk_name, filename);
1071 ReadUnusedBytesFromFile(file, chunk_size);
1073 else if (chunk_info[i].size != -1 &&
1074 chunk_info[i].size != chunk_size)
1076 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1077 chunk_size, chunk_name, filename);
1078 ReadUnusedBytesFromFile(file, chunk_size);
1082 /* call function to load this level chunk */
1083 int chunk_size_expected =
1084 (chunk_info[i].loader)(file, chunk_size, level);
1086 /* the size of some chunks cannot be checked before reading other
1087 chunks first (like "HEAD" and "BODY") that contain some header
1088 information, so check them here */
1089 if (chunk_size_expected != chunk_size)
1091 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1092 chunk_size, chunk_name, filename);
1101 /* ------------------------------------------------------------------------- */
1102 /* functions for loading EM level */
1103 /* ------------------------------------------------------------------------- */
1105 static int map_em_element_yam(int element)
1109 case 0x00: return EL_EMPTY;
1110 case 0x01: return EL_EMERALD;
1111 case 0x02: return EL_DIAMOND;
1112 case 0x03: return EL_ROCK;
1113 case 0x04: return EL_ROBOT;
1114 case 0x05: return EL_SPACESHIP_UP;
1115 case 0x06: return EL_BOMB;
1116 case 0x07: return EL_BUG_UP;
1117 case 0x08: return EL_AMOEBA_DROP;
1118 case 0x09: return EL_NUT;
1119 case 0x0a: return EL_YAMYAM;
1120 case 0x0b: return EL_QUICKSAND_FULL;
1121 case 0x0c: return EL_SAND;
1122 case 0x0d: return EL_WALL_SLIPPERY;
1123 case 0x0e: return EL_STEELWALL;
1124 case 0x0f: return EL_WALL;
1125 case 0x10: return EL_EM_KEY_1;
1126 case 0x11: return EL_EM_KEY_2;
1127 case 0x12: return EL_EM_KEY_4;
1128 case 0x13: return EL_EM_KEY_3;
1129 case 0x14: return EL_MAGIC_WALL;
1130 case 0x15: return EL_ROBOT_WHEEL;
1131 case 0x16: return EL_DYNAMITE;
1133 case 0x17: return EL_EM_KEY_1; /* EMC */
1134 case 0x18: return EL_BUG_UP; /* EMC */
1135 case 0x1a: return EL_DIAMOND; /* EMC */
1136 case 0x1b: return EL_EMERALD; /* EMC */
1137 case 0x25: return EL_NUT; /* EMC */
1138 case 0x80: return EL_EMPTY; /* EMC */
1139 case 0x85: return EL_EM_KEY_1; /* EMC */
1140 case 0x86: return EL_EM_KEY_2; /* EMC */
1141 case 0x87: return EL_EM_KEY_4; /* EMC */
1142 case 0x88: return EL_EM_KEY_3; /* EMC */
1143 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1144 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1145 case 0xaf: return EL_DYNAMITE; /* EMC */
1146 case 0xbd: return EL_SAND; /* EMC */
1149 Error(ERR_WARN, "invalid level element %d", element);
1150 return EL_CHAR_QUESTION;
1154 static int map_em_element_field(int element)
1156 if (element >= 0xc8 && element <= 0xe1)
1157 return EL_CHAR_A + (element - 0xc8);
1158 else if (element >= 0xe2 && element <= 0xeb)
1159 return EL_CHAR_0 + (element - 0xe2);
1163 case 0x00: return EL_ROCK;
1164 case 0x02: return EL_DIAMOND;
1165 case 0x03: return EL_DIAMOND;
1166 case 0x04: return EL_ROBOT;
1167 case 0x05: return EL_ROBOT; /* EMC */
1168 case 0x08: return EL_SPACESHIP_UP;
1169 case 0x09: return EL_SPACESHIP_RIGHT;
1170 case 0x0a: return EL_SPACESHIP_DOWN;
1171 case 0x0b: return EL_SPACESHIP_LEFT;
1172 case 0x0c: return EL_SPACESHIP_UP;
1173 case 0x0d: return EL_SPACESHIP_RIGHT;
1174 case 0x0e: return EL_SPACESHIP_DOWN;
1175 case 0x0f: return EL_SPACESHIP_LEFT;
1176 case 0x10: return EL_BOMB;
1177 case 0x12: return EL_EMERALD;
1178 case 0x13: return EL_EMERALD;
1179 case 0x14: return EL_BUG_UP;
1180 case 0x15: return EL_BUG_RIGHT;
1181 case 0x16: return EL_BUG_DOWN;
1182 case 0x17: return EL_BUG_LEFT;
1183 case 0x18: return EL_BUG_UP;
1184 case 0x19: return EL_BUG_RIGHT;
1185 case 0x1a: return EL_BUG_DOWN;
1186 case 0x1b: return EL_BUG_LEFT;
1187 case 0x1c: return EL_AMOEBA_DROP;
1188 case 0x20: return EL_ROCK;
1189 case 0x24: return EL_MAGIC_WALL;
1190 case 0x25: return EL_NUT;
1192 /* looks like magic wheel, but is _always_ activated */
1193 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1195 case 0x29: return EL_YAMYAM;
1196 case 0x2a: return EL_YAMYAM;
1197 case 0x2b: return EL_YAMYAM; /* EMC */
1198 case 0x2c: return EL_YAMYAM; /* EMC */
1199 case 0x2d: return EL_QUICKSAND_FULL;
1200 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1201 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1202 case 0x3b: return EL_DYNAMITE_ACTIVE;
1203 case 0x3c: return EL_DYNAMITE_ACTIVE;
1204 case 0x3d: return EL_DYNAMITE_ACTIVE;
1205 case 0x3e: return EL_DYNAMITE_ACTIVE;
1206 case 0x3f: return EL_ACID_POOL_BOTTOM;
1207 case 0x40: return EL_EXIT_OPEN;
1208 case 0x41: return EL_EXIT_OPEN;
1209 case 0x42: return EL_EXIT_OPEN;
1210 case 0x43: return EL_BALLOON;
1211 case 0x4e: return EL_INVISIBLE_WALL;
1212 case 0x65: return EL_ACID; /* EMC */
1213 case 0x73: return EL_SAND; /* EMC */
1214 case 0x74: return EL_STEELWALL;
1215 case 0x7b: return EL_ACID;
1216 case 0x80: return EL_EMPTY;
1217 case 0x81: return EL_WALL_SLIPPERY;
1218 case 0x82: return EL_SAND;
1219 case 0x83: return EL_STEELWALL;
1220 case 0x84: return EL_WALL;
1221 case 0x85: return EL_EM_KEY_1;
1222 case 0x86: return EL_EM_KEY_2;
1223 case 0x87: return EL_EM_KEY_4;
1224 case 0x88: return EL_EM_KEY_3;
1225 case 0x89: return EL_EM_GATE_1;
1226 case 0x8a: return EL_EM_GATE_2;
1227 case 0x8b: return EL_EM_GATE_4;
1228 case 0x8c: return EL_EM_GATE_3;
1229 case 0x8d: return EL_INVISIBLE_WALL; /* EMC */
1230 case 0x8e: return EL_EM_GATE_1_GRAY;
1231 case 0x8f: return EL_EM_GATE_2_GRAY;
1232 case 0x90: return EL_EM_GATE_4_GRAY;
1233 case 0x91: return EL_EM_GATE_3_GRAY;
1234 case 0x92: return EL_MAGIC_WALL;
1235 case 0x94: return EL_QUICKSAND_EMPTY;
1236 case 0x95: return EL_ACID_POOL_TOPLEFT;
1237 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1238 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1239 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1240 case 0x99: return EL_ACID;
1241 case 0x9a: return EL_AMOEBA_DEAD;
1242 case 0x9b: return EL_AMOEBA_DEAD;
1243 case 0x9c: return EL_AMOEBA_DEAD;
1244 case 0x9d: return EL_AMOEBA_DEAD;
1245 case 0x9e: return EL_EXIT_CLOSED;
1246 case 0x9f: return EL_CHAR_LESS; /* EMC */
1247 case 0x93: return EL_ROBOT_WHEEL;
1249 /* looks like normal dust, but behaves like wall */
1250 case 0xa0: return EL_WALL; /* EMC */
1252 case 0xa8: return EL_EMC_WALL_1; /* EMC */
1253 case 0xa9: return EL_EMC_WALL_2; /* EMC */
1254 case 0xaa: return EL_EMC_WALL_3; /* EMC */
1255 case 0xab: return EL_EMC_WALL_7; /* EMC */
1256 case 0xae: return EL_CHAR_MINUS; /* EMC */
1257 case 0xaf: return EL_DYNAMITE;
1258 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */
1259 case 0xb1: return EL_EMC_WALL_8; /* EMC */
1261 /* (exact steel wall) */
1262 case 0xb3: return EL_STEELWALL; /* EMC */
1264 case 0xb4: return EL_WALL_SLIPPERY; /* EMC */
1265 case 0xb5: return EL_EMC_WALL_6; /* EMC */
1266 case 0xb6: return EL_EMC_WALL_5; /* EMC */
1267 case 0xb7: return EL_EMC_WALL_4; /* EMC */
1268 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1269 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1270 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1271 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1272 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1273 case 0xbd: return EL_SAND; /* EMC */
1274 case 0xec: return EL_CHAR_PERIOD;
1275 case 0xed: return EL_CHAR_EXCLAM;
1276 case 0xee: return EL_CHAR_COLON;
1277 case 0xef: return EL_CHAR_QUESTION;
1278 case 0xf0: return EL_CHAR_GREATER;
1279 case 0xf1: return EL_CHAR_COPYRIGHT;
1280 case 0xfe: return EL_PLAYER_1;
1281 case 0xff: return EL_PLAYER_2;
1284 Error(ERR_WARN, "invalid level element %d", element);
1285 return EL_CHAR_QUESTION;
1289 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1290 struct LevelFileInfo *level_file_info)
1292 char *filename = level_file_info->filename;
1294 unsigned char body[40][64];
1295 unsigned char *leveldata = &body[0][0];
1296 unsigned char *header = &leveldata[2048];
1297 unsigned char code0 = 0x65;
1298 unsigned char code1 = 0x11;
1299 boolean level_is_crypted = FALSE;
1300 int nr = level_file_info->nr;
1304 /* always start with reliable default values */
1305 setLevelInfoToDefaults(level);
1307 if (!(file = fopen(filename, MODE_READ)))
1309 level->no_level_file = TRUE;
1311 Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1316 for(i = 0; i < 2106; i++)
1317 leveldata[i] = fgetc(file);
1321 /* check if level data is crypted by testing against known starting bytes
1322 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1324 if ((leveldata[0] == 0xf1 ||
1325 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1327 level_is_crypted = TRUE;
1329 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1330 leveldata[0] = 0xf1;
1333 if (level_is_crypted) /* decode crypted level data */
1335 for(i = 0; i < 2106; i++)
1337 leveldata[i] ^= code0;
1338 leveldata[i] -= code1;
1340 code0 = (code0 + 7) & 0xff;
1347 level->time = header[46] * 10;
1348 level->gems_needed = header[47];
1350 /* The original Emerald Mine levels have their level number stored
1351 at the second byte of the level file...
1352 Do not trust this information at other level files, e.g. EMC,
1353 but correct it anyway (normally the first row is completely
1354 steel wall, so the correction does not hurt anyway). */
1356 if (leveldata[1] == nr)
1357 leveldata[1] = leveldata[2]; /* correct level number field */
1359 sprintf(level->name, "Level %d", nr);
1361 level->score[SC_EMERALD] = header[36];
1362 level->score[SC_DIAMOND] = header[37];
1363 level->score[SC_ROBOT] = header[38];
1364 level->score[SC_SPACESHIP] = header[39];
1365 level->score[SC_BUG] = header[40];
1366 level->score[SC_YAMYAM] = header[41];
1367 level->score[SC_NUT] = header[42];
1368 level->score[SC_DYNAMITE] = header[43];
1369 level->score[SC_TIME_BONUS] = header[44];
1371 level->num_yamyam_contents = 4;
1373 for(i = 0; i < level->num_yamyam_contents; i++)
1374 for(y = 0; y < 3; y++)
1375 for(x = 0; x < 3; x++)
1376 level->yamyam_content[i][x][y] =
1377 map_em_element_yam(header[i * 9 + y * 3 + x]);
1379 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1380 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1381 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1382 level->amoeba_content = EL_DIAMOND;
1384 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1386 int new_element = map_em_element_field(body[y][x]);
1388 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1389 new_element = EL_AMOEBA_WET;
1391 level->field[x][y] = new_element;
1394 jx = (header[48] * 256 + header[49]) % 64;
1395 jy = (header[48] * 256 + header[49]) / 64;
1396 level->field[jx][jy] = EL_PLAYER_1;
1398 jx = (header[50] * 256 + header[51]) % 64;
1399 jy = (header[50] * 256 + header[51]) / 64;
1400 level->field[jx][jy] = EL_PLAYER_2;
1403 void LoadLevelFromFileInfo(struct LevelInfo *level,
1404 struct LevelFileInfo *level_file_info)
1406 switch (level_file_info->type)
1408 case LEVEL_FILE_TYPE_RND:
1409 LoadLevelFromFileInfo_RND(level, level_file_info);
1412 case LEVEL_FILE_TYPE_EM:
1413 LoadLevelFromFileInfo_EM(level, level_file_info);
1417 LoadLevelFromFileInfo_RND(level, level_file_info);
1422 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1424 static struct LevelFileInfo level_file_info;
1426 level_file_info.nr = 0; /* unknown */
1427 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1428 level_file_info.filename = filename;
1430 LoadLevelFromFileInfo(level, &level_file_info);
1433 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1435 if (leveldir_current == NULL) /* only when dumping level */
1439 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1442 /* determine correct game engine version of current level */
1444 if (!leveldir_current->latest_engine)
1446 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1447 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1448 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1452 printf("\n::: This level is private or contributed: '%s'\n", filename);
1456 printf("\n::: Use the stored game engine version for this level\n");
1459 /* For all levels which are not forced to use the latest game engine
1460 version (normally user contributed, private and undefined levels),
1461 use the version of the game engine the levels were created for.
1463 Since 2.0.1, the game engine version is now directly stored
1464 in the level file (chunk "VERS"), so there is no need anymore
1465 to set the game version from the file version (except for old,
1466 pre-2.0 levels, where the game version is still taken from the
1467 file format version used to store the level -- see above). */
1469 /* do some special adjustments to support older level versions */
1470 if (level->file_version == FILE_VERSION_1_0)
1472 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1473 Error(ERR_WARN, "using high speed movement for player");
1475 /* player was faster than monsters in (pre-)1.0 levels */
1476 level->double_speed = TRUE;
1479 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1480 if (level->game_version == VERSION_IDENT(2,0,1,0))
1481 level->em_slippery_gems = TRUE;
1486 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1487 leveldir_current->sort_priority, filename);
1491 printf("\n::: Use latest game engine version for this level.\n");
1494 /* For all levels which are forced to use the latest game engine version
1495 (normally all but user contributed, private and undefined levels), set
1496 the game engine version to the actual version; this allows for actual
1497 corrections in the game engine to take effect for existing, converted
1498 levels (from "classic" or other existing games) to make the emulation
1499 of the corresponding game more accurate, while (hopefully) not breaking
1500 existing levels created from other players. */
1503 printf("::: changing engine from %d to %d\n",
1504 level->game_version, GAME_VERSION_ACTUAL);
1507 level->game_version = GAME_VERSION_ACTUAL;
1509 /* Set special EM style gems behaviour: EM style gems slip down from
1510 normal, steel and growing wall. As this is a more fundamental change,
1511 it seems better to set the default behaviour to "off" (as it is more
1512 natural) and make it configurable in the level editor (as a property
1513 of gem style elements). Already existing converted levels (neither
1514 private nor contributed levels) are changed to the new behaviour. */
1516 if (level->file_version < FILE_VERSION_2_0)
1517 level->em_slippery_gems = TRUE;
1521 printf("::: => %d\n", level->game_version);
1525 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1529 /* map custom element change events that have changed in newer versions
1530 (these following values were accidentally changed in version 3.0.1) */
1531 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1533 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1535 int element = EL_CUSTOM_START + i;
1537 /* order of checking and copying events to be mapped is important */
1538 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1540 if (HAS_CHANGE_EVENT(element, j - 2))
1542 SET_CHANGE_EVENT(element, j - 2, FALSE);
1543 SET_CHANGE_EVENT(element, j, TRUE);
1547 /* order of checking and copying events to be mapped is important */
1548 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1550 if (HAS_CHANGE_EVENT(element, j - 1))
1552 SET_CHANGE_EVENT(element, j - 1, FALSE);
1553 SET_CHANGE_EVENT(element, j, TRUE);
1559 /* some custom element change events get mapped since version 3.0.3 */
1560 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1562 int element = EL_CUSTOM_START + i;
1564 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1565 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1567 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1568 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1570 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1574 /* initialize "can_change" field for old levels with only one change page */
1575 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1577 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1579 int element = EL_CUSTOM_START + i;
1581 if (CAN_CHANGE(element))
1582 element_info[element].change->can_change = TRUE;
1587 /* set default push delay values (corrected since version 3.0.7-1) */
1588 if (level->game_version < VERSION_IDENT(3,0,7,1))
1590 game.default_push_delay_fixed = 2;
1591 game.default_push_delay_random = 8;
1595 game.default_push_delay_fixed = 8;
1596 game.default_push_delay_random = 8;
1599 /* set uninitialized push delay values of custom elements in older levels */
1600 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1602 int element = EL_CUSTOM_START + i;
1604 if (element_info[element].push_delay_fixed == -1)
1605 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1606 if (element_info[element].push_delay_random == -1)
1607 element_info[element].push_delay_random = game.default_push_delay_random;
1611 /* initialize element properties for level editor etc. */
1612 InitElementPropertiesEngine(level->game_version);
1615 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1619 /* map elements that have changed in newer versions */
1620 for (y = 0; y < level->fieldy; y++)
1622 for (x = 0; x < level->fieldx; x++)
1624 int element = level->field[x][y];
1626 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1628 /* map game font elements */
1629 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1630 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1631 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1632 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1635 if (level->game_version < VERSION_IDENT(3,0,0,0))
1637 /* map Supaplex gravity tube elements */
1638 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1639 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1640 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1641 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1645 level->field[x][y] = element;
1649 /* copy elements to runtime playfield array */
1650 for (x = 0; x < MAX_LEV_FIELDX; x++)
1651 for (y = 0; y < MAX_LEV_FIELDY; y++)
1652 Feld[x][y] = level->field[x][y];
1654 /* initialize level size variables for faster access */
1655 lev_fieldx = level->fieldx;
1656 lev_fieldy = level->fieldy;
1658 /* determine border element for this level */
1662 void LoadLevelTemplate(int nr)
1665 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1666 char *filename = level_file_info->filename;
1668 LoadLevelFromFileInfo(&level_template, level_file_info);
1670 char *filename = getDefaultLevelFilename(nr);
1672 LoadLevelFromFilename_RND(&level_template, filename);
1675 LoadLevel_InitVersion(&level, filename);
1676 LoadLevel_InitElements(&level, filename);
1678 ActivateLevelTemplate();
1681 void LoadLevel(int nr)
1684 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1685 char *filename = level_file_info->filename;
1687 LoadLevelFromFileInfo(&level, level_file_info);
1689 char *filename = getLevelFilename(nr);
1691 LoadLevelFromFilename_RND(&level, filename);
1694 if (level.use_custom_template)
1695 LoadLevelTemplate(-1);
1698 LoadLevel_InitVersion(&level, filename);
1699 LoadLevel_InitElements(&level, filename);
1700 LoadLevel_InitPlayfield(&level, filename);
1702 LoadLevel_InitLevel(&level, filename);
1706 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1708 putFileVersion(file, level->file_version);
1709 putFileVersion(file, level->game_version);
1712 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1716 putFile8Bit(file, level->fieldx);
1717 putFile8Bit(file, level->fieldy);
1719 putFile16BitBE(file, level->time);
1720 putFile16BitBE(file, level->gems_needed);
1722 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1723 putFile8Bit(file, level->name[i]);
1725 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1726 putFile8Bit(file, level->score[i]);
1728 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1729 for (y = 0; y < 3; y++)
1730 for (x = 0; x < 3; x++)
1731 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1732 level->yamyam_content[i][x][y]));
1733 putFile8Bit(file, level->amoeba_speed);
1734 putFile8Bit(file, level->time_magic_wall);
1735 putFile8Bit(file, level->time_wheel);
1736 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1737 level->amoeba_content));
1738 putFile8Bit(file, (level->double_speed ? 1 : 0));
1739 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1740 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1741 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1743 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1745 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1748 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1752 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1753 putFile8Bit(file, level->author[i]);
1756 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1760 for (y = 0; y < level->fieldy; y++)
1761 for (x = 0; x < level->fieldx; x++)
1762 if (level->encoding_16bit_field)
1763 putFile16BitBE(file, level->field[x][y]);
1765 putFile8Bit(file, level->field[x][y]);
1769 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1773 putFile8Bit(file, EL_YAMYAM);
1774 putFile8Bit(file, level->num_yamyam_contents);
1775 putFile8Bit(file, 0);
1776 putFile8Bit(file, 0);
1778 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1779 for (y = 0; y < 3; y++)
1780 for (x = 0; x < 3; x++)
1781 if (level->encoding_16bit_field)
1782 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1784 putFile8Bit(file, level->yamyam_content[i][x][y]);
1788 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1791 int num_contents, content_xsize, content_ysize;
1792 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1794 if (element == EL_YAMYAM)
1796 num_contents = level->num_yamyam_contents;
1800 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1801 for (y = 0; y < 3; y++)
1802 for (x = 0; x < 3; x++)
1803 content_array[i][x][y] = level->yamyam_content[i][x][y];
1805 else if (element == EL_BD_AMOEBA)
1811 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1812 for (y = 0; y < 3; y++)
1813 for (x = 0; x < 3; x++)
1814 content_array[i][x][y] = EL_EMPTY;
1815 content_array[0][0][0] = level->amoeba_content;
1819 /* chunk header already written -- write empty chunk data */
1820 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1822 Error(ERR_WARN, "cannot save content for element '%d'", element);
1826 putFile16BitBE(file, element);
1827 putFile8Bit(file, num_contents);
1828 putFile8Bit(file, content_xsize);
1829 putFile8Bit(file, content_ysize);
1831 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1833 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1834 for (y = 0; y < 3; y++)
1835 for (x = 0; x < 3; x++)
1836 putFile16BitBE(file, content_array[i][x][y]);
1839 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1842 int envelope_nr = element - EL_ENVELOPE_1;
1843 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1845 putFile16BitBE(file, element);
1846 putFile16BitBE(file, envelope_len);
1847 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1848 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1850 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1852 for (i = 0; i < envelope_len; i++)
1853 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1857 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1858 int num_changed_custom_elements)
1862 putFile16BitBE(file, num_changed_custom_elements);
1864 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1866 int element = EL_CUSTOM_START + i;
1868 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1870 if (check < num_changed_custom_elements)
1872 putFile16BitBE(file, element);
1873 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1880 if (check != num_changed_custom_elements) /* should not happen */
1881 Error(ERR_WARN, "inconsistent number of custom element properties");
1886 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1887 int num_changed_custom_elements)
1891 putFile16BitBE(file, num_changed_custom_elements);
1893 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1895 int element = EL_CUSTOM_START + i;
1897 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1899 if (check < num_changed_custom_elements)
1901 putFile16BitBE(file, element);
1902 putFile16BitBE(file, element_info[element].change->target_element);
1909 if (check != num_changed_custom_elements) /* should not happen */
1910 Error(ERR_WARN, "inconsistent number of custom target elements");
1915 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1916 int num_changed_custom_elements)
1918 int i, j, x, y, check = 0;
1920 putFile16BitBE(file, num_changed_custom_elements);
1922 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1924 int element = EL_CUSTOM_START + i;
1926 if (element_info[element].modified_settings)
1928 if (check < num_changed_custom_elements)
1930 putFile16BitBE(file, element);
1932 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1933 putFile8Bit(file, element_info[element].description[j]);
1935 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1937 /* some free bytes for future properties and padding */
1938 WriteUnusedBytesToFile(file, 7);
1940 putFile8Bit(file, element_info[element].use_gfx_element);
1941 putFile16BitBE(file, element_info[element].gfx_element);
1943 putFile8Bit(file, element_info[element].collect_score);
1944 putFile8Bit(file, element_info[element].collect_count);
1946 putFile16BitBE(file, element_info[element].push_delay_fixed);
1947 putFile16BitBE(file, element_info[element].push_delay_random);
1948 putFile16BitBE(file, element_info[element].move_delay_fixed);
1949 putFile16BitBE(file, element_info[element].move_delay_random);
1951 putFile16BitBE(file, element_info[element].move_pattern);
1952 putFile8Bit(file, element_info[element].move_direction_initial);
1953 putFile8Bit(file, element_info[element].move_stepsize);
1955 for (y = 0; y < 3; y++)
1956 for (x = 0; x < 3; x++)
1957 putFile16BitBE(file, element_info[element].content[x][y]);
1959 putFile32BitBE(file, element_info[element].change->events);
1961 putFile16BitBE(file, element_info[element].change->target_element);
1963 putFile16BitBE(file, element_info[element].change->delay_fixed);
1964 putFile16BitBE(file, element_info[element].change->delay_random);
1965 putFile16BitBE(file, element_info[element].change->delay_frames);
1967 putFile16BitBE(file, element_info[element].change->trigger_element);
1969 putFile8Bit(file, element_info[element].change->explode);
1970 putFile8Bit(file, element_info[element].change->use_content);
1971 putFile8Bit(file, element_info[element].change->only_complete);
1972 putFile8Bit(file, element_info[element].change->use_random_change);
1974 putFile8Bit(file, element_info[element].change->random);
1975 putFile8Bit(file, element_info[element].change->power);
1977 for (y = 0; y < 3; y++)
1978 for (x = 0; x < 3; x++)
1979 putFile16BitBE(file, element_info[element].change->content[x][y]);
1981 putFile8Bit(file, element_info[element].slippery_type);
1983 /* some free bytes for future properties and padding */
1984 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1991 if (check != num_changed_custom_elements) /* should not happen */
1992 Error(ERR_WARN, "inconsistent number of custom element properties");
1996 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1998 struct ElementInfo *ei = &element_info[element];
2001 putFile16BitBE(file, element);
2003 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2004 putFile8Bit(file, ei->description[i]);
2006 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2007 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2009 putFile8Bit(file, ei->num_change_pages);
2011 /* some free bytes for future base property values and padding */
2012 WriteUnusedBytesToFile(file, 5);
2014 /* write custom property values */
2016 putFile8Bit(file, ei->use_gfx_element);
2017 putFile16BitBE(file, ei->gfx_element);
2019 putFile8Bit(file, ei->collect_score);
2020 putFile8Bit(file, ei->collect_count);
2022 putFile16BitBE(file, ei->push_delay_fixed);
2023 putFile16BitBE(file, ei->push_delay_random);
2024 putFile16BitBE(file, ei->move_delay_fixed);
2025 putFile16BitBE(file, ei->move_delay_random);
2027 putFile16BitBE(file, ei->move_pattern);
2028 putFile8Bit(file, ei->move_direction_initial);
2029 putFile8Bit(file, ei->move_stepsize);
2031 putFile8Bit(file, ei->slippery_type);
2033 for (y = 0; y < 3; y++)
2034 for (x = 0; x < 3; x++)
2035 putFile16BitBE(file, ei->content[x][y]);
2037 /* some free bytes for future custom property values and padding */
2038 WriteUnusedBytesToFile(file, 12);
2040 /* write change property values */
2042 for (i = 0; i < ei->num_change_pages; i++)
2044 struct ElementChangeInfo *change = &ei->change_page[i];
2046 putFile32BitBE(file, change->events);
2048 putFile16BitBE(file, change->target_element);
2050 putFile16BitBE(file, change->delay_fixed);
2051 putFile16BitBE(file, change->delay_random);
2052 putFile16BitBE(file, change->delay_frames);
2054 putFile16BitBE(file, change->trigger_element);
2056 putFile8Bit(file, change->explode);
2057 putFile8Bit(file, change->use_content);
2058 putFile8Bit(file, change->only_complete);
2059 putFile8Bit(file, change->use_random_change);
2061 putFile8Bit(file, change->random);
2062 putFile8Bit(file, change->power);
2064 for (y = 0; y < 3; y++)
2065 for (x = 0; x < 3; x++)
2066 putFile16BitBE(file, change->content[x][y]);
2068 putFile8Bit(file, change->can_change);
2070 putFile8Bit(file, change->sides);
2072 /* some free bytes for future change property values and padding */
2073 WriteUnusedBytesToFile(file, 8);
2077 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2079 struct ElementInfo *ei = &element_info[element];
2080 struct ElementGroupInfo *group = ei->group;
2083 putFile16BitBE(file, element);
2085 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2086 putFile8Bit(file, ei->description[i]);
2088 putFile8Bit(file, group->num_elements);
2090 putFile8Bit(file, ei->use_gfx_element);
2091 putFile16BitBE(file, ei->gfx_element);
2093 /* some free bytes for future values and padding */
2094 WriteUnusedBytesToFile(file, 4);
2096 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2097 putFile16BitBE(file, group->element[i]);
2100 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2102 int body_chunk_size;
2106 if (!(file = fopen(filename, MODE_WRITE)))
2108 Error(ERR_WARN, "cannot save level file '%s'", filename);
2112 level->file_version = FILE_VERSION_ACTUAL;
2113 level->game_version = GAME_VERSION_ACTUAL;
2115 /* check level field for 16-bit elements */
2116 level->encoding_16bit_field = FALSE;
2117 for (y = 0; y < level->fieldy; y++)
2118 for (x = 0; x < level->fieldx; x++)
2119 if (level->field[x][y] > 255)
2120 level->encoding_16bit_field = TRUE;
2122 /* check yamyam content for 16-bit elements */
2123 level->encoding_16bit_yamyam = FALSE;
2124 for (i = 0; i < level->num_yamyam_contents; i++)
2125 for (y = 0; y < 3; y++)
2126 for (x = 0; x < 3; x++)
2127 if (level->yamyam_content[i][x][y] > 255)
2128 level->encoding_16bit_yamyam = TRUE;
2130 /* check amoeba content for 16-bit elements */
2131 level->encoding_16bit_amoeba = FALSE;
2132 if (level->amoeba_content > 255)
2133 level->encoding_16bit_amoeba = TRUE;
2135 /* calculate size of "BODY" chunk */
2137 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2139 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2140 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2142 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2143 SaveLevel_VERS(file, level);
2145 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2146 SaveLevel_HEAD(file, level);
2148 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2149 SaveLevel_AUTH(file, level);
2151 putFileChunkBE(file, "BODY", body_chunk_size);
2152 SaveLevel_BODY(file, level);
2154 if (level->encoding_16bit_yamyam ||
2155 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2157 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2158 SaveLevel_CNT2(file, level, EL_YAMYAM);
2161 if (level->encoding_16bit_amoeba)
2163 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2164 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2167 /* check for envelope content */
2168 for (i = 0; i < 4; i++)
2170 if (strlen(level->envelope_text[i]) > 0)
2172 int envelope_len = strlen(level->envelope_text[i]) + 1;
2174 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2175 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2179 /* check for non-default custom elements (unless using template level) */
2180 if (!level->use_custom_template)
2182 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2184 int element = EL_CUSTOM_START + i;
2186 if (element_info[element].modified_settings)
2188 int num_change_pages = element_info[element].num_change_pages;
2190 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2191 SaveLevel_CUS4(file, level, element);
2196 /* check for non-default group elements (unless using template level) */
2197 if (!level->use_custom_template)
2199 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201 int element = EL_GROUP_START + i;
2203 if (element_info[element].modified_settings)
2205 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2206 SaveLevel_GRP1(file, level, element);
2213 SetFilePermissions(filename, PERMS_PRIVATE);
2216 void SaveLevel(int nr)
2218 char *filename = getDefaultLevelFilename(nr);
2220 SaveLevelFromFilename(&level, filename);
2223 void SaveLevelTemplate()
2225 char *filename = getDefaultLevelFilename(-1);
2227 SaveLevelFromFilename(&level, filename);
2230 void DumpLevel(struct LevelInfo *level)
2232 printf_line("-", 79);
2233 printf("Level xxx (file version %08d, game version %08d)\n",
2234 level->file_version, level->game_version);
2235 printf_line("-", 79);
2237 printf("Level Author: '%s'\n", level->author);
2238 printf("Level Title: '%s'\n", level->name);
2240 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2242 printf("Level Time: %d seconds\n", level->time);
2243 printf("Gems needed: %d\n", level->gems_needed);
2245 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2246 printf("Time for Wheel: %d seconds\n", level->time_wheel);
2247 printf("Time for Light: %d seconds\n", level->time_light);
2248 printf("Time for Timegate: %d seconds\n", level->time_timegate);
2250 printf("Amoeba Speed: %d\n", level->amoeba_speed);
2252 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2253 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
2254 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2256 printf_line("-", 79);
2260 /* ========================================================================= */
2261 /* tape file functions */
2262 /* ========================================================================= */
2264 static void setTapeInfoToDefaults()
2268 /* always start with reliable default values (empty tape) */
2271 /* default values (also for pre-1.2 tapes) with only the first player */
2272 tape.player_participates[0] = TRUE;
2273 for (i = 1; i < MAX_PLAYERS; i++)
2274 tape.player_participates[i] = FALSE;
2276 /* at least one (default: the first) player participates in every tape */
2277 tape.num_participating_players = 1;
2279 tape.level_nr = level_nr;
2281 tape.changed = FALSE;
2283 tape.recording = FALSE;
2284 tape.playing = FALSE;
2285 tape.pausing = FALSE;
2288 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2290 tape->file_version = getFileVersion(file);
2291 tape->game_version = getFileVersion(file);
2296 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2300 tape->random_seed = getFile32BitBE(file);
2301 tape->date = getFile32BitBE(file);
2302 tape->length = getFile32BitBE(file);
2304 /* read header fields that are new since version 1.2 */
2305 if (tape->file_version >= FILE_VERSION_1_2)
2307 byte store_participating_players = getFile8Bit(file);
2310 /* since version 1.2, tapes store which players participate in the tape */
2311 tape->num_participating_players = 0;
2312 for (i = 0; i < MAX_PLAYERS; i++)
2314 tape->player_participates[i] = FALSE;
2316 if (store_participating_players & (1 << i))
2318 tape->player_participates[i] = TRUE;
2319 tape->num_participating_players++;
2323 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2325 engine_version = getFileVersion(file);
2326 if (engine_version > 0)
2327 tape->engine_version = engine_version;
2329 tape->engine_version = tape->game_version;
2335 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2337 int level_identifier_size;
2340 level_identifier_size = getFile16BitBE(file);
2342 tape->level_identifier =
2343 checked_realloc(tape->level_identifier, level_identifier_size);
2345 for (i = 0; i < level_identifier_size; i++)
2346 tape->level_identifier[i] = getFile8Bit(file);
2348 tape->level_nr = getFile16BitBE(file);
2350 chunk_size = 2 + level_identifier_size + 2;
2355 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2358 int chunk_size_expected =
2359 (tape->num_participating_players + 1) * tape->length;
2361 if (chunk_size_expected != chunk_size)
2363 ReadUnusedBytesFromFile(file, chunk_size);
2364 return chunk_size_expected;
2367 for (i = 0; i < tape->length; i++)
2369 if (i >= MAX_TAPELEN)
2372 for (j = 0; j < MAX_PLAYERS; j++)
2374 tape->pos[i].action[j] = MV_NO_MOVING;
2376 if (tape->player_participates[j])
2377 tape->pos[i].action[j] = getFile8Bit(file);
2380 tape->pos[i].delay = getFile8Bit(file);
2382 if (tape->file_version == FILE_VERSION_1_0)
2384 /* eliminate possible diagonal moves in old tapes */
2385 /* this is only for backward compatibility */
2387 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2388 byte action = tape->pos[i].action[0];
2389 int k, num_moves = 0;
2391 for (k = 0; k<4; k++)
2393 if (action & joy_dir[k])
2395 tape->pos[i + num_moves].action[0] = joy_dir[k];
2397 tape->pos[i + num_moves].delay = 0;
2406 tape->length += num_moves;
2409 else if (tape->file_version < FILE_VERSION_2_0)
2411 /* convert pre-2.0 tapes to new tape format */
2413 if (tape->pos[i].delay > 1)
2416 tape->pos[i + 1] = tape->pos[i];
2417 tape->pos[i + 1].delay = 1;
2420 for (j = 0; j < MAX_PLAYERS; j++)
2421 tape->pos[i].action[j] = MV_NO_MOVING;
2422 tape->pos[i].delay--;
2433 if (i != tape->length)
2434 chunk_size = (tape->num_participating_players + 1) * i;
2439 void LoadTapeFromFilename(char *filename)
2441 char cookie[MAX_LINE_LEN];
2442 char chunk_name[CHUNK_ID_LEN + 1];
2446 /* always start with reliable default values */
2447 setTapeInfoToDefaults();
2449 if (!(file = fopen(filename, MODE_READ)))
2452 getFileChunkBE(file, chunk_name, NULL);
2453 if (strcmp(chunk_name, "RND1") == 0)
2455 getFile32BitBE(file); /* not used */
2457 getFileChunkBE(file, chunk_name, NULL);
2458 if (strcmp(chunk_name, "TAPE") != 0)
2460 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2465 else /* check for pre-2.0 file format with cookie string */
2467 strcpy(cookie, chunk_name);
2468 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2469 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2470 cookie[strlen(cookie) - 1] = '\0';
2472 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2474 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2479 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2481 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2486 /* pre-2.0 tape files have no game version, so use file version here */
2487 tape.game_version = tape.file_version;
2490 if (tape.file_version < FILE_VERSION_1_2)
2492 /* tape files from versions before 1.2.0 without chunk structure */
2493 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2494 LoadTape_BODY(file, 2 * tape.length, &tape);
2502 int (*loader)(FILE *, int, struct TapeInfo *);
2506 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2507 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2508 { "INFO", -1, LoadTape_INFO },
2509 { "BODY", -1, LoadTape_BODY },
2513 while (getFileChunkBE(file, chunk_name, &chunk_size))
2517 while (chunk_info[i].name != NULL &&
2518 strcmp(chunk_name, chunk_info[i].name) != 0)
2521 if (chunk_info[i].name == NULL)
2523 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2524 chunk_name, filename);
2525 ReadUnusedBytesFromFile(file, chunk_size);
2527 else if (chunk_info[i].size != -1 &&
2528 chunk_info[i].size != chunk_size)
2530 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2531 chunk_size, chunk_name, filename);
2532 ReadUnusedBytesFromFile(file, chunk_size);
2536 /* call function to load this tape chunk */
2537 int chunk_size_expected =
2538 (chunk_info[i].loader)(file, chunk_size, &tape);
2540 /* the size of some chunks cannot be checked before reading other
2541 chunks first (like "HEAD" and "BODY") that contain some header
2542 information, so check them here */
2543 if (chunk_size_expected != chunk_size)
2545 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2546 chunk_size, chunk_name, filename);
2554 tape.length_seconds = GetTapeLength();
2557 printf("::: tape game version: %d\n", tape.game_version);
2558 printf("::: tape engine version: %d\n", tape.engine_version);
2562 void LoadTape(int nr)
2564 char *filename = getTapeFilename(nr);
2566 LoadTapeFromFilename(filename);
2569 void LoadSolutionTape(int nr)
2571 char *filename = getSolutionTapeFilename(nr);
2573 LoadTapeFromFilename(filename);
2576 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2578 putFileVersion(file, tape->file_version);
2579 putFileVersion(file, tape->game_version);
2582 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2585 byte store_participating_players = 0;
2587 /* set bits for participating players for compact storage */
2588 for (i = 0; i < MAX_PLAYERS; i++)
2589 if (tape->player_participates[i])
2590 store_participating_players |= (1 << i);
2592 putFile32BitBE(file, tape->random_seed);
2593 putFile32BitBE(file, tape->date);
2594 putFile32BitBE(file, tape->length);
2596 putFile8Bit(file, store_participating_players);
2598 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2599 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2601 putFileVersion(file, tape->engine_version);
2604 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2606 int level_identifier_size = strlen(tape->level_identifier) + 1;
2609 putFile16BitBE(file, level_identifier_size);
2611 for (i = 0; i < level_identifier_size; i++)
2612 putFile8Bit(file, tape->level_identifier[i]);
2614 putFile16BitBE(file, tape->level_nr);
2617 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2621 for (i = 0; i < tape->length; i++)
2623 for (j = 0; j < MAX_PLAYERS; j++)
2624 if (tape->player_participates[j])
2625 putFile8Bit(file, tape->pos[i].action[j]);
2627 putFile8Bit(file, tape->pos[i].delay);
2631 void SaveTape(int nr)
2633 char *filename = getTapeFilename(nr);
2635 boolean new_tape = TRUE;
2636 int num_participating_players = 0;
2637 int info_chunk_size;
2638 int body_chunk_size;
2641 InitTapeDirectory(leveldir_current->filename);
2643 /* if a tape still exists, ask to overwrite it */
2644 if (access(filename, F_OK) == 0)
2647 if (!Request("Replace old tape ?", REQ_ASK))
2651 if (!(file = fopen(filename, MODE_WRITE)))
2653 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2657 tape.file_version = FILE_VERSION_ACTUAL;
2658 tape.game_version = GAME_VERSION_ACTUAL;
2660 /* count number of participating players */
2661 for (i = 0; i < MAX_PLAYERS; i++)
2662 if (tape.player_participates[i])
2663 num_participating_players++;
2665 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2666 body_chunk_size = (num_participating_players + 1) * tape.length;
2668 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2669 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2671 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2672 SaveTape_VERS(file, &tape);
2674 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2675 SaveTape_HEAD(file, &tape);
2677 putFileChunkBE(file, "INFO", info_chunk_size);
2678 SaveTape_INFO(file, &tape);
2680 putFileChunkBE(file, "BODY", body_chunk_size);
2681 SaveTape_BODY(file, &tape);
2685 SetFilePermissions(filename, PERMS_PRIVATE);
2687 tape.changed = FALSE;
2690 Request("tape saved !", REQ_CONFIRM);
2693 void DumpTape(struct TapeInfo *tape)
2697 if (TAPE_IS_EMPTY(*tape))
2699 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2703 printf_line("-", 79);
2704 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2705 tape->level_nr, tape->file_version, tape->game_version);
2706 printf("Level series identifier: '%s'\n", tape->level_identifier);
2707 printf_line("-", 79);
2709 for (i = 0; i < tape->length; i++)
2711 if (i >= MAX_TAPELEN)
2714 printf("%03d: ", i);
2716 for (j = 0; j < MAX_PLAYERS; j++)
2718 if (tape->player_participates[j])
2720 int action = tape->pos[i].action[j];
2722 printf("%d:%02x ", j, action);
2723 printf("[%c%c%c%c|%c%c] - ",
2724 (action & JOY_LEFT ? '<' : ' '),
2725 (action & JOY_RIGHT ? '>' : ' '),
2726 (action & JOY_UP ? '^' : ' '),
2727 (action & JOY_DOWN ? 'v' : ' '),
2728 (action & JOY_BUTTON_1 ? '1' : ' '),
2729 (action & JOY_BUTTON_2 ? '2' : ' '));
2733 printf("(%03d)\n", tape->pos[i].delay);
2736 printf_line("-", 79);
2740 /* ========================================================================= */
2741 /* score file functions */
2742 /* ========================================================================= */
2744 void LoadScore(int nr)
2747 char *filename = getScoreFilename(nr);
2748 char cookie[MAX_LINE_LEN];
2749 char line[MAX_LINE_LEN];
2753 /* always start with reliable default values */
2754 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2756 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2757 highscore[i].Score = 0;
2760 if (!(file = fopen(filename, MODE_READ)))
2763 /* check file identifier */
2764 fgets(cookie, MAX_LINE_LEN, file);
2765 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2766 cookie[strlen(cookie) - 1] = '\0';
2768 if (!checkCookieString(cookie, SCORE_COOKIE))
2770 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2775 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2777 fscanf(file, "%d", &highscore[i].Score);
2778 fgets(line, MAX_LINE_LEN, file);
2780 if (line[strlen(line) - 1] == '\n')
2781 line[strlen(line) - 1] = '\0';
2783 for (line_ptr = line; *line_ptr; line_ptr++)
2785 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2787 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2788 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2797 void SaveScore(int nr)
2800 char *filename = getScoreFilename(nr);
2803 InitScoreDirectory(leveldir_current->filename);
2805 if (!(file = fopen(filename, MODE_WRITE)))
2807 Error(ERR_WARN, "cannot save score for level %d", nr);
2811 fprintf(file, "%s\n\n", SCORE_COOKIE);
2813 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2814 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2818 SetFilePermissions(filename, PERMS_PUBLIC);
2822 /* ========================================================================= */
2823 /* setup file functions */
2824 /* ========================================================================= */
2826 #define TOKEN_STR_PLAYER_PREFIX "player_"
2829 #define SETUP_TOKEN_PLAYER_NAME 0
2830 #define SETUP_TOKEN_SOUND 1
2831 #define SETUP_TOKEN_SOUND_LOOPS 2
2832 #define SETUP_TOKEN_SOUND_MUSIC 3
2833 #define SETUP_TOKEN_SOUND_SIMPLE 4
2834 #define SETUP_TOKEN_TOONS 5
2835 #define SETUP_TOKEN_SCROLL_DELAY 6
2836 #define SETUP_TOKEN_SOFT_SCROLLING 7
2837 #define SETUP_TOKEN_FADING 8
2838 #define SETUP_TOKEN_AUTORECORD 9
2839 #define SETUP_TOKEN_QUICK_DOORS 10
2840 #define SETUP_TOKEN_TEAM_MODE 11
2841 #define SETUP_TOKEN_HANDICAP 12
2842 #define SETUP_TOKEN_TIME_LIMIT 13
2843 #define SETUP_TOKEN_FULLSCREEN 14
2844 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2845 #define SETUP_TOKEN_GRAPHICS_SET 16
2846 #define SETUP_TOKEN_SOUNDS_SET 17
2847 #define SETUP_TOKEN_MUSIC_SET 18
2848 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2849 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2850 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2852 #define NUM_GLOBAL_SETUP_TOKENS 22
2855 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2856 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2857 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2858 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2859 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2860 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2861 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2862 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2863 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2864 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2865 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2866 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2868 #define NUM_EDITOR_SETUP_TOKENS 12
2870 /* shortcut setup */
2871 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2872 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2873 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2875 #define NUM_SHORTCUT_SETUP_TOKENS 3
2878 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2879 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2880 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2881 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2882 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2883 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2884 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2885 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2886 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2887 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
2888 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2889 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2890 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2891 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2892 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2893 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
2895 #define NUM_PLAYER_SETUP_TOKENS 16
2898 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2899 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2901 #define NUM_SYSTEM_SETUP_TOKENS 2
2904 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2906 #define NUM_OPTIONS_SETUP_TOKENS 1
2909 static struct SetupInfo si;
2910 static struct SetupEditorInfo sei;
2911 static struct SetupShortcutInfo ssi;
2912 static struct SetupInputInfo sii;
2913 static struct SetupSystemInfo syi;
2914 static struct OptionInfo soi;
2916 static struct TokenInfo global_setup_tokens[] =
2918 { TYPE_STRING, &si.player_name, "player_name" },
2919 { TYPE_SWITCH, &si.sound, "sound" },
2920 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2921 { TYPE_SWITCH, &si.sound_music, "background_music" },
2922 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2923 { TYPE_SWITCH, &si.toons, "toons" },
2924 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2925 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2926 { TYPE_SWITCH, &si.fading, "screen_fading" },
2927 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2928 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2929 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2930 { TYPE_SWITCH, &si.handicap, "handicap" },
2931 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2932 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2933 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2934 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2935 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2936 { TYPE_STRING, &si.music_set, "music_set" },
2937 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2938 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2939 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2942 static struct TokenInfo editor_setup_tokens[] =
2944 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2945 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2946 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2947 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2948 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2949 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2950 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2951 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2952 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2953 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2954 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2955 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2958 static struct TokenInfo shortcut_setup_tokens[] =
2960 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2961 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2962 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2965 static struct TokenInfo player_setup_tokens[] =
2967 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2968 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2969 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2970 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2971 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2972 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2973 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2974 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2975 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2976 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
2977 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2978 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2979 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2980 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2981 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2982 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
2985 static struct TokenInfo system_setup_tokens[] =
2987 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2988 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2991 static struct TokenInfo options_setup_tokens[] =
2993 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2996 static char *get_corrected_login_name(char *login_name)
2998 /* needed because player name must be a fixed length string */
2999 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3001 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3002 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3004 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3005 if (strchr(login_name_new, ' '))
3006 *strchr(login_name_new, ' ') = '\0';
3008 return login_name_new;
3011 static void setSetupInfoToDefaults(struct SetupInfo *si)
3015 si->player_name = get_corrected_login_name(getLoginName());
3018 si->sound_loops = TRUE;
3019 si->sound_music = TRUE;
3020 si->sound_simple = TRUE;
3022 si->double_buffering = TRUE;
3023 si->direct_draw = !si->double_buffering;
3024 si->scroll_delay = TRUE;
3025 si->soft_scrolling = TRUE;
3027 si->autorecord = TRUE;
3028 si->quick_doors = FALSE;
3029 si->team_mode = FALSE;
3030 si->handicap = TRUE;
3031 si->time_limit = TRUE;
3032 si->fullscreen = FALSE;
3033 si->ask_on_escape = TRUE;
3035 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3036 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3037 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3038 si->override_level_graphics = FALSE;
3039 si->override_level_sounds = FALSE;
3040 si->override_level_music = FALSE;
3042 si->editor.el_boulderdash = TRUE;
3043 si->editor.el_emerald_mine = TRUE;
3044 si->editor.el_more = TRUE;
3045 si->editor.el_sokoban = TRUE;
3046 si->editor.el_supaplex = TRUE;
3047 si->editor.el_diamond_caves = TRUE;
3048 si->editor.el_dx_boulderdash = TRUE;
3049 si->editor.el_chars = TRUE;
3050 si->editor.el_custom = TRUE;
3051 si->editor.el_custom_more = FALSE;
3053 si->editor.el_headlines = TRUE;
3054 si->editor.el_user_defined = FALSE;
3056 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3057 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3058 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3060 for (i = 0; i < MAX_PLAYERS; i++)
3062 si->input[i].use_joystick = FALSE;
3063 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3064 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3065 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3066 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3067 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3068 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3069 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3070 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3071 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3072 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3073 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3074 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3075 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3076 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3077 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3080 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3081 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3083 si->options.verbose = FALSE;
3086 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3090 if (!setup_file_hash)
3095 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3096 setSetupInfo(global_setup_tokens, i,
3097 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3102 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3103 setSetupInfo(editor_setup_tokens, i,
3104 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3107 /* shortcut setup */
3108 ssi = setup.shortcut;
3109 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3110 setSetupInfo(shortcut_setup_tokens, i,
3111 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3112 setup.shortcut = ssi;
3115 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3119 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3121 sii = setup.input[pnr];
3122 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3124 char full_token[100];
3126 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3127 setSetupInfo(player_setup_tokens, i,
3128 getHashEntry(setup_file_hash, full_token));
3130 setup.input[pnr] = sii;
3135 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3136 setSetupInfo(system_setup_tokens, i,
3137 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3141 soi = setup.options;
3142 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3143 setSetupInfo(options_setup_tokens, i,
3144 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3145 setup.options = soi;
3150 char *filename = getSetupFilename();
3151 SetupFileHash *setup_file_hash = NULL;
3153 /* always start with reliable default values */
3154 setSetupInfoToDefaults(&setup);
3156 setup_file_hash = loadSetupFileHash(filename);
3158 if (setup_file_hash)
3160 char *player_name_new;
3162 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3163 decodeSetupFileHash(setup_file_hash);
3165 setup.direct_draw = !setup.double_buffering;
3167 freeSetupFileHash(setup_file_hash);
3169 /* needed to work around problems with fixed length strings */
3170 player_name_new = get_corrected_login_name(setup.player_name);
3171 free(setup.player_name);
3172 setup.player_name = player_name_new;
3175 Error(ERR_WARN, "using default setup values");
3180 char *filename = getSetupFilename();
3184 InitUserDataDirectory();
3186 if (!(file = fopen(filename, MODE_WRITE)))
3188 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3192 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3193 getCookie("SETUP")));
3194 fprintf(file, "\n");
3198 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3200 /* just to make things nicer :) */
3201 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3202 i == SETUP_TOKEN_GRAPHICS_SET)
3203 fprintf(file, "\n");
3205 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3210 fprintf(file, "\n");
3211 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3212 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3214 /* shortcut setup */
3215 ssi = setup.shortcut;
3216 fprintf(file, "\n");
3217 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3218 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3221 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3225 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3226 fprintf(file, "\n");
3228 sii = setup.input[pnr];
3229 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3230 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3235 fprintf(file, "\n");
3236 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3237 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3240 soi = setup.options;
3241 fprintf(file, "\n");
3242 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3243 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3247 SetFilePermissions(filename, PERMS_PRIVATE);
3250 void LoadCustomElementDescriptions()
3252 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3253 SetupFileHash *setup_file_hash;
3256 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3258 if (element_info[i].custom_description != NULL)
3260 free(element_info[i].custom_description);
3261 element_info[i].custom_description = NULL;
3265 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3268 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3270 char *token = getStringCat2(element_info[i].token_name, ".name");
3271 char *value = getHashEntry(setup_file_hash, token);
3274 element_info[i].custom_description = getStringCopy(value);
3279 freeSetupFileHash(setup_file_hash);
3282 void LoadSpecialMenuDesignSettings()
3284 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3285 SetupFileHash *setup_file_hash;
3288 /* always start with reliable default values from default config */
3289 for (i = 0; image_config_vars[i].token != NULL; i++)
3290 for (j = 0; image_config[j].token != NULL; j++)
3291 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3292 *image_config_vars[i].value =
3293 get_auto_parameter_value(image_config_vars[i].token,
3294 image_config[j].value);
3296 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3299 /* special case: initialize with default values that may be overwritten */
3300 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3302 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3303 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3304 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3306 if (value_x != NULL)
3307 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3308 if (value_y != NULL)
3309 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3310 if (list_size != NULL)
3311 menu.list_size[i] = get_integer_from_string(list_size);
3314 /* read (and overwrite with) values that may be specified in config file */
3315 for (i = 0; image_config_vars[i].token != NULL; i++)
3317 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3320 *image_config_vars[i].value =
3321 get_auto_parameter_value(image_config_vars[i].token, value);
3324 freeSetupFileHash(setup_file_hash);
3327 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3329 char *filename = getEditorSetupFilename();
3330 SetupFileList *setup_file_list, *list;
3331 SetupFileHash *element_hash;
3332 int num_unknown_tokens = 0;
3335 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3338 element_hash = newSetupFileHash();
3340 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3341 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3343 /* determined size may be larger than needed (due to unknown elements) */
3345 for (list = setup_file_list; list != NULL; list = list->next)
3348 /* add space for up to 3 more elements for padding that may be needed */
3351 *elements = checked_malloc(*num_elements * sizeof(int));
3354 for (list = setup_file_list; list != NULL; list = list->next)
3356 char *value = getHashEntry(element_hash, list->token);
3360 (*elements)[(*num_elements)++] = atoi(value);
3364 if (num_unknown_tokens == 0)
3366 Error(ERR_RETURN_LINE, "-");
3367 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3368 Error(ERR_RETURN, "- config file: '%s'", filename);
3370 num_unknown_tokens++;
3373 Error(ERR_RETURN, "- token: '%s'", list->token);
3377 if (num_unknown_tokens > 0)
3378 Error(ERR_RETURN_LINE, "-");
3380 while (*num_elements % 4) /* pad with empty elements, if needed */
3381 (*elements)[(*num_elements)++] = EL_EMPTY;
3383 freeSetupFileList(setup_file_list);
3384 freeSetupFileHash(element_hash);
3388 for (i = 0; i < *num_elements; i++)
3389 printf("editor: element '%s' [%d]\n",
3390 element_info[(*elements)[i]].token_name, (*elements)[i]);
3394 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3397 SetupFileHash *setup_file_hash = NULL;
3398 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3399 char *filename_music, *filename_prefix, *filename_info;
3405 token_to_value_ptr[] =
3407 { "title_header", &tmp_music_file_info.title_header },
3408 { "artist_header", &tmp_music_file_info.artist_header },
3409 { "album_header", &tmp_music_file_info.album_header },
3410 { "year_header", &tmp_music_file_info.year_header },
3412 { "title", &tmp_music_file_info.title },
3413 { "artist", &tmp_music_file_info.artist },
3414 { "album", &tmp_music_file_info.album },
3415 { "year", &tmp_music_file_info.year },
3421 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3422 getCustomMusicFilename(basename));
3424 if (filename_music == NULL)
3427 /* ---------- try to replace file extension ---------- */
3429 filename_prefix = getStringCopy(filename_music);
3430 if (strrchr(filename_prefix, '.') != NULL)
3431 *strrchr(filename_prefix, '.') = '\0';
3432 filename_info = getStringCat2(filename_prefix, ".txt");
3435 printf("trying to load file '%s'...\n", filename_info);
3438 if (fileExists(filename_info))
3439 setup_file_hash = loadSetupFileHash(filename_info);
3441 free(filename_prefix);
3442 free(filename_info);
3444 if (setup_file_hash == NULL)
3446 /* ---------- try to add file extension ---------- */
3448 filename_prefix = getStringCopy(filename_music);
3449 filename_info = getStringCat2(filename_prefix, ".txt");
3452 printf("trying to load file '%s'...\n", filename_info);
3455 if (fileExists(filename_info))
3456 setup_file_hash = loadSetupFileHash(filename_info);
3458 free(filename_prefix);
3459 free(filename_info);
3462 if (setup_file_hash == NULL)
3465 /* ---------- music file info found ---------- */
3467 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3469 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3471 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3473 *token_to_value_ptr[i].value_ptr =
3474 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3477 tmp_music_file_info.basename = getStringCopy(basename);
3478 tmp_music_file_info.music = music;
3479 tmp_music_file_info.is_sound = is_sound;
3481 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3482 *new_music_file_info = tmp_music_file_info;
3484 return new_music_file_info;
3487 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3489 return get_music_file_info_ext(basename, music, FALSE);
3492 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3494 return get_music_file_info_ext(basename, sound, TRUE);
3497 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3498 char *basename, boolean is_sound)
3500 for (; list != NULL; list = list->next)
3501 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3507 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3509 return music_info_listed_ext(list, basename, FALSE);
3512 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3514 return music_info_listed_ext(list, basename, TRUE);
3517 void LoadMusicInfo()
3519 char *music_directory = getCustomMusicDirectory();
3520 int num_music = getMusicListSize();
3521 int num_music_noconf = 0;
3522 int num_sounds = getSoundListSize();
3524 struct dirent *dir_entry;
3525 struct FileInfo *music, *sound;
3526 struct MusicFileInfo *next, **new;
3529 while (music_file_info != NULL)
3531 next = music_file_info->next;
3533 checked_free(music_file_info->basename);
3535 checked_free(music_file_info->title_header);
3536 checked_free(music_file_info->artist_header);
3537 checked_free(music_file_info->album_header);
3538 checked_free(music_file_info->year_header);
3540 checked_free(music_file_info->title);
3541 checked_free(music_file_info->artist);
3542 checked_free(music_file_info->album);
3543 checked_free(music_file_info->year);
3545 free(music_file_info);
3547 music_file_info = next;
3550 new = &music_file_info;
3553 printf("::: num_music == %d\n", num_music);
3556 for (i = 0; i < num_music; i++)
3558 music = getMusicListEntry(i);
3561 printf("::: %d [%08x]\n", i, music->filename);
3564 if (music->filename == NULL)
3567 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3570 /* a configured file may be not recognized as music */
3571 if (!FileIsMusic(music->filename))
3575 printf("::: -> '%s' (configured)\n", music->filename);
3578 if (!music_info_listed(music_file_info, music->filename))
3580 *new = get_music_file_info(music->filename, i);
3582 new = &(*new)->next;
3586 if ((dir = opendir(music_directory)) == NULL)
3588 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3592 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
3594 char *basename = dir_entry->d_name;
3595 boolean music_already_used = FALSE;
3598 /* skip all music files that are configured in music config file */
3599 for (i = 0; i < num_music; i++)
3601 music = getMusicListEntry(i);
3603 if (music->filename == NULL)
3606 if (strcmp(basename, music->filename) == 0)
3608 music_already_used = TRUE;
3613 if (music_already_used)
3616 if (!FileIsMusic(basename))
3620 printf("::: -> '%s' (found in directory)\n", basename);
3623 if (!music_info_listed(music_file_info, basename))
3625 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3627 new = &(*new)->next;
3635 for (i = 0; i < num_sounds; i++)
3637 sound = getSoundListEntry(i);
3639 if (sound->filename == NULL)
3642 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3645 /* a configured file may be not recognized as sound */
3646 if (!FileIsSound(sound->filename))
3650 printf("::: -> '%s' (configured)\n", sound->filename);
3653 if (!sound_info_listed(music_file_info, sound->filename))
3655 *new = get_sound_file_info(sound->filename, i);
3657 new = &(*new)->next;
3663 for (next = music_file_info; next != NULL; next = next->next)
3664 printf("::: title == '%s'\n", next->title);
3668 void add_helpanim_entry(int element, int action, int direction, int delay,
3669 int *num_list_entries)
3671 struct HelpAnimInfo *new_list_entry;
3672 (*num_list_entries)++;
3675 checked_realloc(helpanim_info,
3676 *num_list_entries * sizeof(struct HelpAnimInfo));
3677 new_list_entry = &helpanim_info[*num_list_entries - 1];
3679 new_list_entry->element = element;
3680 new_list_entry->action = action;
3681 new_list_entry->direction = direction;
3682 new_list_entry->delay = delay;
3685 void print_unknown_token(char *filename, char *token, int token_nr)
3689 Error(ERR_RETURN_LINE, "-");
3690 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3691 Error(ERR_RETURN, "- config file: '%s'", filename);
3694 Error(ERR_RETURN, "- token: '%s'", token);
3697 void print_unknown_token_end(int token_nr)
3700 Error(ERR_RETURN_LINE, "-");
3703 void LoadHelpAnimInfo()
3705 char *filename = getHelpAnimFilename();
3706 SetupFileList *setup_file_list = NULL, *list;
3707 SetupFileHash *element_hash, *action_hash, *direction_hash;
3708 int num_list_entries = 0;
3709 int num_unknown_tokens = 0;
3712 if (fileExists(filename))
3713 setup_file_list = loadSetupFileList(filename);
3715 if (setup_file_list == NULL)
3717 /* use reliable default values from static configuration */
3718 SetupFileList *insert_ptr;
3720 insert_ptr = setup_file_list =
3721 newSetupFileList(helpanim_config[0].token,
3722 helpanim_config[0].value);
3724 for (i = 1; helpanim_config[i].token; i++)
3725 insert_ptr = addListEntry(insert_ptr,
3726 helpanim_config[i].token,
3727 helpanim_config[i].value);
3730 element_hash = newSetupFileHash();
3731 action_hash = newSetupFileHash();
3732 direction_hash = newSetupFileHash();
3734 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3735 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3737 for (i = 0; i < NUM_ACTIONS; i++)
3738 setHashEntry(action_hash, element_action_info[i].suffix,
3739 i_to_a(element_action_info[i].value));
3741 /* do not store direction index (bit) here, but direction value! */
3742 for (i = 0; i < NUM_DIRECTIONS; i++)
3743 setHashEntry(direction_hash, element_direction_info[i].suffix,
3744 i_to_a(1 << element_direction_info[i].value));
3746 for (list = setup_file_list; list != NULL; list = list->next)
3748 char *element_token, *action_token, *direction_token;
3749 char *element_value, *action_value, *direction_value;
3750 int delay = atoi(list->value);
3752 if (strcmp(list->token, "end") == 0)
3754 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3759 /* first try to break element into element/action/direction parts;
3760 if this does not work, also accept combined "element[.act][.dir]"
3761 elements (like "dynamite.active"), which are unique elements */
3763 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
3765 element_value = getHashEntry(element_hash, list->token);
3766 if (element_value != NULL) /* element found */
3767 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3771 /* no further suffixes found -- this is not an element */
3772 print_unknown_token(filename, list->token, num_unknown_tokens++);
3778 /* token has format "<prefix>.<something>" */
3780 action_token = strchr(list->token, '.'); /* suffix may be action ... */
3781 direction_token = action_token; /* ... or direction */
3783 element_token = getStringCopy(list->token);
3784 *strchr(element_token, '.') = '\0';
3786 element_value = getHashEntry(element_hash, element_token);
3788 if (element_value == NULL) /* this is no element */
3790 element_value = getHashEntry(element_hash, list->token);
3791 if (element_value != NULL) /* combined element found */
3792 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3795 print_unknown_token(filename, list->token, num_unknown_tokens++);
3797 free(element_token);
3802 action_value = getHashEntry(action_hash, action_token);
3804 if (action_value != NULL) /* action found */
3806 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3809 free(element_token);
3814 direction_value = getHashEntry(direction_hash, direction_token);
3816 if (direction_value != NULL) /* direction found */
3818 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3821 free(element_token);
3826 if (strchr(action_token + 1, '.') == NULL)
3828 /* no further suffixes found -- this is not an action nor direction */
3830 element_value = getHashEntry(element_hash, list->token);
3831 if (element_value != NULL) /* combined element found */
3832 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3835 print_unknown_token(filename, list->token, num_unknown_tokens++);
3837 free(element_token);
3842 /* token has format "<prefix>.<suffix>.<something>" */
3844 direction_token = strchr(action_token + 1, '.');
3846 action_token = getStringCopy(action_token);
3847 *strchr(action_token + 1, '.') = '\0';
3849 action_value = getHashEntry(action_hash, action_token);
3851 if (action_value == NULL) /* this is no action */
3853 element_value = getHashEntry(element_hash, list->token);
3854 if (element_value != NULL) /* combined element found */
3855 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3858 print_unknown_token(filename, list->token, num_unknown_tokens++);
3860 free(element_token);
3866 direction_value = getHashEntry(direction_hash, direction_token);
3868 if (direction_value != NULL) /* direction found */
3870 add_helpanim_entry(atoi(element_value), atoi(action_value),
3871 atoi(direction_value), delay, &num_list_entries);
3873 free(element_token);
3879 /* this is no direction */
3881 element_value = getHashEntry(element_hash, list->token);
3882 if (element_value != NULL) /* combined element found */
3883 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3886 print_unknown_token(filename, list->token, num_unknown_tokens++);
3888 free(element_token);
3892 print_unknown_token_end(num_unknown_tokens);
3894 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3895 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
3897 freeSetupFileList(setup_file_list);
3898 freeSetupFileHash(element_hash);
3899 freeSetupFileHash(action_hash);
3900 freeSetupFileHash(direction_hash);
3904 for (i = 0; i < num_list_entries; i++)
3905 printf("::: %d, %d, %d => %d\n",
3906 helpanim_info[i].element,
3907 helpanim_info[i].action,
3908 helpanim_info[i].direction,
3909 helpanim_info[i].delay);
3913 void LoadHelpTextInfo()
3915 char *filename = getHelpTextFilename();
3918 if (helptext_info != NULL)
3920 freeSetupFileHash(helptext_info);
3921 helptext_info = NULL;
3924 if (fileExists(filename))
3925 helptext_info = loadSetupFileHash(filename);
3927 if (helptext_info == NULL)
3929 /* use reliable default values from static configuration */
3930 helptext_info = newSetupFileHash();
3932 for (i = 0; helptext_config[i].token; i++)
3933 setHashEntry(helptext_info,
3934 helptext_config[i].token,
3935 helptext_config[i].value);
3940 BEGIN_HASH_ITERATION(helptext_info, itr)
3942 printf("::: '%s' => '%s'\n",
3943 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3945 END_HASH_ITERATION(hash, itr)