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;
2162 /* set default push delay values (corrected since version 3.0.7-1) */
2163 if (level->game_version < VERSION_IDENT(3,0,7,1))
2165 game.default_push_delay_fixed = 2;
2166 game.default_push_delay_random = 8;
2170 game.default_push_delay_fixed = 8;
2171 game.default_push_delay_random = 8;
2174 /* set uninitialized push delay values of custom elements in older levels */
2175 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2177 int element = EL_CUSTOM_START + i;
2179 if (element_info[element].push_delay_fixed == -1)
2180 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2181 if (element_info[element].push_delay_random == -1)
2182 element_info[element].push_delay_random = game.default_push_delay_random;
2186 /* initialize element properties for level editor etc. */
2187 InitElementPropertiesEngine(level->game_version);
2190 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2194 /* map elements that have changed in newer versions */
2195 for (y = 0; y < level->fieldy; y++)
2197 for (x = 0; x < level->fieldx; x++)
2199 int element = level->field[x][y];
2201 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2203 /* map game font elements */
2204 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2205 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2206 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2207 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2210 if (level->game_version < VERSION_IDENT(3,0,0,0))
2212 /* map Supaplex gravity tube elements */
2213 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2214 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2215 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2216 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2220 level->field[x][y] = element;
2224 /* copy elements to runtime playfield array */
2225 for (x = 0; x < MAX_LEV_FIELDX; x++)
2226 for (y = 0; y < MAX_LEV_FIELDY; y++)
2227 Feld[x][y] = level->field[x][y];
2229 /* initialize level size variables for faster access */
2230 lev_fieldx = level->fieldx;
2231 lev_fieldy = level->fieldy;
2233 /* determine border element for this level */
2237 void LoadLevelTemplate(int nr)
2240 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2241 char *filename = level_file_info->filename;
2243 LoadLevelFromFileInfo(&level_template, level_file_info);
2245 char *filename = getDefaultLevelFilename(nr);
2247 LoadLevelFromFilename_RND(&level_template, filename);
2250 LoadLevel_InitVersion(&level, filename);
2251 LoadLevel_InitElements(&level, filename);
2253 ActivateLevelTemplate();
2256 void LoadLevel(int nr)
2259 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2260 char *filename = level_file_info->filename;
2262 LoadLevelFromFileInfo(&level, level_file_info);
2264 char *filename = getLevelFilename(nr);
2266 LoadLevelFromFilename_RND(&level, filename);
2269 if (level.use_custom_template)
2270 LoadLevelTemplate(-1);
2273 LoadLevel_InitVersion(&level, filename);
2274 LoadLevel_InitElements(&level, filename);
2275 LoadLevel_InitPlayfield(&level, filename);
2277 LoadLevel_InitLevel(&level, filename);
2281 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2283 putFileVersion(file, level->file_version);
2284 putFileVersion(file, level->game_version);
2287 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2291 putFile8Bit(file, level->fieldx);
2292 putFile8Bit(file, level->fieldy);
2294 putFile16BitBE(file, level->time);
2295 putFile16BitBE(file, level->gems_needed);
2297 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2298 putFile8Bit(file, level->name[i]);
2300 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2301 putFile8Bit(file, level->score[i]);
2303 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2304 for (y = 0; y < 3; y++)
2305 for (x = 0; x < 3; x++)
2306 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2307 level->yamyam_content[i][x][y]));
2308 putFile8Bit(file, level->amoeba_speed);
2309 putFile8Bit(file, level->time_magic_wall);
2310 putFile8Bit(file, level->time_wheel);
2311 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2312 level->amoeba_content));
2313 putFile8Bit(file, (level->double_speed ? 1 : 0));
2314 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2315 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2316 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2318 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2320 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2321 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2323 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2325 putFile16BitBE(file, level->can_move_into_acid);
2327 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2330 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2334 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2335 putFile8Bit(file, level->author[i]);
2338 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2342 for (y = 0; y < level->fieldy; y++)
2343 for (x = 0; x < level->fieldx; x++)
2344 if (level->encoding_16bit_field)
2345 putFile16BitBE(file, level->field[x][y]);
2347 putFile8Bit(file, level->field[x][y]);
2351 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2355 putFile8Bit(file, EL_YAMYAM);
2356 putFile8Bit(file, level->num_yamyam_contents);
2357 putFile8Bit(file, 0);
2358 putFile8Bit(file, 0);
2360 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2361 for (y = 0; y < 3; y++)
2362 for (x = 0; x < 3; x++)
2363 if (level->encoding_16bit_field)
2364 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2366 putFile8Bit(file, level->yamyam_content[i][x][y]);
2370 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2373 int num_contents, content_xsize, content_ysize;
2374 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2376 if (element == EL_YAMYAM)
2378 num_contents = level->num_yamyam_contents;
2382 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2383 for (y = 0; y < 3; y++)
2384 for (x = 0; x < 3; x++)
2385 content_array[i][x][y] = level->yamyam_content[i][x][y];
2387 else if (element == EL_BD_AMOEBA)
2393 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2394 for (y = 0; y < 3; y++)
2395 for (x = 0; x < 3; x++)
2396 content_array[i][x][y] = EL_EMPTY;
2397 content_array[0][0][0] = level->amoeba_content;
2401 /* chunk header already written -- write empty chunk data */
2402 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2404 Error(ERR_WARN, "cannot save content for element '%d'", element);
2408 putFile16BitBE(file, element);
2409 putFile8Bit(file, num_contents);
2410 putFile8Bit(file, content_xsize);
2411 putFile8Bit(file, content_ysize);
2413 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2415 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2416 for (y = 0; y < 3; y++)
2417 for (x = 0; x < 3; x++)
2418 putFile16BitBE(file, content_array[i][x][y]);
2421 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2424 int envelope_nr = element - EL_ENVELOPE_1;
2425 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2427 putFile16BitBE(file, element);
2428 putFile16BitBE(file, envelope_len);
2429 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2430 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2432 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2434 for (i = 0; i < envelope_len; i++)
2435 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2439 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2440 int num_changed_custom_elements)
2444 putFile16BitBE(file, num_changed_custom_elements);
2446 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2448 int element = EL_CUSTOM_START + i;
2450 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2452 if (check < num_changed_custom_elements)
2454 putFile16BitBE(file, element);
2455 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2462 if (check != num_changed_custom_elements) /* should not happen */
2463 Error(ERR_WARN, "inconsistent number of custom element properties");
2468 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2469 int num_changed_custom_elements)
2473 putFile16BitBE(file, num_changed_custom_elements);
2475 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2477 int element = EL_CUSTOM_START + i;
2479 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2481 if (check < num_changed_custom_elements)
2483 putFile16BitBE(file, element);
2484 putFile16BitBE(file, element_info[element].change->target_element);
2491 if (check != num_changed_custom_elements) /* should not happen */
2492 Error(ERR_WARN, "inconsistent number of custom target elements");
2497 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2498 int num_changed_custom_elements)
2500 int i, j, x, y, check = 0;
2502 putFile16BitBE(file, num_changed_custom_elements);
2504 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2506 int element = EL_CUSTOM_START + i;
2508 if (element_info[element].modified_settings)
2510 if (check < num_changed_custom_elements)
2512 putFile16BitBE(file, element);
2514 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2515 putFile8Bit(file, element_info[element].description[j]);
2517 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2519 /* some free bytes for future properties and padding */
2520 WriteUnusedBytesToFile(file, 7);
2522 putFile8Bit(file, element_info[element].use_gfx_element);
2523 putFile16BitBE(file, element_info[element].gfx_element);
2525 putFile8Bit(file, element_info[element].collect_score);
2526 putFile8Bit(file, element_info[element].collect_count);
2528 putFile16BitBE(file, element_info[element].push_delay_fixed);
2529 putFile16BitBE(file, element_info[element].push_delay_random);
2530 putFile16BitBE(file, element_info[element].move_delay_fixed);
2531 putFile16BitBE(file, element_info[element].move_delay_random);
2533 putFile16BitBE(file, element_info[element].move_pattern);
2534 putFile8Bit(file, element_info[element].move_direction_initial);
2535 putFile8Bit(file, element_info[element].move_stepsize);
2537 for (y = 0; y < 3; y++)
2538 for (x = 0; x < 3; x++)
2539 putFile16BitBE(file, element_info[element].content[x][y]);
2541 putFile32BitBE(file, element_info[element].change->events);
2543 putFile16BitBE(file, element_info[element].change->target_element);
2545 putFile16BitBE(file, element_info[element].change->delay_fixed);
2546 putFile16BitBE(file, element_info[element].change->delay_random);
2547 putFile16BitBE(file, element_info[element].change->delay_frames);
2549 putFile16BitBE(file, element_info[element].change->trigger_element);
2551 putFile8Bit(file, element_info[element].change->explode);
2552 putFile8Bit(file, element_info[element].change->use_content);
2553 putFile8Bit(file, element_info[element].change->only_complete);
2554 putFile8Bit(file, element_info[element].change->use_random_change);
2556 putFile8Bit(file, element_info[element].change->random);
2557 putFile8Bit(file, element_info[element].change->power);
2559 for (y = 0; y < 3; y++)
2560 for (x = 0; x < 3; x++)
2561 putFile16BitBE(file, element_info[element].change->content[x][y]);
2563 putFile8Bit(file, element_info[element].slippery_type);
2565 /* some free bytes for future properties and padding */
2566 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2573 if (check != num_changed_custom_elements) /* should not happen */
2574 Error(ERR_WARN, "inconsistent number of custom element properties");
2578 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2580 struct ElementInfo *ei = &element_info[element];
2583 putFile16BitBE(file, element);
2585 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2586 putFile8Bit(file, ei->description[i]);
2588 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2589 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2591 putFile8Bit(file, ei->num_change_pages);
2593 /* some free bytes for future base property values and padding */
2594 WriteUnusedBytesToFile(file, 5);
2596 /* write custom property values */
2598 putFile8Bit(file, ei->use_gfx_element);
2599 putFile16BitBE(file, ei->gfx_element);
2601 putFile8Bit(file, ei->collect_score);
2602 putFile8Bit(file, ei->collect_count);
2604 putFile16BitBE(file, ei->push_delay_fixed);
2605 putFile16BitBE(file, ei->push_delay_random);
2606 putFile16BitBE(file, ei->move_delay_fixed);
2607 putFile16BitBE(file, ei->move_delay_random);
2609 /* bits 0 - 15 of "move_pattern" ... */
2610 putFile16BitBE(file, ei->move_pattern & 0xffff);
2611 putFile8Bit(file, ei->move_direction_initial);
2612 putFile8Bit(file, ei->move_stepsize);
2614 putFile8Bit(file, ei->slippery_type);
2616 for (y = 0; y < 3; y++)
2617 for (x = 0; x < 3; x++)
2618 putFile16BitBE(file, ei->content[x][y]);
2620 putFile16BitBE(file, ei->move_enter_element);
2621 putFile16BitBE(file, ei->move_leave_element);
2622 putFile8Bit(file, ei->move_leave_type);
2624 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2625 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2627 putFile8Bit(file, ei->access_direction);
2629 putFile8Bit(file, ei->explosion_delay);
2630 putFile8Bit(file, ei->ignition_delay);
2632 /* some free bytes for future custom property values and padding */
2633 WriteUnusedBytesToFile(file, 2);
2635 /* write change property values */
2637 for (i = 0; i < ei->num_change_pages; i++)
2639 struct ElementChangeInfo *change = &ei->change_page[i];
2641 putFile32BitBE(file, change->events);
2643 putFile16BitBE(file, change->target_element);
2645 putFile16BitBE(file, change->delay_fixed);
2646 putFile16BitBE(file, change->delay_random);
2647 putFile16BitBE(file, change->delay_frames);
2649 putFile16BitBE(file, change->trigger_element);
2651 putFile8Bit(file, change->explode);
2652 putFile8Bit(file, change->use_content);
2653 putFile8Bit(file, change->only_complete);
2654 putFile8Bit(file, change->use_random_change);
2656 putFile8Bit(file, change->random);
2657 putFile8Bit(file, change->power);
2659 for (y = 0; y < 3; y++)
2660 for (x = 0; x < 3; x++)
2661 putFile16BitBE(file, change->content[x][y]);
2663 putFile8Bit(file, change->can_change);
2665 putFile8Bit(file, change->sides);
2667 /* some free bytes for future change property values and padding */
2668 WriteUnusedBytesToFile(file, 8);
2672 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2674 struct ElementInfo *ei = &element_info[element];
2675 struct ElementGroupInfo *group = ei->group;
2678 putFile16BitBE(file, element);
2680 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2681 putFile8Bit(file, ei->description[i]);
2683 putFile8Bit(file, group->num_elements);
2685 putFile8Bit(file, ei->use_gfx_element);
2686 putFile16BitBE(file, ei->gfx_element);
2688 putFile8Bit(file, group->choice_mode);
2690 /* some free bytes for future values and padding */
2691 WriteUnusedBytesToFile(file, 3);
2693 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2694 putFile16BitBE(file, group->element[i]);
2697 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2699 int body_chunk_size;
2703 if (!(file = fopen(filename, MODE_WRITE)))
2705 Error(ERR_WARN, "cannot save level file '%s'", filename);
2709 level->file_version = FILE_VERSION_ACTUAL;
2710 level->game_version = GAME_VERSION_ACTUAL;
2712 /* check level field for 16-bit elements */
2713 level->encoding_16bit_field = FALSE;
2714 for (y = 0; y < level->fieldy; y++)
2715 for (x = 0; x < level->fieldx; x++)
2716 if (level->field[x][y] > 255)
2717 level->encoding_16bit_field = TRUE;
2719 /* check yamyam content for 16-bit elements */
2720 level->encoding_16bit_yamyam = FALSE;
2721 for (i = 0; i < level->num_yamyam_contents; i++)
2722 for (y = 0; y < 3; y++)
2723 for (x = 0; x < 3; x++)
2724 if (level->yamyam_content[i][x][y] > 255)
2725 level->encoding_16bit_yamyam = TRUE;
2727 /* check amoeba content for 16-bit elements */
2728 level->encoding_16bit_amoeba = FALSE;
2729 if (level->amoeba_content > 255)
2730 level->encoding_16bit_amoeba = TRUE;
2732 /* calculate size of "BODY" chunk */
2734 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2736 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2737 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2739 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2740 SaveLevel_VERS(file, level);
2742 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2743 SaveLevel_HEAD(file, level);
2745 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2746 SaveLevel_AUTH(file, level);
2748 putFileChunkBE(file, "BODY", body_chunk_size);
2749 SaveLevel_BODY(file, level);
2751 if (level->encoding_16bit_yamyam ||
2752 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2754 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2755 SaveLevel_CNT2(file, level, EL_YAMYAM);
2758 if (level->encoding_16bit_amoeba)
2760 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2761 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2764 /* check for envelope content */
2765 for (i = 0; i < 4; i++)
2767 if (strlen(level->envelope_text[i]) > 0)
2769 int envelope_len = strlen(level->envelope_text[i]) + 1;
2771 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2772 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2776 /* check for non-default custom elements (unless using template level) */
2777 if (!level->use_custom_template)
2779 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2781 int element = EL_CUSTOM_START + i;
2783 if (element_info[element].modified_settings)
2785 int num_change_pages = element_info[element].num_change_pages;
2787 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2788 SaveLevel_CUS4(file, level, element);
2793 /* check for non-default group elements (unless using template level) */
2794 if (!level->use_custom_template)
2796 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2798 int element = EL_GROUP_START + i;
2800 if (element_info[element].modified_settings)
2802 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2803 SaveLevel_GRP1(file, level, element);
2810 SetFilePermissions(filename, PERMS_PRIVATE);
2813 void SaveLevel(int nr)
2815 char *filename = getDefaultLevelFilename(nr);
2817 SaveLevelFromFilename(&level, filename);
2820 void SaveLevelTemplate()
2822 char *filename = getDefaultLevelFilename(-1);
2824 SaveLevelFromFilename(&level, filename);
2827 void DumpLevel(struct LevelInfo *level)
2829 printf_line("-", 79);
2830 printf("Level xxx (file version %08d, game version %08d)\n",
2831 level->file_version, level->game_version);
2832 printf_line("-", 79);
2834 printf("Level author: '%s'\n", level->author);
2835 printf("Level title: '%s'\n", level->name);
2837 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2839 printf("Level time: %d seconds\n", level->time);
2840 printf("Gems needed: %d\n", level->gems_needed);
2842 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2843 printf("Time for wheel: %d seconds\n", level->time_wheel);
2844 printf("Time for light: %d seconds\n", level->time_light);
2845 printf("Time for timegate: %d seconds\n", level->time_timegate);
2847 printf("Amoeba speed: %d\n", level->amoeba_speed);
2849 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2850 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2851 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2852 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2853 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2854 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
2856 printf_line("-", 79);
2860 /* ========================================================================= */
2861 /* tape file functions */
2862 /* ========================================================================= */
2864 static void setTapeInfoToDefaults()
2868 /* always start with reliable default values (empty tape) */
2871 /* default values (also for pre-1.2 tapes) with only the first player */
2872 tape.player_participates[0] = TRUE;
2873 for (i = 1; i < MAX_PLAYERS; i++)
2874 tape.player_participates[i] = FALSE;
2876 /* at least one (default: the first) player participates in every tape */
2877 tape.num_participating_players = 1;
2879 tape.level_nr = level_nr;
2881 tape.changed = FALSE;
2883 tape.recording = FALSE;
2884 tape.playing = FALSE;
2885 tape.pausing = FALSE;
2888 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2890 tape->file_version = getFileVersion(file);
2891 tape->game_version = getFileVersion(file);
2896 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2900 tape->random_seed = getFile32BitBE(file);
2901 tape->date = getFile32BitBE(file);
2902 tape->length = getFile32BitBE(file);
2904 /* read header fields that are new since version 1.2 */
2905 if (tape->file_version >= FILE_VERSION_1_2)
2907 byte store_participating_players = getFile8Bit(file);
2910 /* since version 1.2, tapes store which players participate in the tape */
2911 tape->num_participating_players = 0;
2912 for (i = 0; i < MAX_PLAYERS; i++)
2914 tape->player_participates[i] = FALSE;
2916 if (store_participating_players & (1 << i))
2918 tape->player_participates[i] = TRUE;
2919 tape->num_participating_players++;
2923 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2925 engine_version = getFileVersion(file);
2926 if (engine_version > 0)
2927 tape->engine_version = engine_version;
2929 tape->engine_version = tape->game_version;
2935 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2937 int level_identifier_size;
2940 level_identifier_size = getFile16BitBE(file);
2942 tape->level_identifier =
2943 checked_realloc(tape->level_identifier, level_identifier_size);
2945 for (i = 0; i < level_identifier_size; i++)
2946 tape->level_identifier[i] = getFile8Bit(file);
2948 tape->level_nr = getFile16BitBE(file);
2950 chunk_size = 2 + level_identifier_size + 2;
2955 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2958 int chunk_size_expected =
2959 (tape->num_participating_players + 1) * tape->length;
2961 if (chunk_size_expected != chunk_size)
2963 ReadUnusedBytesFromFile(file, chunk_size);
2964 return chunk_size_expected;
2967 for (i = 0; i < tape->length; i++)
2969 if (i >= MAX_TAPELEN)
2972 for (j = 0; j < MAX_PLAYERS; j++)
2974 tape->pos[i].action[j] = MV_NO_MOVING;
2976 if (tape->player_participates[j])
2977 tape->pos[i].action[j] = getFile8Bit(file);
2980 tape->pos[i].delay = getFile8Bit(file);
2982 if (tape->file_version == FILE_VERSION_1_0)
2984 /* eliminate possible diagonal moves in old tapes */
2985 /* this is only for backward compatibility */
2987 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2988 byte action = tape->pos[i].action[0];
2989 int k, num_moves = 0;
2991 for (k = 0; k<4; k++)
2993 if (action & joy_dir[k])
2995 tape->pos[i + num_moves].action[0] = joy_dir[k];
2997 tape->pos[i + num_moves].delay = 0;
3006 tape->length += num_moves;
3009 else if (tape->file_version < FILE_VERSION_2_0)
3011 /* convert pre-2.0 tapes to new tape format */
3013 if (tape->pos[i].delay > 1)
3016 tape->pos[i + 1] = tape->pos[i];
3017 tape->pos[i + 1].delay = 1;
3020 for (j = 0; j < MAX_PLAYERS; j++)
3021 tape->pos[i].action[j] = MV_NO_MOVING;
3022 tape->pos[i].delay--;
3033 if (i != tape->length)
3034 chunk_size = (tape->num_participating_players + 1) * i;
3039 void LoadTapeFromFilename(char *filename)
3041 char cookie[MAX_LINE_LEN];
3042 char chunk_name[CHUNK_ID_LEN + 1];
3046 /* always start with reliable default values */
3047 setTapeInfoToDefaults();
3049 if (!(file = fopen(filename, MODE_READ)))
3052 getFileChunkBE(file, chunk_name, NULL);
3053 if (strcmp(chunk_name, "RND1") == 0)
3055 getFile32BitBE(file); /* not used */
3057 getFileChunkBE(file, chunk_name, NULL);
3058 if (strcmp(chunk_name, "TAPE") != 0)
3060 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3065 else /* check for pre-2.0 file format with cookie string */
3067 strcpy(cookie, chunk_name);
3068 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3069 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3070 cookie[strlen(cookie) - 1] = '\0';
3072 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3074 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3079 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3081 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3086 /* pre-2.0 tape files have no game version, so use file version here */
3087 tape.game_version = tape.file_version;
3090 if (tape.file_version < FILE_VERSION_1_2)
3092 /* tape files from versions before 1.2.0 without chunk structure */
3093 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3094 LoadTape_BODY(file, 2 * tape.length, &tape);
3102 int (*loader)(FILE *, int, struct TapeInfo *);
3106 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3107 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3108 { "INFO", -1, LoadTape_INFO },
3109 { "BODY", -1, LoadTape_BODY },
3113 while (getFileChunkBE(file, chunk_name, &chunk_size))
3117 while (chunk_info[i].name != NULL &&
3118 strcmp(chunk_name, chunk_info[i].name) != 0)
3121 if (chunk_info[i].name == NULL)
3123 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3124 chunk_name, filename);
3125 ReadUnusedBytesFromFile(file, chunk_size);
3127 else if (chunk_info[i].size != -1 &&
3128 chunk_info[i].size != chunk_size)
3130 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3131 chunk_size, chunk_name, filename);
3132 ReadUnusedBytesFromFile(file, chunk_size);
3136 /* call function to load this tape chunk */
3137 int chunk_size_expected =
3138 (chunk_info[i].loader)(file, chunk_size, &tape);
3140 /* the size of some chunks cannot be checked before reading other
3141 chunks first (like "HEAD" and "BODY") that contain some header
3142 information, so check them here */
3143 if (chunk_size_expected != chunk_size)
3145 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3146 chunk_size, chunk_name, filename);
3154 tape.length_seconds = GetTapeLength();
3157 printf("::: tape game version: %d\n", tape.game_version);
3158 printf("::: tape engine version: %d\n", tape.engine_version);
3162 void LoadTape(int nr)
3164 char *filename = getTapeFilename(nr);
3166 LoadTapeFromFilename(filename);
3169 void LoadSolutionTape(int nr)
3171 char *filename = getSolutionTapeFilename(nr);
3173 LoadTapeFromFilename(filename);
3176 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3178 putFileVersion(file, tape->file_version);
3179 putFileVersion(file, tape->game_version);
3182 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3185 byte store_participating_players = 0;
3187 /* set bits for participating players for compact storage */
3188 for (i = 0; i < MAX_PLAYERS; i++)
3189 if (tape->player_participates[i])
3190 store_participating_players |= (1 << i);
3192 putFile32BitBE(file, tape->random_seed);
3193 putFile32BitBE(file, tape->date);
3194 putFile32BitBE(file, tape->length);
3196 putFile8Bit(file, store_participating_players);
3198 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3199 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3201 putFileVersion(file, tape->engine_version);
3204 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3206 int level_identifier_size = strlen(tape->level_identifier) + 1;
3209 putFile16BitBE(file, level_identifier_size);
3211 for (i = 0; i < level_identifier_size; i++)
3212 putFile8Bit(file, tape->level_identifier[i]);
3214 putFile16BitBE(file, tape->level_nr);
3217 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3221 for (i = 0; i < tape->length; i++)
3223 for (j = 0; j < MAX_PLAYERS; j++)
3224 if (tape->player_participates[j])
3225 putFile8Bit(file, tape->pos[i].action[j]);
3227 putFile8Bit(file, tape->pos[i].delay);
3231 void SaveTape(int nr)
3233 char *filename = getTapeFilename(nr);
3235 boolean new_tape = TRUE;
3236 int num_participating_players = 0;
3237 int info_chunk_size;
3238 int body_chunk_size;
3241 InitTapeDirectory(leveldir_current->subdir);
3243 /* if a tape still exists, ask to overwrite it */
3244 if (access(filename, F_OK) == 0)
3247 if (!Request("Replace old tape ?", REQ_ASK))
3251 if (!(file = fopen(filename, MODE_WRITE)))
3253 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3257 tape.file_version = FILE_VERSION_ACTUAL;
3258 tape.game_version = GAME_VERSION_ACTUAL;
3260 /* count number of participating players */
3261 for (i = 0; i < MAX_PLAYERS; i++)
3262 if (tape.player_participates[i])
3263 num_participating_players++;
3265 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3266 body_chunk_size = (num_participating_players + 1) * tape.length;
3268 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3269 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3271 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3272 SaveTape_VERS(file, &tape);
3274 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3275 SaveTape_HEAD(file, &tape);
3277 putFileChunkBE(file, "INFO", info_chunk_size);
3278 SaveTape_INFO(file, &tape);
3280 putFileChunkBE(file, "BODY", body_chunk_size);
3281 SaveTape_BODY(file, &tape);
3285 SetFilePermissions(filename, PERMS_PRIVATE);
3287 tape.changed = FALSE;
3290 Request("tape saved !", REQ_CONFIRM);
3293 void DumpTape(struct TapeInfo *tape)
3297 if (TAPE_IS_EMPTY(*tape))
3299 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3303 printf_line("-", 79);
3304 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3305 tape->level_nr, tape->file_version, tape->game_version);
3306 printf("Level series identifier: '%s'\n", tape->level_identifier);
3307 printf_line("-", 79);
3309 for (i = 0; i < tape->length; i++)
3311 if (i >= MAX_TAPELEN)
3314 printf("%03d: ", i);
3316 for (j = 0; j < MAX_PLAYERS; j++)
3318 if (tape->player_participates[j])
3320 int action = tape->pos[i].action[j];
3322 printf("%d:%02x ", j, action);
3323 printf("[%c%c%c%c|%c%c] - ",
3324 (action & JOY_LEFT ? '<' : ' '),
3325 (action & JOY_RIGHT ? '>' : ' '),
3326 (action & JOY_UP ? '^' : ' '),
3327 (action & JOY_DOWN ? 'v' : ' '),
3328 (action & JOY_BUTTON_1 ? '1' : ' '),
3329 (action & JOY_BUTTON_2 ? '2' : ' '));
3333 printf("(%03d)\n", tape->pos[i].delay);
3336 printf_line("-", 79);
3340 /* ========================================================================= */
3341 /* score file functions */
3342 /* ========================================================================= */
3344 void LoadScore(int nr)
3347 char *filename = getScoreFilename(nr);
3348 char cookie[MAX_LINE_LEN];
3349 char line[MAX_LINE_LEN];
3353 /* always start with reliable default values */
3354 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3356 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3357 highscore[i].Score = 0;
3360 if (!(file = fopen(filename, MODE_READ)))
3363 /* check file identifier */
3364 fgets(cookie, MAX_LINE_LEN, file);
3365 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3366 cookie[strlen(cookie) - 1] = '\0';
3368 if (!checkCookieString(cookie, SCORE_COOKIE))
3370 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3375 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3377 fscanf(file, "%d", &highscore[i].Score);
3378 fgets(line, MAX_LINE_LEN, file);
3380 if (line[strlen(line) - 1] == '\n')
3381 line[strlen(line) - 1] = '\0';
3383 for (line_ptr = line; *line_ptr; line_ptr++)
3385 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3387 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3388 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3397 void SaveScore(int nr)
3400 char *filename = getScoreFilename(nr);
3403 InitScoreDirectory(leveldir_current->subdir);
3405 if (!(file = fopen(filename, MODE_WRITE)))
3407 Error(ERR_WARN, "cannot save score for level %d", nr);
3411 fprintf(file, "%s\n\n", SCORE_COOKIE);
3413 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3414 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3418 SetFilePermissions(filename, PERMS_PUBLIC);
3422 /* ========================================================================= */
3423 /* setup file functions */
3424 /* ========================================================================= */
3426 #define TOKEN_STR_PLAYER_PREFIX "player_"
3429 #define SETUP_TOKEN_PLAYER_NAME 0
3430 #define SETUP_TOKEN_SOUND 1
3431 #define SETUP_TOKEN_SOUND_LOOPS 2
3432 #define SETUP_TOKEN_SOUND_MUSIC 3
3433 #define SETUP_TOKEN_SOUND_SIMPLE 4
3434 #define SETUP_TOKEN_TOONS 5
3435 #define SETUP_TOKEN_SCROLL_DELAY 6
3436 #define SETUP_TOKEN_SOFT_SCROLLING 7
3437 #define SETUP_TOKEN_FADING 8
3438 #define SETUP_TOKEN_AUTORECORD 9
3439 #define SETUP_TOKEN_QUICK_DOORS 10
3440 #define SETUP_TOKEN_TEAM_MODE 11
3441 #define SETUP_TOKEN_HANDICAP 12
3442 #define SETUP_TOKEN_TIME_LIMIT 13
3443 #define SETUP_TOKEN_FULLSCREEN 14
3444 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3445 #define SETUP_TOKEN_GRAPHICS_SET 16
3446 #define SETUP_TOKEN_SOUNDS_SET 17
3447 #define SETUP_TOKEN_MUSIC_SET 18
3448 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3449 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3450 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3452 #define NUM_GLOBAL_SETUP_TOKENS 22
3455 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3456 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3457 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3458 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3459 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3460 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3461 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3462 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3463 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3464 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3465 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3466 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3468 #define NUM_EDITOR_SETUP_TOKENS 12
3470 /* shortcut setup */
3471 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3472 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3473 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3475 #define NUM_SHORTCUT_SETUP_TOKENS 3
3478 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3479 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3480 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3481 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3482 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3483 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3484 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3485 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3486 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3487 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3488 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3489 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3490 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3491 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3492 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3493 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3495 #define NUM_PLAYER_SETUP_TOKENS 16
3498 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3499 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3501 #define NUM_SYSTEM_SETUP_TOKENS 2
3504 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3506 #define NUM_OPTIONS_SETUP_TOKENS 1
3509 static struct SetupInfo si;
3510 static struct SetupEditorInfo sei;
3511 static struct SetupShortcutInfo ssi;
3512 static struct SetupInputInfo sii;
3513 static struct SetupSystemInfo syi;
3514 static struct OptionInfo soi;
3516 static struct TokenInfo global_setup_tokens[] =
3518 { TYPE_STRING, &si.player_name, "player_name" },
3519 { TYPE_SWITCH, &si.sound, "sound" },
3520 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3521 { TYPE_SWITCH, &si.sound_music, "background_music" },
3522 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3523 { TYPE_SWITCH, &si.toons, "toons" },
3524 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3525 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3526 { TYPE_SWITCH, &si.fading, "screen_fading" },
3527 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3528 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3529 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3530 { TYPE_SWITCH, &si.handicap, "handicap" },
3531 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3532 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3533 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3534 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3535 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3536 { TYPE_STRING, &si.music_set, "music_set" },
3537 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3538 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3539 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3542 static struct TokenInfo editor_setup_tokens[] =
3544 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3545 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3546 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3547 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3548 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3549 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3550 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3551 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3552 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3553 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3554 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3555 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3558 static struct TokenInfo shortcut_setup_tokens[] =
3560 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3561 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3562 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3565 static struct TokenInfo player_setup_tokens[] =
3567 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3568 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3569 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3570 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3571 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3572 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3573 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3574 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3575 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3576 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3577 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3578 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3579 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3580 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3581 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3582 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3585 static struct TokenInfo system_setup_tokens[] =
3587 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3588 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3591 static struct TokenInfo options_setup_tokens[] =
3593 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3596 static char *get_corrected_login_name(char *login_name)
3598 /* needed because player name must be a fixed length string */
3599 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3601 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3602 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3604 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3605 if (strchr(login_name_new, ' '))
3606 *strchr(login_name_new, ' ') = '\0';
3608 return login_name_new;
3611 static void setSetupInfoToDefaults(struct SetupInfo *si)
3615 si->player_name = get_corrected_login_name(getLoginName());
3618 si->sound_loops = TRUE;
3619 si->sound_music = TRUE;
3620 si->sound_simple = TRUE;
3622 si->double_buffering = TRUE;
3623 si->direct_draw = !si->double_buffering;
3624 si->scroll_delay = TRUE;
3625 si->soft_scrolling = TRUE;
3627 si->autorecord = TRUE;
3628 si->quick_doors = FALSE;
3629 si->team_mode = FALSE;
3630 si->handicap = TRUE;
3631 si->time_limit = TRUE;
3632 si->fullscreen = FALSE;
3633 si->ask_on_escape = TRUE;
3635 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3636 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3637 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3638 si->override_level_graphics = FALSE;
3639 si->override_level_sounds = FALSE;
3640 si->override_level_music = FALSE;
3642 si->editor.el_boulderdash = TRUE;
3643 si->editor.el_emerald_mine = TRUE;
3644 si->editor.el_more = TRUE;
3645 si->editor.el_sokoban = TRUE;
3646 si->editor.el_supaplex = TRUE;
3647 si->editor.el_diamond_caves = TRUE;
3648 si->editor.el_dx_boulderdash = TRUE;
3649 si->editor.el_chars = TRUE;
3650 si->editor.el_custom = TRUE;
3651 si->editor.el_custom_more = FALSE;
3653 si->editor.el_headlines = TRUE;
3654 si->editor.el_user_defined = FALSE;
3656 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3657 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3658 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3660 for (i = 0; i < MAX_PLAYERS; i++)
3662 si->input[i].use_joystick = FALSE;
3663 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3664 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3665 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3666 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3667 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3668 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3669 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3670 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3671 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3672 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3673 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3674 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3675 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3676 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3677 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3680 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3681 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3683 si->options.verbose = FALSE;
3686 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3690 if (!setup_file_hash)
3695 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3696 setSetupInfo(global_setup_tokens, i,
3697 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3702 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3703 setSetupInfo(editor_setup_tokens, i,
3704 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3707 /* shortcut setup */
3708 ssi = setup.shortcut;
3709 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3710 setSetupInfo(shortcut_setup_tokens, i,
3711 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3712 setup.shortcut = ssi;
3715 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3719 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3721 sii = setup.input[pnr];
3722 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3724 char full_token[100];
3726 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3727 setSetupInfo(player_setup_tokens, i,
3728 getHashEntry(setup_file_hash, full_token));
3730 setup.input[pnr] = sii;
3735 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3736 setSetupInfo(system_setup_tokens, i,
3737 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3741 soi = setup.options;
3742 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3743 setSetupInfo(options_setup_tokens, i,
3744 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3745 setup.options = soi;
3750 char *filename = getSetupFilename();
3751 SetupFileHash *setup_file_hash = NULL;
3753 /* always start with reliable default values */
3754 setSetupInfoToDefaults(&setup);
3756 setup_file_hash = loadSetupFileHash(filename);
3758 if (setup_file_hash)
3760 char *player_name_new;
3762 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3763 decodeSetupFileHash(setup_file_hash);
3765 setup.direct_draw = !setup.double_buffering;
3767 freeSetupFileHash(setup_file_hash);
3769 /* needed to work around problems with fixed length strings */
3770 player_name_new = get_corrected_login_name(setup.player_name);
3771 free(setup.player_name);
3772 setup.player_name = player_name_new;
3775 Error(ERR_WARN, "using default setup values");
3780 char *filename = getSetupFilename();
3784 InitUserDataDirectory();
3786 if (!(file = fopen(filename, MODE_WRITE)))
3788 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3792 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3793 getCookie("SETUP")));
3794 fprintf(file, "\n");
3798 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3800 /* just to make things nicer :) */
3801 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3802 i == SETUP_TOKEN_GRAPHICS_SET)
3803 fprintf(file, "\n");
3805 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3810 fprintf(file, "\n");
3811 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3812 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3814 /* shortcut setup */
3815 ssi = setup.shortcut;
3816 fprintf(file, "\n");
3817 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3818 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3821 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3825 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3826 fprintf(file, "\n");
3828 sii = setup.input[pnr];
3829 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3830 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3835 fprintf(file, "\n");
3836 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3837 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3840 soi = setup.options;
3841 fprintf(file, "\n");
3842 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3843 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3847 SetFilePermissions(filename, PERMS_PRIVATE);
3850 void LoadCustomElementDescriptions()
3852 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3853 SetupFileHash *setup_file_hash;
3856 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3858 if (element_info[i].custom_description != NULL)
3860 free(element_info[i].custom_description);
3861 element_info[i].custom_description = NULL;
3865 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3868 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3870 char *token = getStringCat2(element_info[i].token_name, ".name");
3871 char *value = getHashEntry(setup_file_hash, token);
3874 element_info[i].custom_description = getStringCopy(value);
3879 freeSetupFileHash(setup_file_hash);
3882 void LoadSpecialMenuDesignSettings()
3884 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3885 SetupFileHash *setup_file_hash;
3888 /* always start with reliable default values from default config */
3889 for (i = 0; image_config_vars[i].token != NULL; i++)
3890 for (j = 0; image_config[j].token != NULL; j++)
3891 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3892 *image_config_vars[i].value =
3893 get_auto_parameter_value(image_config_vars[i].token,
3894 image_config[j].value);
3896 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3899 /* special case: initialize with default values that may be overwritten */
3900 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3902 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3903 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3904 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3906 if (value_x != NULL)
3907 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3908 if (value_y != NULL)
3909 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3910 if (list_size != NULL)
3911 menu.list_size[i] = get_integer_from_string(list_size);
3914 /* read (and overwrite with) values that may be specified in config file */
3915 for (i = 0; image_config_vars[i].token != NULL; i++)
3917 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3920 *image_config_vars[i].value =
3921 get_auto_parameter_value(image_config_vars[i].token, value);
3924 freeSetupFileHash(setup_file_hash);
3927 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3929 char *filename = getEditorSetupFilename();
3930 SetupFileList *setup_file_list, *list;
3931 SetupFileHash *element_hash;
3932 int num_unknown_tokens = 0;
3935 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3938 element_hash = newSetupFileHash();
3940 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3941 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3943 /* determined size may be larger than needed (due to unknown elements) */
3945 for (list = setup_file_list; list != NULL; list = list->next)
3948 /* add space for up to 3 more elements for padding that may be needed */
3951 *elements = checked_malloc(*num_elements * sizeof(int));
3954 for (list = setup_file_list; list != NULL; list = list->next)
3956 char *value = getHashEntry(element_hash, list->token);
3960 (*elements)[(*num_elements)++] = atoi(value);
3964 if (num_unknown_tokens == 0)
3966 Error(ERR_RETURN_LINE, "-");
3967 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3968 Error(ERR_RETURN, "- config file: '%s'", filename);
3970 num_unknown_tokens++;
3973 Error(ERR_RETURN, "- token: '%s'", list->token);
3977 if (num_unknown_tokens > 0)
3978 Error(ERR_RETURN_LINE, "-");
3980 while (*num_elements % 4) /* pad with empty elements, if needed */
3981 (*elements)[(*num_elements)++] = EL_EMPTY;
3983 freeSetupFileList(setup_file_list);
3984 freeSetupFileHash(element_hash);
3988 for (i = 0; i < *num_elements; i++)
3989 printf("editor: element '%s' [%d]\n",
3990 element_info[(*elements)[i]].token_name, (*elements)[i]);
3994 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3997 SetupFileHash *setup_file_hash = NULL;
3998 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3999 char *filename_music, *filename_prefix, *filename_info;
4005 token_to_value_ptr[] =
4007 { "title_header", &tmp_music_file_info.title_header },
4008 { "artist_header", &tmp_music_file_info.artist_header },
4009 { "album_header", &tmp_music_file_info.album_header },
4010 { "year_header", &tmp_music_file_info.year_header },
4012 { "title", &tmp_music_file_info.title },
4013 { "artist", &tmp_music_file_info.artist },
4014 { "album", &tmp_music_file_info.album },
4015 { "year", &tmp_music_file_info.year },
4021 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4022 getCustomMusicFilename(basename));
4024 if (filename_music == NULL)
4027 /* ---------- try to replace file extension ---------- */
4029 filename_prefix = getStringCopy(filename_music);
4030 if (strrchr(filename_prefix, '.') != NULL)
4031 *strrchr(filename_prefix, '.') = '\0';
4032 filename_info = getStringCat2(filename_prefix, ".txt");
4035 printf("trying to load file '%s'...\n", filename_info);
4038 if (fileExists(filename_info))
4039 setup_file_hash = loadSetupFileHash(filename_info);
4041 free(filename_prefix);
4042 free(filename_info);
4044 if (setup_file_hash == NULL)
4046 /* ---------- try to add file extension ---------- */
4048 filename_prefix = getStringCopy(filename_music);
4049 filename_info = getStringCat2(filename_prefix, ".txt");
4052 printf("trying to load file '%s'...\n", filename_info);
4055 if (fileExists(filename_info))
4056 setup_file_hash = loadSetupFileHash(filename_info);
4058 free(filename_prefix);
4059 free(filename_info);
4062 if (setup_file_hash == NULL)
4065 /* ---------- music file info found ---------- */
4067 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4069 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4071 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4073 *token_to_value_ptr[i].value_ptr =
4074 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4077 tmp_music_file_info.basename = getStringCopy(basename);
4078 tmp_music_file_info.music = music;
4079 tmp_music_file_info.is_sound = is_sound;
4081 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4082 *new_music_file_info = tmp_music_file_info;
4084 return new_music_file_info;
4087 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4089 return get_music_file_info_ext(basename, music, FALSE);
4092 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4094 return get_music_file_info_ext(basename, sound, TRUE);
4097 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4098 char *basename, boolean is_sound)
4100 for (; list != NULL; list = list->next)
4101 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4107 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4109 return music_info_listed_ext(list, basename, FALSE);
4112 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4114 return music_info_listed_ext(list, basename, TRUE);
4117 void LoadMusicInfo()
4119 char *music_directory = getCustomMusicDirectory();
4120 int num_music = getMusicListSize();
4121 int num_music_noconf = 0;
4122 int num_sounds = getSoundListSize();
4124 struct dirent *dir_entry;
4125 struct FileInfo *music, *sound;
4126 struct MusicFileInfo *next, **new;
4129 while (music_file_info != NULL)
4131 next = music_file_info->next;
4133 checked_free(music_file_info->basename);
4135 checked_free(music_file_info->title_header);
4136 checked_free(music_file_info->artist_header);
4137 checked_free(music_file_info->album_header);
4138 checked_free(music_file_info->year_header);
4140 checked_free(music_file_info->title);
4141 checked_free(music_file_info->artist);
4142 checked_free(music_file_info->album);
4143 checked_free(music_file_info->year);
4145 free(music_file_info);
4147 music_file_info = next;
4150 new = &music_file_info;
4153 printf("::: num_music == %d\n", num_music);
4156 for (i = 0; i < num_music; i++)
4158 music = getMusicListEntry(i);
4161 printf("::: %d [%08x]\n", i, music->filename);
4164 if (music->filename == NULL)
4167 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4170 /* a configured file may be not recognized as music */
4171 if (!FileIsMusic(music->filename))
4175 printf("::: -> '%s' (configured)\n", music->filename);
4178 if (!music_info_listed(music_file_info, music->filename))
4180 *new = get_music_file_info(music->filename, i);
4182 new = &(*new)->next;
4186 if ((dir = opendir(music_directory)) == NULL)
4188 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4192 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4194 char *basename = dir_entry->d_name;
4195 boolean music_already_used = FALSE;
4198 /* skip all music files that are configured in music config file */
4199 for (i = 0; i < num_music; i++)
4201 music = getMusicListEntry(i);
4203 if (music->filename == NULL)
4206 if (strcmp(basename, music->filename) == 0)
4208 music_already_used = TRUE;
4213 if (music_already_used)
4216 if (!FileIsMusic(basename))
4220 printf("::: -> '%s' (found in directory)\n", basename);
4223 if (!music_info_listed(music_file_info, basename))
4225 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4227 new = &(*new)->next;
4235 for (i = 0; i < num_sounds; i++)
4237 sound = getSoundListEntry(i);
4239 if (sound->filename == NULL)
4242 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4245 /* a configured file may be not recognized as sound */
4246 if (!FileIsSound(sound->filename))
4250 printf("::: -> '%s' (configured)\n", sound->filename);
4253 if (!sound_info_listed(music_file_info, sound->filename))
4255 *new = get_sound_file_info(sound->filename, i);
4257 new = &(*new)->next;
4263 for (next = music_file_info; next != NULL; next = next->next)
4264 printf("::: title == '%s'\n", next->title);
4268 void add_helpanim_entry(int element, int action, int direction, int delay,
4269 int *num_list_entries)
4271 struct HelpAnimInfo *new_list_entry;
4272 (*num_list_entries)++;
4275 checked_realloc(helpanim_info,
4276 *num_list_entries * sizeof(struct HelpAnimInfo));
4277 new_list_entry = &helpanim_info[*num_list_entries - 1];
4279 new_list_entry->element = element;
4280 new_list_entry->action = action;
4281 new_list_entry->direction = direction;
4282 new_list_entry->delay = delay;
4285 void print_unknown_token(char *filename, char *token, int token_nr)
4289 Error(ERR_RETURN_LINE, "-");
4290 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4291 Error(ERR_RETURN, "- config file: '%s'", filename);
4294 Error(ERR_RETURN, "- token: '%s'", token);
4297 void print_unknown_token_end(int token_nr)
4300 Error(ERR_RETURN_LINE, "-");
4303 void LoadHelpAnimInfo()
4305 char *filename = getHelpAnimFilename();
4306 SetupFileList *setup_file_list = NULL, *list;
4307 SetupFileHash *element_hash, *action_hash, *direction_hash;
4308 int num_list_entries = 0;
4309 int num_unknown_tokens = 0;
4312 if (fileExists(filename))
4313 setup_file_list = loadSetupFileList(filename);
4315 if (setup_file_list == NULL)
4317 /* use reliable default values from static configuration */
4318 SetupFileList *insert_ptr;
4320 insert_ptr = setup_file_list =
4321 newSetupFileList(helpanim_config[0].token,
4322 helpanim_config[0].value);
4324 for (i = 1; helpanim_config[i].token; i++)
4325 insert_ptr = addListEntry(insert_ptr,
4326 helpanim_config[i].token,
4327 helpanim_config[i].value);
4330 element_hash = newSetupFileHash();
4331 action_hash = newSetupFileHash();
4332 direction_hash = newSetupFileHash();
4334 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4335 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4337 for (i = 0; i < NUM_ACTIONS; i++)
4338 setHashEntry(action_hash, element_action_info[i].suffix,
4339 i_to_a(element_action_info[i].value));
4341 /* do not store direction index (bit) here, but direction value! */
4342 for (i = 0; i < NUM_DIRECTIONS; i++)
4343 setHashEntry(direction_hash, element_direction_info[i].suffix,
4344 i_to_a(1 << element_direction_info[i].value));
4346 for (list = setup_file_list; list != NULL; list = list->next)
4348 char *element_token, *action_token, *direction_token;
4349 char *element_value, *action_value, *direction_value;
4350 int delay = atoi(list->value);
4352 if (strcmp(list->token, "end") == 0)
4354 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4359 /* first try to break element into element/action/direction parts;
4360 if this does not work, also accept combined "element[.act][.dir]"
4361 elements (like "dynamite.active"), which are unique elements */
4363 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4365 element_value = getHashEntry(element_hash, list->token);
4366 if (element_value != NULL) /* element found */
4367 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4371 /* no further suffixes found -- this is not an element */
4372 print_unknown_token(filename, list->token, num_unknown_tokens++);
4378 /* token has format "<prefix>.<something>" */
4380 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4381 direction_token = action_token; /* ... or direction */
4383 element_token = getStringCopy(list->token);
4384 *strchr(element_token, '.') = '\0';
4386 element_value = getHashEntry(element_hash, element_token);
4388 if (element_value == NULL) /* this is no element */
4390 element_value = getHashEntry(element_hash, list->token);
4391 if (element_value != NULL) /* combined element found */
4392 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4395 print_unknown_token(filename, list->token, num_unknown_tokens++);
4397 free(element_token);
4402 action_value = getHashEntry(action_hash, action_token);
4404 if (action_value != NULL) /* action found */
4406 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4409 free(element_token);
4414 direction_value = getHashEntry(direction_hash, direction_token);
4416 if (direction_value != NULL) /* direction found */
4418 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4421 free(element_token);
4426 if (strchr(action_token + 1, '.') == NULL)
4428 /* no further suffixes found -- this is not an action nor direction */
4430 element_value = getHashEntry(element_hash, list->token);
4431 if (element_value != NULL) /* combined element found */
4432 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4435 print_unknown_token(filename, list->token, num_unknown_tokens++);
4437 free(element_token);
4442 /* token has format "<prefix>.<suffix>.<something>" */
4444 direction_token = strchr(action_token + 1, '.');
4446 action_token = getStringCopy(action_token);
4447 *strchr(action_token + 1, '.') = '\0';
4449 action_value = getHashEntry(action_hash, action_token);
4451 if (action_value == NULL) /* this is no action */
4453 element_value = getHashEntry(element_hash, list->token);
4454 if (element_value != NULL) /* combined element found */
4455 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4458 print_unknown_token(filename, list->token, num_unknown_tokens++);
4460 free(element_token);
4466 direction_value = getHashEntry(direction_hash, direction_token);
4468 if (direction_value != NULL) /* direction found */
4470 add_helpanim_entry(atoi(element_value), atoi(action_value),
4471 atoi(direction_value), delay, &num_list_entries);
4473 free(element_token);
4479 /* this is no direction */
4481 element_value = getHashEntry(element_hash, list->token);
4482 if (element_value != NULL) /* combined element found */
4483 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4486 print_unknown_token(filename, list->token, num_unknown_tokens++);
4488 free(element_token);
4492 print_unknown_token_end(num_unknown_tokens);
4494 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4495 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4497 freeSetupFileList(setup_file_list);
4498 freeSetupFileHash(element_hash);
4499 freeSetupFileHash(action_hash);
4500 freeSetupFileHash(direction_hash);
4504 for (i = 0; i < num_list_entries; i++)
4505 printf("::: %d, %d, %d => %d\n",
4506 helpanim_info[i].element,
4507 helpanim_info[i].action,
4508 helpanim_info[i].direction,
4509 helpanim_info[i].delay);
4513 void LoadHelpTextInfo()
4515 char *filename = getHelpTextFilename();
4518 if (helptext_info != NULL)
4520 freeSetupFileHash(helptext_info);
4521 helptext_info = NULL;
4524 if (fileExists(filename))
4525 helptext_info = loadSetupFileHash(filename);
4527 if (helptext_info == NULL)
4529 /* use reliable default values from static configuration */
4530 helptext_info = newSetupFileHash();
4532 for (i = 0; helptext_config[i].token; i++)
4533 setHashEntry(helptext_info,
4534 helptext_config[i].token,
4535 helptext_config[i].value);
4540 BEGIN_HASH_ITERATION(helptext_info, itr)
4542 printf("::: '%s' => '%s'\n",
4543 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4545 END_HASH_ITERATION(hash, itr)