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 8 /* 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;
145 level->amoeba_speed = 10;
147 level->time_magic_wall = 10;
148 level->time_wheel = 10;
149 level->time_light = 10;
150 level->time_timegate = 10;
152 level->amoeba_content = EL_DIAMOND;
154 level->double_speed = FALSE;
155 level->initial_gravity = FALSE;
156 level->em_slippery_gems = FALSE;
157 level->block_last_field = FALSE;
158 level->sp_block_last_field = TRUE;
160 level->use_spring_bug = FALSE;
162 level->can_move_into_acid = ~0; /* everything can move into acid */
164 level->use_custom_template = FALSE;
166 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
167 level->name[i] = '\0';
168 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
169 level->author[i] = '\0';
171 strcpy(level->name, NAMELESS_LEVEL_NAME);
172 strcpy(level->author, ANONYMOUS_NAME);
174 for (i = 0; i < 4; i++)
176 level->envelope_text[i][0] = '\0';
177 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
178 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
181 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
182 level->score[i] = 10;
184 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
185 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
186 for (x = 0; x < 3; x++)
187 for (y = 0; y < 3; y++)
188 level->yamyam_content[i][x][y] =
189 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
191 level->field[0][0] = EL_PLAYER_1;
192 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
194 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
198 setElementChangePages(&element_info[element], 1);
199 setElementChangeInfoToDefaults(element_info[element].change);
201 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
203 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
204 element_info[element].description[j] = '\0';
206 if (element_info[element].custom_description != NULL)
207 strncpy(element_info[element].description,
208 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
210 strcpy(element_info[element].description,
211 element_info[element].editor_description);
213 element_info[element].use_gfx_element = FALSE;
214 element_info[element].gfx_element = EL_EMPTY_SPACE;
217 if (IS_CUSTOM_ELEMENT(element))
219 element_info[element].access_direction = MV_ALL_DIRECTIONS;
221 element_info[element].collect_score = 10; /* special default */
222 element_info[element].collect_count = 1; /* special default */
224 element_info[element].push_delay_fixed = -1; /* initialize later */
225 element_info[element].push_delay_random = -1; /* initialize later */
226 element_info[element].move_delay_fixed = 0;
227 element_info[element].move_delay_random = 0;
229 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
230 element_info[element].move_direction_initial = MV_START_AUTOMATIC;
231 element_info[element].move_stepsize = TILEX / 8;
232 element_info[element].move_enter_element = EL_EMPTY_SPACE;
233 element_info[element].move_leave_element = EL_EMPTY_SPACE;
234 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
236 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
238 element_info[element].explosion_delay = 18;
239 element_info[element].ignition_delay = 8;
241 for (x = 0; x < 3; x++)
242 for (y = 0; y < 3; y++)
243 element_info[element].content[x][y] = EL_EMPTY_SPACE;
245 element_info[element].access_type = 0;
246 element_info[element].access_layer = 0;
247 element_info[element].access_protected = 0;
248 element_info[element].walk_to_action = 0;
249 element_info[element].smash_targets = 0;
250 element_info[element].deadliness = 0;
251 element_info[element].consistency = 0;
253 element_info[element].can_explode_by_fire = FALSE;
254 element_info[element].can_explode_smashed = FALSE;
255 element_info[element].can_explode_impact = FALSE;
257 element_info[element].current_change_page = 0;
259 /* start with no properties at all */
260 for (j = 0; j < NUM_EP_BITFIELDS; j++)
261 Properties[element][j] = EP_BITMASK_DEFAULT;
263 /* now set default properties */
264 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
266 element_info[element].modified_settings = FALSE;
268 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
270 /* initialize memory for list of elements in group */
271 if (element_info[element].group == NULL)
272 element_info[element].group =
273 checked_malloc(sizeof(struct ElementGroupInfo));
275 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
276 element_info[element].group->element[j] = EL_EMPTY_SPACE;
278 /* default: only one element in group */
279 element_info[element].group->num_elements = 1;
281 element_info[element].group->choice_mode = ANIM_RANDOM;
285 BorderElement = EL_STEELWALL;
287 level->no_level_file = FALSE;
289 if (leveldir_current == NULL) /* only when dumping level */
292 /* try to determine better author name than 'anonymous' */
293 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
295 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
296 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
300 switch (LEVELCLASS(leveldir_current))
302 case LEVELCLASS_TUTORIAL:
303 strcpy(level->author, PROGRAM_AUTHOR_STRING);
306 case LEVELCLASS_CONTRIB:
307 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
308 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
311 case LEVELCLASS_PRIVATE:
312 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
313 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
317 /* keep default value */
323 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
325 level_file_info->nr = 0;
326 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
327 level_file_info->packed = FALSE;
328 level_file_info->basename = NULL;
329 level_file_info->filename = NULL;
332 static void ActivateLevelTemplate()
334 /* Currently there is no special action needed to activate the template
335 data, because 'element_info' and 'Properties' overwrite the original
336 level data, while all other variables do not change. */
339 static char *getLevelFilenameFromBasename(char *basename)
341 static char *filename = NULL;
343 checked_free(filename);
345 filename = getPath2(getCurrentLevelDir(), basename);
350 static int getFileTypeFromBasename(char *basename)
352 static char *filename = NULL;
353 struct stat file_status;
355 /* ---------- try to determine file type from filename ---------- */
357 /* check for typical filename of a Supaplex level package file */
358 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
359 strncmp(basename, "LEVELS.D", 8) == 0))
360 return LEVEL_FILE_TYPE_SP;
362 /* ---------- try to determine file type from filesize ---------- */
364 checked_free(filename);
365 filename = getPath2(getCurrentLevelDir(), basename);
367 if (stat(filename, &file_status) == 0)
369 /* check for typical filesize of a Supaplex level package file */
370 if (file_status.st_size == 170496)
371 return LEVEL_FILE_TYPE_SP;
374 return LEVEL_FILE_TYPE_UNKNOWN;
377 static char *getSingleLevelBasename(int nr, int type)
379 static char basename[MAX_FILENAME_LEN];
380 char *level_filename = getStringCopy(leveldir_current->level_filename);
382 if (level_filename == NULL)
383 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
387 case LEVEL_FILE_TYPE_RND:
389 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
391 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
394 case LEVEL_FILE_TYPE_EM:
395 sprintf(basename, "%d", nr);
398 case LEVEL_FILE_TYPE_UNKNOWN:
400 sprintf(basename, level_filename, nr);
404 free(level_filename);
409 static char *getPackedLevelBasename(int type)
411 static char basename[MAX_FILENAME_LEN];
412 char *directory = getCurrentLevelDir();
414 struct dirent *dir_entry;
416 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
418 if ((dir = opendir(directory)) == NULL)
420 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
425 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
427 char *entry_basename = dir_entry->d_name;
428 int entry_type = getFileTypeFromBasename(entry_basename);
430 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
432 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
435 strcpy(basename, entry_basename);
447 static char *getSingleLevelFilename(int nr, int type)
449 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
453 static char *getPackedLevelFilename(int type)
455 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
459 char *getDefaultLevelFilename(int nr)
461 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
464 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
469 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
470 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
473 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
478 lfi->basename = getPackedLevelBasename(lfi->type);
479 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
482 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
484 /* special case: level number is negative => check for level template file */
487 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
492 if (leveldir_current->level_filename != NULL)
494 /* check for file name/pattern specified in "levelinfo.conf" */
495 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
496 if (fileExists(lfi->filename))
500 /* check for native Rocks'n'Diamonds level file */
501 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
502 if (fileExists(lfi->filename))
505 /* check for classic Emerald Mine level file */
506 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
507 if (fileExists(lfi->filename))
510 /* check for various packed level file formats */
511 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
512 if (fileExists(lfi->filename))
515 /* no known level file found -- try to use default values */
516 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
519 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
521 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
522 lfi->type = getFileTypeFromBasename(lfi->basename);
525 static struct LevelFileInfo *getLevelFileInfo(int nr)
527 static struct LevelFileInfo level_file_info;
529 /* always start with reliable default values */
530 setFileInfoToDefaults(&level_file_info);
532 level_file_info.nr = nr; /* set requested level number */
534 determineLevelFileInfo_Filename(&level_file_info);
535 determineLevelFileInfo_Filetype(&level_file_info);
537 return &level_file_info;
541 /* ------------------------------------------------------------------------- */
542 /* functions for loading R'n'D level */
543 /* ------------------------------------------------------------------------- */
545 int getMappedElement(int element)
547 /* map some (historic, now obsolete) elements */
552 case EL_PLAYER_OBSOLETE:
553 element = EL_PLAYER_1;
556 case EL_KEY_OBSOLETE:
559 case EL_EM_KEY_1_FILE_OBSOLETE:
560 element = EL_EM_KEY_1;
563 case EL_EM_KEY_2_FILE_OBSOLETE:
564 element = EL_EM_KEY_2;
567 case EL_EM_KEY_3_FILE_OBSOLETE:
568 element = EL_EM_KEY_3;
571 case EL_EM_KEY_4_FILE_OBSOLETE:
572 element = EL_EM_KEY_4;
575 case EL_ENVELOPE_OBSOLETE:
576 element = EL_ENVELOPE_1;
584 if (element >= NUM_FILE_ELEMENTS)
586 Error(ERR_WARN, "invalid level element %d", element);
588 element = EL_UNKNOWN;
593 if (element >= NUM_FILE_ELEMENTS)
595 Error(ERR_WARN, "invalid level element %d", element);
597 element = EL_UNKNOWN;
599 else if (element == EL_PLAYER_OBSOLETE)
600 element = EL_PLAYER_1;
601 else if (element == EL_KEY_OBSOLETE)
608 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
610 level->file_version = getFileVersion(file);
611 level->game_version = getFileVersion(file);
616 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
620 level->fieldx = getFile8Bit(file);
621 level->fieldy = getFile8Bit(file);
623 level->time = getFile16BitBE(file);
624 level->gems_needed = getFile16BitBE(file);
626 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
627 level->name[i] = getFile8Bit(file);
628 level->name[MAX_LEVEL_NAME_LEN] = 0;
630 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
631 level->score[i] = getFile8Bit(file);
633 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
634 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
635 for (y = 0; y < 3; y++)
636 for (x = 0; x < 3; x++)
637 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
639 level->amoeba_speed = getFile8Bit(file);
640 level->time_magic_wall = getFile8Bit(file);
641 level->time_wheel = getFile8Bit(file);
642 level->amoeba_content = getMappedElement(getFile8Bit(file));
643 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
644 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
645 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
646 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
648 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
650 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
651 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
653 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
655 level->can_move_into_acid = getFile16BitBE(file);
657 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
662 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
666 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
667 level->author[i] = getFile8Bit(file);
668 level->author[MAX_LEVEL_NAME_LEN] = 0;
673 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
676 int chunk_size_expected = level->fieldx * level->fieldy;
678 /* Note: "chunk_size" was wrong before version 2.0 when elements are
679 stored with 16-bit encoding (and should be twice as big then).
680 Even worse, playfield data was stored 16-bit when only yamyam content
681 contained 16-bit elements and vice versa. */
683 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
684 chunk_size_expected *= 2;
686 if (chunk_size_expected != chunk_size)
688 ReadUnusedBytesFromFile(file, chunk_size);
689 return chunk_size_expected;
692 for (y = 0; y < level->fieldy; y++)
693 for (x = 0; x < level->fieldx; x++)
695 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
700 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
704 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
705 int chunk_size_expected = header_size + content_size;
707 /* Note: "chunk_size" was wrong before version 2.0 when elements are
708 stored with 16-bit encoding (and should be twice as big then).
709 Even worse, playfield data was stored 16-bit when only yamyam content
710 contained 16-bit elements and vice versa. */
712 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
713 chunk_size_expected += content_size;
715 if (chunk_size_expected != chunk_size)
717 ReadUnusedBytesFromFile(file, chunk_size);
718 return chunk_size_expected;
722 level->num_yamyam_contents = getFile8Bit(file);
726 /* correct invalid number of content fields -- should never happen */
727 if (level->num_yamyam_contents < 1 ||
728 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
729 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
731 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
732 for (y = 0; y < 3; y++)
733 for (x = 0; x < 3; x++)
734 level->yamyam_content[i][x][y] =
735 getMappedElement(level->encoding_16bit_field ?
736 getFile16BitBE(file) : getFile8Bit(file));
740 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
744 int num_contents, content_xsize, content_ysize;
745 int content_array[MAX_ELEMENT_CONTENTS][3][3];
747 element = getMappedElement(getFile16BitBE(file));
748 num_contents = getFile8Bit(file);
749 content_xsize = getFile8Bit(file);
750 content_ysize = getFile8Bit(file);
752 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
754 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
755 for (y = 0; y < 3; y++)
756 for (x = 0; x < 3; x++)
757 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
759 /* correct invalid number of content fields -- should never happen */
760 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
761 num_contents = STD_ELEMENT_CONTENTS;
763 if (element == EL_YAMYAM)
765 level->num_yamyam_contents = num_contents;
767 for (i = 0; i < num_contents; i++)
768 for (y = 0; y < 3; y++)
769 for (x = 0; x < 3; x++)
770 level->yamyam_content[i][x][y] = content_array[i][x][y];
772 else if (element == EL_BD_AMOEBA)
774 level->amoeba_content = content_array[0][0][0];
778 Error(ERR_WARN, "cannot load content for element '%d'", element);
784 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
790 int chunk_size_expected;
792 element = getMappedElement(getFile16BitBE(file));
793 if (!IS_ENVELOPE(element))
794 element = EL_ENVELOPE_1;
796 envelope_nr = element - EL_ENVELOPE_1;
798 envelope_len = getFile16BitBE(file);
800 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
801 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
803 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
805 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
806 if (chunk_size_expected != chunk_size)
808 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
809 return chunk_size_expected;
812 for (i = 0; i < envelope_len; i++)
813 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
818 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
820 int num_changed_custom_elements = getFile16BitBE(file);
821 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
824 if (chunk_size_expected != chunk_size)
826 ReadUnusedBytesFromFile(file, chunk_size - 2);
827 return chunk_size_expected;
830 for (i = 0; i < num_changed_custom_elements; i++)
832 int element = getFile16BitBE(file);
833 int properties = getFile32BitBE(file);
835 if (IS_CUSTOM_ELEMENT(element))
836 Properties[element][EP_BITFIELD_BASE] = properties;
838 Error(ERR_WARN, "invalid custom element number %d", element);
844 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
846 int num_changed_custom_elements = getFile16BitBE(file);
847 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
850 if (chunk_size_expected != chunk_size)
852 ReadUnusedBytesFromFile(file, chunk_size - 2);
853 return chunk_size_expected;
856 for (i = 0; i < num_changed_custom_elements; i++)
858 int element = getFile16BitBE(file);
859 int custom_target_element = getFile16BitBE(file);
861 if (IS_CUSTOM_ELEMENT(element))
862 element_info[element].change->target_element = custom_target_element;
864 Error(ERR_WARN, "invalid custom element number %d", element);
870 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
872 int num_changed_custom_elements = getFile16BitBE(file);
873 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
876 if (chunk_size_expected != chunk_size)
878 ReadUnusedBytesFromFile(file, chunk_size - 2);
879 return chunk_size_expected;
882 for (i = 0; i < num_changed_custom_elements; i++)
884 int element = getFile16BitBE(file);
886 if (!IS_CUSTOM_ELEMENT(element))
888 Error(ERR_WARN, "invalid custom element number %d", element);
890 element = EL_INTERNAL_DUMMY;
893 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
894 element_info[element].description[j] = getFile8Bit(file);
895 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
897 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
899 /* some free bytes for future properties and padding */
900 ReadUnusedBytesFromFile(file, 7);
902 element_info[element].use_gfx_element = getFile8Bit(file);
903 element_info[element].gfx_element =
904 getMappedElement(getFile16BitBE(file));
906 element_info[element].collect_score = getFile8Bit(file);
907 element_info[element].collect_count = getFile8Bit(file);
909 element_info[element].push_delay_fixed = getFile16BitBE(file);
910 element_info[element].push_delay_random = getFile16BitBE(file);
911 element_info[element].move_delay_fixed = getFile16BitBE(file);
912 element_info[element].move_delay_random = getFile16BitBE(file);
914 element_info[element].move_pattern = getFile16BitBE(file);
915 element_info[element].move_direction_initial = getFile8Bit(file);
916 element_info[element].move_stepsize = getFile8Bit(file);
918 for (y = 0; y < 3; y++)
919 for (x = 0; x < 3; x++)
920 element_info[element].content[x][y] =
921 getMappedElement(getFile16BitBE(file));
923 element_info[element].change->events = getFile32BitBE(file);
925 element_info[element].change->target_element =
926 getMappedElement(getFile16BitBE(file));
928 element_info[element].change->delay_fixed = getFile16BitBE(file);
929 element_info[element].change->delay_random = getFile16BitBE(file);
930 element_info[element].change->delay_frames = getFile16BitBE(file);
932 element_info[element].change->trigger_element =
933 getMappedElement(getFile16BitBE(file));
935 element_info[element].change->explode = getFile8Bit(file);
936 element_info[element].change->use_content = getFile8Bit(file);
937 element_info[element].change->only_complete = getFile8Bit(file);
938 element_info[element].change->use_random_change = getFile8Bit(file);
940 element_info[element].change->random = getFile8Bit(file);
941 element_info[element].change->power = getFile8Bit(file);
943 for (y = 0; y < 3; y++)
944 for (x = 0; x < 3; x++)
945 element_info[element].change->content[x][y] =
946 getMappedElement(getFile16BitBE(file));
948 element_info[element].slippery_type = getFile8Bit(file);
950 /* some free bytes for future properties and padding */
951 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
953 /* mark that this custom element has been modified */
954 element_info[element].modified_settings = TRUE;
960 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
962 struct ElementInfo *ei;
963 int chunk_size_expected;
967 element = getFile16BitBE(file);
969 if (!IS_CUSTOM_ELEMENT(element))
971 Error(ERR_WARN, "invalid custom element number %d", element);
973 ReadUnusedBytesFromFile(file, chunk_size - 2);
977 ei = &element_info[element];
979 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
980 ei->description[i] = getFile8Bit(file);
981 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
983 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
984 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
986 ei->num_change_pages = getFile8Bit(file);
988 /* some free bytes for future base property values and padding */
989 ReadUnusedBytesFromFile(file, 5);
991 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
992 if (chunk_size_expected != chunk_size)
994 ReadUnusedBytesFromFile(file, chunk_size - 48);
995 return chunk_size_expected;
998 /* read custom property values */
1000 ei->use_gfx_element = getFile8Bit(file);
1001 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1003 ei->collect_score = getFile8Bit(file);
1004 ei->collect_count = getFile8Bit(file);
1006 ei->push_delay_fixed = getFile16BitBE(file);
1007 ei->push_delay_random = getFile16BitBE(file);
1008 ei->move_delay_fixed = getFile16BitBE(file);
1009 ei->move_delay_random = getFile16BitBE(file);
1011 /* bits 0 - 15 of "move_pattern" ... */
1012 ei->move_pattern = getFile16BitBE(file);
1013 ei->move_direction_initial = getFile8Bit(file);
1014 ei->move_stepsize = getFile8Bit(file);
1016 ei->slippery_type = getFile8Bit(file);
1018 for (y = 0; y < 3; y++)
1019 for (x = 0; x < 3; x++)
1020 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1022 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1023 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1024 ei->move_leave_type = getFile8Bit(file);
1026 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1027 ei->move_pattern |= (getFile16BitBE(file) << 16);
1029 ei->access_direction = getFile8Bit(file);
1031 ei->explosion_delay = getFile8Bit(file);
1032 ei->ignition_delay = getFile8Bit(file);
1034 /* some free bytes for future custom property values and padding */
1035 ReadUnusedBytesFromFile(file, 2);
1037 /* read change property values */
1039 setElementChangePages(ei, ei->num_change_pages);
1041 for (i = 0; i < ei->num_change_pages; i++)
1043 struct ElementChangeInfo *change = &ei->change_page[i];
1045 /* always start with reliable default values */
1046 setElementChangeInfoToDefaults(change);
1048 change->events = getFile32BitBE(file);
1050 change->target_element = getMappedElement(getFile16BitBE(file));
1052 change->delay_fixed = getFile16BitBE(file);
1053 change->delay_random = getFile16BitBE(file);
1054 change->delay_frames = getFile16BitBE(file);
1056 change->trigger_element = getMappedElement(getFile16BitBE(file));
1058 change->explode = getFile8Bit(file);
1059 change->use_content = getFile8Bit(file);
1060 change->only_complete = getFile8Bit(file);
1061 change->use_random_change = getFile8Bit(file);
1063 change->random = getFile8Bit(file);
1064 change->power = getFile8Bit(file);
1066 for (y = 0; y < 3; y++)
1067 for (x = 0; x < 3; x++)
1068 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1070 change->can_change = getFile8Bit(file);
1072 change->sides = getFile8Bit(file);
1074 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
1075 change->sides = CH_SIDE_ANY;
1077 /* some free bytes for future change property values and padding */
1078 ReadUnusedBytesFromFile(file, 8);
1081 /* mark this custom element as modified */
1082 ei->modified_settings = TRUE;
1087 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1089 struct ElementInfo *ei;
1090 struct ElementGroupInfo *group;
1094 element = getFile16BitBE(file);
1096 if (!IS_GROUP_ELEMENT(element))
1098 Error(ERR_WARN, "invalid group element number %d", element);
1100 ReadUnusedBytesFromFile(file, chunk_size - 2);
1104 ei = &element_info[element];
1106 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1107 ei->description[i] = getFile8Bit(file);
1108 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1110 group = element_info[element].group;
1112 group->num_elements = getFile8Bit(file);
1114 ei->use_gfx_element = getFile8Bit(file);
1115 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1117 group->choice_mode = getFile8Bit(file);
1119 /* some free bytes for future values and padding */
1120 ReadUnusedBytesFromFile(file, 3);
1122 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1123 group->element[i] = getMappedElement(getFile16BitBE(file));
1125 /* mark this group element as modified */
1126 element_info[element].modified_settings = TRUE;
1131 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1132 struct LevelFileInfo *level_file_info)
1134 char *filename = level_file_info->filename;
1135 char cookie[MAX_LINE_LEN];
1136 char chunk_name[CHUNK_ID_LEN + 1];
1140 if (!(file = fopen(filename, MODE_READ)))
1142 level->no_level_file = TRUE;
1144 if (level != &level_template)
1145 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1150 getFileChunkBE(file, chunk_name, NULL);
1151 if (strcmp(chunk_name, "RND1") == 0)
1153 getFile32BitBE(file); /* not used */
1155 getFileChunkBE(file, chunk_name, NULL);
1156 if (strcmp(chunk_name, "CAVE") != 0)
1158 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1163 else /* check for pre-2.0 file format with cookie string */
1165 strcpy(cookie, chunk_name);
1166 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1167 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1168 cookie[strlen(cookie) - 1] = '\0';
1170 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1172 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1177 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1179 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1184 /* pre-2.0 level files have no game version, so use file version here */
1185 level->game_version = level->file_version;
1188 if (level->file_version < FILE_VERSION_1_2)
1190 /* level files from versions before 1.2.0 without chunk structure */
1191 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1192 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1200 int (*loader)(FILE *, int, struct LevelInfo *);
1204 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1205 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1206 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1207 { "BODY", -1, LoadLevel_BODY },
1208 { "CONT", -1, LoadLevel_CONT },
1209 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1210 { "CNT3", -1, LoadLevel_CNT3 },
1211 { "CUS1", -1, LoadLevel_CUS1 },
1212 { "CUS2", -1, LoadLevel_CUS2 },
1213 { "CUS3", -1, LoadLevel_CUS3 },
1214 { "CUS4", -1, LoadLevel_CUS4 },
1215 { "GRP1", -1, LoadLevel_GRP1 },
1219 while (getFileChunkBE(file, chunk_name, &chunk_size))
1223 while (chunk_info[i].name != NULL &&
1224 strcmp(chunk_name, chunk_info[i].name) != 0)
1227 if (chunk_info[i].name == NULL)
1229 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1230 chunk_name, filename);
1231 ReadUnusedBytesFromFile(file, chunk_size);
1233 else if (chunk_info[i].size != -1 &&
1234 chunk_info[i].size != chunk_size)
1236 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1237 chunk_size, chunk_name, filename);
1238 ReadUnusedBytesFromFile(file, chunk_size);
1242 /* call function to load this level chunk */
1243 int chunk_size_expected =
1244 (chunk_info[i].loader)(file, chunk_size, level);
1246 /* the size of some chunks cannot be checked before reading other
1247 chunks first (like "HEAD" and "BODY") that contain some header
1248 information, so check them here */
1249 if (chunk_size_expected != chunk_size)
1251 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1252 chunk_size, chunk_name, filename);
1261 /* ------------------------------------------------------------------------- */
1262 /* functions for loading EM level */
1263 /* ------------------------------------------------------------------------- */
1265 static int map_em_element_yam(int element)
1269 case 0x00: return EL_EMPTY;
1270 case 0x01: return EL_EMERALD;
1271 case 0x02: return EL_DIAMOND;
1272 case 0x03: return EL_ROCK;
1273 case 0x04: return EL_ROBOT;
1274 case 0x05: return EL_SPACESHIP_UP;
1275 case 0x06: return EL_BOMB;
1276 case 0x07: return EL_BUG_UP;
1277 case 0x08: return EL_AMOEBA_DROP;
1278 case 0x09: return EL_NUT;
1279 case 0x0a: return EL_YAMYAM;
1280 case 0x0b: return EL_QUICKSAND_FULL;
1281 case 0x0c: return EL_SAND;
1282 case 0x0d: return EL_WALL_SLIPPERY;
1283 case 0x0e: return EL_STEELWALL;
1284 case 0x0f: return EL_WALL;
1285 case 0x10: return EL_EM_KEY_1;
1286 case 0x11: return EL_EM_KEY_2;
1287 case 0x12: return EL_EM_KEY_4;
1288 case 0x13: return EL_EM_KEY_3;
1289 case 0x14: return EL_MAGIC_WALL;
1290 case 0x15: return EL_ROBOT_WHEEL;
1291 case 0x16: return EL_DYNAMITE;
1293 case 0x17: return EL_EM_KEY_1; /* EMC */
1294 case 0x18: return EL_BUG_UP; /* EMC */
1295 case 0x1a: return EL_DIAMOND; /* EMC */
1296 case 0x1b: return EL_EMERALD; /* EMC */
1297 case 0x25: return EL_NUT; /* EMC */
1298 case 0x80: return EL_EMPTY; /* EMC */
1299 case 0x85: return EL_EM_KEY_1; /* EMC */
1300 case 0x86: return EL_EM_KEY_2; /* EMC */
1301 case 0x87: return EL_EM_KEY_4; /* EMC */
1302 case 0x88: return EL_EM_KEY_3; /* EMC */
1303 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1304 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1305 case 0xaf: return EL_DYNAMITE; /* EMC */
1306 case 0xbd: return EL_SAND; /* EMC */
1309 Error(ERR_WARN, "invalid level element %d", element);
1314 static int map_em_element_field(int element)
1316 if (element >= 0xc8 && element <= 0xe1)
1317 return EL_CHAR_A + (element - 0xc8);
1318 else if (element >= 0xe2 && element <= 0xeb)
1319 return EL_CHAR_0 + (element - 0xe2);
1323 case 0x00: return EL_ROCK;
1324 case 0x01: return EL_ROCK; /* EMC */
1325 case 0x02: return EL_DIAMOND;
1326 case 0x03: return EL_DIAMOND;
1327 case 0x04: return EL_ROBOT;
1328 case 0x05: return EL_ROBOT; /* EMC */
1329 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1330 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1331 case 0x08: return EL_SPACESHIP_UP;
1332 case 0x09: return EL_SPACESHIP_RIGHT;
1333 case 0x0a: return EL_SPACESHIP_DOWN;
1334 case 0x0b: return EL_SPACESHIP_LEFT;
1335 case 0x0c: return EL_SPACESHIP_UP;
1336 case 0x0d: return EL_SPACESHIP_RIGHT;
1337 case 0x0e: return EL_SPACESHIP_DOWN;
1338 case 0x0f: return EL_SPACESHIP_LEFT;
1340 case 0x10: return EL_BOMB;
1341 case 0x11: return EL_BOMB; /* EMC */
1342 case 0x12: return EL_EMERALD;
1343 case 0x13: return EL_EMERALD;
1344 case 0x14: return EL_BUG_UP;
1345 case 0x15: return EL_BUG_RIGHT;
1346 case 0x16: return EL_BUG_DOWN;
1347 case 0x17: return EL_BUG_LEFT;
1348 case 0x18: return EL_BUG_UP;
1349 case 0x19: return EL_BUG_RIGHT;
1350 case 0x1a: return EL_BUG_DOWN;
1351 case 0x1b: return EL_BUG_LEFT;
1352 case 0x1c: return EL_AMOEBA_DROP;
1353 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1354 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1355 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1357 case 0x20: return EL_ROCK;
1358 case 0x21: return EL_BOMB; /* EMC */
1359 case 0x22: return EL_DIAMOND; /* EMC */
1360 case 0x23: return EL_EMERALD; /* EMC */
1361 case 0x24: return EL_MAGIC_WALL;
1362 case 0x25: return EL_NUT;
1363 case 0x26: return EL_NUT; /* EMC */
1364 case 0x27: return EL_NUT; /* EMC */
1366 /* looks like magic wheel, but is _always_ activated */
1367 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1369 case 0x29: return EL_YAMYAM; /* up */
1370 case 0x2a: return EL_YAMYAM; /* down */
1371 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1372 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1373 case 0x2d: return EL_QUICKSAND_FULL;
1374 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1375 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1377 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1378 case 0x31: return EL_SAND; /* EMC */
1379 case 0x32: return EL_SAND; /* EMC */
1380 case 0x33: return EL_SAND; /* EMC */
1381 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1382 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1383 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1384 case 0x37: return EL_SAND; /* EMC */
1385 case 0x38: return EL_ROCK; /* EMC */
1386 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1387 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1388 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1389 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1390 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1391 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1392 case 0x3f: return EL_ACID_POOL_BOTTOM;
1394 case 0x40: return EL_EXIT_OPEN; /* 1 */
1395 case 0x41: return EL_EXIT_OPEN; /* 2 */
1396 case 0x42: return EL_EXIT_OPEN; /* 3 */
1397 case 0x43: return EL_BALLOON; /* EMC */
1398 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1399 case 0x45: return EL_SPRING; /* EMC */
1400 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1401 case 0x47: return EL_SPRING; /* left */ /* EMC */
1402 case 0x48: return EL_SPRING; /* right */ /* EMC */
1403 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1404 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1405 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1406 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1407 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1408 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1409 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1411 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1412 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1413 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1414 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1415 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1416 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1417 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1418 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1419 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1420 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1421 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1422 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1423 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1424 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1425 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1426 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1428 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1429 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1430 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1431 case 0x63: return EL_SPRING; /* left */ /* EMC */
1432 case 0x64: return EL_SPRING; /* right */ /* EMC */
1433 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1434 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1435 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1436 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1437 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1438 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1439 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1440 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1441 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1442 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1443 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1445 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1446 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1447 case 0x72: return EL_NUT; /* left */ /* EMC */
1448 case 0x73: return EL_SAND; /* EMC (? "nut") */
1449 case 0x74: return EL_STEELWALL;
1450 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1451 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1452 case 0x77: return EL_BOMB; /* left */ /* EMC */
1453 case 0x78: return EL_BOMB; /* right */ /* EMC */
1454 case 0x79: return EL_ROCK; /* left */ /* EMC */
1455 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1456 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1457 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1458 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1459 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1460 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1462 case 0x80: return EL_EMPTY;
1463 case 0x81: return EL_WALL_SLIPPERY;
1464 case 0x82: return EL_SAND;
1465 case 0x83: return EL_STEELWALL;
1466 case 0x84: return EL_WALL;
1467 case 0x85: return EL_EM_KEY_1;
1468 case 0x86: return EL_EM_KEY_2;
1469 case 0x87: return EL_EM_KEY_4;
1470 case 0x88: return EL_EM_KEY_3;
1471 case 0x89: return EL_EM_GATE_1;
1472 case 0x8a: return EL_EM_GATE_2;
1473 case 0x8b: return EL_EM_GATE_4;
1474 case 0x8c: return EL_EM_GATE_3;
1475 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1476 case 0x8e: return EL_EM_GATE_1_GRAY;
1477 case 0x8f: return EL_EM_GATE_2_GRAY;
1479 case 0x90: return EL_EM_GATE_4_GRAY;
1480 case 0x91: return EL_EM_GATE_3_GRAY;
1481 case 0x92: return EL_MAGIC_WALL;
1482 case 0x93: return EL_ROBOT_WHEEL;
1483 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1484 case 0x95: return EL_ACID_POOL_TOPLEFT;
1485 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1486 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1487 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1488 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1489 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1490 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1491 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1492 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1493 case 0x9e: return EL_EXIT_CLOSED;
1494 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1496 /* looks like normal sand, but behaves like wall */
1497 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1498 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1499 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1500 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1501 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1502 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1503 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1504 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1505 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1506 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1507 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1508 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1509 case 0xac: return EL_CHAR_COMMA; /* EMC */
1510 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1511 case 0xae: return EL_CHAR_MINUS; /* EMC */
1512 case 0xaf: return EL_DYNAMITE;
1514 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1515 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1516 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1517 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1518 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1519 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1520 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1521 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1522 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1523 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1524 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1525 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1526 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1527 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1528 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1529 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1531 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1532 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1533 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1534 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1535 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1536 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1537 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1538 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1540 /* characters: see above */
1542 case 0xec: return EL_CHAR_PERIOD;
1543 case 0xed: return EL_CHAR_EXCLAM;
1544 case 0xee: return EL_CHAR_COLON;
1545 case 0xef: return EL_CHAR_QUESTION;
1547 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1548 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1549 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1550 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1551 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1552 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1553 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1554 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1556 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1557 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1558 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1559 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1560 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1561 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1563 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1564 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1567 /* should never happen (all 8-bit value cases should be handled) */
1568 Error(ERR_WARN, "invalid level element %d", element);
1573 #define EM_LEVEL_SIZE 2106
1574 #define EM_LEVEL_XSIZE 64
1575 #define EM_LEVEL_YSIZE 32
1577 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1578 struct LevelFileInfo *level_file_info)
1580 char *filename = level_file_info->filename;
1582 unsigned char leveldata[EM_LEVEL_SIZE];
1583 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1584 unsigned char code0 = 0x65;
1585 unsigned char code1 = 0x11;
1586 boolean level_is_crypted = FALSE;
1587 int nr = level_file_info->nr;
1590 if (!(file = fopen(filename, MODE_READ)))
1592 level->no_level_file = TRUE;
1594 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1599 for(i = 0; i < EM_LEVEL_SIZE; i++)
1600 leveldata[i] = fgetc(file);
1604 /* check if level data is crypted by testing against known starting bytes
1605 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1607 if ((leveldata[0] == 0xf1 ||
1608 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1610 level_is_crypted = TRUE;
1612 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1613 leveldata[0] = 0xf1;
1616 if (level_is_crypted) /* decode crypted level data */
1618 for(i = 0; i < EM_LEVEL_SIZE; i++)
1620 leveldata[i] ^= code0;
1621 leveldata[i] -= code1;
1623 code0 = (code0 + 7) & 0xff;
1627 level->fieldx = EM_LEVEL_XSIZE;
1628 level->fieldy = EM_LEVEL_YSIZE;
1630 level->time = header[46] * 10;
1631 level->gems_needed = header[47];
1633 /* The original Emerald Mine levels have their level number stored
1634 at the second byte of the level file...
1635 Do not trust this information at other level files, e.g. EMC,
1636 but correct it anyway (normally the first row is completely
1637 steel wall, so the correction does not hurt anyway). */
1639 if (leveldata[1] == nr)
1640 leveldata[1] = leveldata[2]; /* correct level number field */
1642 sprintf(level->name, "Level %d", nr); /* set level name */
1644 level->score[SC_EMERALD] = header[36];
1645 level->score[SC_DIAMOND] = header[37];
1646 level->score[SC_ROBOT] = header[38];
1647 level->score[SC_SPACESHIP] = header[39];
1648 level->score[SC_BUG] = header[40];
1649 level->score[SC_YAMYAM] = header[41];
1650 level->score[SC_NUT] = header[42];
1651 level->score[SC_DYNAMITE] = header[43];
1652 level->score[SC_TIME_BONUS] = header[44];
1654 level->num_yamyam_contents = 4;
1656 for(i = 0; i < level->num_yamyam_contents; i++)
1657 for(y = 0; y < 3; y++)
1658 for(x = 0; x < 3; x++)
1659 level->yamyam_content[i][x][y] =
1660 map_em_element_yam(header[i * 9 + y * 3 + x]);
1662 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1663 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1664 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1665 level->amoeba_content = EL_DIAMOND;
1667 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1669 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1671 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1672 new_element = EL_AMOEBA_WET;
1674 level->field[x][y] = new_element;
1677 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1678 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1679 level->field[x][y] = EL_PLAYER_1;
1681 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1682 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1683 level->field[x][y] = EL_PLAYER_2;
1686 /* ------------------------------------------------------------------------- */
1687 /* functions for loading SP level */
1688 /* ------------------------------------------------------------------------- */
1690 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1691 #define SP_LEVEL_SIZE 1536
1692 #define SP_LEVEL_XSIZE 60
1693 #define SP_LEVEL_YSIZE 24
1694 #define SP_LEVEL_NAME_LEN 23
1696 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1701 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1702 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1704 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1706 int element_old = fgetc(file);
1709 if (element_old <= 0x27)
1710 element_new = getMappedElement(EL_SP_START + element_old);
1711 else if (element_old == 0x28)
1712 element_new = EL_INVISIBLE_WALL;
1715 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1716 Error(ERR_WARN, "invalid level element %d", element_old);
1718 element_new = EL_UNKNOWN;
1721 level->field[x][y] = element_new;
1725 ReadUnusedBytesFromFile(file, 4);
1727 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1728 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1730 ReadUnusedBytesFromFile(file, 1);
1732 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1733 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1734 level->name[i] = fgetc(file);
1735 level->name[SP_LEVEL_NAME_LEN] = '\0';
1737 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1738 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1740 /* number of infotrons needed; 0 means that Supaplex will count the total
1741 amount of infotrons in the level and use the low byte of that number.
1742 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1743 level->gems_needed = fgetc(file);
1745 /* information about special gravity port entries */
1746 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1748 level->fieldx = SP_LEVEL_XSIZE;
1749 level->fieldy = SP_LEVEL_YSIZE;
1751 level->time = 0; /* no time limit */
1752 level->amoeba_speed = 0;
1753 level->time_magic_wall = 0;
1754 level->time_wheel = 0;
1755 level->amoeba_content = EL_EMPTY;
1757 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1758 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1760 /* there are no yamyams in supaplex levels */
1761 for(i = 0; i < level->num_yamyam_contents; i++)
1762 for(y = 0; y < 3; y++)
1763 for(x = 0; x < 3; x++)
1764 level->yamyam_content[i][x][y] = EL_EMPTY;
1767 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1768 struct LevelFileInfo *level_file_info)
1770 char *filename = level_file_info->filename;
1772 int nr = level_file_info->nr - leveldir_current->first_level;
1774 char name_first, name_last;
1775 struct LevelInfo multipart_level;
1776 int multipart_xpos, multipart_ypos;
1777 boolean is_multipart_level;
1778 boolean is_first_part;
1779 boolean reading_multipart_level = FALSE;
1780 boolean use_empty_level = FALSE;
1782 if (!(file = fopen(filename, MODE_READ)))
1784 level->no_level_file = TRUE;
1786 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1791 /* position file stream to the requested level inside the level package */
1792 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1794 level->no_level_file = TRUE;
1796 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1801 /* there exist Supaplex level package files with multi-part levels which
1802 can be detected as follows: instead of leading and trailing dashes ('-')
1803 to pad the level name, they have leading and trailing numbers which are
1804 the x and y coordinations of the current part of the multi-part level;
1805 if there are '?' characters instead of numbers on the left or right side
1806 of the level name, the multi-part level consists of only horizontal or
1809 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1811 LoadLevelFromFileStream_SP(file, level, l);
1813 /* check if this level is a part of a bigger multi-part level */
1815 name_first = level->name[0];
1816 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1818 is_multipart_level =
1819 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1820 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1823 ((name_first == '?' || name_first == '1') &&
1824 (name_last == '?' || name_last == '1'));
1826 /* correct leading multipart level meta information in level name */
1827 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1828 level->name[i] = '-';
1830 /* correct trailing multipart level meta information in level name */
1831 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1832 level->name[i] = '-';
1834 /* ---------- check for normal single level ---------- */
1836 if (!reading_multipart_level && !is_multipart_level)
1838 /* the current level is simply a normal single-part level, and we are
1839 not reading a multi-part level yet, so return the level as it is */
1844 /* ---------- check for empty level (unused multi-part) ---------- */
1846 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1848 /* this is a part of a multi-part level, but not the first part
1849 (and we are not already reading parts of a multi-part level);
1850 in this case, use an empty level instead of the single part */
1852 use_empty_level = TRUE;
1857 /* ---------- check for finished multi-part level ---------- */
1859 if (reading_multipart_level &&
1860 (!is_multipart_level ||
1861 strcmp(level->name, multipart_level.name) != 0))
1863 /* we are already reading parts of a multi-part level, but this level is
1864 either not a multi-part level, or a part of a different multi-part
1865 level; in both cases, the multi-part level seems to be complete */
1870 /* ---------- here we have one part of a multi-part level ---------- */
1872 reading_multipart_level = TRUE;
1874 if (is_first_part) /* start with first part of new multi-part level */
1876 /* copy level info structure from first part */
1877 multipart_level = *level;
1879 /* clear playfield of new multi-part level */
1880 for (y = 0; y < MAX_LEV_FIELDY; y++)
1881 for (x = 0; x < MAX_LEV_FIELDX; x++)
1882 multipart_level.field[x][y] = EL_EMPTY;
1885 if (name_first == '?')
1887 if (name_last == '?')
1890 multipart_xpos = (int)(name_first - '0');
1891 multipart_ypos = (int)(name_last - '0');
1894 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1895 multipart_xpos, multipart_ypos, multipart_level.name);
1898 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1899 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1901 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1906 multipart_level.fieldx = MAX(multipart_level.fieldx,
1907 multipart_xpos * SP_LEVEL_XSIZE);
1908 multipart_level.fieldy = MAX(multipart_level.fieldy,
1909 multipart_ypos * SP_LEVEL_YSIZE);
1911 /* copy level part at the right position of multi-part level */
1912 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1914 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1916 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1917 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1919 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1926 if (use_empty_level)
1928 setLevelInfoToDefaults(level);
1930 level->fieldx = SP_LEVEL_XSIZE;
1931 level->fieldy = SP_LEVEL_YSIZE;
1933 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1934 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1935 level->field[x][y] = EL_EMPTY;
1937 strcpy(level->name, "-------- EMPTY --------");
1939 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1942 if (reading_multipart_level)
1943 *level = multipart_level;
1946 /* ------------------------------------------------------------------------- */
1947 /* functions for loading generic level */
1948 /* ------------------------------------------------------------------------- */
1950 void LoadLevelFromFileInfo(struct LevelInfo *level,
1951 struct LevelFileInfo *level_file_info)
1953 /* always start with reliable default values */
1954 setLevelInfoToDefaults(level);
1956 switch (level_file_info->type)
1958 case LEVEL_FILE_TYPE_RND:
1959 LoadLevelFromFileInfo_RND(level, level_file_info);
1962 case LEVEL_FILE_TYPE_EM:
1963 LoadLevelFromFileInfo_EM(level, level_file_info);
1966 case LEVEL_FILE_TYPE_SP:
1967 LoadLevelFromFileInfo_SP(level, level_file_info);
1971 LoadLevelFromFileInfo_RND(level, level_file_info);
1976 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1978 static struct LevelFileInfo level_file_info;
1980 /* always start with reliable default values */
1981 setFileInfoToDefaults(&level_file_info);
1983 level_file_info.nr = 0; /* unknown level number */
1984 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1985 level_file_info.filename = filename;
1987 LoadLevelFromFileInfo(level, &level_file_info);
1990 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1992 if (leveldir_current == NULL) /* only when dumping level */
1996 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1999 /* determine correct game engine version of current level */
2001 if (!leveldir_current->latest_engine)
2003 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2004 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2005 IS_LEVELCLASS_UNDEFINED(leveldir_current))
2009 printf("\n::: This level is private or contributed: '%s'\n", filename);
2013 printf("\n::: Use the stored game engine version for this level\n");
2016 /* For all levels which are not forced to use the latest game engine
2017 version (normally user contributed, private and undefined levels),
2018 use the version of the game engine the levels were created for.
2020 Since 2.0.1, the game engine version is now directly stored
2021 in the level file (chunk "VERS"), so there is no need anymore
2022 to set the game version from the file version (except for old,
2023 pre-2.0 levels, where the game version is still taken from the
2024 file format version used to store the level -- see above). */
2026 /* do some special adjustments to support older level versions */
2027 if (level->file_version == FILE_VERSION_1_0)
2029 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2030 Error(ERR_WARN, "using high speed movement for player");
2032 /* player was faster than monsters in (pre-)1.0 levels */
2033 level->double_speed = TRUE;
2036 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2037 if (level->game_version == VERSION_IDENT(2,0,1,0))
2038 level->em_slippery_gems = TRUE;
2040 if (level->game_version < VERSION_IDENT(2,2,0,0))
2041 level->use_spring_bug = TRUE;
2043 if (level->game_version < VERSION_IDENT(3,0,9,0))
2047 level->can_move_into_acid = 0; /* nothing can move into acid */
2049 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
2050 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2051 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
2052 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
2054 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2055 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2061 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2062 leveldir_current->sort_priority, filename);
2066 printf("\n::: Use latest game engine version for this level.\n");
2069 /* For all levels which are forced to use the latest game engine version
2070 (normally all but user contributed, private and undefined levels), set
2071 the game engine version to the actual version; this allows for actual
2072 corrections in the game engine to take effect for existing, converted
2073 levels (from "classic" or other existing games) to make the emulation
2074 of the corresponding game more accurate, while (hopefully) not breaking
2075 existing levels created from other players. */
2078 printf("::: changing engine from %d to %d\n",
2079 level->game_version, GAME_VERSION_ACTUAL);
2082 level->game_version = GAME_VERSION_ACTUAL;
2084 /* Set special EM style gems behaviour: EM style gems slip down from
2085 normal, steel and growing wall. As this is a more fundamental change,
2086 it seems better to set the default behaviour to "off" (as it is more
2087 natural) and make it configurable in the level editor (as a property
2088 of gem style elements). Already existing converted levels (neither
2089 private nor contributed levels) are changed to the new behaviour. */
2091 if (level->file_version < FILE_VERSION_2_0)
2092 level->em_slippery_gems = TRUE;
2096 printf("::: => %d\n", level->game_version);
2100 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2104 /* map custom element change events that have changed in newer versions
2105 (these following values were accidentally changed in version 3.0.1) */
2106 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2108 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2110 int element = EL_CUSTOM_START + i;
2112 /* order of checking and copying events to be mapped is important */
2113 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2115 if (HAS_CHANGE_EVENT(element, j - 2))
2117 SET_CHANGE_EVENT(element, j - 2, FALSE);
2118 SET_CHANGE_EVENT(element, j, TRUE);
2122 /* order of checking and copying events to be mapped is important */
2123 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2125 if (HAS_CHANGE_EVENT(element, j - 1))
2127 SET_CHANGE_EVENT(element, j - 1, FALSE);
2128 SET_CHANGE_EVENT(element, j, TRUE);
2134 /* some custom element change events get mapped since version 3.0.3 */
2135 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2137 int element = EL_CUSTOM_START + i;
2139 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2140 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2142 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2143 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2145 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2149 /* initialize "can_change" field for old levels with only one change page */
2150 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2152 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2154 int element = EL_CUSTOM_START + i;
2156 if (CAN_CHANGE(element))
2157 element_info[element].change->can_change = TRUE;
2161 /* correct field access direction (for old levels without this option) */
2162 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2164 int element = EL_CUSTOM_START + i;
2166 if (element_info[element].access_direction == MV_NO_MOVING)
2167 element_info[element].access_direction = MV_ALL_DIRECTIONS;
2171 /* set default push delay values (corrected since version 3.0.7-1) */
2172 if (level->game_version < VERSION_IDENT(3,0,7,1))
2174 game.default_push_delay_fixed = 2;
2175 game.default_push_delay_random = 8;
2179 game.default_push_delay_fixed = 8;
2180 game.default_push_delay_random = 8;
2183 /* set uninitialized push delay values of custom elements in older levels */
2184 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2186 int element = EL_CUSTOM_START + i;
2188 if (element_info[element].push_delay_fixed == -1)
2189 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2190 if (element_info[element].push_delay_random == -1)
2191 element_info[element].push_delay_random = game.default_push_delay_random;
2195 /* initialize element properties for level editor etc. */
2196 InitElementPropertiesEngine(level->game_version);
2199 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2203 /* map elements that have changed in newer versions */
2204 for (y = 0; y < level->fieldy; y++)
2206 for (x = 0; x < level->fieldx; x++)
2208 int element = level->field[x][y];
2210 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2212 /* map game font elements */
2213 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2214 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2215 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2216 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2219 if (level->game_version < VERSION_IDENT(3,0,0,0))
2221 /* map Supaplex gravity tube elements */
2222 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2223 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2224 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2225 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2229 level->field[x][y] = element;
2233 /* copy elements to runtime playfield array */
2234 for (x = 0; x < MAX_LEV_FIELDX; x++)
2235 for (y = 0; y < MAX_LEV_FIELDY; y++)
2236 Feld[x][y] = level->field[x][y];
2238 /* initialize level size variables for faster access */
2239 lev_fieldx = level->fieldx;
2240 lev_fieldy = level->fieldy;
2242 /* determine border element for this level */
2246 void LoadLevelTemplate(int nr)
2249 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2250 char *filename = level_file_info->filename;
2252 LoadLevelFromFileInfo(&level_template, level_file_info);
2254 char *filename = getDefaultLevelFilename(nr);
2256 LoadLevelFromFilename_RND(&level_template, filename);
2259 LoadLevel_InitVersion(&level, filename);
2260 LoadLevel_InitElements(&level, filename);
2262 ActivateLevelTemplate();
2265 void LoadLevel(int nr)
2268 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2269 char *filename = level_file_info->filename;
2271 LoadLevelFromFileInfo(&level, level_file_info);
2273 char *filename = getLevelFilename(nr);
2275 LoadLevelFromFilename_RND(&level, filename);
2278 if (level.use_custom_template)
2279 LoadLevelTemplate(-1);
2282 LoadLevel_InitVersion(&level, filename);
2283 LoadLevel_InitElements(&level, filename);
2284 LoadLevel_InitPlayfield(&level, filename);
2286 LoadLevel_InitLevel(&level, filename);
2290 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2292 putFileVersion(file, level->file_version);
2293 putFileVersion(file, level->game_version);
2296 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2300 putFile8Bit(file, level->fieldx);
2301 putFile8Bit(file, level->fieldy);
2303 putFile16BitBE(file, level->time);
2304 putFile16BitBE(file, level->gems_needed);
2306 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2307 putFile8Bit(file, level->name[i]);
2309 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2310 putFile8Bit(file, level->score[i]);
2312 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2313 for (y = 0; y < 3; y++)
2314 for (x = 0; x < 3; x++)
2315 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2316 level->yamyam_content[i][x][y]));
2317 putFile8Bit(file, level->amoeba_speed);
2318 putFile8Bit(file, level->time_magic_wall);
2319 putFile8Bit(file, level->time_wheel);
2320 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2321 level->amoeba_content));
2322 putFile8Bit(file, (level->double_speed ? 1 : 0));
2323 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2324 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2325 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2327 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2329 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2330 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2332 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2334 putFile16BitBE(file, level->can_move_into_acid);
2336 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2339 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2343 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2344 putFile8Bit(file, level->author[i]);
2347 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2351 for (y = 0; y < level->fieldy; y++)
2352 for (x = 0; x < level->fieldx; x++)
2353 if (level->encoding_16bit_field)
2354 putFile16BitBE(file, level->field[x][y]);
2356 putFile8Bit(file, level->field[x][y]);
2360 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2364 putFile8Bit(file, EL_YAMYAM);
2365 putFile8Bit(file, level->num_yamyam_contents);
2366 putFile8Bit(file, 0);
2367 putFile8Bit(file, 0);
2369 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2370 for (y = 0; y < 3; y++)
2371 for (x = 0; x < 3; x++)
2372 if (level->encoding_16bit_field)
2373 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2375 putFile8Bit(file, level->yamyam_content[i][x][y]);
2379 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2382 int num_contents, content_xsize, content_ysize;
2383 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2385 if (element == EL_YAMYAM)
2387 num_contents = level->num_yamyam_contents;
2391 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2392 for (y = 0; y < 3; y++)
2393 for (x = 0; x < 3; x++)
2394 content_array[i][x][y] = level->yamyam_content[i][x][y];
2396 else if (element == EL_BD_AMOEBA)
2402 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2403 for (y = 0; y < 3; y++)
2404 for (x = 0; x < 3; x++)
2405 content_array[i][x][y] = EL_EMPTY;
2406 content_array[0][0][0] = level->amoeba_content;
2410 /* chunk header already written -- write empty chunk data */
2411 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2413 Error(ERR_WARN, "cannot save content for element '%d'", element);
2417 putFile16BitBE(file, element);
2418 putFile8Bit(file, num_contents);
2419 putFile8Bit(file, content_xsize);
2420 putFile8Bit(file, content_ysize);
2422 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2424 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2425 for (y = 0; y < 3; y++)
2426 for (x = 0; x < 3; x++)
2427 putFile16BitBE(file, content_array[i][x][y]);
2430 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2433 int envelope_nr = element - EL_ENVELOPE_1;
2434 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2436 putFile16BitBE(file, element);
2437 putFile16BitBE(file, envelope_len);
2438 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2439 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2441 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2443 for (i = 0; i < envelope_len; i++)
2444 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2448 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2449 int num_changed_custom_elements)
2453 putFile16BitBE(file, num_changed_custom_elements);
2455 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2457 int element = EL_CUSTOM_START + i;
2459 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2461 if (check < num_changed_custom_elements)
2463 putFile16BitBE(file, element);
2464 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2471 if (check != num_changed_custom_elements) /* should not happen */
2472 Error(ERR_WARN, "inconsistent number of custom element properties");
2477 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2478 int num_changed_custom_elements)
2482 putFile16BitBE(file, num_changed_custom_elements);
2484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2486 int element = EL_CUSTOM_START + i;
2488 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2490 if (check < num_changed_custom_elements)
2492 putFile16BitBE(file, element);
2493 putFile16BitBE(file, element_info[element].change->target_element);
2500 if (check != num_changed_custom_elements) /* should not happen */
2501 Error(ERR_WARN, "inconsistent number of custom target elements");
2506 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2507 int num_changed_custom_elements)
2509 int i, j, x, y, check = 0;
2511 putFile16BitBE(file, num_changed_custom_elements);
2513 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2515 int element = EL_CUSTOM_START + i;
2517 if (element_info[element].modified_settings)
2519 if (check < num_changed_custom_elements)
2521 putFile16BitBE(file, element);
2523 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2524 putFile8Bit(file, element_info[element].description[j]);
2526 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2528 /* some free bytes for future properties and padding */
2529 WriteUnusedBytesToFile(file, 7);
2531 putFile8Bit(file, element_info[element].use_gfx_element);
2532 putFile16BitBE(file, element_info[element].gfx_element);
2534 putFile8Bit(file, element_info[element].collect_score);
2535 putFile8Bit(file, element_info[element].collect_count);
2537 putFile16BitBE(file, element_info[element].push_delay_fixed);
2538 putFile16BitBE(file, element_info[element].push_delay_random);
2539 putFile16BitBE(file, element_info[element].move_delay_fixed);
2540 putFile16BitBE(file, element_info[element].move_delay_random);
2542 putFile16BitBE(file, element_info[element].move_pattern);
2543 putFile8Bit(file, element_info[element].move_direction_initial);
2544 putFile8Bit(file, element_info[element].move_stepsize);
2546 for (y = 0; y < 3; y++)
2547 for (x = 0; x < 3; x++)
2548 putFile16BitBE(file, element_info[element].content[x][y]);
2550 putFile32BitBE(file, element_info[element].change->events);
2552 putFile16BitBE(file, element_info[element].change->target_element);
2554 putFile16BitBE(file, element_info[element].change->delay_fixed);
2555 putFile16BitBE(file, element_info[element].change->delay_random);
2556 putFile16BitBE(file, element_info[element].change->delay_frames);
2558 putFile16BitBE(file, element_info[element].change->trigger_element);
2560 putFile8Bit(file, element_info[element].change->explode);
2561 putFile8Bit(file, element_info[element].change->use_content);
2562 putFile8Bit(file, element_info[element].change->only_complete);
2563 putFile8Bit(file, element_info[element].change->use_random_change);
2565 putFile8Bit(file, element_info[element].change->random);
2566 putFile8Bit(file, element_info[element].change->power);
2568 for (y = 0; y < 3; y++)
2569 for (x = 0; x < 3; x++)
2570 putFile16BitBE(file, element_info[element].change->content[x][y]);
2572 putFile8Bit(file, element_info[element].slippery_type);
2574 /* some free bytes for future properties and padding */
2575 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2582 if (check != num_changed_custom_elements) /* should not happen */
2583 Error(ERR_WARN, "inconsistent number of custom element properties");
2587 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2589 struct ElementInfo *ei = &element_info[element];
2592 putFile16BitBE(file, element);
2594 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2595 putFile8Bit(file, ei->description[i]);
2597 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2598 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2600 putFile8Bit(file, ei->num_change_pages);
2602 /* some free bytes for future base property values and padding */
2603 WriteUnusedBytesToFile(file, 5);
2605 /* write custom property values */
2607 putFile8Bit(file, ei->use_gfx_element);
2608 putFile16BitBE(file, ei->gfx_element);
2610 putFile8Bit(file, ei->collect_score);
2611 putFile8Bit(file, ei->collect_count);
2613 putFile16BitBE(file, ei->push_delay_fixed);
2614 putFile16BitBE(file, ei->push_delay_random);
2615 putFile16BitBE(file, ei->move_delay_fixed);
2616 putFile16BitBE(file, ei->move_delay_random);
2618 /* bits 0 - 15 of "move_pattern" ... */
2619 putFile16BitBE(file, ei->move_pattern & 0xffff);
2620 putFile8Bit(file, ei->move_direction_initial);
2621 putFile8Bit(file, ei->move_stepsize);
2623 putFile8Bit(file, ei->slippery_type);
2625 for (y = 0; y < 3; y++)
2626 for (x = 0; x < 3; x++)
2627 putFile16BitBE(file, ei->content[x][y]);
2629 putFile16BitBE(file, ei->move_enter_element);
2630 putFile16BitBE(file, ei->move_leave_element);
2631 putFile8Bit(file, ei->move_leave_type);
2633 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2634 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2636 putFile8Bit(file, ei->access_direction);
2638 putFile8Bit(file, ei->explosion_delay);
2639 putFile8Bit(file, ei->ignition_delay);
2641 /* some free bytes for future custom property values and padding */
2642 WriteUnusedBytesToFile(file, 2);
2644 /* write change property values */
2646 for (i = 0; i < ei->num_change_pages; i++)
2648 struct ElementChangeInfo *change = &ei->change_page[i];
2650 putFile32BitBE(file, change->events);
2652 putFile16BitBE(file, change->target_element);
2654 putFile16BitBE(file, change->delay_fixed);
2655 putFile16BitBE(file, change->delay_random);
2656 putFile16BitBE(file, change->delay_frames);
2658 putFile16BitBE(file, change->trigger_element);
2660 putFile8Bit(file, change->explode);
2661 putFile8Bit(file, change->use_content);
2662 putFile8Bit(file, change->only_complete);
2663 putFile8Bit(file, change->use_random_change);
2665 putFile8Bit(file, change->random);
2666 putFile8Bit(file, change->power);
2668 for (y = 0; y < 3; y++)
2669 for (x = 0; x < 3; x++)
2670 putFile16BitBE(file, change->content[x][y]);
2672 putFile8Bit(file, change->can_change);
2674 putFile8Bit(file, change->sides);
2676 /* some free bytes for future change property values and padding */
2677 WriteUnusedBytesToFile(file, 8);
2681 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2683 struct ElementInfo *ei = &element_info[element];
2684 struct ElementGroupInfo *group = ei->group;
2687 putFile16BitBE(file, element);
2689 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2690 putFile8Bit(file, ei->description[i]);
2692 putFile8Bit(file, group->num_elements);
2694 putFile8Bit(file, ei->use_gfx_element);
2695 putFile16BitBE(file, ei->gfx_element);
2697 putFile8Bit(file, group->choice_mode);
2699 /* some free bytes for future values and padding */
2700 WriteUnusedBytesToFile(file, 3);
2702 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2703 putFile16BitBE(file, group->element[i]);
2706 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2708 int body_chunk_size;
2712 if (!(file = fopen(filename, MODE_WRITE)))
2714 Error(ERR_WARN, "cannot save level file '%s'", filename);
2718 level->file_version = FILE_VERSION_ACTUAL;
2719 level->game_version = GAME_VERSION_ACTUAL;
2721 /* check level field for 16-bit elements */
2722 level->encoding_16bit_field = FALSE;
2723 for (y = 0; y < level->fieldy; y++)
2724 for (x = 0; x < level->fieldx; x++)
2725 if (level->field[x][y] > 255)
2726 level->encoding_16bit_field = TRUE;
2728 /* check yamyam content for 16-bit elements */
2729 level->encoding_16bit_yamyam = FALSE;
2730 for (i = 0; i < level->num_yamyam_contents; i++)
2731 for (y = 0; y < 3; y++)
2732 for (x = 0; x < 3; x++)
2733 if (level->yamyam_content[i][x][y] > 255)
2734 level->encoding_16bit_yamyam = TRUE;
2736 /* check amoeba content for 16-bit elements */
2737 level->encoding_16bit_amoeba = FALSE;
2738 if (level->amoeba_content > 255)
2739 level->encoding_16bit_amoeba = TRUE;
2741 /* calculate size of "BODY" chunk */
2743 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2745 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2746 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2748 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2749 SaveLevel_VERS(file, level);
2751 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2752 SaveLevel_HEAD(file, level);
2754 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2755 SaveLevel_AUTH(file, level);
2757 putFileChunkBE(file, "BODY", body_chunk_size);
2758 SaveLevel_BODY(file, level);
2760 if (level->encoding_16bit_yamyam ||
2761 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2763 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2764 SaveLevel_CNT2(file, level, EL_YAMYAM);
2767 if (level->encoding_16bit_amoeba)
2769 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2770 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2773 /* check for envelope content */
2774 for (i = 0; i < 4; i++)
2776 if (strlen(level->envelope_text[i]) > 0)
2778 int envelope_len = strlen(level->envelope_text[i]) + 1;
2780 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2781 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2785 /* check for non-default custom elements (unless using template level) */
2786 if (!level->use_custom_template)
2788 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2790 int element = EL_CUSTOM_START + i;
2792 if (element_info[element].modified_settings)
2794 int num_change_pages = element_info[element].num_change_pages;
2796 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2797 SaveLevel_CUS4(file, level, element);
2802 /* check for non-default group elements (unless using template level) */
2803 if (!level->use_custom_template)
2805 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2807 int element = EL_GROUP_START + i;
2809 if (element_info[element].modified_settings)
2811 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2812 SaveLevel_GRP1(file, level, element);
2819 SetFilePermissions(filename, PERMS_PRIVATE);
2822 void SaveLevel(int nr)
2824 char *filename = getDefaultLevelFilename(nr);
2826 SaveLevelFromFilename(&level, filename);
2829 void SaveLevelTemplate()
2831 char *filename = getDefaultLevelFilename(-1);
2833 SaveLevelFromFilename(&level, filename);
2836 void DumpLevel(struct LevelInfo *level)
2838 printf_line("-", 79);
2839 printf("Level xxx (file version %08d, game version %08d)\n",
2840 level->file_version, level->game_version);
2841 printf_line("-", 79);
2843 printf("Level author: '%s'\n", level->author);
2844 printf("Level title: '%s'\n", level->name);
2846 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2848 printf("Level time: %d seconds\n", level->time);
2849 printf("Gems needed: %d\n", level->gems_needed);
2851 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2852 printf("Time for wheel: %d seconds\n", level->time_wheel);
2853 printf("Time for light: %d seconds\n", level->time_light);
2854 printf("Time for timegate: %d seconds\n", level->time_timegate);
2856 printf("Amoeba speed: %d\n", level->amoeba_speed);
2858 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2859 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2860 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2861 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2862 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2863 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
2865 printf_line("-", 79);
2869 /* ========================================================================= */
2870 /* tape file functions */
2871 /* ========================================================================= */
2873 static void setTapeInfoToDefaults()
2877 /* always start with reliable default values (empty tape) */
2880 /* default values (also for pre-1.2 tapes) with only the first player */
2881 tape.player_participates[0] = TRUE;
2882 for (i = 1; i < MAX_PLAYERS; i++)
2883 tape.player_participates[i] = FALSE;
2885 /* at least one (default: the first) player participates in every tape */
2886 tape.num_participating_players = 1;
2888 tape.level_nr = level_nr;
2890 tape.changed = FALSE;
2892 tape.recording = FALSE;
2893 tape.playing = FALSE;
2894 tape.pausing = FALSE;
2897 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2899 tape->file_version = getFileVersion(file);
2900 tape->game_version = getFileVersion(file);
2905 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2909 tape->random_seed = getFile32BitBE(file);
2910 tape->date = getFile32BitBE(file);
2911 tape->length = getFile32BitBE(file);
2913 /* read header fields that are new since version 1.2 */
2914 if (tape->file_version >= FILE_VERSION_1_2)
2916 byte store_participating_players = getFile8Bit(file);
2919 /* since version 1.2, tapes store which players participate in the tape */
2920 tape->num_participating_players = 0;
2921 for (i = 0; i < MAX_PLAYERS; i++)
2923 tape->player_participates[i] = FALSE;
2925 if (store_participating_players & (1 << i))
2927 tape->player_participates[i] = TRUE;
2928 tape->num_participating_players++;
2932 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2934 engine_version = getFileVersion(file);
2935 if (engine_version > 0)
2936 tape->engine_version = engine_version;
2938 tape->engine_version = tape->game_version;
2944 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2946 int level_identifier_size;
2949 level_identifier_size = getFile16BitBE(file);
2951 tape->level_identifier =
2952 checked_realloc(tape->level_identifier, level_identifier_size);
2954 for (i = 0; i < level_identifier_size; i++)
2955 tape->level_identifier[i] = getFile8Bit(file);
2957 tape->level_nr = getFile16BitBE(file);
2959 chunk_size = 2 + level_identifier_size + 2;
2964 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2967 int chunk_size_expected =
2968 (tape->num_participating_players + 1) * tape->length;
2970 if (chunk_size_expected != chunk_size)
2972 ReadUnusedBytesFromFile(file, chunk_size);
2973 return chunk_size_expected;
2976 for (i = 0; i < tape->length; i++)
2978 if (i >= MAX_TAPELEN)
2981 for (j = 0; j < MAX_PLAYERS; j++)
2983 tape->pos[i].action[j] = MV_NO_MOVING;
2985 if (tape->player_participates[j])
2986 tape->pos[i].action[j] = getFile8Bit(file);
2989 tape->pos[i].delay = getFile8Bit(file);
2991 if (tape->file_version == FILE_VERSION_1_0)
2993 /* eliminate possible diagonal moves in old tapes */
2994 /* this is only for backward compatibility */
2996 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2997 byte action = tape->pos[i].action[0];
2998 int k, num_moves = 0;
3000 for (k = 0; k<4; k++)
3002 if (action & joy_dir[k])
3004 tape->pos[i + num_moves].action[0] = joy_dir[k];
3006 tape->pos[i + num_moves].delay = 0;
3015 tape->length += num_moves;
3018 else if (tape->file_version < FILE_VERSION_2_0)
3020 /* convert pre-2.0 tapes to new tape format */
3022 if (tape->pos[i].delay > 1)
3025 tape->pos[i + 1] = tape->pos[i];
3026 tape->pos[i + 1].delay = 1;
3029 for (j = 0; j < MAX_PLAYERS; j++)
3030 tape->pos[i].action[j] = MV_NO_MOVING;
3031 tape->pos[i].delay--;
3042 if (i != tape->length)
3043 chunk_size = (tape->num_participating_players + 1) * i;
3048 void LoadTapeFromFilename(char *filename)
3050 char cookie[MAX_LINE_LEN];
3051 char chunk_name[CHUNK_ID_LEN + 1];
3055 /* always start with reliable default values */
3056 setTapeInfoToDefaults();
3058 if (!(file = fopen(filename, MODE_READ)))
3061 getFileChunkBE(file, chunk_name, NULL);
3062 if (strcmp(chunk_name, "RND1") == 0)
3064 getFile32BitBE(file); /* not used */
3066 getFileChunkBE(file, chunk_name, NULL);
3067 if (strcmp(chunk_name, "TAPE") != 0)
3069 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3074 else /* check for pre-2.0 file format with cookie string */
3076 strcpy(cookie, chunk_name);
3077 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3078 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3079 cookie[strlen(cookie) - 1] = '\0';
3081 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3083 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3088 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3090 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3095 /* pre-2.0 tape files have no game version, so use file version here */
3096 tape.game_version = tape.file_version;
3099 if (tape.file_version < FILE_VERSION_1_2)
3101 /* tape files from versions before 1.2.0 without chunk structure */
3102 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3103 LoadTape_BODY(file, 2 * tape.length, &tape);
3111 int (*loader)(FILE *, int, struct TapeInfo *);
3115 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3116 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3117 { "INFO", -1, LoadTape_INFO },
3118 { "BODY", -1, LoadTape_BODY },
3122 while (getFileChunkBE(file, chunk_name, &chunk_size))
3126 while (chunk_info[i].name != NULL &&
3127 strcmp(chunk_name, chunk_info[i].name) != 0)
3130 if (chunk_info[i].name == NULL)
3132 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3133 chunk_name, filename);
3134 ReadUnusedBytesFromFile(file, chunk_size);
3136 else if (chunk_info[i].size != -1 &&
3137 chunk_info[i].size != chunk_size)
3139 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3140 chunk_size, chunk_name, filename);
3141 ReadUnusedBytesFromFile(file, chunk_size);
3145 /* call function to load this tape chunk */
3146 int chunk_size_expected =
3147 (chunk_info[i].loader)(file, chunk_size, &tape);
3149 /* the size of some chunks cannot be checked before reading other
3150 chunks first (like "HEAD" and "BODY") that contain some header
3151 information, so check them here */
3152 if (chunk_size_expected != chunk_size)
3154 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3155 chunk_size, chunk_name, filename);
3163 tape.length_seconds = GetTapeLength();
3166 printf("::: tape game version: %d\n", tape.game_version);
3167 printf("::: tape engine version: %d\n", tape.engine_version);
3171 void LoadTape(int nr)
3173 char *filename = getTapeFilename(nr);
3175 LoadTapeFromFilename(filename);
3178 void LoadSolutionTape(int nr)
3180 char *filename = getSolutionTapeFilename(nr);
3182 LoadTapeFromFilename(filename);
3185 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3187 putFileVersion(file, tape->file_version);
3188 putFileVersion(file, tape->game_version);
3191 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3194 byte store_participating_players = 0;
3196 /* set bits for participating players for compact storage */
3197 for (i = 0; i < MAX_PLAYERS; i++)
3198 if (tape->player_participates[i])
3199 store_participating_players |= (1 << i);
3201 putFile32BitBE(file, tape->random_seed);
3202 putFile32BitBE(file, tape->date);
3203 putFile32BitBE(file, tape->length);
3205 putFile8Bit(file, store_participating_players);
3207 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3208 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3210 putFileVersion(file, tape->engine_version);
3213 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3215 int level_identifier_size = strlen(tape->level_identifier) + 1;
3218 putFile16BitBE(file, level_identifier_size);
3220 for (i = 0; i < level_identifier_size; i++)
3221 putFile8Bit(file, tape->level_identifier[i]);
3223 putFile16BitBE(file, tape->level_nr);
3226 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3230 for (i = 0; i < tape->length; i++)
3232 for (j = 0; j < MAX_PLAYERS; j++)
3233 if (tape->player_participates[j])
3234 putFile8Bit(file, tape->pos[i].action[j]);
3236 putFile8Bit(file, tape->pos[i].delay);
3240 void SaveTape(int nr)
3242 char *filename = getTapeFilename(nr);
3244 boolean new_tape = TRUE;
3245 int num_participating_players = 0;
3246 int info_chunk_size;
3247 int body_chunk_size;
3250 InitTapeDirectory(leveldir_current->subdir);
3252 /* if a tape still exists, ask to overwrite it */
3253 if (access(filename, F_OK) == 0)
3256 if (!Request("Replace old tape ?", REQ_ASK))
3260 if (!(file = fopen(filename, MODE_WRITE)))
3262 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3266 tape.file_version = FILE_VERSION_ACTUAL;
3267 tape.game_version = GAME_VERSION_ACTUAL;
3269 /* count number of participating players */
3270 for (i = 0; i < MAX_PLAYERS; i++)
3271 if (tape.player_participates[i])
3272 num_participating_players++;
3274 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3275 body_chunk_size = (num_participating_players + 1) * tape.length;
3277 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3278 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3280 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3281 SaveTape_VERS(file, &tape);
3283 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3284 SaveTape_HEAD(file, &tape);
3286 putFileChunkBE(file, "INFO", info_chunk_size);
3287 SaveTape_INFO(file, &tape);
3289 putFileChunkBE(file, "BODY", body_chunk_size);
3290 SaveTape_BODY(file, &tape);
3294 SetFilePermissions(filename, PERMS_PRIVATE);
3296 tape.changed = FALSE;
3299 Request("tape saved !", REQ_CONFIRM);
3302 void DumpTape(struct TapeInfo *tape)
3306 if (TAPE_IS_EMPTY(*tape))
3308 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3312 printf_line("-", 79);
3313 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3314 tape->level_nr, tape->file_version, tape->game_version);
3315 printf("Level series identifier: '%s'\n", tape->level_identifier);
3316 printf_line("-", 79);
3318 for (i = 0; i < tape->length; i++)
3320 if (i >= MAX_TAPELEN)
3323 printf("%03d: ", i);
3325 for (j = 0; j < MAX_PLAYERS; j++)
3327 if (tape->player_participates[j])
3329 int action = tape->pos[i].action[j];
3331 printf("%d:%02x ", j, action);
3332 printf("[%c%c%c%c|%c%c] - ",
3333 (action & JOY_LEFT ? '<' : ' '),
3334 (action & JOY_RIGHT ? '>' : ' '),
3335 (action & JOY_UP ? '^' : ' '),
3336 (action & JOY_DOWN ? 'v' : ' '),
3337 (action & JOY_BUTTON_1 ? '1' : ' '),
3338 (action & JOY_BUTTON_2 ? '2' : ' '));
3342 printf("(%03d)\n", tape->pos[i].delay);
3345 printf_line("-", 79);
3349 /* ========================================================================= */
3350 /* score file functions */
3351 /* ========================================================================= */
3353 void LoadScore(int nr)
3356 char *filename = getScoreFilename(nr);
3357 char cookie[MAX_LINE_LEN];
3358 char line[MAX_LINE_LEN];
3362 /* always start with reliable default values */
3363 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3365 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3366 highscore[i].Score = 0;
3369 if (!(file = fopen(filename, MODE_READ)))
3372 /* check file identifier */
3373 fgets(cookie, MAX_LINE_LEN, file);
3374 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3375 cookie[strlen(cookie) - 1] = '\0';
3377 if (!checkCookieString(cookie, SCORE_COOKIE))
3379 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3384 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3386 fscanf(file, "%d", &highscore[i].Score);
3387 fgets(line, MAX_LINE_LEN, file);
3389 if (line[strlen(line) - 1] == '\n')
3390 line[strlen(line) - 1] = '\0';
3392 for (line_ptr = line; *line_ptr; line_ptr++)
3394 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3396 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3397 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3406 void SaveScore(int nr)
3409 char *filename = getScoreFilename(nr);
3412 InitScoreDirectory(leveldir_current->subdir);
3414 if (!(file = fopen(filename, MODE_WRITE)))
3416 Error(ERR_WARN, "cannot save score for level %d", nr);
3420 fprintf(file, "%s\n\n", SCORE_COOKIE);
3422 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3423 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3427 SetFilePermissions(filename, PERMS_PUBLIC);
3431 /* ========================================================================= */
3432 /* setup file functions */
3433 /* ========================================================================= */
3435 #define TOKEN_STR_PLAYER_PREFIX "player_"
3438 #define SETUP_TOKEN_PLAYER_NAME 0
3439 #define SETUP_TOKEN_SOUND 1
3440 #define SETUP_TOKEN_SOUND_LOOPS 2
3441 #define SETUP_TOKEN_SOUND_MUSIC 3
3442 #define SETUP_TOKEN_SOUND_SIMPLE 4
3443 #define SETUP_TOKEN_TOONS 5
3444 #define SETUP_TOKEN_SCROLL_DELAY 6
3445 #define SETUP_TOKEN_SOFT_SCROLLING 7
3446 #define SETUP_TOKEN_FADING 8
3447 #define SETUP_TOKEN_AUTORECORD 9
3448 #define SETUP_TOKEN_QUICK_DOORS 10
3449 #define SETUP_TOKEN_TEAM_MODE 11
3450 #define SETUP_TOKEN_HANDICAP 12
3451 #define SETUP_TOKEN_TIME_LIMIT 13
3452 #define SETUP_TOKEN_FULLSCREEN 14
3453 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3454 #define SETUP_TOKEN_GRAPHICS_SET 16
3455 #define SETUP_TOKEN_SOUNDS_SET 17
3456 #define SETUP_TOKEN_MUSIC_SET 18
3457 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3458 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3459 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3461 #define NUM_GLOBAL_SETUP_TOKENS 22
3464 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3465 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3466 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3467 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3468 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3469 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3470 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3471 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3472 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3473 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3474 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3475 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3477 #define NUM_EDITOR_SETUP_TOKENS 12
3479 /* shortcut setup */
3480 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3481 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3482 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3484 #define NUM_SHORTCUT_SETUP_TOKENS 3
3487 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3488 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3489 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3490 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3491 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3492 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3493 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3494 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3495 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3496 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3497 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3498 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3499 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3500 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3501 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3502 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3504 #define NUM_PLAYER_SETUP_TOKENS 16
3507 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3508 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3510 #define NUM_SYSTEM_SETUP_TOKENS 2
3513 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3515 #define NUM_OPTIONS_SETUP_TOKENS 1
3518 static struct SetupInfo si;
3519 static struct SetupEditorInfo sei;
3520 static struct SetupShortcutInfo ssi;
3521 static struct SetupInputInfo sii;
3522 static struct SetupSystemInfo syi;
3523 static struct OptionInfo soi;
3525 static struct TokenInfo global_setup_tokens[] =
3527 { TYPE_STRING, &si.player_name, "player_name" },
3528 { TYPE_SWITCH, &si.sound, "sound" },
3529 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3530 { TYPE_SWITCH, &si.sound_music, "background_music" },
3531 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3532 { TYPE_SWITCH, &si.toons, "toons" },
3533 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3534 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3535 { TYPE_SWITCH, &si.fading, "screen_fading" },
3536 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3537 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3538 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3539 { TYPE_SWITCH, &si.handicap, "handicap" },
3540 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3541 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3542 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3543 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3544 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3545 { TYPE_STRING, &si.music_set, "music_set" },
3546 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3547 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3548 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3551 static struct TokenInfo editor_setup_tokens[] =
3553 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3554 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3555 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3556 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3557 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3558 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3559 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3560 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3561 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3562 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3563 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3564 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3567 static struct TokenInfo shortcut_setup_tokens[] =
3569 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3570 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3571 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3574 static struct TokenInfo player_setup_tokens[] =
3576 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3577 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3578 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3579 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3580 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3581 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3582 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3583 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3584 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3585 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3586 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3587 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3588 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3589 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3590 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3591 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3594 static struct TokenInfo system_setup_tokens[] =
3596 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3597 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3600 static struct TokenInfo options_setup_tokens[] =
3602 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3605 static char *get_corrected_login_name(char *login_name)
3607 /* needed because player name must be a fixed length string */
3608 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3610 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3611 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3613 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3614 if (strchr(login_name_new, ' '))
3615 *strchr(login_name_new, ' ') = '\0';
3617 return login_name_new;
3620 static void setSetupInfoToDefaults(struct SetupInfo *si)
3624 si->player_name = get_corrected_login_name(getLoginName());
3627 si->sound_loops = TRUE;
3628 si->sound_music = TRUE;
3629 si->sound_simple = TRUE;
3631 si->double_buffering = TRUE;
3632 si->direct_draw = !si->double_buffering;
3633 si->scroll_delay = TRUE;
3634 si->soft_scrolling = TRUE;
3636 si->autorecord = TRUE;
3637 si->quick_doors = FALSE;
3638 si->team_mode = FALSE;
3639 si->handicap = TRUE;
3640 si->time_limit = TRUE;
3641 si->fullscreen = FALSE;
3642 si->ask_on_escape = TRUE;
3644 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3645 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3646 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3647 si->override_level_graphics = FALSE;
3648 si->override_level_sounds = FALSE;
3649 si->override_level_music = FALSE;
3651 si->editor.el_boulderdash = TRUE;
3652 si->editor.el_emerald_mine = TRUE;
3653 si->editor.el_more = TRUE;
3654 si->editor.el_sokoban = TRUE;
3655 si->editor.el_supaplex = TRUE;
3656 si->editor.el_diamond_caves = TRUE;
3657 si->editor.el_dx_boulderdash = TRUE;
3658 si->editor.el_chars = TRUE;
3659 si->editor.el_custom = TRUE;
3660 si->editor.el_custom_more = FALSE;
3662 si->editor.el_headlines = TRUE;
3663 si->editor.el_user_defined = FALSE;
3665 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3666 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3667 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3669 for (i = 0; i < MAX_PLAYERS; i++)
3671 si->input[i].use_joystick = FALSE;
3672 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3673 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3674 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3675 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3676 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3677 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3678 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3679 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3680 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3681 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3682 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3683 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3684 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3685 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3686 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3689 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3690 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3692 si->options.verbose = FALSE;
3695 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3699 if (!setup_file_hash)
3704 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3705 setSetupInfo(global_setup_tokens, i,
3706 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3711 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3712 setSetupInfo(editor_setup_tokens, i,
3713 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3716 /* shortcut setup */
3717 ssi = setup.shortcut;
3718 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3719 setSetupInfo(shortcut_setup_tokens, i,
3720 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3721 setup.shortcut = ssi;
3724 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3728 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3730 sii = setup.input[pnr];
3731 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3733 char full_token[100];
3735 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3736 setSetupInfo(player_setup_tokens, i,
3737 getHashEntry(setup_file_hash, full_token));
3739 setup.input[pnr] = sii;
3744 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3745 setSetupInfo(system_setup_tokens, i,
3746 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3750 soi = setup.options;
3751 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3752 setSetupInfo(options_setup_tokens, i,
3753 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3754 setup.options = soi;
3759 char *filename = getSetupFilename();
3760 SetupFileHash *setup_file_hash = NULL;
3762 /* always start with reliable default values */
3763 setSetupInfoToDefaults(&setup);
3765 setup_file_hash = loadSetupFileHash(filename);
3767 if (setup_file_hash)
3769 char *player_name_new;
3771 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3772 decodeSetupFileHash(setup_file_hash);
3774 setup.direct_draw = !setup.double_buffering;
3776 freeSetupFileHash(setup_file_hash);
3778 /* needed to work around problems with fixed length strings */
3779 player_name_new = get_corrected_login_name(setup.player_name);
3780 free(setup.player_name);
3781 setup.player_name = player_name_new;
3784 Error(ERR_WARN, "using default setup values");
3789 char *filename = getSetupFilename();
3793 InitUserDataDirectory();
3795 if (!(file = fopen(filename, MODE_WRITE)))
3797 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3801 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3802 getCookie("SETUP")));
3803 fprintf(file, "\n");
3807 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3809 /* just to make things nicer :) */
3810 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3811 i == SETUP_TOKEN_GRAPHICS_SET)
3812 fprintf(file, "\n");
3814 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3819 fprintf(file, "\n");
3820 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3821 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3823 /* shortcut setup */
3824 ssi = setup.shortcut;
3825 fprintf(file, "\n");
3826 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3827 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3830 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3834 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3835 fprintf(file, "\n");
3837 sii = setup.input[pnr];
3838 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3839 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3844 fprintf(file, "\n");
3845 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3846 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3849 soi = setup.options;
3850 fprintf(file, "\n");
3851 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3852 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3856 SetFilePermissions(filename, PERMS_PRIVATE);
3859 void LoadCustomElementDescriptions()
3861 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3862 SetupFileHash *setup_file_hash;
3865 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3867 if (element_info[i].custom_description != NULL)
3869 free(element_info[i].custom_description);
3870 element_info[i].custom_description = NULL;
3874 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3877 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3879 char *token = getStringCat2(element_info[i].token_name, ".name");
3880 char *value = getHashEntry(setup_file_hash, token);
3883 element_info[i].custom_description = getStringCopy(value);
3888 freeSetupFileHash(setup_file_hash);
3891 void LoadSpecialMenuDesignSettings()
3893 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3894 SetupFileHash *setup_file_hash;
3897 /* always start with reliable default values from default config */
3898 for (i = 0; image_config_vars[i].token != NULL; i++)
3899 for (j = 0; image_config[j].token != NULL; j++)
3900 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3901 *image_config_vars[i].value =
3902 get_auto_parameter_value(image_config_vars[i].token,
3903 image_config[j].value);
3905 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3908 /* special case: initialize with default values that may be overwritten */
3909 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3911 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3912 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3913 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3915 if (value_x != NULL)
3916 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3917 if (value_y != NULL)
3918 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3919 if (list_size != NULL)
3920 menu.list_size[i] = get_integer_from_string(list_size);
3923 /* read (and overwrite with) values that may be specified in config file */
3924 for (i = 0; image_config_vars[i].token != NULL; i++)
3926 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3929 *image_config_vars[i].value =
3930 get_auto_parameter_value(image_config_vars[i].token, value);
3933 freeSetupFileHash(setup_file_hash);
3936 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3938 char *filename = getEditorSetupFilename();
3939 SetupFileList *setup_file_list, *list;
3940 SetupFileHash *element_hash;
3941 int num_unknown_tokens = 0;
3944 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3947 element_hash = newSetupFileHash();
3949 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3950 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3952 /* determined size may be larger than needed (due to unknown elements) */
3954 for (list = setup_file_list; list != NULL; list = list->next)
3957 /* add space for up to 3 more elements for padding that may be needed */
3960 *elements = checked_malloc(*num_elements * sizeof(int));
3963 for (list = setup_file_list; list != NULL; list = list->next)
3965 char *value = getHashEntry(element_hash, list->token);
3969 (*elements)[(*num_elements)++] = atoi(value);
3973 if (num_unknown_tokens == 0)
3975 Error(ERR_RETURN_LINE, "-");
3976 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3977 Error(ERR_RETURN, "- config file: '%s'", filename);
3979 num_unknown_tokens++;
3982 Error(ERR_RETURN, "- token: '%s'", list->token);
3986 if (num_unknown_tokens > 0)
3987 Error(ERR_RETURN_LINE, "-");
3989 while (*num_elements % 4) /* pad with empty elements, if needed */
3990 (*elements)[(*num_elements)++] = EL_EMPTY;
3992 freeSetupFileList(setup_file_list);
3993 freeSetupFileHash(element_hash);
3997 for (i = 0; i < *num_elements; i++)
3998 printf("editor: element '%s' [%d]\n",
3999 element_info[(*elements)[i]].token_name, (*elements)[i]);
4003 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4006 SetupFileHash *setup_file_hash = NULL;
4007 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4008 char *filename_music, *filename_prefix, *filename_info;
4014 token_to_value_ptr[] =
4016 { "title_header", &tmp_music_file_info.title_header },
4017 { "artist_header", &tmp_music_file_info.artist_header },
4018 { "album_header", &tmp_music_file_info.album_header },
4019 { "year_header", &tmp_music_file_info.year_header },
4021 { "title", &tmp_music_file_info.title },
4022 { "artist", &tmp_music_file_info.artist },
4023 { "album", &tmp_music_file_info.album },
4024 { "year", &tmp_music_file_info.year },
4030 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4031 getCustomMusicFilename(basename));
4033 if (filename_music == NULL)
4036 /* ---------- try to replace file extension ---------- */
4038 filename_prefix = getStringCopy(filename_music);
4039 if (strrchr(filename_prefix, '.') != NULL)
4040 *strrchr(filename_prefix, '.') = '\0';
4041 filename_info = getStringCat2(filename_prefix, ".txt");
4044 printf("trying to load file '%s'...\n", filename_info);
4047 if (fileExists(filename_info))
4048 setup_file_hash = loadSetupFileHash(filename_info);
4050 free(filename_prefix);
4051 free(filename_info);
4053 if (setup_file_hash == NULL)
4055 /* ---------- try to add file extension ---------- */
4057 filename_prefix = getStringCopy(filename_music);
4058 filename_info = getStringCat2(filename_prefix, ".txt");
4061 printf("trying to load file '%s'...\n", filename_info);
4064 if (fileExists(filename_info))
4065 setup_file_hash = loadSetupFileHash(filename_info);
4067 free(filename_prefix);
4068 free(filename_info);
4071 if (setup_file_hash == NULL)
4074 /* ---------- music file info found ---------- */
4076 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4078 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4080 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4082 *token_to_value_ptr[i].value_ptr =
4083 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4086 tmp_music_file_info.basename = getStringCopy(basename);
4087 tmp_music_file_info.music = music;
4088 tmp_music_file_info.is_sound = is_sound;
4090 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4091 *new_music_file_info = tmp_music_file_info;
4093 return new_music_file_info;
4096 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4098 return get_music_file_info_ext(basename, music, FALSE);
4101 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4103 return get_music_file_info_ext(basename, sound, TRUE);
4106 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4107 char *basename, boolean is_sound)
4109 for (; list != NULL; list = list->next)
4110 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4116 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4118 return music_info_listed_ext(list, basename, FALSE);
4121 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4123 return music_info_listed_ext(list, basename, TRUE);
4126 void LoadMusicInfo()
4128 char *music_directory = getCustomMusicDirectory();
4129 int num_music = getMusicListSize();
4130 int num_music_noconf = 0;
4131 int num_sounds = getSoundListSize();
4133 struct dirent *dir_entry;
4134 struct FileInfo *music, *sound;
4135 struct MusicFileInfo *next, **new;
4138 while (music_file_info != NULL)
4140 next = music_file_info->next;
4142 checked_free(music_file_info->basename);
4144 checked_free(music_file_info->title_header);
4145 checked_free(music_file_info->artist_header);
4146 checked_free(music_file_info->album_header);
4147 checked_free(music_file_info->year_header);
4149 checked_free(music_file_info->title);
4150 checked_free(music_file_info->artist);
4151 checked_free(music_file_info->album);
4152 checked_free(music_file_info->year);
4154 free(music_file_info);
4156 music_file_info = next;
4159 new = &music_file_info;
4162 printf("::: num_music == %d\n", num_music);
4165 for (i = 0; i < num_music; i++)
4167 music = getMusicListEntry(i);
4170 printf("::: %d [%08x]\n", i, music->filename);
4173 if (music->filename == NULL)
4176 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4179 /* a configured file may be not recognized as music */
4180 if (!FileIsMusic(music->filename))
4184 printf("::: -> '%s' (configured)\n", music->filename);
4187 if (!music_info_listed(music_file_info, music->filename))
4189 *new = get_music_file_info(music->filename, i);
4191 new = &(*new)->next;
4195 if ((dir = opendir(music_directory)) == NULL)
4197 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4201 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4203 char *basename = dir_entry->d_name;
4204 boolean music_already_used = FALSE;
4207 /* skip all music files that are configured in music config file */
4208 for (i = 0; i < num_music; i++)
4210 music = getMusicListEntry(i);
4212 if (music->filename == NULL)
4215 if (strcmp(basename, music->filename) == 0)
4217 music_already_used = TRUE;
4222 if (music_already_used)
4225 if (!FileIsMusic(basename))
4229 printf("::: -> '%s' (found in directory)\n", basename);
4232 if (!music_info_listed(music_file_info, basename))
4234 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4236 new = &(*new)->next;
4244 for (i = 0; i < num_sounds; i++)
4246 sound = getSoundListEntry(i);
4248 if (sound->filename == NULL)
4251 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4254 /* a configured file may be not recognized as sound */
4255 if (!FileIsSound(sound->filename))
4259 printf("::: -> '%s' (configured)\n", sound->filename);
4262 if (!sound_info_listed(music_file_info, sound->filename))
4264 *new = get_sound_file_info(sound->filename, i);
4266 new = &(*new)->next;
4272 for (next = music_file_info; next != NULL; next = next->next)
4273 printf("::: title == '%s'\n", next->title);
4277 void add_helpanim_entry(int element, int action, int direction, int delay,
4278 int *num_list_entries)
4280 struct HelpAnimInfo *new_list_entry;
4281 (*num_list_entries)++;
4284 checked_realloc(helpanim_info,
4285 *num_list_entries * sizeof(struct HelpAnimInfo));
4286 new_list_entry = &helpanim_info[*num_list_entries - 1];
4288 new_list_entry->element = element;
4289 new_list_entry->action = action;
4290 new_list_entry->direction = direction;
4291 new_list_entry->delay = delay;
4294 void print_unknown_token(char *filename, char *token, int token_nr)
4298 Error(ERR_RETURN_LINE, "-");
4299 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4300 Error(ERR_RETURN, "- config file: '%s'", filename);
4303 Error(ERR_RETURN, "- token: '%s'", token);
4306 void print_unknown_token_end(int token_nr)
4309 Error(ERR_RETURN_LINE, "-");
4312 void LoadHelpAnimInfo()
4314 char *filename = getHelpAnimFilename();
4315 SetupFileList *setup_file_list = NULL, *list;
4316 SetupFileHash *element_hash, *action_hash, *direction_hash;
4317 int num_list_entries = 0;
4318 int num_unknown_tokens = 0;
4321 if (fileExists(filename))
4322 setup_file_list = loadSetupFileList(filename);
4324 if (setup_file_list == NULL)
4326 /* use reliable default values from static configuration */
4327 SetupFileList *insert_ptr;
4329 insert_ptr = setup_file_list =
4330 newSetupFileList(helpanim_config[0].token,
4331 helpanim_config[0].value);
4333 for (i = 1; helpanim_config[i].token; i++)
4334 insert_ptr = addListEntry(insert_ptr,
4335 helpanim_config[i].token,
4336 helpanim_config[i].value);
4339 element_hash = newSetupFileHash();
4340 action_hash = newSetupFileHash();
4341 direction_hash = newSetupFileHash();
4343 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4344 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4346 for (i = 0; i < NUM_ACTIONS; i++)
4347 setHashEntry(action_hash, element_action_info[i].suffix,
4348 i_to_a(element_action_info[i].value));
4350 /* do not store direction index (bit) here, but direction value! */
4351 for (i = 0; i < NUM_DIRECTIONS; i++)
4352 setHashEntry(direction_hash, element_direction_info[i].suffix,
4353 i_to_a(1 << element_direction_info[i].value));
4355 for (list = setup_file_list; list != NULL; list = list->next)
4357 char *element_token, *action_token, *direction_token;
4358 char *element_value, *action_value, *direction_value;
4359 int delay = atoi(list->value);
4361 if (strcmp(list->token, "end") == 0)
4363 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4368 /* first try to break element into element/action/direction parts;
4369 if this does not work, also accept combined "element[.act][.dir]"
4370 elements (like "dynamite.active"), which are unique elements */
4372 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4374 element_value = getHashEntry(element_hash, list->token);
4375 if (element_value != NULL) /* element found */
4376 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4380 /* no further suffixes found -- this is not an element */
4381 print_unknown_token(filename, list->token, num_unknown_tokens++);
4387 /* token has format "<prefix>.<something>" */
4389 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4390 direction_token = action_token; /* ... or direction */
4392 element_token = getStringCopy(list->token);
4393 *strchr(element_token, '.') = '\0';
4395 element_value = getHashEntry(element_hash, element_token);
4397 if (element_value == NULL) /* this is no element */
4399 element_value = getHashEntry(element_hash, list->token);
4400 if (element_value != NULL) /* combined element found */
4401 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4404 print_unknown_token(filename, list->token, num_unknown_tokens++);
4406 free(element_token);
4411 action_value = getHashEntry(action_hash, action_token);
4413 if (action_value != NULL) /* action found */
4415 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4418 free(element_token);
4423 direction_value = getHashEntry(direction_hash, direction_token);
4425 if (direction_value != NULL) /* direction found */
4427 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4430 free(element_token);
4435 if (strchr(action_token + 1, '.') == NULL)
4437 /* no further suffixes found -- this is not an action nor direction */
4439 element_value = getHashEntry(element_hash, list->token);
4440 if (element_value != NULL) /* combined element found */
4441 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4444 print_unknown_token(filename, list->token, num_unknown_tokens++);
4446 free(element_token);
4451 /* token has format "<prefix>.<suffix>.<something>" */
4453 direction_token = strchr(action_token + 1, '.');
4455 action_token = getStringCopy(action_token);
4456 *strchr(action_token + 1, '.') = '\0';
4458 action_value = getHashEntry(action_hash, action_token);
4460 if (action_value == NULL) /* this is no action */
4462 element_value = getHashEntry(element_hash, list->token);
4463 if (element_value != NULL) /* combined element found */
4464 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4467 print_unknown_token(filename, list->token, num_unknown_tokens++);
4469 free(element_token);
4475 direction_value = getHashEntry(direction_hash, direction_token);
4477 if (direction_value != NULL) /* direction found */
4479 add_helpanim_entry(atoi(element_value), atoi(action_value),
4480 atoi(direction_value), delay, &num_list_entries);
4482 free(element_token);
4488 /* this is no direction */
4490 element_value = getHashEntry(element_hash, list->token);
4491 if (element_value != NULL) /* combined element found */
4492 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4495 print_unknown_token(filename, list->token, num_unknown_tokens++);
4497 free(element_token);
4501 print_unknown_token_end(num_unknown_tokens);
4503 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4504 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4506 freeSetupFileList(setup_file_list);
4507 freeSetupFileHash(element_hash);
4508 freeSetupFileHash(action_hash);
4509 freeSetupFileHash(direction_hash);
4513 for (i = 0; i < num_list_entries; i++)
4514 printf("::: %d, %d, %d => %d\n",
4515 helpanim_info[i].element,
4516 helpanim_info[i].action,
4517 helpanim_info[i].direction,
4518 helpanim_info[i].delay);
4522 void LoadHelpTextInfo()
4524 char *filename = getHelpTextFilename();
4527 if (helptext_info != NULL)
4529 freeSetupFileHash(helptext_info);
4530 helptext_info = NULL;
4533 if (fileExists(filename))
4534 helptext_info = loadSetupFileHash(filename);
4536 if (helptext_info == NULL)
4538 /* use reliable default values from static configuration */
4539 helptext_info = newSetupFileHash();
4541 for (i = 0; helptext_config[i].token; i++)
4542 setHashEntry(helptext_info,
4543 helptext_config[i].token,
4544 helptext_config[i].value);
4549 BEGIN_HASH_ITERATION(helptext_info, itr)
4551 printf("::: '%s' => '%s'\n",
4552 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4554 END_HASH_ITERATION(hash, itr)