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 11 /* 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_BD 2
56 #define LEVEL_FILE_TYPE_EM 3
57 #define LEVEL_FILE_TYPE_SP 4
58 #define LEVEL_FILE_TYPE_DX 5
59 #define LEVEL_FILE_TYPE_SB 6
60 #define LEVEL_FILE_TYPE_DC 7
62 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
63 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
65 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
66 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
69 /* ========================================================================= */
70 /* level file functions */
71 /* ========================================================================= */
73 void setElementChangePages(struct ElementInfo *ei, int change_pages)
75 int change_page_size = sizeof(struct ElementChangeInfo);
77 ei->num_change_pages = MAX(1, change_pages);
80 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
82 if (ei->current_change_page >= ei->num_change_pages)
83 ei->current_change_page = ei->num_change_pages - 1;
85 ei->change = &ei->change_page[ei->current_change_page];
88 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
92 change->can_change = FALSE;
94 change->events = CE_BITMASK_DEFAULT;
95 change->sides = CH_SIDE_ANY;
97 change->target_element = EL_EMPTY_SPACE;
99 change->delay_fixed = 0;
100 change->delay_random = 0;
101 change->delay_frames = 1;
103 change->trigger_element = EL_EMPTY_SPACE;
105 change->explode = FALSE;
106 change->use_content = FALSE;
107 change->only_complete = FALSE;
108 change->use_random_change = FALSE;
109 change->random = 100;
110 change->power = CP_NON_DESTRUCTIVE;
112 for (x = 0; x < 3; x++)
113 for (y = 0; y < 3; y++)
114 change->content[x][y] = EL_EMPTY_SPACE;
116 change->direct_action = 0;
117 change->other_action = 0;
119 change->pre_change_function = NULL;
120 change->change_function = NULL;
121 change->post_change_function = NULL;
124 static void setLevelInfoToDefaults(struct LevelInfo *level)
128 level->file_version = FILE_VERSION_ACTUAL;
129 level->game_version = GAME_VERSION_ACTUAL;
131 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
132 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
133 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
135 level->fieldx = STD_LEV_FIELDX;
136 level->fieldy = STD_LEV_FIELDY;
138 for (x = 0; x < MAX_LEV_FIELDX; x++)
139 for (y = 0; y < MAX_LEV_FIELDY; y++)
140 level->field[x][y] = EL_SAND;
143 level->gems_needed = 0;
144 level->amoeba_speed = 10;
145 level->time_magic_wall = 10;
146 level->time_wheel = 10;
147 level->time_light = 10;
148 level->time_timegate = 10;
149 level->amoeba_content = EL_DIAMOND;
150 level->double_speed = FALSE;
151 level->initial_gravity = FALSE;
152 level->em_slippery_gems = FALSE;
153 level->block_last_field = FALSE;
154 level->sp_block_last_field = TRUE;
156 level->use_custom_template = FALSE;
158 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
159 level->name[i] = '\0';
160 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
161 level->author[i] = '\0';
163 strcpy(level->name, NAMELESS_LEVEL_NAME);
164 strcpy(level->author, ANONYMOUS_NAME);
166 for (i = 0; i < 4; i++)
168 level->envelope_text[i][0] = '\0';
169 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
170 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
173 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
174 level->score[i] = 10;
176 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
177 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
178 for (x = 0; x < 3; x++)
179 for (y = 0; y < 3; y++)
180 level->yamyam_content[i][x][y] =
181 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
183 level->field[0][0] = EL_PLAYER_1;
184 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
186 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
190 setElementChangePages(&element_info[element], 1);
191 setElementChangeInfoToDefaults(element_info[element].change);
193 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
195 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
196 element_info[element].description[j] = '\0';
198 if (element_info[element].custom_description != NULL)
199 strncpy(element_info[element].description,
200 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
202 strcpy(element_info[element].description,
203 element_info[element].editor_description);
205 element_info[element].use_gfx_element = FALSE;
206 element_info[element].gfx_element = EL_EMPTY_SPACE;
209 if (IS_CUSTOM_ELEMENT(element))
211 element_info[element].collect_score = 10; /* special default */
212 element_info[element].collect_count = 1; /* special default */
214 element_info[element].push_delay_fixed = -1; /* initialize later */
215 element_info[element].push_delay_random = -1; /* initialize later */
216 element_info[element].move_delay_fixed = 0;
217 element_info[element].move_delay_random = 0;
219 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
220 element_info[element].move_direction_initial = MV_START_AUTOMATIC;
221 element_info[element].move_stepsize = TILEX / 8;
222 element_info[element].move_enter_element = EL_EMPTY_SPACE;
223 element_info[element].move_leave_element = EL_EMPTY_SPACE;
224 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
226 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
228 for (x = 0; x < 3; x++)
229 for (y = 0; y < 3; y++)
230 element_info[element].content[x][y] = EL_EMPTY_SPACE;
232 element_info[element].access_type = 0;
233 element_info[element].access_layer = 0;
234 element_info[element].access_protected = 0;
235 element_info[element].walk_to_action = 0;
236 element_info[element].smash_targets = 0;
237 element_info[element].deadliness = 0;
238 element_info[element].consistency = 0;
240 element_info[element].can_explode_by_fire = FALSE;
241 element_info[element].can_explode_smashed = FALSE;
242 element_info[element].can_explode_impact = FALSE;
244 element_info[element].current_change_page = 0;
246 /* start with no properties at all */
247 for (j = 0; j < NUM_EP_BITFIELDS; j++)
248 Properties[element][j] = EP_BITMASK_DEFAULT;
250 element_info[element].modified_settings = FALSE;
252 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
254 /* initialize memory for list of elements in group */
255 if (element_info[element].group == NULL)
256 element_info[element].group =
257 checked_malloc(sizeof(struct ElementGroupInfo));
259 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
260 element_info[element].group->element[j] = EL_EMPTY_SPACE;
262 /* default: only one element in group */
263 element_info[element].group->num_elements = 1;
265 element_info[element].group->choice_mode = ANIM_RANDOM;
269 BorderElement = EL_STEELWALL;
271 level->no_level_file = FALSE;
273 if (leveldir_current == NULL) /* only when dumping level */
276 /* try to determine better author name than 'anonymous' */
277 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
279 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
280 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
284 switch (LEVELCLASS(leveldir_current))
286 case LEVELCLASS_TUTORIAL:
287 strcpy(level->author, PROGRAM_AUTHOR_STRING);
290 case LEVELCLASS_CONTRIB:
291 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
292 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
295 case LEVELCLASS_PRIVATE:
296 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
297 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
301 /* keep default value */
307 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
309 level_file_info->nr = 0;
310 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
311 level_file_info->packed = FALSE;
312 level_file_info->basename = NULL;
313 level_file_info->filename = NULL;
316 static void ActivateLevelTemplate()
318 /* Currently there is no special action needed to activate the template
319 data, because 'element_info' and 'Properties' overwrite the original
320 level data, while all other variables do not change. */
323 static char *getLevelFilenameFromBasename(char *basename)
325 static char *filename = NULL;
327 checked_free(filename);
329 filename = getPath2(getCurrentLevelDir(), basename);
334 static int getFileTypeFromBasename(char *basename)
336 static char *filename = NULL;
337 struct stat file_status;
339 /* ---------- try to determine file type from filename ---------- */
341 /* check for typical filename of a Supaplex level package file */
342 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
343 strncmp(basename, "LEVELS.D", 8) == 0))
344 return LEVEL_FILE_TYPE_SP;
346 /* ---------- try to determine file type from filesize ---------- */
348 checked_free(filename);
349 filename = getPath2(getCurrentLevelDir(), basename);
351 if (stat(filename, &file_status) == 0)
353 /* check for typical filesize of a Supaplex level package file */
354 if (file_status.st_size == 170496)
355 return LEVEL_FILE_TYPE_SP;
358 return LEVEL_FILE_TYPE_UNKNOWN;
361 static char *getSingleLevelBasename(int nr, int type)
363 static char basename[MAX_FILENAME_LEN];
364 char *level_filename = getStringCopy(leveldir_current->level_filename);
366 if (level_filename == NULL)
367 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
371 case LEVEL_FILE_TYPE_RND:
373 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
375 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
378 case LEVEL_FILE_TYPE_EM:
379 sprintf(basename, "%d", nr);
382 case LEVEL_FILE_TYPE_UNKNOWN:
384 sprintf(basename, level_filename, nr);
388 free(level_filename);
393 static char *getPackedLevelBasename(int type)
395 static char basename[MAX_FILENAME_LEN];
396 char *directory = getCurrentLevelDir();
398 struct dirent *dir_entry;
400 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
402 if ((dir = opendir(directory)) == NULL)
404 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
409 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
411 char *entry_basename = dir_entry->d_name;
412 int entry_type = getFileTypeFromBasename(entry_basename);
414 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
416 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
419 strcpy(basename, entry_basename);
431 static char *getSingleLevelFilename(int nr, int type)
433 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
437 static char *getPackedLevelFilename(int type)
439 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
443 char *getDefaultLevelFilename(int nr)
445 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
448 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
453 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
454 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
457 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
462 lfi->basename = getPackedLevelBasename(lfi->type);
463 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
466 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
468 /* special case: level number is negative => check for level template file */
471 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
476 if (leveldir_current->level_filename != NULL)
478 /* check for file name/pattern specified in "levelinfo.conf" */
479 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
480 if (fileExists(lfi->filename))
484 /* check for native Rocks'n'Diamonds level file */
485 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
486 if (fileExists(lfi->filename))
489 /* check for classic Emerald Mine level file */
490 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
491 if (fileExists(lfi->filename))
494 /* check for various packed level file formats */
495 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
496 if (fileExists(lfi->filename))
499 /* no known level file found -- try to use default values */
500 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
503 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
505 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
506 lfi->type = getFileTypeFromBasename(lfi->basename);
509 static struct LevelFileInfo *getLevelFileInfo(int nr)
511 static struct LevelFileInfo level_file_info;
513 /* always start with reliable default values */
514 setFileInfoToDefaults(&level_file_info);
516 level_file_info.nr = nr; /* set requested level number */
518 determineLevelFileInfo_Filename(&level_file_info);
519 determineLevelFileInfo_Filetype(&level_file_info);
521 return &level_file_info;
525 /* ------------------------------------------------------------------------- */
526 /* functions for loading R'n'D level */
527 /* ------------------------------------------------------------------------- */
529 int getMappedElement(int element)
531 /* map some (historic, now obsolete) elements */
536 case EL_PLAYER_OBSOLETE:
537 element = EL_PLAYER_1;
540 case EL_KEY_OBSOLETE:
543 case EL_EM_KEY_1_FILE_OBSOLETE:
544 element = EL_EM_KEY_1;
547 case EL_EM_KEY_2_FILE_OBSOLETE:
548 element = EL_EM_KEY_2;
551 case EL_EM_KEY_3_FILE_OBSOLETE:
552 element = EL_EM_KEY_3;
555 case EL_EM_KEY_4_FILE_OBSOLETE:
556 element = EL_EM_KEY_4;
559 case EL_ENVELOPE_OBSOLETE:
560 element = EL_ENVELOPE_1;
568 if (element >= NUM_FILE_ELEMENTS)
570 Error(ERR_WARN, "invalid level element %d", element);
572 element = EL_UNKNOWN;
577 if (element >= NUM_FILE_ELEMENTS)
579 Error(ERR_WARN, "invalid level element %d", element);
581 element = EL_UNKNOWN;
583 else if (element == EL_PLAYER_OBSOLETE)
584 element = EL_PLAYER_1;
585 else if (element == EL_KEY_OBSOLETE)
592 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
594 level->file_version = getFileVersion(file);
595 level->game_version = getFileVersion(file);
600 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
604 level->fieldx = getFile8Bit(file);
605 level->fieldy = getFile8Bit(file);
607 level->time = getFile16BitBE(file);
608 level->gems_needed = getFile16BitBE(file);
610 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
611 level->name[i] = getFile8Bit(file);
612 level->name[MAX_LEVEL_NAME_LEN] = 0;
614 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
615 level->score[i] = getFile8Bit(file);
617 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
618 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
619 for (y = 0; y < 3; y++)
620 for (x = 0; x < 3; x++)
621 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
623 level->amoeba_speed = getFile8Bit(file);
624 level->time_magic_wall = getFile8Bit(file);
625 level->time_wheel = getFile8Bit(file);
626 level->amoeba_content = getMappedElement(getFile8Bit(file));
627 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
628 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
629 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
630 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
632 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
634 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
635 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
637 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
642 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
646 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
647 level->author[i] = getFile8Bit(file);
648 level->author[MAX_LEVEL_NAME_LEN] = 0;
653 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
656 int chunk_size_expected = level->fieldx * level->fieldy;
658 /* Note: "chunk_size" was wrong before version 2.0 when elements are
659 stored with 16-bit encoding (and should be twice as big then).
660 Even worse, playfield data was stored 16-bit when only yamyam content
661 contained 16-bit elements and vice versa. */
663 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
664 chunk_size_expected *= 2;
666 if (chunk_size_expected != chunk_size)
668 ReadUnusedBytesFromFile(file, chunk_size);
669 return chunk_size_expected;
672 for (y = 0; y < level->fieldy; y++)
673 for (x = 0; x < level->fieldx; x++)
675 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
680 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
684 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
685 int chunk_size_expected = header_size + content_size;
687 /* Note: "chunk_size" was wrong before version 2.0 when elements are
688 stored with 16-bit encoding (and should be twice as big then).
689 Even worse, playfield data was stored 16-bit when only yamyam content
690 contained 16-bit elements and vice versa. */
692 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
693 chunk_size_expected += content_size;
695 if (chunk_size_expected != chunk_size)
697 ReadUnusedBytesFromFile(file, chunk_size);
698 return chunk_size_expected;
702 level->num_yamyam_contents = getFile8Bit(file);
706 /* correct invalid number of content fields -- should never happen */
707 if (level->num_yamyam_contents < 1 ||
708 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
709 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
711 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
712 for (y = 0; y < 3; y++)
713 for (x = 0; x < 3; x++)
714 level->yamyam_content[i][x][y] =
715 getMappedElement(level->encoding_16bit_field ?
716 getFile16BitBE(file) : getFile8Bit(file));
720 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
724 int num_contents, content_xsize, content_ysize;
725 int content_array[MAX_ELEMENT_CONTENTS][3][3];
727 element = getMappedElement(getFile16BitBE(file));
728 num_contents = getFile8Bit(file);
729 content_xsize = getFile8Bit(file);
730 content_ysize = getFile8Bit(file);
732 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
734 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
735 for (y = 0; y < 3; y++)
736 for (x = 0; x < 3; x++)
737 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
739 /* correct invalid number of content fields -- should never happen */
740 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
741 num_contents = STD_ELEMENT_CONTENTS;
743 if (element == EL_YAMYAM)
745 level->num_yamyam_contents = num_contents;
747 for (i = 0; i < num_contents; i++)
748 for (y = 0; y < 3; y++)
749 for (x = 0; x < 3; x++)
750 level->yamyam_content[i][x][y] = content_array[i][x][y];
752 else if (element == EL_BD_AMOEBA)
754 level->amoeba_content = content_array[0][0][0];
758 Error(ERR_WARN, "cannot load content for element '%d'", element);
764 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
770 int chunk_size_expected;
772 element = getMappedElement(getFile16BitBE(file));
773 if (!IS_ENVELOPE(element))
774 element = EL_ENVELOPE_1;
776 envelope_nr = element - EL_ENVELOPE_1;
778 envelope_len = getFile16BitBE(file);
780 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
781 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
783 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
785 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
786 if (chunk_size_expected != chunk_size)
788 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
789 return chunk_size_expected;
792 for (i = 0; i < envelope_len; i++)
793 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
798 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
800 int num_changed_custom_elements = getFile16BitBE(file);
801 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
804 if (chunk_size_expected != chunk_size)
806 ReadUnusedBytesFromFile(file, chunk_size - 2);
807 return chunk_size_expected;
810 for (i = 0; i < num_changed_custom_elements; i++)
812 int element = getFile16BitBE(file);
813 int properties = getFile32BitBE(file);
815 if (IS_CUSTOM_ELEMENT(element))
816 Properties[element][EP_BITFIELD_BASE] = properties;
818 Error(ERR_WARN, "invalid custom element number %d", element);
824 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
826 int num_changed_custom_elements = getFile16BitBE(file);
827 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
830 if (chunk_size_expected != chunk_size)
832 ReadUnusedBytesFromFile(file, chunk_size - 2);
833 return chunk_size_expected;
836 for (i = 0; i < num_changed_custom_elements; i++)
838 int element = getFile16BitBE(file);
839 int custom_target_element = getFile16BitBE(file);
841 if (IS_CUSTOM_ELEMENT(element))
842 element_info[element].change->target_element = custom_target_element;
844 Error(ERR_WARN, "invalid custom element number %d", element);
850 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
852 int num_changed_custom_elements = getFile16BitBE(file);
853 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
856 if (chunk_size_expected != chunk_size)
858 ReadUnusedBytesFromFile(file, chunk_size - 2);
859 return chunk_size_expected;
862 for (i = 0; i < num_changed_custom_elements; i++)
864 int element = getFile16BitBE(file);
866 if (!IS_CUSTOM_ELEMENT(element))
868 Error(ERR_WARN, "invalid custom element number %d", element);
870 element = EL_INTERNAL_DUMMY;
873 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
874 element_info[element].description[j] = getFile8Bit(file);
875 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
877 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
879 /* some free bytes for future properties and padding */
880 ReadUnusedBytesFromFile(file, 7);
882 element_info[element].use_gfx_element = getFile8Bit(file);
883 element_info[element].gfx_element =
884 getMappedElement(getFile16BitBE(file));
886 element_info[element].collect_score = getFile8Bit(file);
887 element_info[element].collect_count = getFile8Bit(file);
889 element_info[element].push_delay_fixed = getFile16BitBE(file);
890 element_info[element].push_delay_random = getFile16BitBE(file);
891 element_info[element].move_delay_fixed = getFile16BitBE(file);
892 element_info[element].move_delay_random = getFile16BitBE(file);
894 element_info[element].move_pattern = getFile16BitBE(file);
895 element_info[element].move_direction_initial = getFile8Bit(file);
896 element_info[element].move_stepsize = getFile8Bit(file);
898 for (y = 0; y < 3; y++)
899 for (x = 0; x < 3; x++)
900 element_info[element].content[x][y] =
901 getMappedElement(getFile16BitBE(file));
903 element_info[element].change->events = getFile32BitBE(file);
905 element_info[element].change->target_element =
906 getMappedElement(getFile16BitBE(file));
908 element_info[element].change->delay_fixed = getFile16BitBE(file);
909 element_info[element].change->delay_random = getFile16BitBE(file);
910 element_info[element].change->delay_frames = getFile16BitBE(file);
912 element_info[element].change->trigger_element =
913 getMappedElement(getFile16BitBE(file));
915 element_info[element].change->explode = getFile8Bit(file);
916 element_info[element].change->use_content = getFile8Bit(file);
917 element_info[element].change->only_complete = getFile8Bit(file);
918 element_info[element].change->use_random_change = getFile8Bit(file);
920 element_info[element].change->random = getFile8Bit(file);
921 element_info[element].change->power = getFile8Bit(file);
923 for (y = 0; y < 3; y++)
924 for (x = 0; x < 3; x++)
925 element_info[element].change->content[x][y] =
926 getMappedElement(getFile16BitBE(file));
928 element_info[element].slippery_type = getFile8Bit(file);
930 /* some free bytes for future properties and padding */
931 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
933 /* mark that this custom element has been modified */
934 element_info[element].modified_settings = TRUE;
940 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
942 struct ElementInfo *ei;
943 int chunk_size_expected;
947 element = getFile16BitBE(file);
949 if (!IS_CUSTOM_ELEMENT(element))
951 Error(ERR_WARN, "invalid custom element number %d", element);
953 ReadUnusedBytesFromFile(file, chunk_size - 2);
957 ei = &element_info[element];
959 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
960 ei->description[i] = getFile8Bit(file);
961 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
963 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
964 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
966 ei->num_change_pages = getFile8Bit(file);
968 /* some free bytes for future base property values and padding */
969 ReadUnusedBytesFromFile(file, 5);
971 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
972 if (chunk_size_expected != chunk_size)
974 ReadUnusedBytesFromFile(file, chunk_size - 48);
975 return chunk_size_expected;
978 /* read custom property values */
980 ei->use_gfx_element = getFile8Bit(file);
981 ei->gfx_element = getMappedElement(getFile16BitBE(file));
983 ei->collect_score = getFile8Bit(file);
984 ei->collect_count = getFile8Bit(file);
986 ei->push_delay_fixed = getFile16BitBE(file);
987 ei->push_delay_random = getFile16BitBE(file);
988 ei->move_delay_fixed = getFile16BitBE(file);
989 ei->move_delay_random = getFile16BitBE(file);
991 /* bits 0 - 15 of "move_pattern" ... */
992 ei->move_pattern = getFile16BitBE(file);
993 ei->move_direction_initial = getFile8Bit(file);
994 ei->move_stepsize = getFile8Bit(file);
996 ei->slippery_type = getFile8Bit(file);
998 for (y = 0; y < 3; y++)
999 for (x = 0; x < 3; x++)
1000 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1002 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1003 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1004 ei->move_leave_type = getFile8Bit(file);
1006 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1007 ei->move_pattern |= (getFile16BitBE(file) << 16);
1009 /* some free bytes for future custom property values and padding */
1010 ReadUnusedBytesFromFile(file, 5);
1012 /* read change property values */
1014 setElementChangePages(ei, ei->num_change_pages);
1016 for (i = 0; i < ei->num_change_pages; i++)
1018 struct ElementChangeInfo *change = &ei->change_page[i];
1020 /* always start with reliable default values */
1021 setElementChangeInfoToDefaults(change);
1023 change->events = getFile32BitBE(file);
1025 change->target_element = getMappedElement(getFile16BitBE(file));
1027 change->delay_fixed = getFile16BitBE(file);
1028 change->delay_random = getFile16BitBE(file);
1029 change->delay_frames = getFile16BitBE(file);
1031 change->trigger_element = getMappedElement(getFile16BitBE(file));
1033 change->explode = getFile8Bit(file);
1034 change->use_content = getFile8Bit(file);
1035 change->only_complete = getFile8Bit(file);
1036 change->use_random_change = getFile8Bit(file);
1038 change->random = getFile8Bit(file);
1039 change->power = getFile8Bit(file);
1041 for (y = 0; y < 3; y++)
1042 for (x = 0; x < 3; x++)
1043 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1045 change->can_change = getFile8Bit(file);
1047 change->sides = getFile8Bit(file);
1049 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
1050 change->sides = CH_SIDE_ANY;
1052 /* some free bytes for future change property values and padding */
1053 ReadUnusedBytesFromFile(file, 8);
1056 /* mark this custom element as modified */
1057 ei->modified_settings = TRUE;
1062 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1064 struct ElementInfo *ei;
1065 struct ElementGroupInfo *group;
1069 element = getFile16BitBE(file);
1071 if (!IS_GROUP_ELEMENT(element))
1073 Error(ERR_WARN, "invalid group element number %d", element);
1075 ReadUnusedBytesFromFile(file, chunk_size - 2);
1079 ei = &element_info[element];
1081 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1082 ei->description[i] = getFile8Bit(file);
1083 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1085 group = element_info[element].group;
1087 group->num_elements = getFile8Bit(file);
1089 ei->use_gfx_element = getFile8Bit(file);
1090 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1092 group->choice_mode = getFile8Bit(file);
1094 /* some free bytes for future values and padding */
1095 ReadUnusedBytesFromFile(file, 3);
1097 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1098 group->element[i] = getMappedElement(getFile16BitBE(file));
1100 /* mark this group element as modified */
1101 element_info[element].modified_settings = TRUE;
1106 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1107 struct LevelFileInfo *level_file_info)
1109 char *filename = level_file_info->filename;
1110 char cookie[MAX_LINE_LEN];
1111 char chunk_name[CHUNK_ID_LEN + 1];
1115 if (!(file = fopen(filename, MODE_READ)))
1117 level->no_level_file = TRUE;
1119 if (level != &level_template)
1120 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1125 getFileChunkBE(file, chunk_name, NULL);
1126 if (strcmp(chunk_name, "RND1") == 0)
1128 getFile32BitBE(file); /* not used */
1130 getFileChunkBE(file, chunk_name, NULL);
1131 if (strcmp(chunk_name, "CAVE") != 0)
1133 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1138 else /* check for pre-2.0 file format with cookie string */
1140 strcpy(cookie, chunk_name);
1141 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1142 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1143 cookie[strlen(cookie) - 1] = '\0';
1145 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1147 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1152 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1154 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1159 /* pre-2.0 level files have no game version, so use file version here */
1160 level->game_version = level->file_version;
1163 if (level->file_version < FILE_VERSION_1_2)
1165 /* level files from versions before 1.2.0 without chunk structure */
1166 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1167 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1175 int (*loader)(FILE *, int, struct LevelInfo *);
1179 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1180 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1181 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1182 { "BODY", -1, LoadLevel_BODY },
1183 { "CONT", -1, LoadLevel_CONT },
1184 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1185 { "CNT3", -1, LoadLevel_CNT3 },
1186 { "CUS1", -1, LoadLevel_CUS1 },
1187 { "CUS2", -1, LoadLevel_CUS2 },
1188 { "CUS3", -1, LoadLevel_CUS3 },
1189 { "CUS4", -1, LoadLevel_CUS4 },
1190 { "GRP1", -1, LoadLevel_GRP1 },
1194 while (getFileChunkBE(file, chunk_name, &chunk_size))
1198 while (chunk_info[i].name != NULL &&
1199 strcmp(chunk_name, chunk_info[i].name) != 0)
1202 if (chunk_info[i].name == NULL)
1204 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1205 chunk_name, filename);
1206 ReadUnusedBytesFromFile(file, chunk_size);
1208 else if (chunk_info[i].size != -1 &&
1209 chunk_info[i].size != chunk_size)
1211 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1212 chunk_size, chunk_name, filename);
1213 ReadUnusedBytesFromFile(file, chunk_size);
1217 /* call function to load this level chunk */
1218 int chunk_size_expected =
1219 (chunk_info[i].loader)(file, chunk_size, level);
1221 /* the size of some chunks cannot be checked before reading other
1222 chunks first (like "HEAD" and "BODY") that contain some header
1223 information, so check them here */
1224 if (chunk_size_expected != chunk_size)
1226 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1227 chunk_size, chunk_name, filename);
1236 /* ------------------------------------------------------------------------- */
1237 /* functions for loading EM level */
1238 /* ------------------------------------------------------------------------- */
1240 static int map_em_element_yam(int element)
1244 case 0x00: return EL_EMPTY;
1245 case 0x01: return EL_EMERALD;
1246 case 0x02: return EL_DIAMOND;
1247 case 0x03: return EL_ROCK;
1248 case 0x04: return EL_ROBOT;
1249 case 0x05: return EL_SPACESHIP_UP;
1250 case 0x06: return EL_BOMB;
1251 case 0x07: return EL_BUG_UP;
1252 case 0x08: return EL_AMOEBA_DROP;
1253 case 0x09: return EL_NUT;
1254 case 0x0a: return EL_YAMYAM;
1255 case 0x0b: return EL_QUICKSAND_FULL;
1256 case 0x0c: return EL_SAND;
1257 case 0x0d: return EL_WALL_SLIPPERY;
1258 case 0x0e: return EL_STEELWALL;
1259 case 0x0f: return EL_WALL;
1260 case 0x10: return EL_EM_KEY_1;
1261 case 0x11: return EL_EM_KEY_2;
1262 case 0x12: return EL_EM_KEY_4;
1263 case 0x13: return EL_EM_KEY_3;
1264 case 0x14: return EL_MAGIC_WALL;
1265 case 0x15: return EL_ROBOT_WHEEL;
1266 case 0x16: return EL_DYNAMITE;
1268 case 0x17: return EL_EM_KEY_1; /* EMC */
1269 case 0x18: return EL_BUG_UP; /* EMC */
1270 case 0x1a: return EL_DIAMOND; /* EMC */
1271 case 0x1b: return EL_EMERALD; /* EMC */
1272 case 0x25: return EL_NUT; /* EMC */
1273 case 0x80: return EL_EMPTY; /* EMC */
1274 case 0x85: return EL_EM_KEY_1; /* EMC */
1275 case 0x86: return EL_EM_KEY_2; /* EMC */
1276 case 0x87: return EL_EM_KEY_4; /* EMC */
1277 case 0x88: return EL_EM_KEY_3; /* EMC */
1278 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1279 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1280 case 0xaf: return EL_DYNAMITE; /* EMC */
1281 case 0xbd: return EL_SAND; /* EMC */
1284 Error(ERR_WARN, "invalid level element %d", element);
1289 static int map_em_element_field(int element)
1291 if (element >= 0xc8 && element <= 0xe1)
1292 return EL_CHAR_A + (element - 0xc8);
1293 else if (element >= 0xe2 && element <= 0xeb)
1294 return EL_CHAR_0 + (element - 0xe2);
1298 case 0x00: return EL_ROCK;
1299 case 0x01: return EL_ROCK; /* EMC */
1300 case 0x02: return EL_DIAMOND;
1301 case 0x03: return EL_DIAMOND;
1302 case 0x04: return EL_ROBOT;
1303 case 0x05: return EL_ROBOT; /* EMC */
1304 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1305 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1306 case 0x08: return EL_SPACESHIP_UP;
1307 case 0x09: return EL_SPACESHIP_RIGHT;
1308 case 0x0a: return EL_SPACESHIP_DOWN;
1309 case 0x0b: return EL_SPACESHIP_LEFT;
1310 case 0x0c: return EL_SPACESHIP_UP;
1311 case 0x0d: return EL_SPACESHIP_RIGHT;
1312 case 0x0e: return EL_SPACESHIP_DOWN;
1313 case 0x0f: return EL_SPACESHIP_LEFT;
1315 case 0x10: return EL_BOMB;
1316 case 0x11: return EL_BOMB; /* EMC */
1317 case 0x12: return EL_EMERALD;
1318 case 0x13: return EL_EMERALD;
1319 case 0x14: return EL_BUG_UP;
1320 case 0x15: return EL_BUG_RIGHT;
1321 case 0x16: return EL_BUG_DOWN;
1322 case 0x17: return EL_BUG_LEFT;
1323 case 0x18: return EL_BUG_UP;
1324 case 0x19: return EL_BUG_RIGHT;
1325 case 0x1a: return EL_BUG_DOWN;
1326 case 0x1b: return EL_BUG_LEFT;
1327 case 0x1c: return EL_AMOEBA_DROP;
1328 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1329 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1330 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1332 case 0x20: return EL_ROCK;
1333 case 0x21: return EL_BOMB; /* EMC */
1334 case 0x22: return EL_DIAMOND; /* EMC */
1335 case 0x23: return EL_EMERALD; /* EMC */
1336 case 0x24: return EL_MAGIC_WALL;
1337 case 0x25: return EL_NUT;
1338 case 0x26: return EL_NUT; /* EMC */
1339 case 0x27: return EL_NUT; /* EMC */
1341 /* looks like magic wheel, but is _always_ activated */
1342 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1344 case 0x29: return EL_YAMYAM; /* up */
1345 case 0x2a: return EL_YAMYAM; /* down */
1346 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1347 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1348 case 0x2d: return EL_QUICKSAND_FULL;
1349 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1350 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1352 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1353 case 0x31: return EL_SAND; /* EMC */
1354 case 0x32: return EL_SAND; /* EMC */
1355 case 0x33: return EL_SAND; /* EMC */
1356 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1357 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1358 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1359 case 0x37: return EL_SAND; /* EMC */
1360 case 0x38: return EL_ROCK; /* EMC */
1361 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1362 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1363 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1364 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1365 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1366 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1367 case 0x3f: return EL_ACID_POOL_BOTTOM;
1369 case 0x40: return EL_EXIT_OPEN; /* 1 */
1370 case 0x41: return EL_EXIT_OPEN; /* 2 */
1371 case 0x42: return EL_EXIT_OPEN; /* 3 */
1372 case 0x43: return EL_BALLOON; /* EMC */
1373 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1374 case 0x45: return EL_SPRING; /* EMC */
1375 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1376 case 0x47: return EL_SPRING; /* left */ /* EMC */
1377 case 0x48: return EL_SPRING; /* right */ /* EMC */
1378 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1379 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1380 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1381 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1382 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1383 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1384 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1386 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1387 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1388 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1389 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1390 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1391 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1392 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1393 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1394 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1395 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1396 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1397 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1398 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1399 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1400 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1401 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1403 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1404 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1405 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1406 case 0x63: return EL_SPRING; /* left */ /* EMC */
1407 case 0x64: return EL_SPRING; /* right */ /* EMC */
1408 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1409 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1410 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1411 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1412 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1413 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1414 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1415 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1416 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1417 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1418 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1420 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1421 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1422 case 0x72: return EL_NUT; /* left */ /* EMC */
1423 case 0x73: return EL_SAND; /* EMC (? "nut") */
1424 case 0x74: return EL_STEELWALL;
1425 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1426 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1427 case 0x77: return EL_BOMB; /* left */ /* EMC */
1428 case 0x78: return EL_BOMB; /* right */ /* EMC */
1429 case 0x79: return EL_ROCK; /* left */ /* EMC */
1430 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1431 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1432 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1433 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1434 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1435 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1437 case 0x80: return EL_EMPTY;
1438 case 0x81: return EL_WALL_SLIPPERY;
1439 case 0x82: return EL_SAND;
1440 case 0x83: return EL_STEELWALL;
1441 case 0x84: return EL_WALL;
1442 case 0x85: return EL_EM_KEY_1;
1443 case 0x86: return EL_EM_KEY_2;
1444 case 0x87: return EL_EM_KEY_4;
1445 case 0x88: return EL_EM_KEY_3;
1446 case 0x89: return EL_EM_GATE_1;
1447 case 0x8a: return EL_EM_GATE_2;
1448 case 0x8b: return EL_EM_GATE_4;
1449 case 0x8c: return EL_EM_GATE_3;
1450 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1451 case 0x8e: return EL_EM_GATE_1_GRAY;
1452 case 0x8f: return EL_EM_GATE_2_GRAY;
1454 case 0x90: return EL_EM_GATE_4_GRAY;
1455 case 0x91: return EL_EM_GATE_3_GRAY;
1456 case 0x92: return EL_MAGIC_WALL;
1457 case 0x93: return EL_ROBOT_WHEEL;
1458 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1459 case 0x95: return EL_ACID_POOL_TOPLEFT;
1460 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1461 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1462 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1463 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1464 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1465 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1466 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1467 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1468 case 0x9e: return EL_EXIT_CLOSED;
1469 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1471 /* looks like normal sand, but behaves like wall */
1472 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1473 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1474 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1475 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1476 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1477 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1478 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1479 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1480 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1481 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1482 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1483 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1484 case 0xac: return EL_CHAR_COMMA; /* EMC */
1485 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1486 case 0xae: return EL_CHAR_MINUS; /* EMC */
1487 case 0xaf: return EL_DYNAMITE;
1489 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1490 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1491 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1492 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1493 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1494 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1495 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1496 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1497 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1498 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1499 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1500 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1501 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1502 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1503 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1504 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1506 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1507 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1508 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1509 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1510 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1511 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1512 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1513 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1515 /* characters: see above */
1517 case 0xec: return EL_CHAR_PERIOD;
1518 case 0xed: return EL_CHAR_EXCLAM;
1519 case 0xee: return EL_CHAR_COLON;
1520 case 0xef: return EL_CHAR_QUESTION;
1522 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1523 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1524 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1525 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1526 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1527 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1528 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1529 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1531 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1532 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1533 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1534 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1535 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1536 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1538 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1539 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1542 /* should never happen (all 8-bit value cases should be handled) */
1543 Error(ERR_WARN, "invalid level element %d", element);
1548 #define EM_LEVEL_SIZE 2106
1549 #define EM_LEVEL_XSIZE 64
1550 #define EM_LEVEL_YSIZE 32
1552 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1553 struct LevelFileInfo *level_file_info)
1555 char *filename = level_file_info->filename;
1557 unsigned char leveldata[EM_LEVEL_SIZE];
1558 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1559 unsigned char code0 = 0x65;
1560 unsigned char code1 = 0x11;
1561 boolean level_is_crypted = FALSE;
1562 int nr = level_file_info->nr;
1565 if (!(file = fopen(filename, MODE_READ)))
1567 level->no_level_file = TRUE;
1569 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1574 for(i = 0; i < EM_LEVEL_SIZE; i++)
1575 leveldata[i] = fgetc(file);
1579 /* check if level data is crypted by testing against known starting bytes
1580 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1582 if ((leveldata[0] == 0xf1 ||
1583 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1585 level_is_crypted = TRUE;
1587 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1588 leveldata[0] = 0xf1;
1591 if (level_is_crypted) /* decode crypted level data */
1593 for(i = 0; i < EM_LEVEL_SIZE; i++)
1595 leveldata[i] ^= code0;
1596 leveldata[i] -= code1;
1598 code0 = (code0 + 7) & 0xff;
1602 level->fieldx = EM_LEVEL_XSIZE;
1603 level->fieldy = EM_LEVEL_YSIZE;
1605 level->time = header[46] * 10;
1606 level->gems_needed = header[47];
1608 /* The original Emerald Mine levels have their level number stored
1609 at the second byte of the level file...
1610 Do not trust this information at other level files, e.g. EMC,
1611 but correct it anyway (normally the first row is completely
1612 steel wall, so the correction does not hurt anyway). */
1614 if (leveldata[1] == nr)
1615 leveldata[1] = leveldata[2]; /* correct level number field */
1617 sprintf(level->name, "Level %d", nr); /* set level name */
1619 level->score[SC_EMERALD] = header[36];
1620 level->score[SC_DIAMOND] = header[37];
1621 level->score[SC_ROBOT] = header[38];
1622 level->score[SC_SPACESHIP] = header[39];
1623 level->score[SC_BUG] = header[40];
1624 level->score[SC_YAMYAM] = header[41];
1625 level->score[SC_NUT] = header[42];
1626 level->score[SC_DYNAMITE] = header[43];
1627 level->score[SC_TIME_BONUS] = header[44];
1629 level->num_yamyam_contents = 4;
1631 for(i = 0; i < level->num_yamyam_contents; i++)
1632 for(y = 0; y < 3; y++)
1633 for(x = 0; x < 3; x++)
1634 level->yamyam_content[i][x][y] =
1635 map_em_element_yam(header[i * 9 + y * 3 + x]);
1637 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1638 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1639 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1640 level->amoeba_content = EL_DIAMOND;
1642 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1644 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1646 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1647 new_element = EL_AMOEBA_WET;
1649 level->field[x][y] = new_element;
1652 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1653 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1654 level->field[x][y] = EL_PLAYER_1;
1656 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1657 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1658 level->field[x][y] = EL_PLAYER_2;
1661 /* ------------------------------------------------------------------------- */
1662 /* functions for loading SP level */
1663 /* ------------------------------------------------------------------------- */
1665 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1666 #define SP_LEVEL_SIZE 1536
1667 #define SP_LEVEL_XSIZE 60
1668 #define SP_LEVEL_YSIZE 24
1669 #define SP_LEVEL_NAME_LEN 23
1671 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1676 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1677 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1679 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1681 int element_old = fgetc(file);
1684 if (element_old <= 0x27)
1685 element_new = getMappedElement(EL_SP_START + element_old);
1686 else if (element_old == 0x28)
1687 element_new = EL_INVISIBLE_WALL;
1690 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1691 Error(ERR_WARN, "invalid level element %d", element_old);
1693 element_new = EL_UNKNOWN;
1696 level->field[x][y] = element_new;
1700 ReadUnusedBytesFromFile(file, 4);
1702 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1703 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1705 ReadUnusedBytesFromFile(file, 1);
1707 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1708 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1709 level->name[i] = fgetc(file);
1710 level->name[SP_LEVEL_NAME_LEN] = '\0';
1712 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1713 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1715 /* number of infotrons needed; 0 means that Supaplex will count the total
1716 amount of infotrons in the level and use the low byte of that number.
1717 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1718 level->gems_needed = fgetc(file);
1720 /* information about special gravity port entries */
1721 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1723 level->fieldx = SP_LEVEL_XSIZE;
1724 level->fieldy = SP_LEVEL_YSIZE;
1726 level->time = 0; /* no time limit */
1727 level->amoeba_speed = 0;
1728 level->time_magic_wall = 0;
1729 level->time_wheel = 0;
1730 level->amoeba_content = EL_EMPTY;
1732 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1733 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1735 /* there are no yamyams in supaplex levels */
1736 for(i = 0; i < level->num_yamyam_contents; i++)
1737 for(y = 0; y < 3; y++)
1738 for(x = 0; x < 3; x++)
1739 level->yamyam_content[i][x][y] = EL_EMPTY;
1742 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1743 struct LevelFileInfo *level_file_info)
1745 char *filename = level_file_info->filename;
1747 int nr = level_file_info->nr - leveldir_current->first_level;
1749 char name_first, name_last;
1750 struct LevelInfo multipart_level;
1751 int multipart_xpos, multipart_ypos;
1752 boolean is_multipart_level;
1753 boolean is_first_part;
1754 boolean reading_multipart_level = FALSE;
1755 boolean use_empty_level = FALSE;
1757 if (!(file = fopen(filename, MODE_READ)))
1759 level->no_level_file = TRUE;
1761 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1766 /* position file stream to the requested level inside the level package */
1767 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1769 level->no_level_file = TRUE;
1771 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1776 /* there exist Supaplex level package files with multi-part levels which
1777 can be detected as follows: instead of leading and trailing dashes ('-')
1778 to pad the level name, they have leading and trailing numbers which are
1779 the x and y coordinations of the current part of the multi-part level;
1780 if there are '?' characters instead of numbers on the left or right side
1781 of the level name, the multi-part level consists of only horizontal or
1784 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1786 LoadLevelFromFileStream_SP(file, level, l);
1788 /* check if this level is a part of a bigger multi-part level */
1790 name_first = level->name[0];
1791 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1793 is_multipart_level =
1794 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1795 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1798 ((name_first == '?' || name_first == '1') &&
1799 (name_last == '?' || name_last == '1'));
1801 /* correct leading multipart level meta information in level name */
1802 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1803 level->name[i] = '-';
1805 /* correct trailing multipart level meta information in level name */
1806 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1807 level->name[i] = '-';
1809 /* ---------- check for normal single level ---------- */
1811 if (!reading_multipart_level && !is_multipart_level)
1813 /* the current level is simply a normal single-part level, and we are
1814 not reading a multi-part level yet, so return the level as it is */
1819 /* ---------- check for empty level (unused multi-part) ---------- */
1821 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1823 /* this is a part of a multi-part level, but not the first part
1824 (and we are not already reading parts of a multi-part level);
1825 in this case, use an empty level instead of the single part */
1827 use_empty_level = TRUE;
1832 /* ---------- check for finished multi-part level ---------- */
1834 if (reading_multipart_level &&
1835 (!is_multipart_level ||
1836 strcmp(level->name, multipart_level.name) != 0))
1838 /* we are already reading parts of a multi-part level, but this level is
1839 either not a multi-part level, or a part of a different multi-part
1840 level; in both cases, the multi-part level seems to be complete */
1845 /* ---------- here we have one part of a multi-part level ---------- */
1847 reading_multipart_level = TRUE;
1849 if (is_first_part) /* start with first part of new multi-part level */
1851 /* copy level info structure from first part */
1852 multipart_level = *level;
1854 /* clear playfield of new multi-part level */
1855 for (y = 0; y < MAX_LEV_FIELDY; y++)
1856 for (x = 0; x < MAX_LEV_FIELDX; x++)
1857 multipart_level.field[x][y] = EL_EMPTY;
1860 if (name_first == '?')
1862 if (name_last == '?')
1865 multipart_xpos = (int)(name_first - '0');
1866 multipart_ypos = (int)(name_last - '0');
1869 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1870 multipart_xpos, multipart_ypos, multipart_level.name);
1873 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1874 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1876 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1881 multipart_level.fieldx = MAX(multipart_level.fieldx,
1882 multipart_xpos * SP_LEVEL_XSIZE);
1883 multipart_level.fieldy = MAX(multipart_level.fieldy,
1884 multipart_ypos * SP_LEVEL_YSIZE);
1886 /* copy level part at the right position of multi-part level */
1887 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1889 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1891 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1892 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1894 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1901 if (use_empty_level)
1903 setLevelInfoToDefaults(level);
1905 level->fieldx = SP_LEVEL_XSIZE;
1906 level->fieldy = SP_LEVEL_YSIZE;
1908 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1909 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1910 level->field[x][y] = EL_EMPTY;
1912 strcpy(level->name, "-------- EMPTY --------");
1914 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1917 if (reading_multipart_level)
1918 *level = multipart_level;
1921 /* ------------------------------------------------------------------------- */
1922 /* functions for loading generic level */
1923 /* ------------------------------------------------------------------------- */
1925 void LoadLevelFromFileInfo(struct LevelInfo *level,
1926 struct LevelFileInfo *level_file_info)
1928 /* always start with reliable default values */
1929 setLevelInfoToDefaults(level);
1931 switch (level_file_info->type)
1933 case LEVEL_FILE_TYPE_RND:
1934 LoadLevelFromFileInfo_RND(level, level_file_info);
1937 case LEVEL_FILE_TYPE_EM:
1938 LoadLevelFromFileInfo_EM(level, level_file_info);
1941 case LEVEL_FILE_TYPE_SP:
1942 LoadLevelFromFileInfo_SP(level, level_file_info);
1946 LoadLevelFromFileInfo_RND(level, level_file_info);
1951 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1953 static struct LevelFileInfo level_file_info;
1955 /* always start with reliable default values */
1956 setFileInfoToDefaults(&level_file_info);
1958 level_file_info.nr = 0; /* unknown level number */
1959 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1960 level_file_info.filename = filename;
1962 LoadLevelFromFileInfo(level, &level_file_info);
1965 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1967 if (leveldir_current == NULL) /* only when dumping level */
1971 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1974 /* determine correct game engine version of current level */
1976 if (!leveldir_current->latest_engine)
1978 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1979 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1980 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1984 printf("\n::: This level is private or contributed: '%s'\n", filename);
1988 printf("\n::: Use the stored game engine version for this level\n");
1991 /* For all levels which are not forced to use the latest game engine
1992 version (normally user contributed, private and undefined levels),
1993 use the version of the game engine the levels were created for.
1995 Since 2.0.1, the game engine version is now directly stored
1996 in the level file (chunk "VERS"), so there is no need anymore
1997 to set the game version from the file version (except for old,
1998 pre-2.0 levels, where the game version is still taken from the
1999 file format version used to store the level -- see above). */
2001 /* do some special adjustments to support older level versions */
2002 if (level->file_version == FILE_VERSION_1_0)
2004 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2005 Error(ERR_WARN, "using high speed movement for player");
2007 /* player was faster than monsters in (pre-)1.0 levels */
2008 level->double_speed = TRUE;
2011 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2012 if (level->game_version == VERSION_IDENT(2,0,1,0))
2013 level->em_slippery_gems = TRUE;
2018 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2019 leveldir_current->sort_priority, filename);
2023 printf("\n::: Use latest game engine version for this level.\n");
2026 /* For all levels which are forced to use the latest game engine version
2027 (normally all but user contributed, private and undefined levels), set
2028 the game engine version to the actual version; this allows for actual
2029 corrections in the game engine to take effect for existing, converted
2030 levels (from "classic" or other existing games) to make the emulation
2031 of the corresponding game more accurate, while (hopefully) not breaking
2032 existing levels created from other players. */
2035 printf("::: changing engine from %d to %d\n",
2036 level->game_version, GAME_VERSION_ACTUAL);
2039 level->game_version = GAME_VERSION_ACTUAL;
2041 /* Set special EM style gems behaviour: EM style gems slip down from
2042 normal, steel and growing wall. As this is a more fundamental change,
2043 it seems better to set the default behaviour to "off" (as it is more
2044 natural) and make it configurable in the level editor (as a property
2045 of gem style elements). Already existing converted levels (neither
2046 private nor contributed levels) are changed to the new behaviour. */
2048 if (level->file_version < FILE_VERSION_2_0)
2049 level->em_slippery_gems = TRUE;
2053 printf("::: => %d\n", level->game_version);
2057 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2061 /* map custom element change events that have changed in newer versions
2062 (these following values were accidentally changed in version 3.0.1) */
2063 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2065 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2067 int element = EL_CUSTOM_START + i;
2069 /* order of checking and copying events to be mapped is important */
2070 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2072 if (HAS_CHANGE_EVENT(element, j - 2))
2074 SET_CHANGE_EVENT(element, j - 2, FALSE);
2075 SET_CHANGE_EVENT(element, j, TRUE);
2079 /* order of checking and copying events to be mapped is important */
2080 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2082 if (HAS_CHANGE_EVENT(element, j - 1))
2084 SET_CHANGE_EVENT(element, j - 1, FALSE);
2085 SET_CHANGE_EVENT(element, j, TRUE);
2091 /* some custom element change events get mapped since version 3.0.3 */
2092 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2094 int element = EL_CUSTOM_START + i;
2096 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2097 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2099 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2100 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2102 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2106 /* initialize "can_change" field for old levels with only one change page */
2107 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2109 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2111 int element = EL_CUSTOM_START + i;
2113 if (CAN_CHANGE(element))
2114 element_info[element].change->can_change = TRUE;
2119 /* set default push delay values (corrected since version 3.0.7-1) */
2120 if (level->game_version < VERSION_IDENT(3,0,7,1))
2122 game.default_push_delay_fixed = 2;
2123 game.default_push_delay_random = 8;
2127 game.default_push_delay_fixed = 8;
2128 game.default_push_delay_random = 8;
2131 /* set uninitialized push delay values of custom elements in older levels */
2132 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2134 int element = EL_CUSTOM_START + i;
2136 if (element_info[element].push_delay_fixed == -1)
2137 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2138 if (element_info[element].push_delay_random == -1)
2139 element_info[element].push_delay_random = game.default_push_delay_random;
2143 /* initialize element properties for level editor etc. */
2144 InitElementPropertiesEngine(level->game_version);
2147 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2151 /* map elements that have changed in newer versions */
2152 for (y = 0; y < level->fieldy; y++)
2154 for (x = 0; x < level->fieldx; x++)
2156 int element = level->field[x][y];
2158 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2160 /* map game font elements */
2161 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2162 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2163 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2164 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2167 if (level->game_version < VERSION_IDENT(3,0,0,0))
2169 /* map Supaplex gravity tube elements */
2170 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2171 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2172 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2173 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2177 level->field[x][y] = element;
2181 /* copy elements to runtime playfield array */
2182 for (x = 0; x < MAX_LEV_FIELDX; x++)
2183 for (y = 0; y < MAX_LEV_FIELDY; y++)
2184 Feld[x][y] = level->field[x][y];
2186 /* initialize level size variables for faster access */
2187 lev_fieldx = level->fieldx;
2188 lev_fieldy = level->fieldy;
2190 /* determine border element for this level */
2194 void LoadLevelTemplate(int nr)
2197 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2198 char *filename = level_file_info->filename;
2200 LoadLevelFromFileInfo(&level_template, level_file_info);
2202 char *filename = getDefaultLevelFilename(nr);
2204 LoadLevelFromFilename_RND(&level_template, filename);
2207 LoadLevel_InitVersion(&level, filename);
2208 LoadLevel_InitElements(&level, filename);
2210 ActivateLevelTemplate();
2213 void LoadLevel(int nr)
2216 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2217 char *filename = level_file_info->filename;
2219 LoadLevelFromFileInfo(&level, level_file_info);
2221 char *filename = getLevelFilename(nr);
2223 LoadLevelFromFilename_RND(&level, filename);
2226 if (level.use_custom_template)
2227 LoadLevelTemplate(-1);
2230 LoadLevel_InitVersion(&level, filename);
2231 LoadLevel_InitElements(&level, filename);
2232 LoadLevel_InitPlayfield(&level, filename);
2234 LoadLevel_InitLevel(&level, filename);
2238 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2240 putFileVersion(file, level->file_version);
2241 putFileVersion(file, level->game_version);
2244 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2248 putFile8Bit(file, level->fieldx);
2249 putFile8Bit(file, level->fieldy);
2251 putFile16BitBE(file, level->time);
2252 putFile16BitBE(file, level->gems_needed);
2254 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2255 putFile8Bit(file, level->name[i]);
2257 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2258 putFile8Bit(file, level->score[i]);
2260 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2261 for (y = 0; y < 3; y++)
2262 for (x = 0; x < 3; x++)
2263 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2264 level->yamyam_content[i][x][y]));
2265 putFile8Bit(file, level->amoeba_speed);
2266 putFile8Bit(file, level->time_magic_wall);
2267 putFile8Bit(file, level->time_wheel);
2268 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2269 level->amoeba_content));
2270 putFile8Bit(file, (level->double_speed ? 1 : 0));
2271 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2272 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2273 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2275 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2277 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2278 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2280 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2283 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2287 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2288 putFile8Bit(file, level->author[i]);
2291 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2295 for (y = 0; y < level->fieldy; y++)
2296 for (x = 0; x < level->fieldx; x++)
2297 if (level->encoding_16bit_field)
2298 putFile16BitBE(file, level->field[x][y]);
2300 putFile8Bit(file, level->field[x][y]);
2304 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2308 putFile8Bit(file, EL_YAMYAM);
2309 putFile8Bit(file, level->num_yamyam_contents);
2310 putFile8Bit(file, 0);
2311 putFile8Bit(file, 0);
2313 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2314 for (y = 0; y < 3; y++)
2315 for (x = 0; x < 3; x++)
2316 if (level->encoding_16bit_field)
2317 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2319 putFile8Bit(file, level->yamyam_content[i][x][y]);
2323 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2326 int num_contents, content_xsize, content_ysize;
2327 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2329 if (element == EL_YAMYAM)
2331 num_contents = level->num_yamyam_contents;
2335 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2336 for (y = 0; y < 3; y++)
2337 for (x = 0; x < 3; x++)
2338 content_array[i][x][y] = level->yamyam_content[i][x][y];
2340 else if (element == EL_BD_AMOEBA)
2346 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2347 for (y = 0; y < 3; y++)
2348 for (x = 0; x < 3; x++)
2349 content_array[i][x][y] = EL_EMPTY;
2350 content_array[0][0][0] = level->amoeba_content;
2354 /* chunk header already written -- write empty chunk data */
2355 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2357 Error(ERR_WARN, "cannot save content for element '%d'", element);
2361 putFile16BitBE(file, element);
2362 putFile8Bit(file, num_contents);
2363 putFile8Bit(file, content_xsize);
2364 putFile8Bit(file, content_ysize);
2366 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2368 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2369 for (y = 0; y < 3; y++)
2370 for (x = 0; x < 3; x++)
2371 putFile16BitBE(file, content_array[i][x][y]);
2374 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2377 int envelope_nr = element - EL_ENVELOPE_1;
2378 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2380 putFile16BitBE(file, element);
2381 putFile16BitBE(file, envelope_len);
2382 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2383 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2385 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2387 for (i = 0; i < envelope_len; i++)
2388 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2392 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2393 int num_changed_custom_elements)
2397 putFile16BitBE(file, num_changed_custom_elements);
2399 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2401 int element = EL_CUSTOM_START + i;
2403 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2405 if (check < num_changed_custom_elements)
2407 putFile16BitBE(file, element);
2408 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2415 if (check != num_changed_custom_elements) /* should not happen */
2416 Error(ERR_WARN, "inconsistent number of custom element properties");
2421 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2422 int num_changed_custom_elements)
2426 putFile16BitBE(file, num_changed_custom_elements);
2428 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2430 int element = EL_CUSTOM_START + i;
2432 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2434 if (check < num_changed_custom_elements)
2436 putFile16BitBE(file, element);
2437 putFile16BitBE(file, element_info[element].change->target_element);
2444 if (check != num_changed_custom_elements) /* should not happen */
2445 Error(ERR_WARN, "inconsistent number of custom target elements");
2450 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2451 int num_changed_custom_elements)
2453 int i, j, x, y, check = 0;
2455 putFile16BitBE(file, num_changed_custom_elements);
2457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2459 int element = EL_CUSTOM_START + i;
2461 if (element_info[element].modified_settings)
2463 if (check < num_changed_custom_elements)
2465 putFile16BitBE(file, element);
2467 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2468 putFile8Bit(file, element_info[element].description[j]);
2470 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2472 /* some free bytes for future properties and padding */
2473 WriteUnusedBytesToFile(file, 7);
2475 putFile8Bit(file, element_info[element].use_gfx_element);
2476 putFile16BitBE(file, element_info[element].gfx_element);
2478 putFile8Bit(file, element_info[element].collect_score);
2479 putFile8Bit(file, element_info[element].collect_count);
2481 putFile16BitBE(file, element_info[element].push_delay_fixed);
2482 putFile16BitBE(file, element_info[element].push_delay_random);
2483 putFile16BitBE(file, element_info[element].move_delay_fixed);
2484 putFile16BitBE(file, element_info[element].move_delay_random);
2486 putFile16BitBE(file, element_info[element].move_pattern);
2487 putFile8Bit(file, element_info[element].move_direction_initial);
2488 putFile8Bit(file, element_info[element].move_stepsize);
2490 for (y = 0; y < 3; y++)
2491 for (x = 0; x < 3; x++)
2492 putFile16BitBE(file, element_info[element].content[x][y]);
2494 putFile32BitBE(file, element_info[element].change->events);
2496 putFile16BitBE(file, element_info[element].change->target_element);
2498 putFile16BitBE(file, element_info[element].change->delay_fixed);
2499 putFile16BitBE(file, element_info[element].change->delay_random);
2500 putFile16BitBE(file, element_info[element].change->delay_frames);
2502 putFile16BitBE(file, element_info[element].change->trigger_element);
2504 putFile8Bit(file, element_info[element].change->explode);
2505 putFile8Bit(file, element_info[element].change->use_content);
2506 putFile8Bit(file, element_info[element].change->only_complete);
2507 putFile8Bit(file, element_info[element].change->use_random_change);
2509 putFile8Bit(file, element_info[element].change->random);
2510 putFile8Bit(file, element_info[element].change->power);
2512 for (y = 0; y < 3; y++)
2513 for (x = 0; x < 3; x++)
2514 putFile16BitBE(file, element_info[element].change->content[x][y]);
2516 putFile8Bit(file, element_info[element].slippery_type);
2518 /* some free bytes for future properties and padding */
2519 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2526 if (check != num_changed_custom_elements) /* should not happen */
2527 Error(ERR_WARN, "inconsistent number of custom element properties");
2531 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2533 struct ElementInfo *ei = &element_info[element];
2536 putFile16BitBE(file, element);
2538 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2539 putFile8Bit(file, ei->description[i]);
2541 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2542 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2544 putFile8Bit(file, ei->num_change_pages);
2546 /* some free bytes for future base property values and padding */
2547 WriteUnusedBytesToFile(file, 5);
2549 /* write custom property values */
2551 putFile8Bit(file, ei->use_gfx_element);
2552 putFile16BitBE(file, ei->gfx_element);
2554 putFile8Bit(file, ei->collect_score);
2555 putFile8Bit(file, ei->collect_count);
2557 putFile16BitBE(file, ei->push_delay_fixed);
2558 putFile16BitBE(file, ei->push_delay_random);
2559 putFile16BitBE(file, ei->move_delay_fixed);
2560 putFile16BitBE(file, ei->move_delay_random);
2562 /* bits 0 - 15 of "move_pattern" ... */
2563 putFile16BitBE(file, ei->move_pattern & 0xffff);
2564 putFile8Bit(file, ei->move_direction_initial);
2565 putFile8Bit(file, ei->move_stepsize);
2567 putFile8Bit(file, ei->slippery_type);
2569 for (y = 0; y < 3; y++)
2570 for (x = 0; x < 3; x++)
2571 putFile16BitBE(file, ei->content[x][y]);
2573 putFile16BitBE(file, ei->move_enter_element);
2574 putFile16BitBE(file, ei->move_leave_element);
2575 putFile8Bit(file, ei->move_leave_type);
2577 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2578 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2580 /* some free bytes for future custom property values and padding */
2581 WriteUnusedBytesToFile(file, 5);
2583 /* write change property values */
2585 for (i = 0; i < ei->num_change_pages; i++)
2587 struct ElementChangeInfo *change = &ei->change_page[i];
2589 putFile32BitBE(file, change->events);
2591 putFile16BitBE(file, change->target_element);
2593 putFile16BitBE(file, change->delay_fixed);
2594 putFile16BitBE(file, change->delay_random);
2595 putFile16BitBE(file, change->delay_frames);
2597 putFile16BitBE(file, change->trigger_element);
2599 putFile8Bit(file, change->explode);
2600 putFile8Bit(file, change->use_content);
2601 putFile8Bit(file, change->only_complete);
2602 putFile8Bit(file, change->use_random_change);
2604 putFile8Bit(file, change->random);
2605 putFile8Bit(file, change->power);
2607 for (y = 0; y < 3; y++)
2608 for (x = 0; x < 3; x++)
2609 putFile16BitBE(file, change->content[x][y]);
2611 putFile8Bit(file, change->can_change);
2613 putFile8Bit(file, change->sides);
2615 /* some free bytes for future change property values and padding */
2616 WriteUnusedBytesToFile(file, 8);
2620 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2622 struct ElementInfo *ei = &element_info[element];
2623 struct ElementGroupInfo *group = ei->group;
2626 putFile16BitBE(file, element);
2628 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2629 putFile8Bit(file, ei->description[i]);
2631 putFile8Bit(file, group->num_elements);
2633 putFile8Bit(file, ei->use_gfx_element);
2634 putFile16BitBE(file, ei->gfx_element);
2636 putFile8Bit(file, group->choice_mode);
2638 /* some free bytes for future values and padding */
2639 WriteUnusedBytesToFile(file, 3);
2641 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2642 putFile16BitBE(file, group->element[i]);
2645 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2647 int body_chunk_size;
2651 if (!(file = fopen(filename, MODE_WRITE)))
2653 Error(ERR_WARN, "cannot save level file '%s'", filename);
2657 level->file_version = FILE_VERSION_ACTUAL;
2658 level->game_version = GAME_VERSION_ACTUAL;
2660 /* check level field for 16-bit elements */
2661 level->encoding_16bit_field = FALSE;
2662 for (y = 0; y < level->fieldy; y++)
2663 for (x = 0; x < level->fieldx; x++)
2664 if (level->field[x][y] > 255)
2665 level->encoding_16bit_field = TRUE;
2667 /* check yamyam content for 16-bit elements */
2668 level->encoding_16bit_yamyam = FALSE;
2669 for (i = 0; i < level->num_yamyam_contents; i++)
2670 for (y = 0; y < 3; y++)
2671 for (x = 0; x < 3; x++)
2672 if (level->yamyam_content[i][x][y] > 255)
2673 level->encoding_16bit_yamyam = TRUE;
2675 /* check amoeba content for 16-bit elements */
2676 level->encoding_16bit_amoeba = FALSE;
2677 if (level->amoeba_content > 255)
2678 level->encoding_16bit_amoeba = TRUE;
2680 /* calculate size of "BODY" chunk */
2682 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2684 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2685 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2687 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2688 SaveLevel_VERS(file, level);
2690 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2691 SaveLevel_HEAD(file, level);
2693 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2694 SaveLevel_AUTH(file, level);
2696 putFileChunkBE(file, "BODY", body_chunk_size);
2697 SaveLevel_BODY(file, level);
2699 if (level->encoding_16bit_yamyam ||
2700 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2702 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2703 SaveLevel_CNT2(file, level, EL_YAMYAM);
2706 if (level->encoding_16bit_amoeba)
2708 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2709 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2712 /* check for envelope content */
2713 for (i = 0; i < 4; i++)
2715 if (strlen(level->envelope_text[i]) > 0)
2717 int envelope_len = strlen(level->envelope_text[i]) + 1;
2719 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2720 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2724 /* check for non-default custom elements (unless using template level) */
2725 if (!level->use_custom_template)
2727 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2729 int element = EL_CUSTOM_START + i;
2731 if (element_info[element].modified_settings)
2733 int num_change_pages = element_info[element].num_change_pages;
2735 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2736 SaveLevel_CUS4(file, level, element);
2741 /* check for non-default group elements (unless using template level) */
2742 if (!level->use_custom_template)
2744 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2746 int element = EL_GROUP_START + i;
2748 if (element_info[element].modified_settings)
2750 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2751 SaveLevel_GRP1(file, level, element);
2758 SetFilePermissions(filename, PERMS_PRIVATE);
2761 void SaveLevel(int nr)
2763 char *filename = getDefaultLevelFilename(nr);
2765 SaveLevelFromFilename(&level, filename);
2768 void SaveLevelTemplate()
2770 char *filename = getDefaultLevelFilename(-1);
2772 SaveLevelFromFilename(&level, filename);
2775 void DumpLevel(struct LevelInfo *level)
2777 printf_line("-", 79);
2778 printf("Level xxx (file version %08d, game version %08d)\n",
2779 level->file_version, level->game_version);
2780 printf_line("-", 79);
2782 printf("Level author: '%s'\n", level->author);
2783 printf("Level title: '%s'\n", level->name);
2785 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2787 printf("Level time: %d seconds\n", level->time);
2788 printf("Gems needed: %d\n", level->gems_needed);
2790 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2791 printf("Time for wheel: %d seconds\n", level->time_wheel);
2792 printf("Time for light: %d seconds\n", level->time_light);
2793 printf("Time for timegate: %d seconds\n", level->time_timegate);
2795 printf("Amoeba speed: %d\n", level->amoeba_speed);
2797 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2798 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2799 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2800 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2801 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2803 printf_line("-", 79);
2807 /* ========================================================================= */
2808 /* tape file functions */
2809 /* ========================================================================= */
2811 static void setTapeInfoToDefaults()
2815 /* always start with reliable default values (empty tape) */
2818 /* default values (also for pre-1.2 tapes) with only the first player */
2819 tape.player_participates[0] = TRUE;
2820 for (i = 1; i < MAX_PLAYERS; i++)
2821 tape.player_participates[i] = FALSE;
2823 /* at least one (default: the first) player participates in every tape */
2824 tape.num_participating_players = 1;
2826 tape.level_nr = level_nr;
2828 tape.changed = FALSE;
2830 tape.recording = FALSE;
2831 tape.playing = FALSE;
2832 tape.pausing = FALSE;
2835 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2837 tape->file_version = getFileVersion(file);
2838 tape->game_version = getFileVersion(file);
2843 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2847 tape->random_seed = getFile32BitBE(file);
2848 tape->date = getFile32BitBE(file);
2849 tape->length = getFile32BitBE(file);
2851 /* read header fields that are new since version 1.2 */
2852 if (tape->file_version >= FILE_VERSION_1_2)
2854 byte store_participating_players = getFile8Bit(file);
2857 /* since version 1.2, tapes store which players participate in the tape */
2858 tape->num_participating_players = 0;
2859 for (i = 0; i < MAX_PLAYERS; i++)
2861 tape->player_participates[i] = FALSE;
2863 if (store_participating_players & (1 << i))
2865 tape->player_participates[i] = TRUE;
2866 tape->num_participating_players++;
2870 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2872 engine_version = getFileVersion(file);
2873 if (engine_version > 0)
2874 tape->engine_version = engine_version;
2876 tape->engine_version = tape->game_version;
2882 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2884 int level_identifier_size;
2887 level_identifier_size = getFile16BitBE(file);
2889 tape->level_identifier =
2890 checked_realloc(tape->level_identifier, level_identifier_size);
2892 for (i = 0; i < level_identifier_size; i++)
2893 tape->level_identifier[i] = getFile8Bit(file);
2895 tape->level_nr = getFile16BitBE(file);
2897 chunk_size = 2 + level_identifier_size + 2;
2902 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2905 int chunk_size_expected =
2906 (tape->num_participating_players + 1) * tape->length;
2908 if (chunk_size_expected != chunk_size)
2910 ReadUnusedBytesFromFile(file, chunk_size);
2911 return chunk_size_expected;
2914 for (i = 0; i < tape->length; i++)
2916 if (i >= MAX_TAPELEN)
2919 for (j = 0; j < MAX_PLAYERS; j++)
2921 tape->pos[i].action[j] = MV_NO_MOVING;
2923 if (tape->player_participates[j])
2924 tape->pos[i].action[j] = getFile8Bit(file);
2927 tape->pos[i].delay = getFile8Bit(file);
2929 if (tape->file_version == FILE_VERSION_1_0)
2931 /* eliminate possible diagonal moves in old tapes */
2932 /* this is only for backward compatibility */
2934 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2935 byte action = tape->pos[i].action[0];
2936 int k, num_moves = 0;
2938 for (k = 0; k<4; k++)
2940 if (action & joy_dir[k])
2942 tape->pos[i + num_moves].action[0] = joy_dir[k];
2944 tape->pos[i + num_moves].delay = 0;
2953 tape->length += num_moves;
2956 else if (tape->file_version < FILE_VERSION_2_0)
2958 /* convert pre-2.0 tapes to new tape format */
2960 if (tape->pos[i].delay > 1)
2963 tape->pos[i + 1] = tape->pos[i];
2964 tape->pos[i + 1].delay = 1;
2967 for (j = 0; j < MAX_PLAYERS; j++)
2968 tape->pos[i].action[j] = MV_NO_MOVING;
2969 tape->pos[i].delay--;
2980 if (i != tape->length)
2981 chunk_size = (tape->num_participating_players + 1) * i;
2986 void LoadTapeFromFilename(char *filename)
2988 char cookie[MAX_LINE_LEN];
2989 char chunk_name[CHUNK_ID_LEN + 1];
2993 /* always start with reliable default values */
2994 setTapeInfoToDefaults();
2996 if (!(file = fopen(filename, MODE_READ)))
2999 getFileChunkBE(file, chunk_name, NULL);
3000 if (strcmp(chunk_name, "RND1") == 0)
3002 getFile32BitBE(file); /* not used */
3004 getFileChunkBE(file, chunk_name, NULL);
3005 if (strcmp(chunk_name, "TAPE") != 0)
3007 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3012 else /* check for pre-2.0 file format with cookie string */
3014 strcpy(cookie, chunk_name);
3015 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3016 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3017 cookie[strlen(cookie) - 1] = '\0';
3019 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3021 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3026 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3028 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3033 /* pre-2.0 tape files have no game version, so use file version here */
3034 tape.game_version = tape.file_version;
3037 if (tape.file_version < FILE_VERSION_1_2)
3039 /* tape files from versions before 1.2.0 without chunk structure */
3040 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3041 LoadTape_BODY(file, 2 * tape.length, &tape);
3049 int (*loader)(FILE *, int, struct TapeInfo *);
3053 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3054 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3055 { "INFO", -1, LoadTape_INFO },
3056 { "BODY", -1, LoadTape_BODY },
3060 while (getFileChunkBE(file, chunk_name, &chunk_size))
3064 while (chunk_info[i].name != NULL &&
3065 strcmp(chunk_name, chunk_info[i].name) != 0)
3068 if (chunk_info[i].name == NULL)
3070 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3071 chunk_name, filename);
3072 ReadUnusedBytesFromFile(file, chunk_size);
3074 else if (chunk_info[i].size != -1 &&
3075 chunk_info[i].size != chunk_size)
3077 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3078 chunk_size, chunk_name, filename);
3079 ReadUnusedBytesFromFile(file, chunk_size);
3083 /* call function to load this tape chunk */
3084 int chunk_size_expected =
3085 (chunk_info[i].loader)(file, chunk_size, &tape);
3087 /* the size of some chunks cannot be checked before reading other
3088 chunks first (like "HEAD" and "BODY") that contain some header
3089 information, so check them here */
3090 if (chunk_size_expected != chunk_size)
3092 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3093 chunk_size, chunk_name, filename);
3101 tape.length_seconds = GetTapeLength();
3104 printf("::: tape game version: %d\n", tape.game_version);
3105 printf("::: tape engine version: %d\n", tape.engine_version);
3109 void LoadTape(int nr)
3111 char *filename = getTapeFilename(nr);
3113 LoadTapeFromFilename(filename);
3116 void LoadSolutionTape(int nr)
3118 char *filename = getSolutionTapeFilename(nr);
3120 LoadTapeFromFilename(filename);
3123 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3125 putFileVersion(file, tape->file_version);
3126 putFileVersion(file, tape->game_version);
3129 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3132 byte store_participating_players = 0;
3134 /* set bits for participating players for compact storage */
3135 for (i = 0; i < MAX_PLAYERS; i++)
3136 if (tape->player_participates[i])
3137 store_participating_players |= (1 << i);
3139 putFile32BitBE(file, tape->random_seed);
3140 putFile32BitBE(file, tape->date);
3141 putFile32BitBE(file, tape->length);
3143 putFile8Bit(file, store_participating_players);
3145 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3146 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3148 putFileVersion(file, tape->engine_version);
3151 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3153 int level_identifier_size = strlen(tape->level_identifier) + 1;
3156 putFile16BitBE(file, level_identifier_size);
3158 for (i = 0; i < level_identifier_size; i++)
3159 putFile8Bit(file, tape->level_identifier[i]);
3161 putFile16BitBE(file, tape->level_nr);
3164 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3168 for (i = 0; i < tape->length; i++)
3170 for (j = 0; j < MAX_PLAYERS; j++)
3171 if (tape->player_participates[j])
3172 putFile8Bit(file, tape->pos[i].action[j]);
3174 putFile8Bit(file, tape->pos[i].delay);
3178 void SaveTape(int nr)
3180 char *filename = getTapeFilename(nr);
3182 boolean new_tape = TRUE;
3183 int num_participating_players = 0;
3184 int info_chunk_size;
3185 int body_chunk_size;
3188 InitTapeDirectory(leveldir_current->subdir);
3190 /* if a tape still exists, ask to overwrite it */
3191 if (access(filename, F_OK) == 0)
3194 if (!Request("Replace old tape ?", REQ_ASK))
3198 if (!(file = fopen(filename, MODE_WRITE)))
3200 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3204 tape.file_version = FILE_VERSION_ACTUAL;
3205 tape.game_version = GAME_VERSION_ACTUAL;
3207 /* count number of participating players */
3208 for (i = 0; i < MAX_PLAYERS; i++)
3209 if (tape.player_participates[i])
3210 num_participating_players++;
3212 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3213 body_chunk_size = (num_participating_players + 1) * tape.length;
3215 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3216 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3218 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3219 SaveTape_VERS(file, &tape);
3221 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3222 SaveTape_HEAD(file, &tape);
3224 putFileChunkBE(file, "INFO", info_chunk_size);
3225 SaveTape_INFO(file, &tape);
3227 putFileChunkBE(file, "BODY", body_chunk_size);
3228 SaveTape_BODY(file, &tape);
3232 SetFilePermissions(filename, PERMS_PRIVATE);
3234 tape.changed = FALSE;
3237 Request("tape saved !", REQ_CONFIRM);
3240 void DumpTape(struct TapeInfo *tape)
3244 if (TAPE_IS_EMPTY(*tape))
3246 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3250 printf_line("-", 79);
3251 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3252 tape->level_nr, tape->file_version, tape->game_version);
3253 printf("Level series identifier: '%s'\n", tape->level_identifier);
3254 printf_line("-", 79);
3256 for (i = 0; i < tape->length; i++)
3258 if (i >= MAX_TAPELEN)
3261 printf("%03d: ", i);
3263 for (j = 0; j < MAX_PLAYERS; j++)
3265 if (tape->player_participates[j])
3267 int action = tape->pos[i].action[j];
3269 printf("%d:%02x ", j, action);
3270 printf("[%c%c%c%c|%c%c] - ",
3271 (action & JOY_LEFT ? '<' : ' '),
3272 (action & JOY_RIGHT ? '>' : ' '),
3273 (action & JOY_UP ? '^' : ' '),
3274 (action & JOY_DOWN ? 'v' : ' '),
3275 (action & JOY_BUTTON_1 ? '1' : ' '),
3276 (action & JOY_BUTTON_2 ? '2' : ' '));
3280 printf("(%03d)\n", tape->pos[i].delay);
3283 printf_line("-", 79);
3287 /* ========================================================================= */
3288 /* score file functions */
3289 /* ========================================================================= */
3291 void LoadScore(int nr)
3294 char *filename = getScoreFilename(nr);
3295 char cookie[MAX_LINE_LEN];
3296 char line[MAX_LINE_LEN];
3300 /* always start with reliable default values */
3301 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3303 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3304 highscore[i].Score = 0;
3307 if (!(file = fopen(filename, MODE_READ)))
3310 /* check file identifier */
3311 fgets(cookie, MAX_LINE_LEN, file);
3312 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3313 cookie[strlen(cookie) - 1] = '\0';
3315 if (!checkCookieString(cookie, SCORE_COOKIE))
3317 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3322 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3324 fscanf(file, "%d", &highscore[i].Score);
3325 fgets(line, MAX_LINE_LEN, file);
3327 if (line[strlen(line) - 1] == '\n')
3328 line[strlen(line) - 1] = '\0';
3330 for (line_ptr = line; *line_ptr; line_ptr++)
3332 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3334 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3335 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3344 void SaveScore(int nr)
3347 char *filename = getScoreFilename(nr);
3350 InitScoreDirectory(leveldir_current->subdir);
3352 if (!(file = fopen(filename, MODE_WRITE)))
3354 Error(ERR_WARN, "cannot save score for level %d", nr);
3358 fprintf(file, "%s\n\n", SCORE_COOKIE);
3360 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3361 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3365 SetFilePermissions(filename, PERMS_PUBLIC);
3369 /* ========================================================================= */
3370 /* setup file functions */
3371 /* ========================================================================= */
3373 #define TOKEN_STR_PLAYER_PREFIX "player_"
3376 #define SETUP_TOKEN_PLAYER_NAME 0
3377 #define SETUP_TOKEN_SOUND 1
3378 #define SETUP_TOKEN_SOUND_LOOPS 2
3379 #define SETUP_TOKEN_SOUND_MUSIC 3
3380 #define SETUP_TOKEN_SOUND_SIMPLE 4
3381 #define SETUP_TOKEN_TOONS 5
3382 #define SETUP_TOKEN_SCROLL_DELAY 6
3383 #define SETUP_TOKEN_SOFT_SCROLLING 7
3384 #define SETUP_TOKEN_FADING 8
3385 #define SETUP_TOKEN_AUTORECORD 9
3386 #define SETUP_TOKEN_QUICK_DOORS 10
3387 #define SETUP_TOKEN_TEAM_MODE 11
3388 #define SETUP_TOKEN_HANDICAP 12
3389 #define SETUP_TOKEN_TIME_LIMIT 13
3390 #define SETUP_TOKEN_FULLSCREEN 14
3391 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3392 #define SETUP_TOKEN_GRAPHICS_SET 16
3393 #define SETUP_TOKEN_SOUNDS_SET 17
3394 #define SETUP_TOKEN_MUSIC_SET 18
3395 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3396 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3397 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3399 #define NUM_GLOBAL_SETUP_TOKENS 22
3402 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3403 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3404 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3405 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3406 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3407 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3408 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3409 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3410 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3411 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3412 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3413 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3415 #define NUM_EDITOR_SETUP_TOKENS 12
3417 /* shortcut setup */
3418 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3419 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3420 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3422 #define NUM_SHORTCUT_SETUP_TOKENS 3
3425 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3426 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3427 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3428 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3429 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3430 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3431 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3432 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3433 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3434 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3435 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3436 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3437 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3438 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3439 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3440 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3442 #define NUM_PLAYER_SETUP_TOKENS 16
3445 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3446 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3448 #define NUM_SYSTEM_SETUP_TOKENS 2
3451 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3453 #define NUM_OPTIONS_SETUP_TOKENS 1
3456 static struct SetupInfo si;
3457 static struct SetupEditorInfo sei;
3458 static struct SetupShortcutInfo ssi;
3459 static struct SetupInputInfo sii;
3460 static struct SetupSystemInfo syi;
3461 static struct OptionInfo soi;
3463 static struct TokenInfo global_setup_tokens[] =
3465 { TYPE_STRING, &si.player_name, "player_name" },
3466 { TYPE_SWITCH, &si.sound, "sound" },
3467 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3468 { TYPE_SWITCH, &si.sound_music, "background_music" },
3469 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3470 { TYPE_SWITCH, &si.toons, "toons" },
3471 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3472 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3473 { TYPE_SWITCH, &si.fading, "screen_fading" },
3474 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3475 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3476 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3477 { TYPE_SWITCH, &si.handicap, "handicap" },
3478 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3479 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3480 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3481 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3482 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3483 { TYPE_STRING, &si.music_set, "music_set" },
3484 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3485 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3486 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3489 static struct TokenInfo editor_setup_tokens[] =
3491 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3492 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3493 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3494 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3495 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3496 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3497 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3498 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3499 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3500 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3501 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3502 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3505 static struct TokenInfo shortcut_setup_tokens[] =
3507 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3508 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3509 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3512 static struct TokenInfo player_setup_tokens[] =
3514 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3515 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3516 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3517 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3518 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3519 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3520 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3521 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3522 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3523 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3524 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3525 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3526 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3527 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3528 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3529 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3532 static struct TokenInfo system_setup_tokens[] =
3534 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3535 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3538 static struct TokenInfo options_setup_tokens[] =
3540 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3543 static char *get_corrected_login_name(char *login_name)
3545 /* needed because player name must be a fixed length string */
3546 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3548 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3549 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3551 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3552 if (strchr(login_name_new, ' '))
3553 *strchr(login_name_new, ' ') = '\0';
3555 return login_name_new;
3558 static void setSetupInfoToDefaults(struct SetupInfo *si)
3562 si->player_name = get_corrected_login_name(getLoginName());
3565 si->sound_loops = TRUE;
3566 si->sound_music = TRUE;
3567 si->sound_simple = TRUE;
3569 si->double_buffering = TRUE;
3570 si->direct_draw = !si->double_buffering;
3571 si->scroll_delay = TRUE;
3572 si->soft_scrolling = TRUE;
3574 si->autorecord = TRUE;
3575 si->quick_doors = FALSE;
3576 si->team_mode = FALSE;
3577 si->handicap = TRUE;
3578 si->time_limit = TRUE;
3579 si->fullscreen = FALSE;
3580 si->ask_on_escape = TRUE;
3582 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3583 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3584 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3585 si->override_level_graphics = FALSE;
3586 si->override_level_sounds = FALSE;
3587 si->override_level_music = FALSE;
3589 si->editor.el_boulderdash = TRUE;
3590 si->editor.el_emerald_mine = TRUE;
3591 si->editor.el_more = TRUE;
3592 si->editor.el_sokoban = TRUE;
3593 si->editor.el_supaplex = TRUE;
3594 si->editor.el_diamond_caves = TRUE;
3595 si->editor.el_dx_boulderdash = TRUE;
3596 si->editor.el_chars = TRUE;
3597 si->editor.el_custom = TRUE;
3598 si->editor.el_custom_more = FALSE;
3600 si->editor.el_headlines = TRUE;
3601 si->editor.el_user_defined = FALSE;
3603 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3604 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3605 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3607 for (i = 0; i < MAX_PLAYERS; i++)
3609 si->input[i].use_joystick = FALSE;
3610 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3611 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3612 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3613 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3614 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3615 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3616 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3617 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3618 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3619 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3620 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3621 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3622 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3623 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3624 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3627 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3628 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3630 si->options.verbose = FALSE;
3633 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3637 if (!setup_file_hash)
3642 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3643 setSetupInfo(global_setup_tokens, i,
3644 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3649 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3650 setSetupInfo(editor_setup_tokens, i,
3651 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3654 /* shortcut setup */
3655 ssi = setup.shortcut;
3656 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3657 setSetupInfo(shortcut_setup_tokens, i,
3658 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3659 setup.shortcut = ssi;
3662 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3666 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3668 sii = setup.input[pnr];
3669 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3671 char full_token[100];
3673 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3674 setSetupInfo(player_setup_tokens, i,
3675 getHashEntry(setup_file_hash, full_token));
3677 setup.input[pnr] = sii;
3682 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3683 setSetupInfo(system_setup_tokens, i,
3684 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3688 soi = setup.options;
3689 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3690 setSetupInfo(options_setup_tokens, i,
3691 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3692 setup.options = soi;
3697 char *filename = getSetupFilename();
3698 SetupFileHash *setup_file_hash = NULL;
3700 /* always start with reliable default values */
3701 setSetupInfoToDefaults(&setup);
3703 setup_file_hash = loadSetupFileHash(filename);
3705 if (setup_file_hash)
3707 char *player_name_new;
3709 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3710 decodeSetupFileHash(setup_file_hash);
3712 setup.direct_draw = !setup.double_buffering;
3714 freeSetupFileHash(setup_file_hash);
3716 /* needed to work around problems with fixed length strings */
3717 player_name_new = get_corrected_login_name(setup.player_name);
3718 free(setup.player_name);
3719 setup.player_name = player_name_new;
3722 Error(ERR_WARN, "using default setup values");
3727 char *filename = getSetupFilename();
3731 InitUserDataDirectory();
3733 if (!(file = fopen(filename, MODE_WRITE)))
3735 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3739 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3740 getCookie("SETUP")));
3741 fprintf(file, "\n");
3745 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3747 /* just to make things nicer :) */
3748 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3749 i == SETUP_TOKEN_GRAPHICS_SET)
3750 fprintf(file, "\n");
3752 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3757 fprintf(file, "\n");
3758 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3759 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3761 /* shortcut setup */
3762 ssi = setup.shortcut;
3763 fprintf(file, "\n");
3764 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3765 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3768 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3772 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3773 fprintf(file, "\n");
3775 sii = setup.input[pnr];
3776 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3777 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3782 fprintf(file, "\n");
3783 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3784 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3787 soi = setup.options;
3788 fprintf(file, "\n");
3789 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3790 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3794 SetFilePermissions(filename, PERMS_PRIVATE);
3797 void LoadCustomElementDescriptions()
3799 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3800 SetupFileHash *setup_file_hash;
3803 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3805 if (element_info[i].custom_description != NULL)
3807 free(element_info[i].custom_description);
3808 element_info[i].custom_description = NULL;
3812 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3815 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3817 char *token = getStringCat2(element_info[i].token_name, ".name");
3818 char *value = getHashEntry(setup_file_hash, token);
3821 element_info[i].custom_description = getStringCopy(value);
3826 freeSetupFileHash(setup_file_hash);
3829 void LoadSpecialMenuDesignSettings()
3831 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3832 SetupFileHash *setup_file_hash;
3835 /* always start with reliable default values from default config */
3836 for (i = 0; image_config_vars[i].token != NULL; i++)
3837 for (j = 0; image_config[j].token != NULL; j++)
3838 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3839 *image_config_vars[i].value =
3840 get_auto_parameter_value(image_config_vars[i].token,
3841 image_config[j].value);
3843 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3846 /* special case: initialize with default values that may be overwritten */
3847 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3849 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3850 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3851 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3853 if (value_x != NULL)
3854 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3855 if (value_y != NULL)
3856 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3857 if (list_size != NULL)
3858 menu.list_size[i] = get_integer_from_string(list_size);
3861 /* read (and overwrite with) values that may be specified in config file */
3862 for (i = 0; image_config_vars[i].token != NULL; i++)
3864 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3867 *image_config_vars[i].value =
3868 get_auto_parameter_value(image_config_vars[i].token, value);
3871 freeSetupFileHash(setup_file_hash);
3874 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3876 char *filename = getEditorSetupFilename();
3877 SetupFileList *setup_file_list, *list;
3878 SetupFileHash *element_hash;
3879 int num_unknown_tokens = 0;
3882 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3885 element_hash = newSetupFileHash();
3887 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3888 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3890 /* determined size may be larger than needed (due to unknown elements) */
3892 for (list = setup_file_list; list != NULL; list = list->next)
3895 /* add space for up to 3 more elements for padding that may be needed */
3898 *elements = checked_malloc(*num_elements * sizeof(int));
3901 for (list = setup_file_list; list != NULL; list = list->next)
3903 char *value = getHashEntry(element_hash, list->token);
3907 (*elements)[(*num_elements)++] = atoi(value);
3911 if (num_unknown_tokens == 0)
3913 Error(ERR_RETURN_LINE, "-");
3914 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3915 Error(ERR_RETURN, "- config file: '%s'", filename);
3917 num_unknown_tokens++;
3920 Error(ERR_RETURN, "- token: '%s'", list->token);
3924 if (num_unknown_tokens > 0)
3925 Error(ERR_RETURN_LINE, "-");
3927 while (*num_elements % 4) /* pad with empty elements, if needed */
3928 (*elements)[(*num_elements)++] = EL_EMPTY;
3930 freeSetupFileList(setup_file_list);
3931 freeSetupFileHash(element_hash);
3935 for (i = 0; i < *num_elements; i++)
3936 printf("editor: element '%s' [%d]\n",
3937 element_info[(*elements)[i]].token_name, (*elements)[i]);
3941 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3944 SetupFileHash *setup_file_hash = NULL;
3945 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3946 char *filename_music, *filename_prefix, *filename_info;
3952 token_to_value_ptr[] =
3954 { "title_header", &tmp_music_file_info.title_header },
3955 { "artist_header", &tmp_music_file_info.artist_header },
3956 { "album_header", &tmp_music_file_info.album_header },
3957 { "year_header", &tmp_music_file_info.year_header },
3959 { "title", &tmp_music_file_info.title },
3960 { "artist", &tmp_music_file_info.artist },
3961 { "album", &tmp_music_file_info.album },
3962 { "year", &tmp_music_file_info.year },
3968 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3969 getCustomMusicFilename(basename));
3971 if (filename_music == NULL)
3974 /* ---------- try to replace file extension ---------- */
3976 filename_prefix = getStringCopy(filename_music);
3977 if (strrchr(filename_prefix, '.') != NULL)
3978 *strrchr(filename_prefix, '.') = '\0';
3979 filename_info = getStringCat2(filename_prefix, ".txt");
3982 printf("trying to load file '%s'...\n", filename_info);
3985 if (fileExists(filename_info))
3986 setup_file_hash = loadSetupFileHash(filename_info);
3988 free(filename_prefix);
3989 free(filename_info);
3991 if (setup_file_hash == NULL)
3993 /* ---------- try to add file extension ---------- */
3995 filename_prefix = getStringCopy(filename_music);
3996 filename_info = getStringCat2(filename_prefix, ".txt");
3999 printf("trying to load file '%s'...\n", filename_info);
4002 if (fileExists(filename_info))
4003 setup_file_hash = loadSetupFileHash(filename_info);
4005 free(filename_prefix);
4006 free(filename_info);
4009 if (setup_file_hash == NULL)
4012 /* ---------- music file info found ---------- */
4014 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4016 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4018 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4020 *token_to_value_ptr[i].value_ptr =
4021 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4024 tmp_music_file_info.basename = getStringCopy(basename);
4025 tmp_music_file_info.music = music;
4026 tmp_music_file_info.is_sound = is_sound;
4028 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4029 *new_music_file_info = tmp_music_file_info;
4031 return new_music_file_info;
4034 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4036 return get_music_file_info_ext(basename, music, FALSE);
4039 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4041 return get_music_file_info_ext(basename, sound, TRUE);
4044 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4045 char *basename, boolean is_sound)
4047 for (; list != NULL; list = list->next)
4048 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4054 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4056 return music_info_listed_ext(list, basename, FALSE);
4059 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4061 return music_info_listed_ext(list, basename, TRUE);
4064 void LoadMusicInfo()
4066 char *music_directory = getCustomMusicDirectory();
4067 int num_music = getMusicListSize();
4068 int num_music_noconf = 0;
4069 int num_sounds = getSoundListSize();
4071 struct dirent *dir_entry;
4072 struct FileInfo *music, *sound;
4073 struct MusicFileInfo *next, **new;
4076 while (music_file_info != NULL)
4078 next = music_file_info->next;
4080 checked_free(music_file_info->basename);
4082 checked_free(music_file_info->title_header);
4083 checked_free(music_file_info->artist_header);
4084 checked_free(music_file_info->album_header);
4085 checked_free(music_file_info->year_header);
4087 checked_free(music_file_info->title);
4088 checked_free(music_file_info->artist);
4089 checked_free(music_file_info->album);
4090 checked_free(music_file_info->year);
4092 free(music_file_info);
4094 music_file_info = next;
4097 new = &music_file_info;
4100 printf("::: num_music == %d\n", num_music);
4103 for (i = 0; i < num_music; i++)
4105 music = getMusicListEntry(i);
4108 printf("::: %d [%08x]\n", i, music->filename);
4111 if (music->filename == NULL)
4114 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4117 /* a configured file may be not recognized as music */
4118 if (!FileIsMusic(music->filename))
4122 printf("::: -> '%s' (configured)\n", music->filename);
4125 if (!music_info_listed(music_file_info, music->filename))
4127 *new = get_music_file_info(music->filename, i);
4129 new = &(*new)->next;
4133 if ((dir = opendir(music_directory)) == NULL)
4135 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4139 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4141 char *basename = dir_entry->d_name;
4142 boolean music_already_used = FALSE;
4145 /* skip all music files that are configured in music config file */
4146 for (i = 0; i < num_music; i++)
4148 music = getMusicListEntry(i);
4150 if (music->filename == NULL)
4153 if (strcmp(basename, music->filename) == 0)
4155 music_already_used = TRUE;
4160 if (music_already_used)
4163 if (!FileIsMusic(basename))
4167 printf("::: -> '%s' (found in directory)\n", basename);
4170 if (!music_info_listed(music_file_info, basename))
4172 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4174 new = &(*new)->next;
4182 for (i = 0; i < num_sounds; i++)
4184 sound = getSoundListEntry(i);
4186 if (sound->filename == NULL)
4189 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4192 /* a configured file may be not recognized as sound */
4193 if (!FileIsSound(sound->filename))
4197 printf("::: -> '%s' (configured)\n", sound->filename);
4200 if (!sound_info_listed(music_file_info, sound->filename))
4202 *new = get_sound_file_info(sound->filename, i);
4204 new = &(*new)->next;
4210 for (next = music_file_info; next != NULL; next = next->next)
4211 printf("::: title == '%s'\n", next->title);
4215 void add_helpanim_entry(int element, int action, int direction, int delay,
4216 int *num_list_entries)
4218 struct HelpAnimInfo *new_list_entry;
4219 (*num_list_entries)++;
4222 checked_realloc(helpanim_info,
4223 *num_list_entries * sizeof(struct HelpAnimInfo));
4224 new_list_entry = &helpanim_info[*num_list_entries - 1];
4226 new_list_entry->element = element;
4227 new_list_entry->action = action;
4228 new_list_entry->direction = direction;
4229 new_list_entry->delay = delay;
4232 void print_unknown_token(char *filename, char *token, int token_nr)
4236 Error(ERR_RETURN_LINE, "-");
4237 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4238 Error(ERR_RETURN, "- config file: '%s'", filename);
4241 Error(ERR_RETURN, "- token: '%s'", token);
4244 void print_unknown_token_end(int token_nr)
4247 Error(ERR_RETURN_LINE, "-");
4250 void LoadHelpAnimInfo()
4252 char *filename = getHelpAnimFilename();
4253 SetupFileList *setup_file_list = NULL, *list;
4254 SetupFileHash *element_hash, *action_hash, *direction_hash;
4255 int num_list_entries = 0;
4256 int num_unknown_tokens = 0;
4259 if (fileExists(filename))
4260 setup_file_list = loadSetupFileList(filename);
4262 if (setup_file_list == NULL)
4264 /* use reliable default values from static configuration */
4265 SetupFileList *insert_ptr;
4267 insert_ptr = setup_file_list =
4268 newSetupFileList(helpanim_config[0].token,
4269 helpanim_config[0].value);
4271 for (i = 1; helpanim_config[i].token; i++)
4272 insert_ptr = addListEntry(insert_ptr,
4273 helpanim_config[i].token,
4274 helpanim_config[i].value);
4277 element_hash = newSetupFileHash();
4278 action_hash = newSetupFileHash();
4279 direction_hash = newSetupFileHash();
4281 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4282 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4284 for (i = 0; i < NUM_ACTIONS; i++)
4285 setHashEntry(action_hash, element_action_info[i].suffix,
4286 i_to_a(element_action_info[i].value));
4288 /* do not store direction index (bit) here, but direction value! */
4289 for (i = 0; i < NUM_DIRECTIONS; i++)
4290 setHashEntry(direction_hash, element_direction_info[i].suffix,
4291 i_to_a(1 << element_direction_info[i].value));
4293 for (list = setup_file_list; list != NULL; list = list->next)
4295 char *element_token, *action_token, *direction_token;
4296 char *element_value, *action_value, *direction_value;
4297 int delay = atoi(list->value);
4299 if (strcmp(list->token, "end") == 0)
4301 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4306 /* first try to break element into element/action/direction parts;
4307 if this does not work, also accept combined "element[.act][.dir]"
4308 elements (like "dynamite.active"), which are unique elements */
4310 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4312 element_value = getHashEntry(element_hash, list->token);
4313 if (element_value != NULL) /* element found */
4314 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4318 /* no further suffixes found -- this is not an element */
4319 print_unknown_token(filename, list->token, num_unknown_tokens++);
4325 /* token has format "<prefix>.<something>" */
4327 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4328 direction_token = action_token; /* ... or direction */
4330 element_token = getStringCopy(list->token);
4331 *strchr(element_token, '.') = '\0';
4333 element_value = getHashEntry(element_hash, element_token);
4335 if (element_value == NULL) /* this is no element */
4337 element_value = getHashEntry(element_hash, list->token);
4338 if (element_value != NULL) /* combined element found */
4339 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4342 print_unknown_token(filename, list->token, num_unknown_tokens++);
4344 free(element_token);
4349 action_value = getHashEntry(action_hash, action_token);
4351 if (action_value != NULL) /* action found */
4353 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4356 free(element_token);
4361 direction_value = getHashEntry(direction_hash, direction_token);
4363 if (direction_value != NULL) /* direction found */
4365 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4368 free(element_token);
4373 if (strchr(action_token + 1, '.') == NULL)
4375 /* no further suffixes found -- this is not an action nor direction */
4377 element_value = getHashEntry(element_hash, list->token);
4378 if (element_value != NULL) /* combined element found */
4379 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4382 print_unknown_token(filename, list->token, num_unknown_tokens++);
4384 free(element_token);
4389 /* token has format "<prefix>.<suffix>.<something>" */
4391 direction_token = strchr(action_token + 1, '.');
4393 action_token = getStringCopy(action_token);
4394 *strchr(action_token + 1, '.') = '\0';
4396 action_value = getHashEntry(action_hash, action_token);
4398 if (action_value == NULL) /* this is no action */
4400 element_value = getHashEntry(element_hash, list->token);
4401 if (element_value != NULL) /* combined element found */
4402 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4405 print_unknown_token(filename, list->token, num_unknown_tokens++);
4407 free(element_token);
4413 direction_value = getHashEntry(direction_hash, direction_token);
4415 if (direction_value != NULL) /* direction found */
4417 add_helpanim_entry(atoi(element_value), atoi(action_value),
4418 atoi(direction_value), delay, &num_list_entries);
4420 free(element_token);
4426 /* this is no direction */
4428 element_value = getHashEntry(element_hash, list->token);
4429 if (element_value != NULL) /* combined element found */
4430 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4433 print_unknown_token(filename, list->token, num_unknown_tokens++);
4435 free(element_token);
4439 print_unknown_token_end(num_unknown_tokens);
4441 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4442 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4444 freeSetupFileList(setup_file_list);
4445 freeSetupFileHash(element_hash);
4446 freeSetupFileHash(action_hash);
4447 freeSetupFileHash(direction_hash);
4451 for (i = 0; i < num_list_entries; i++)
4452 printf("::: %d, %d, %d => %d\n",
4453 helpanim_info[i].element,
4454 helpanim_info[i].action,
4455 helpanim_info[i].direction,
4456 helpanim_info[i].delay);
4460 void LoadHelpTextInfo()
4462 char *filename = getHelpTextFilename();
4465 if (helptext_info != NULL)
4467 freeSetupFileHash(helptext_info);
4468 helptext_info = NULL;
4471 if (fileExists(filename))
4472 helptext_info = loadSetupFileHash(filename);
4474 if (helptext_info == NULL)
4476 /* use reliable default values from static configuration */
4477 helptext_info = newSetupFileHash();
4479 for (i = 0; helptext_config[i].token; i++)
4480 setHashEntry(helptext_info,
4481 helptext_config[i].token,
4482 helptext_config[i].value);
4487 BEGIN_HASH_ITERATION(helptext_info, itr)
4489 printf("::: '%s' => '%s'\n",
4490 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4492 END_HASH_ITERATION(hash, itr)