1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 11 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
40 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
41 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
52 /* values for level file type identifier */
53 #define LEVEL_FILE_TYPE_UNKNOWN 0
54 #define LEVEL_FILE_TYPE_RND 1
55 #define LEVEL_FILE_TYPE_BD 2
56 #define LEVEL_FILE_TYPE_EM 3
57 #define LEVEL_FILE_TYPE_SP 4
58 #define LEVEL_FILE_TYPE_DX 5
59 #define LEVEL_FILE_TYPE_SB 6
60 #define LEVEL_FILE_TYPE_DC 7
62 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
63 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
65 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
66 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
69 /* ========================================================================= */
70 /* level file functions */
71 /* ========================================================================= */
73 void setElementChangePages(struct ElementInfo *ei, int change_pages)
75 int change_page_size = sizeof(struct ElementChangeInfo);
77 ei->num_change_pages = MAX(1, change_pages);
80 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
82 if (ei->current_change_page >= ei->num_change_pages)
83 ei->current_change_page = ei->num_change_pages - 1;
85 ei->change = &ei->change_page[ei->current_change_page];
88 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
92 change->can_change = FALSE;
94 change->events = CE_BITMASK_DEFAULT;
95 change->sides = CH_SIDE_ANY;
97 change->target_element = EL_EMPTY_SPACE;
99 change->delay_fixed = 0;
100 change->delay_random = 0;
101 change->delay_frames = 1;
103 change->trigger_element = EL_EMPTY_SPACE;
105 change->explode = FALSE;
106 change->use_content = FALSE;
107 change->only_complete = FALSE;
108 change->use_random_change = FALSE;
109 change->random = 100;
110 change->power = CP_NON_DESTRUCTIVE;
112 for (x = 0; x < 3; x++)
113 for (y = 0; y < 3; y++)
114 change->content[x][y] = EL_EMPTY_SPACE;
116 change->direct_action = 0;
117 change->other_action = 0;
119 change->pre_change_function = NULL;
120 change->change_function = NULL;
121 change->post_change_function = NULL;
124 static void setLevelInfoToDefaults(struct LevelInfo *level)
128 level->file_version = FILE_VERSION_ACTUAL;
129 level->game_version = GAME_VERSION_ACTUAL;
131 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
132 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
133 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
135 level->fieldx = STD_LEV_FIELDX;
136 level->fieldy = STD_LEV_FIELDY;
138 for (x = 0; x < MAX_LEV_FIELDX; x++)
139 for (y = 0; y < MAX_LEV_FIELDY; y++)
140 level->field[x][y] = EL_SAND;
143 level->gems_needed = 0;
144 level->amoeba_speed = 10;
145 level->time_magic_wall = 10;
146 level->time_wheel = 10;
147 level->time_light = 10;
148 level->time_timegate = 10;
149 level->amoeba_content = EL_DIAMOND;
150 level->double_speed = FALSE;
151 level->initial_gravity = FALSE;
152 level->em_slippery_gems = FALSE;
153 level->block_last_field = FALSE;
154 level->sp_block_last_field = TRUE;
156 level->use_custom_template = FALSE;
158 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
159 level->name[i] = '\0';
160 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
161 level->author[i] = '\0';
163 strcpy(level->name, NAMELESS_LEVEL_NAME);
164 strcpy(level->author, ANONYMOUS_NAME);
166 for (i = 0; i < 4; i++)
168 level->envelope_text[i][0] = '\0';
169 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
170 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
173 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
174 level->score[i] = 10;
176 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
177 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
178 for (x = 0; x < 3; x++)
179 for (y = 0; y < 3; y++)
180 level->yamyam_content[i][x][y] =
181 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
183 level->field[0][0] = EL_PLAYER_1;
184 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
186 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
190 setElementChangePages(&element_info[element], 1);
191 setElementChangeInfoToDefaults(element_info[element].change);
193 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
195 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
196 element_info[element].description[j] = '\0';
198 if (element_info[element].custom_description != NULL)
199 strncpy(element_info[element].description,
200 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
202 strcpy(element_info[element].description,
203 element_info[element].editor_description);
205 element_info[element].use_gfx_element = FALSE;
206 element_info[element].gfx_element = EL_EMPTY_SPACE;
209 if (IS_CUSTOM_ELEMENT(element))
211 element_info[element].collect_score = 10; /* special default */
212 element_info[element].collect_count = 1; /* special default */
214 element_info[element].push_delay_fixed = -1; /* initialize later */
215 element_info[element].push_delay_random = -1; /* initialize later */
216 element_info[element].move_delay_fixed = 0;
217 element_info[element].move_delay_random = 0;
219 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
220 element_info[element].move_direction_initial = MV_AUTOMATIC;
221 element_info[element].move_stepsize = TILEX / 8;
222 element_info[element].move_enter_element = EL_EMPTY_SPACE;
223 element_info[element].move_leave_element = EL_EMPTY_SPACE;
224 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
226 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
228 for (x = 0; x < 3; x++)
229 for (y = 0; y < 3; y++)
230 element_info[element].content[x][y] = EL_EMPTY_SPACE;
232 element_info[element].access_type = 0;
233 element_info[element].access_layer = 0;
234 element_info[element].walk_to_action = 0;
235 element_info[element].smash_targets = 0;
236 element_info[element].deadliness = 0;
237 element_info[element].consistency = 0;
239 element_info[element].can_explode_by_fire = FALSE;
240 element_info[element].can_explode_smashed = FALSE;
241 element_info[element].can_explode_impact = FALSE;
243 element_info[element].current_change_page = 0;
245 /* start with no properties at all */
246 for (j = 0; j < NUM_EP_BITFIELDS; j++)
247 Properties[element][j] = EP_BITMASK_DEFAULT;
249 element_info[element].modified_settings = FALSE;
251 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
253 /* initialize memory for list of elements in group */
254 if (element_info[element].group == NULL)
255 element_info[element].group =
256 checked_malloc(sizeof(struct ElementGroupInfo));
258 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
259 element_info[element].group->element[j] = EL_EMPTY_SPACE;
261 /* default: only one element in group */
262 element_info[element].group->num_elements = 1;
264 element_info[element].group->choice_mode = ANIM_RANDOM;
268 BorderElement = EL_STEELWALL;
270 level->no_level_file = FALSE;
272 if (leveldir_current == NULL) /* only when dumping level */
275 /* try to determine better author name than 'anonymous' */
276 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
278 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
279 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
283 switch (LEVELCLASS(leveldir_current))
285 case LEVELCLASS_TUTORIAL:
286 strcpy(level->author, PROGRAM_AUTHOR_STRING);
289 case LEVELCLASS_CONTRIB:
290 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
291 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
294 case LEVELCLASS_PRIVATE:
295 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
296 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
300 /* keep default value */
306 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
308 level_file_info->nr = 0;
309 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
310 level_file_info->packed = FALSE;
311 level_file_info->basename = NULL;
312 level_file_info->filename = NULL;
315 static void ActivateLevelTemplate()
317 /* Currently there is no special action needed to activate the template
318 data, because 'element_info' and 'Properties' overwrite the original
319 level data, while all other variables do not change. */
322 static char *getLevelFilenameFromBasename(char *basename)
324 static char *filename = NULL;
326 checked_free(filename);
328 filename = getPath2(getCurrentLevelDir(), basename);
333 static int getFileTypeFromBasename(char *basename)
335 static char *filename = NULL;
336 struct stat file_status;
338 /* ---------- try to determine file type from filename ---------- */
340 /* check for typical filename of a Supaplex level package file */
341 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
342 strncmp(basename, "LEVELS.D", 8) == 0))
343 return LEVEL_FILE_TYPE_SP;
345 /* ---------- try to determine file type from filesize ---------- */
347 checked_free(filename);
348 filename = getPath2(getCurrentLevelDir(), basename);
350 if (stat(filename, &file_status) == 0)
352 /* check for typical filesize of a Supaplex level package file */
353 if (file_status.st_size == 170496)
354 return LEVEL_FILE_TYPE_SP;
357 return LEVEL_FILE_TYPE_UNKNOWN;
360 static char *getSingleLevelBasename(int nr, int type)
362 static char basename[MAX_FILENAME_LEN];
363 char *level_filename = getStringCopy(leveldir_current->level_filename);
365 if (level_filename == NULL)
366 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
370 case LEVEL_FILE_TYPE_RND:
372 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
374 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
377 case LEVEL_FILE_TYPE_EM:
378 sprintf(basename, "%d", nr);
381 case LEVEL_FILE_TYPE_UNKNOWN:
383 sprintf(basename, level_filename, nr);
387 free(level_filename);
392 static char *getPackedLevelBasename(int type)
394 static char basename[MAX_FILENAME_LEN];
395 char *directory = getCurrentLevelDir();
397 struct dirent *dir_entry;
399 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
401 if ((dir = opendir(directory)) == NULL)
403 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
408 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
410 char *entry_basename = dir_entry->d_name;
411 int entry_type = getFileTypeFromBasename(entry_basename);
413 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
415 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
418 strcpy(basename, entry_basename);
430 static char *getSingleLevelFilename(int nr, int type)
432 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
436 static char *getPackedLevelFilename(int type)
438 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
442 char *getDefaultLevelFilename(int nr)
444 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
447 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
452 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
453 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
456 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
461 lfi->basename = getPackedLevelBasename(lfi->type);
462 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
465 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
467 /* special case: level number is negative => check for level template file */
470 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
475 if (leveldir_current->level_filename != NULL)
477 /* check for file name/pattern specified in "levelinfo.conf" */
478 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
479 if (fileExists(lfi->filename))
483 /* check for native Rocks'n'Diamonds level file */
484 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
485 if (fileExists(lfi->filename))
488 /* check for classic Emerald Mine level file */
489 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
490 if (fileExists(lfi->filename))
493 /* check for various packed level file formats */
494 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
495 if (fileExists(lfi->filename))
498 /* no known level file found -- try to use default values */
499 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
502 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
504 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
505 lfi->type = getFileTypeFromBasename(lfi->basename);
508 static struct LevelFileInfo *getLevelFileInfo(int nr)
510 static struct LevelFileInfo level_file_info;
512 /* always start with reliable default values */
513 setFileInfoToDefaults(&level_file_info);
515 level_file_info.nr = nr; /* set requested level number */
517 determineLevelFileInfo_Filename(&level_file_info);
518 determineLevelFileInfo_Filetype(&level_file_info);
520 return &level_file_info;
524 /* ------------------------------------------------------------------------- */
525 /* functions for loading R'n'D level */
526 /* ------------------------------------------------------------------------- */
528 int getMappedElement(int element)
530 /* map some (historic, now obsolete) elements */
535 case EL_PLAYER_OBSOLETE:
536 element = EL_PLAYER_1;
539 case EL_KEY_OBSOLETE:
542 case EL_EM_KEY_1_FILE_OBSOLETE:
543 element = EL_EM_KEY_1;
546 case EL_EM_KEY_2_FILE_OBSOLETE:
547 element = EL_EM_KEY_2;
550 case EL_EM_KEY_3_FILE_OBSOLETE:
551 element = EL_EM_KEY_3;
554 case EL_EM_KEY_4_FILE_OBSOLETE:
555 element = EL_EM_KEY_4;
558 case EL_ENVELOPE_OBSOLETE:
559 element = EL_ENVELOPE_1;
567 if (element >= NUM_FILE_ELEMENTS)
569 Error(ERR_WARN, "invalid level element %d", element);
571 element = EL_UNKNOWN;
576 if (element >= NUM_FILE_ELEMENTS)
578 Error(ERR_WARN, "invalid level element %d", element);
580 element = EL_UNKNOWN;
582 else if (element == EL_PLAYER_OBSOLETE)
583 element = EL_PLAYER_1;
584 else if (element == EL_KEY_OBSOLETE)
591 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
593 level->file_version = getFileVersion(file);
594 level->game_version = getFileVersion(file);
599 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
603 level->fieldx = getFile8Bit(file);
604 level->fieldy = getFile8Bit(file);
606 level->time = getFile16BitBE(file);
607 level->gems_needed = getFile16BitBE(file);
609 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
610 level->name[i] = getFile8Bit(file);
611 level->name[MAX_LEVEL_NAME_LEN] = 0;
613 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
614 level->score[i] = getFile8Bit(file);
616 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
617 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
618 for (y = 0; y < 3; y++)
619 for (x = 0; x < 3; x++)
620 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
622 level->amoeba_speed = getFile8Bit(file);
623 level->time_magic_wall = getFile8Bit(file);
624 level->time_wheel = getFile8Bit(file);
625 level->amoeba_content = getMappedElement(getFile8Bit(file));
626 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
627 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
628 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
629 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
631 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
633 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
634 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
636 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
641 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
645 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
646 level->author[i] = getFile8Bit(file);
647 level->author[MAX_LEVEL_NAME_LEN] = 0;
652 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
655 int chunk_size_expected = level->fieldx * level->fieldy;
657 /* Note: "chunk_size" was wrong before version 2.0 when elements are
658 stored with 16-bit encoding (and should be twice as big then).
659 Even worse, playfield data was stored 16-bit when only yamyam content
660 contained 16-bit elements and vice versa. */
662 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
663 chunk_size_expected *= 2;
665 if (chunk_size_expected != chunk_size)
667 ReadUnusedBytesFromFile(file, chunk_size);
668 return chunk_size_expected;
671 for (y = 0; y < level->fieldy; y++)
672 for (x = 0; x < level->fieldx; x++)
674 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
679 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
683 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
684 int chunk_size_expected = header_size + content_size;
686 /* Note: "chunk_size" was wrong before version 2.0 when elements are
687 stored with 16-bit encoding (and should be twice as big then).
688 Even worse, playfield data was stored 16-bit when only yamyam content
689 contained 16-bit elements and vice versa. */
691 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
692 chunk_size_expected += content_size;
694 if (chunk_size_expected != chunk_size)
696 ReadUnusedBytesFromFile(file, chunk_size);
697 return chunk_size_expected;
701 level->num_yamyam_contents = getFile8Bit(file);
705 /* correct invalid number of content fields -- should never happen */
706 if (level->num_yamyam_contents < 1 ||
707 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
708 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
710 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
711 for (y = 0; y < 3; y++)
712 for (x = 0; x < 3; x++)
713 level->yamyam_content[i][x][y] =
714 getMappedElement(level->encoding_16bit_field ?
715 getFile16BitBE(file) : getFile8Bit(file));
719 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
723 int num_contents, content_xsize, content_ysize;
724 int content_array[MAX_ELEMENT_CONTENTS][3][3];
726 element = getMappedElement(getFile16BitBE(file));
727 num_contents = getFile8Bit(file);
728 content_xsize = getFile8Bit(file);
729 content_ysize = getFile8Bit(file);
731 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
733 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
734 for (y = 0; y < 3; y++)
735 for (x = 0; x < 3; x++)
736 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
738 /* correct invalid number of content fields -- should never happen */
739 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
740 num_contents = STD_ELEMENT_CONTENTS;
742 if (element == EL_YAMYAM)
744 level->num_yamyam_contents = num_contents;
746 for (i = 0; i < num_contents; i++)
747 for (y = 0; y < 3; y++)
748 for (x = 0; x < 3; x++)
749 level->yamyam_content[i][x][y] = content_array[i][x][y];
751 else if (element == EL_BD_AMOEBA)
753 level->amoeba_content = content_array[0][0][0];
757 Error(ERR_WARN, "cannot load content for element '%d'", element);
763 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
769 int chunk_size_expected;
771 element = getMappedElement(getFile16BitBE(file));
772 if (!IS_ENVELOPE(element))
773 element = EL_ENVELOPE_1;
775 envelope_nr = element - EL_ENVELOPE_1;
777 envelope_len = getFile16BitBE(file);
779 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
780 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
782 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
784 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
785 if (chunk_size_expected != chunk_size)
787 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
788 return chunk_size_expected;
791 for (i = 0; i < envelope_len; i++)
792 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
797 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
799 int num_changed_custom_elements = getFile16BitBE(file);
800 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
803 if (chunk_size_expected != chunk_size)
805 ReadUnusedBytesFromFile(file, chunk_size - 2);
806 return chunk_size_expected;
809 for (i = 0; i < num_changed_custom_elements; i++)
811 int element = getFile16BitBE(file);
812 int properties = getFile32BitBE(file);
814 if (IS_CUSTOM_ELEMENT(element))
815 Properties[element][EP_BITFIELD_BASE] = properties;
817 Error(ERR_WARN, "invalid custom element number %d", element);
823 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
825 int num_changed_custom_elements = getFile16BitBE(file);
826 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
829 if (chunk_size_expected != chunk_size)
831 ReadUnusedBytesFromFile(file, chunk_size - 2);
832 return chunk_size_expected;
835 for (i = 0; i < num_changed_custom_elements; i++)
837 int element = getFile16BitBE(file);
838 int custom_target_element = getFile16BitBE(file);
840 if (IS_CUSTOM_ELEMENT(element))
841 element_info[element].change->target_element = custom_target_element;
843 Error(ERR_WARN, "invalid custom element number %d", element);
849 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
851 int num_changed_custom_elements = getFile16BitBE(file);
852 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
855 if (chunk_size_expected != chunk_size)
857 ReadUnusedBytesFromFile(file, chunk_size - 2);
858 return chunk_size_expected;
861 for (i = 0; i < num_changed_custom_elements; i++)
863 int element = getFile16BitBE(file);
865 if (!IS_CUSTOM_ELEMENT(element))
867 Error(ERR_WARN, "invalid custom element number %d", element);
869 element = EL_INTERNAL_DUMMY;
872 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
873 element_info[element].description[j] = getFile8Bit(file);
874 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
876 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
878 /* some free bytes for future properties and padding */
879 ReadUnusedBytesFromFile(file, 7);
881 element_info[element].use_gfx_element = getFile8Bit(file);
882 element_info[element].gfx_element =
883 getMappedElement(getFile16BitBE(file));
885 element_info[element].collect_score = getFile8Bit(file);
886 element_info[element].collect_count = getFile8Bit(file);
888 element_info[element].push_delay_fixed = getFile16BitBE(file);
889 element_info[element].push_delay_random = getFile16BitBE(file);
890 element_info[element].move_delay_fixed = getFile16BitBE(file);
891 element_info[element].move_delay_random = getFile16BitBE(file);
893 element_info[element].move_pattern = getFile16BitBE(file);
894 element_info[element].move_direction_initial = getFile8Bit(file);
895 element_info[element].move_stepsize = getFile8Bit(file);
897 for (y = 0; y < 3; y++)
898 for (x = 0; x < 3; x++)
899 element_info[element].content[x][y] =
900 getMappedElement(getFile16BitBE(file));
902 element_info[element].change->events = getFile32BitBE(file);
904 element_info[element].change->target_element =
905 getMappedElement(getFile16BitBE(file));
907 element_info[element].change->delay_fixed = getFile16BitBE(file);
908 element_info[element].change->delay_random = getFile16BitBE(file);
909 element_info[element].change->delay_frames = getFile16BitBE(file);
911 element_info[element].change->trigger_element =
912 getMappedElement(getFile16BitBE(file));
914 element_info[element].change->explode = getFile8Bit(file);
915 element_info[element].change->use_content = getFile8Bit(file);
916 element_info[element].change->only_complete = getFile8Bit(file);
917 element_info[element].change->use_random_change = getFile8Bit(file);
919 element_info[element].change->random = getFile8Bit(file);
920 element_info[element].change->power = getFile8Bit(file);
922 for (y = 0; y < 3; y++)
923 for (x = 0; x < 3; x++)
924 element_info[element].change->content[x][y] =
925 getMappedElement(getFile16BitBE(file));
927 element_info[element].slippery_type = getFile8Bit(file);
929 /* some free bytes for future properties and padding */
930 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
932 /* mark that this custom element has been modified */
933 element_info[element].modified_settings = TRUE;
939 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
941 struct ElementInfo *ei;
942 int chunk_size_expected;
946 element = getFile16BitBE(file);
948 if (!IS_CUSTOM_ELEMENT(element))
950 Error(ERR_WARN, "invalid custom element number %d", element);
952 ReadUnusedBytesFromFile(file, chunk_size - 2);
956 ei = &element_info[element];
958 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
959 ei->description[i] = getFile8Bit(file);
960 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
962 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
963 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
965 ei->num_change_pages = getFile8Bit(file);
967 /* some free bytes for future base property values and padding */
968 ReadUnusedBytesFromFile(file, 5);
970 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
971 if (chunk_size_expected != chunk_size)
973 ReadUnusedBytesFromFile(file, chunk_size - 48);
974 return chunk_size_expected;
977 /* read custom property values */
979 ei->use_gfx_element = getFile8Bit(file);
980 ei->gfx_element = getMappedElement(getFile16BitBE(file));
982 ei->collect_score = getFile8Bit(file);
983 ei->collect_count = getFile8Bit(file);
985 ei->push_delay_fixed = getFile16BitBE(file);
986 ei->push_delay_random = getFile16BitBE(file);
987 ei->move_delay_fixed = getFile16BitBE(file);
988 ei->move_delay_random = getFile16BitBE(file);
990 ei->move_pattern = getFile16BitBE(file);
991 ei->move_direction_initial = getFile8Bit(file);
992 ei->move_stepsize = getFile8Bit(file);
994 ei->slippery_type = getFile8Bit(file);
996 for (y = 0; y < 3; y++)
997 for (x = 0; x < 3; x++)
998 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1000 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1001 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1002 ei->move_leave_type = getFile8Bit(file);
1004 /* some free bytes for future custom property values and padding */
1005 ReadUnusedBytesFromFile(file, 7);
1007 /* read change property values */
1009 setElementChangePages(ei, ei->num_change_pages);
1011 for (i = 0; i < ei->num_change_pages; i++)
1013 struct ElementChangeInfo *change = &ei->change_page[i];
1015 /* always start with reliable default values */
1016 setElementChangeInfoToDefaults(change);
1018 change->events = getFile32BitBE(file);
1020 change->target_element = getMappedElement(getFile16BitBE(file));
1022 change->delay_fixed = getFile16BitBE(file);
1023 change->delay_random = getFile16BitBE(file);
1024 change->delay_frames = getFile16BitBE(file);
1026 change->trigger_element = getMappedElement(getFile16BitBE(file));
1028 change->explode = getFile8Bit(file);
1029 change->use_content = getFile8Bit(file);
1030 change->only_complete = getFile8Bit(file);
1031 change->use_random_change = getFile8Bit(file);
1033 change->random = getFile8Bit(file);
1034 change->power = getFile8Bit(file);
1036 for (y = 0; y < 3; y++)
1037 for (x = 0; x < 3; x++)
1038 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1040 change->can_change = getFile8Bit(file);
1042 change->sides = getFile8Bit(file);
1044 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
1045 change->sides = CH_SIDE_ANY;
1047 /* some free bytes for future change property values and padding */
1048 ReadUnusedBytesFromFile(file, 8);
1051 /* mark this custom element as modified */
1052 ei->modified_settings = TRUE;
1057 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1059 struct ElementInfo *ei;
1060 struct ElementGroupInfo *group;
1064 element = getFile16BitBE(file);
1066 if (!IS_GROUP_ELEMENT(element))
1068 Error(ERR_WARN, "invalid group element number %d", element);
1070 ReadUnusedBytesFromFile(file, chunk_size - 2);
1074 ei = &element_info[element];
1076 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1077 ei->description[i] = getFile8Bit(file);
1078 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1080 group = element_info[element].group;
1082 group->num_elements = getFile8Bit(file);
1084 ei->use_gfx_element = getFile8Bit(file);
1085 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1087 group->choice_mode = getFile8Bit(file);
1089 /* some free bytes for future values and padding */
1090 ReadUnusedBytesFromFile(file, 3);
1092 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1093 group->element[i] = getMappedElement(getFile16BitBE(file));
1095 /* mark this group element as modified */
1096 element_info[element].modified_settings = TRUE;
1101 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1102 struct LevelFileInfo *level_file_info)
1104 char *filename = level_file_info->filename;
1105 char cookie[MAX_LINE_LEN];
1106 char chunk_name[CHUNK_ID_LEN + 1];
1110 if (!(file = fopen(filename, MODE_READ)))
1112 level->no_level_file = TRUE;
1114 if (level != &level_template)
1115 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1120 getFileChunkBE(file, chunk_name, NULL);
1121 if (strcmp(chunk_name, "RND1") == 0)
1123 getFile32BitBE(file); /* not used */
1125 getFileChunkBE(file, chunk_name, NULL);
1126 if (strcmp(chunk_name, "CAVE") != 0)
1128 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1133 else /* check for pre-2.0 file format with cookie string */
1135 strcpy(cookie, chunk_name);
1136 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1137 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1138 cookie[strlen(cookie) - 1] = '\0';
1140 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1142 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1147 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1149 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1154 /* pre-2.0 level files have no game version, so use file version here */
1155 level->game_version = level->file_version;
1158 if (level->file_version < FILE_VERSION_1_2)
1160 /* level files from versions before 1.2.0 without chunk structure */
1161 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1162 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1170 int (*loader)(FILE *, int, struct LevelInfo *);
1174 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1175 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1176 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1177 { "BODY", -1, LoadLevel_BODY },
1178 { "CONT", -1, LoadLevel_CONT },
1179 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1180 { "CNT3", -1, LoadLevel_CNT3 },
1181 { "CUS1", -1, LoadLevel_CUS1 },
1182 { "CUS2", -1, LoadLevel_CUS2 },
1183 { "CUS3", -1, LoadLevel_CUS3 },
1184 { "CUS4", -1, LoadLevel_CUS4 },
1185 { "GRP1", -1, LoadLevel_GRP1 },
1189 while (getFileChunkBE(file, chunk_name, &chunk_size))
1193 while (chunk_info[i].name != NULL &&
1194 strcmp(chunk_name, chunk_info[i].name) != 0)
1197 if (chunk_info[i].name == NULL)
1199 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1200 chunk_name, filename);
1201 ReadUnusedBytesFromFile(file, chunk_size);
1203 else if (chunk_info[i].size != -1 &&
1204 chunk_info[i].size != chunk_size)
1206 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1207 chunk_size, chunk_name, filename);
1208 ReadUnusedBytesFromFile(file, chunk_size);
1212 /* call function to load this level chunk */
1213 int chunk_size_expected =
1214 (chunk_info[i].loader)(file, chunk_size, level);
1216 /* the size of some chunks cannot be checked before reading other
1217 chunks first (like "HEAD" and "BODY") that contain some header
1218 information, so check them here */
1219 if (chunk_size_expected != chunk_size)
1221 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1222 chunk_size, chunk_name, filename);
1231 /* ------------------------------------------------------------------------- */
1232 /* functions for loading EM level */
1233 /* ------------------------------------------------------------------------- */
1235 static int map_em_element_yam(int element)
1239 case 0x00: return EL_EMPTY;
1240 case 0x01: return EL_EMERALD;
1241 case 0x02: return EL_DIAMOND;
1242 case 0x03: return EL_ROCK;
1243 case 0x04: return EL_ROBOT;
1244 case 0x05: return EL_SPACESHIP_UP;
1245 case 0x06: return EL_BOMB;
1246 case 0x07: return EL_BUG_UP;
1247 case 0x08: return EL_AMOEBA_DROP;
1248 case 0x09: return EL_NUT;
1249 case 0x0a: return EL_YAMYAM;
1250 case 0x0b: return EL_QUICKSAND_FULL;
1251 case 0x0c: return EL_SAND;
1252 case 0x0d: return EL_WALL_SLIPPERY;
1253 case 0x0e: return EL_STEELWALL;
1254 case 0x0f: return EL_WALL;
1255 case 0x10: return EL_EM_KEY_1;
1256 case 0x11: return EL_EM_KEY_2;
1257 case 0x12: return EL_EM_KEY_4;
1258 case 0x13: return EL_EM_KEY_3;
1259 case 0x14: return EL_MAGIC_WALL;
1260 case 0x15: return EL_ROBOT_WHEEL;
1261 case 0x16: return EL_DYNAMITE;
1263 case 0x17: return EL_EM_KEY_1; /* EMC */
1264 case 0x18: return EL_BUG_UP; /* EMC */
1265 case 0x1a: return EL_DIAMOND; /* EMC */
1266 case 0x1b: return EL_EMERALD; /* EMC */
1267 case 0x25: return EL_NUT; /* EMC */
1268 case 0x80: return EL_EMPTY; /* EMC */
1269 case 0x85: return EL_EM_KEY_1; /* EMC */
1270 case 0x86: return EL_EM_KEY_2; /* EMC */
1271 case 0x87: return EL_EM_KEY_4; /* EMC */
1272 case 0x88: return EL_EM_KEY_3; /* EMC */
1273 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1274 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1275 case 0xaf: return EL_DYNAMITE; /* EMC */
1276 case 0xbd: return EL_SAND; /* EMC */
1279 Error(ERR_WARN, "invalid level element %d", element);
1284 static int map_em_element_field(int element)
1286 if (element >= 0xc8 && element <= 0xe1)
1287 return EL_CHAR_A + (element - 0xc8);
1288 else if (element >= 0xe2 && element <= 0xeb)
1289 return EL_CHAR_0 + (element - 0xe2);
1293 case 0x00: return EL_ROCK;
1294 case 0x01: return EL_ROCK; /* EMC */
1295 case 0x02: return EL_DIAMOND;
1296 case 0x03: return EL_DIAMOND;
1297 case 0x04: return EL_ROBOT;
1298 case 0x05: return EL_ROBOT; /* EMC */
1299 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1300 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1301 case 0x08: return EL_SPACESHIP_UP;
1302 case 0x09: return EL_SPACESHIP_RIGHT;
1303 case 0x0a: return EL_SPACESHIP_DOWN;
1304 case 0x0b: return EL_SPACESHIP_LEFT;
1305 case 0x0c: return EL_SPACESHIP_UP;
1306 case 0x0d: return EL_SPACESHIP_RIGHT;
1307 case 0x0e: return EL_SPACESHIP_DOWN;
1308 case 0x0f: return EL_SPACESHIP_LEFT;
1310 case 0x10: return EL_BOMB;
1311 case 0x11: return EL_BOMB; /* EMC */
1312 case 0x12: return EL_EMERALD;
1313 case 0x13: return EL_EMERALD;
1314 case 0x14: return EL_BUG_UP;
1315 case 0x15: return EL_BUG_RIGHT;
1316 case 0x16: return EL_BUG_DOWN;
1317 case 0x17: return EL_BUG_LEFT;
1318 case 0x18: return EL_BUG_UP;
1319 case 0x19: return EL_BUG_RIGHT;
1320 case 0x1a: return EL_BUG_DOWN;
1321 case 0x1b: return EL_BUG_LEFT;
1322 case 0x1c: return EL_AMOEBA_DROP;
1323 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1324 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1325 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1327 case 0x20: return EL_ROCK;
1328 case 0x21: return EL_BOMB; /* EMC */
1329 case 0x22: return EL_DIAMOND; /* EMC */
1330 case 0x23: return EL_EMERALD; /* EMC */
1331 case 0x24: return EL_MAGIC_WALL;
1332 case 0x25: return EL_NUT;
1333 case 0x26: return EL_NUT; /* EMC */
1334 case 0x27: return EL_NUT; /* EMC */
1336 /* looks like magic wheel, but is _always_ activated */
1337 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1339 case 0x29: return EL_YAMYAM; /* up */
1340 case 0x2a: return EL_YAMYAM; /* down */
1341 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1342 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1343 case 0x2d: return EL_QUICKSAND_FULL;
1344 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1345 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1347 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1348 case 0x31: return EL_SAND; /* EMC */
1349 case 0x32: return EL_SAND; /* EMC */
1350 case 0x33: return EL_SAND; /* EMC */
1351 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1352 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1353 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1354 case 0x37: return EL_SAND; /* EMC */
1355 case 0x38: return EL_ROCK; /* EMC */
1356 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1357 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1358 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1359 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1360 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1361 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1362 case 0x3f: return EL_ACID_POOL_BOTTOM;
1364 case 0x40: return EL_EXIT_OPEN; /* 1 */
1365 case 0x41: return EL_EXIT_OPEN; /* 2 */
1366 case 0x42: return EL_EXIT_OPEN; /* 3 */
1367 case 0x43: return EL_BALLOON; /* EMC */
1368 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1369 case 0x45: return EL_SPRING; /* EMC */
1370 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1371 case 0x47: return EL_SPRING; /* left */ /* EMC */
1372 case 0x48: return EL_SPRING; /* right */ /* EMC */
1373 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1374 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1375 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1376 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1377 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1378 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1379 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1381 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1382 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1383 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1384 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1385 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1386 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1387 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1388 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1389 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1390 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1391 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1392 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1393 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1394 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1395 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1396 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1398 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1399 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1400 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1401 case 0x63: return EL_SPRING; /* left */ /* EMC */
1402 case 0x64: return EL_SPRING; /* right */ /* EMC */
1403 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1404 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1405 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1406 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1407 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1408 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1409 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1410 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1411 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1412 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1413 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1415 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1416 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1417 case 0x72: return EL_NUT; /* left */ /* EMC */
1418 case 0x73: return EL_SAND; /* EMC (? "nut") */
1419 case 0x74: return EL_STEELWALL;
1420 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1421 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1422 case 0x77: return EL_BOMB; /* left */ /* EMC */
1423 case 0x78: return EL_BOMB; /* right */ /* EMC */
1424 case 0x79: return EL_ROCK; /* left */ /* EMC */
1425 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1426 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1427 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1428 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1429 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1430 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1432 case 0x80: return EL_EMPTY;
1433 case 0x81: return EL_WALL_SLIPPERY;
1434 case 0x82: return EL_SAND;
1435 case 0x83: return EL_STEELWALL;
1436 case 0x84: return EL_WALL;
1437 case 0x85: return EL_EM_KEY_1;
1438 case 0x86: return EL_EM_KEY_2;
1439 case 0x87: return EL_EM_KEY_4;
1440 case 0x88: return EL_EM_KEY_3;
1441 case 0x89: return EL_EM_GATE_1;
1442 case 0x8a: return EL_EM_GATE_2;
1443 case 0x8b: return EL_EM_GATE_4;
1444 case 0x8c: return EL_EM_GATE_3;
1445 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1446 case 0x8e: return EL_EM_GATE_1_GRAY;
1447 case 0x8f: return EL_EM_GATE_2_GRAY;
1449 case 0x90: return EL_EM_GATE_4_GRAY;
1450 case 0x91: return EL_EM_GATE_3_GRAY;
1451 case 0x92: return EL_MAGIC_WALL;
1452 case 0x93: return EL_ROBOT_WHEEL;
1453 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1454 case 0x95: return EL_ACID_POOL_TOPLEFT;
1455 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1456 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1457 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1458 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1459 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1460 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1461 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1462 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1463 case 0x9e: return EL_EXIT_CLOSED;
1464 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1466 /* looks like normal sand, but behaves like wall */
1467 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1468 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1469 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1470 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1471 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1472 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1473 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1474 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1475 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1476 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1477 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1478 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1479 case 0xac: return EL_CHAR_COMMA; /* EMC */
1480 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1481 case 0xae: return EL_CHAR_MINUS; /* EMC */
1482 case 0xaf: return EL_DYNAMITE;
1484 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1485 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1486 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1487 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1488 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1489 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1490 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1491 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1492 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1493 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1494 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1495 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1496 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1497 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1498 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1499 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1501 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1502 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1503 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1504 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1505 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1506 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1507 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1508 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1510 /* characters: see above */
1512 case 0xec: return EL_CHAR_PERIOD;
1513 case 0xed: return EL_CHAR_EXCLAM;
1514 case 0xee: return EL_CHAR_COLON;
1515 case 0xef: return EL_CHAR_QUESTION;
1517 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1518 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1519 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1520 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1521 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1522 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1523 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1524 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1526 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1527 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1528 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1529 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1530 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1531 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1533 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1534 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1537 /* should never happen (all 8-bit value cases should be handled) */
1538 Error(ERR_WARN, "invalid level element %d", element);
1543 #define EM_LEVEL_SIZE 2106
1544 #define EM_LEVEL_XSIZE 64
1545 #define EM_LEVEL_YSIZE 32
1547 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1548 struct LevelFileInfo *level_file_info)
1550 char *filename = level_file_info->filename;
1552 unsigned char leveldata[EM_LEVEL_SIZE];
1553 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1554 unsigned char code0 = 0x65;
1555 unsigned char code1 = 0x11;
1556 boolean level_is_crypted = FALSE;
1557 int nr = level_file_info->nr;
1560 if (!(file = fopen(filename, MODE_READ)))
1562 level->no_level_file = TRUE;
1564 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1569 for(i = 0; i < EM_LEVEL_SIZE; i++)
1570 leveldata[i] = fgetc(file);
1574 /* check if level data is crypted by testing against known starting bytes
1575 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1577 if ((leveldata[0] == 0xf1 ||
1578 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1580 level_is_crypted = TRUE;
1582 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1583 leveldata[0] = 0xf1;
1586 if (level_is_crypted) /* decode crypted level data */
1588 for(i = 0; i < EM_LEVEL_SIZE; i++)
1590 leveldata[i] ^= code0;
1591 leveldata[i] -= code1;
1593 code0 = (code0 + 7) & 0xff;
1597 level->fieldx = EM_LEVEL_XSIZE;
1598 level->fieldy = EM_LEVEL_YSIZE;
1600 level->time = header[46] * 10;
1601 level->gems_needed = header[47];
1603 /* The original Emerald Mine levels have their level number stored
1604 at the second byte of the level file...
1605 Do not trust this information at other level files, e.g. EMC,
1606 but correct it anyway (normally the first row is completely
1607 steel wall, so the correction does not hurt anyway). */
1609 if (leveldata[1] == nr)
1610 leveldata[1] = leveldata[2]; /* correct level number field */
1612 sprintf(level->name, "Level %d", nr); /* set level name */
1614 level->score[SC_EMERALD] = header[36];
1615 level->score[SC_DIAMOND] = header[37];
1616 level->score[SC_ROBOT] = header[38];
1617 level->score[SC_SPACESHIP] = header[39];
1618 level->score[SC_BUG] = header[40];
1619 level->score[SC_YAMYAM] = header[41];
1620 level->score[SC_NUT] = header[42];
1621 level->score[SC_DYNAMITE] = header[43];
1622 level->score[SC_TIME_BONUS] = header[44];
1624 level->num_yamyam_contents = 4;
1626 for(i = 0; i < level->num_yamyam_contents; i++)
1627 for(y = 0; y < 3; y++)
1628 for(x = 0; x < 3; x++)
1629 level->yamyam_content[i][x][y] =
1630 map_em_element_yam(header[i * 9 + y * 3 + x]);
1632 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1633 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1634 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1635 level->amoeba_content = EL_DIAMOND;
1637 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1639 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1641 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1642 new_element = EL_AMOEBA_WET;
1644 level->field[x][y] = new_element;
1647 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1648 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1649 level->field[x][y] = EL_PLAYER_1;
1651 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1652 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1653 level->field[x][y] = EL_PLAYER_2;
1656 /* ------------------------------------------------------------------------- */
1657 /* functions for loading SP level */
1658 /* ------------------------------------------------------------------------- */
1660 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1661 #define SP_LEVEL_SIZE 1536
1662 #define SP_LEVEL_XSIZE 60
1663 #define SP_LEVEL_YSIZE 24
1664 #define SP_LEVEL_NAME_LEN 23
1666 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1671 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1672 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1674 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1676 int element_old = fgetc(file);
1679 if (element_old <= 0x27)
1680 element_new = getMappedElement(EL_SP_START + element_old);
1681 else if (element_old == 0x28)
1682 element_new = EL_INVISIBLE_WALL;
1685 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1686 Error(ERR_WARN, "invalid level element %d", element_old);
1688 element_new = EL_UNKNOWN;
1691 level->field[x][y] = element_new;
1695 ReadUnusedBytesFromFile(file, 4);
1697 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1698 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1700 ReadUnusedBytesFromFile(file, 1);
1702 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1703 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1704 level->name[i] = fgetc(file);
1705 level->name[SP_LEVEL_NAME_LEN] = '\0';
1707 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1708 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1710 /* number of infotrons needed; 0 means that Supaplex will count the total
1711 amount of infotrons in the level and use the low byte of that number.
1712 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1713 level->gems_needed = fgetc(file);
1715 /* information about special gravity port entries */
1716 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1718 level->fieldx = SP_LEVEL_XSIZE;
1719 level->fieldy = SP_LEVEL_YSIZE;
1721 level->time = 0; /* no time limit */
1722 level->amoeba_speed = 0;
1723 level->time_magic_wall = 0;
1724 level->time_wheel = 0;
1725 level->amoeba_content = EL_EMPTY;
1727 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1728 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1730 /* there are no yamyams in supaplex levels */
1731 for(i = 0; i < level->num_yamyam_contents; i++)
1732 for(y = 0; y < 3; y++)
1733 for(x = 0; x < 3; x++)
1734 level->yamyam_content[i][x][y] = EL_EMPTY;
1737 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1738 struct LevelFileInfo *level_file_info)
1740 char *filename = level_file_info->filename;
1742 int nr = level_file_info->nr - leveldir_current->first_level;
1744 char name_first, name_last;
1745 struct LevelInfo multipart_level;
1746 int multipart_xpos, multipart_ypos;
1747 boolean is_multipart_level;
1748 boolean is_first_part;
1749 boolean reading_multipart_level = FALSE;
1750 boolean use_empty_level = FALSE;
1752 if (!(file = fopen(filename, MODE_READ)))
1754 level->no_level_file = TRUE;
1756 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1761 /* position file stream to the requested level inside the level package */
1762 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1764 level->no_level_file = TRUE;
1766 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1771 /* there exist Supaplex level package files with multi-part levels which
1772 can be detected as follows: instead of leading and trailing dashes ('-')
1773 to pad the level name, they have leading and trailing numbers which are
1774 the x and y coordinations of the current part of the multi-part level;
1775 if there are '?' characters instead of numbers on the left or right side
1776 of the level name, the multi-part level consists of only horizontal or
1779 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1781 LoadLevelFromFileStream_SP(file, level, l);
1783 /* check if this level is a part of a bigger multi-part level */
1785 name_first = level->name[0];
1786 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1788 is_multipart_level =
1789 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1790 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1793 ((name_first == '?' || name_first == '1') &&
1794 (name_last == '?' || name_last == '1'));
1796 /* correct leading multipart level meta information in level name */
1797 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1798 level->name[i] = '-';
1800 /* correct trailing multipart level meta information in level name */
1801 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1802 level->name[i] = '-';
1804 /* ---------- check for normal single level ---------- */
1806 if (!reading_multipart_level && !is_multipart_level)
1808 /* the current level is simply a normal single-part level, and we are
1809 not reading a multi-part level yet, so return the level as it is */
1814 /* ---------- check for empty level (unused multi-part) ---------- */
1816 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1818 /* this is a part of a multi-part level, but not the first part
1819 (and we are not already reading parts of a multi-part level);
1820 in this case, use an empty level instead of the single part */
1822 use_empty_level = TRUE;
1827 /* ---------- check for finished multi-part level ---------- */
1829 if (reading_multipart_level &&
1830 (!is_multipart_level ||
1831 strcmp(level->name, multipart_level.name) != 0))
1833 /* we are already reading parts of a multi-part level, but this level is
1834 either not a multi-part level, or a part of a different multi-part
1835 level; in both cases, the multi-part level seems to be complete */
1840 /* ---------- here we have one part of a multi-part level ---------- */
1842 reading_multipart_level = TRUE;
1844 if (is_first_part) /* start with first part of new multi-part level */
1846 /* copy level info structure from first part */
1847 multipart_level = *level;
1849 /* clear playfield of new multi-part level */
1850 for (y = 0; y < MAX_LEV_FIELDY; y++)
1851 for (x = 0; x < MAX_LEV_FIELDX; x++)
1852 multipart_level.field[x][y] = EL_EMPTY;
1855 if (name_first == '?')
1857 if (name_last == '?')
1860 multipart_xpos = (int)(name_first - '0');
1861 multipart_ypos = (int)(name_last - '0');
1864 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1865 multipart_xpos, multipart_ypos, multipart_level.name);
1868 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1869 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1871 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1876 multipart_level.fieldx = MAX(multipart_level.fieldx,
1877 multipart_xpos * SP_LEVEL_XSIZE);
1878 multipart_level.fieldy = MAX(multipart_level.fieldy,
1879 multipart_ypos * SP_LEVEL_YSIZE);
1881 /* copy level part at the right position of multi-part level */
1882 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1884 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1886 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1887 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1889 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1896 if (use_empty_level)
1898 setLevelInfoToDefaults(level);
1900 level->fieldx = SP_LEVEL_XSIZE;
1901 level->fieldy = SP_LEVEL_YSIZE;
1903 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1904 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1905 level->field[x][y] = EL_EMPTY;
1907 strcpy(level->name, "-------- EMPTY --------");
1909 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1912 if (reading_multipart_level)
1913 *level = multipart_level;
1916 /* ------------------------------------------------------------------------- */
1917 /* functions for loading generic level */
1918 /* ------------------------------------------------------------------------- */
1920 void LoadLevelFromFileInfo(struct LevelInfo *level,
1921 struct LevelFileInfo *level_file_info)
1923 /* always start with reliable default values */
1924 setLevelInfoToDefaults(level);
1926 switch (level_file_info->type)
1928 case LEVEL_FILE_TYPE_RND:
1929 LoadLevelFromFileInfo_RND(level, level_file_info);
1932 case LEVEL_FILE_TYPE_EM:
1933 LoadLevelFromFileInfo_EM(level, level_file_info);
1936 case LEVEL_FILE_TYPE_SP:
1937 LoadLevelFromFileInfo_SP(level, level_file_info);
1941 LoadLevelFromFileInfo_RND(level, level_file_info);
1946 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1948 static struct LevelFileInfo level_file_info;
1950 /* always start with reliable default values */
1951 setFileInfoToDefaults(&level_file_info);
1953 level_file_info.nr = 0; /* unknown level number */
1954 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1955 level_file_info.filename = filename;
1957 LoadLevelFromFileInfo(level, &level_file_info);
1960 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1962 if (leveldir_current == NULL) /* only when dumping level */
1966 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1969 /* determine correct game engine version of current level */
1971 if (!leveldir_current->latest_engine)
1973 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1974 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1975 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1979 printf("\n::: This level is private or contributed: '%s'\n", filename);
1983 printf("\n::: Use the stored game engine version for this level\n");
1986 /* For all levels which are not forced to use the latest game engine
1987 version (normally user contributed, private and undefined levels),
1988 use the version of the game engine the levels were created for.
1990 Since 2.0.1, the game engine version is now directly stored
1991 in the level file (chunk "VERS"), so there is no need anymore
1992 to set the game version from the file version (except for old,
1993 pre-2.0 levels, where the game version is still taken from the
1994 file format version used to store the level -- see above). */
1996 /* do some special adjustments to support older level versions */
1997 if (level->file_version == FILE_VERSION_1_0)
1999 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2000 Error(ERR_WARN, "using high speed movement for player");
2002 /* player was faster than monsters in (pre-)1.0 levels */
2003 level->double_speed = TRUE;
2006 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2007 if (level->game_version == VERSION_IDENT(2,0,1,0))
2008 level->em_slippery_gems = TRUE;
2013 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2014 leveldir_current->sort_priority, filename);
2018 printf("\n::: Use latest game engine version for this level.\n");
2021 /* For all levels which are forced to use the latest game engine version
2022 (normally all but user contributed, private and undefined levels), set
2023 the game engine version to the actual version; this allows for actual
2024 corrections in the game engine to take effect for existing, converted
2025 levels (from "classic" or other existing games) to make the emulation
2026 of the corresponding game more accurate, while (hopefully) not breaking
2027 existing levels created from other players. */
2030 printf("::: changing engine from %d to %d\n",
2031 level->game_version, GAME_VERSION_ACTUAL);
2034 level->game_version = GAME_VERSION_ACTUAL;
2036 /* Set special EM style gems behaviour: EM style gems slip down from
2037 normal, steel and growing wall. As this is a more fundamental change,
2038 it seems better to set the default behaviour to "off" (as it is more
2039 natural) and make it configurable in the level editor (as a property
2040 of gem style elements). Already existing converted levels (neither
2041 private nor contributed levels) are changed to the new behaviour. */
2043 if (level->file_version < FILE_VERSION_2_0)
2044 level->em_slippery_gems = TRUE;
2048 printf("::: => %d\n", level->game_version);
2052 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2056 /* map custom element change events that have changed in newer versions
2057 (these following values were accidentally changed in version 3.0.1) */
2058 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2060 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2062 int element = EL_CUSTOM_START + i;
2064 /* order of checking and copying events to be mapped is important */
2065 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2067 if (HAS_CHANGE_EVENT(element, j - 2))
2069 SET_CHANGE_EVENT(element, j - 2, FALSE);
2070 SET_CHANGE_EVENT(element, j, TRUE);
2074 /* order of checking and copying events to be mapped is important */
2075 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2077 if (HAS_CHANGE_EVENT(element, j - 1))
2079 SET_CHANGE_EVENT(element, j - 1, FALSE);
2080 SET_CHANGE_EVENT(element, j, TRUE);
2086 /* some custom element change events get mapped since version 3.0.3 */
2087 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2089 int element = EL_CUSTOM_START + i;
2091 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2092 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2094 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2095 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2097 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2101 /* initialize "can_change" field for old levels with only one change page */
2102 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2104 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2106 int element = EL_CUSTOM_START + i;
2108 if (CAN_CHANGE(element))
2109 element_info[element].change->can_change = TRUE;
2114 /* set default push delay values (corrected since version 3.0.7-1) */
2115 if (level->game_version < VERSION_IDENT(3,0,7,1))
2117 game.default_push_delay_fixed = 2;
2118 game.default_push_delay_random = 8;
2122 game.default_push_delay_fixed = 8;
2123 game.default_push_delay_random = 8;
2126 /* set uninitialized push delay values of custom elements in older levels */
2127 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2129 int element = EL_CUSTOM_START + i;
2131 if (element_info[element].push_delay_fixed == -1)
2132 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2133 if (element_info[element].push_delay_random == -1)
2134 element_info[element].push_delay_random = game.default_push_delay_random;
2138 /* initialize element properties for level editor etc. */
2139 InitElementPropertiesEngine(level->game_version);
2142 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2146 /* map elements that have changed in newer versions */
2147 for (y = 0; y < level->fieldy; y++)
2149 for (x = 0; x < level->fieldx; x++)
2151 int element = level->field[x][y];
2153 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2155 /* map game font elements */
2156 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2157 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2158 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2159 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2162 if (level->game_version < VERSION_IDENT(3,0,0,0))
2164 /* map Supaplex gravity tube elements */
2165 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2166 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2167 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2168 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2172 level->field[x][y] = element;
2176 /* copy elements to runtime playfield array */
2177 for (x = 0; x < MAX_LEV_FIELDX; x++)
2178 for (y = 0; y < MAX_LEV_FIELDY; y++)
2179 Feld[x][y] = level->field[x][y];
2181 /* initialize level size variables for faster access */
2182 lev_fieldx = level->fieldx;
2183 lev_fieldy = level->fieldy;
2185 /* determine border element for this level */
2189 void LoadLevelTemplate(int nr)
2192 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2193 char *filename = level_file_info->filename;
2195 LoadLevelFromFileInfo(&level_template, level_file_info);
2197 char *filename = getDefaultLevelFilename(nr);
2199 LoadLevelFromFilename_RND(&level_template, filename);
2202 LoadLevel_InitVersion(&level, filename);
2203 LoadLevel_InitElements(&level, filename);
2205 ActivateLevelTemplate();
2208 void LoadLevel(int nr)
2211 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2212 char *filename = level_file_info->filename;
2214 LoadLevelFromFileInfo(&level, level_file_info);
2216 char *filename = getLevelFilename(nr);
2218 LoadLevelFromFilename_RND(&level, filename);
2221 if (level.use_custom_template)
2222 LoadLevelTemplate(-1);
2225 LoadLevel_InitVersion(&level, filename);
2226 LoadLevel_InitElements(&level, filename);
2227 LoadLevel_InitPlayfield(&level, filename);
2229 LoadLevel_InitLevel(&level, filename);
2233 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2235 putFileVersion(file, level->file_version);
2236 putFileVersion(file, level->game_version);
2239 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2243 putFile8Bit(file, level->fieldx);
2244 putFile8Bit(file, level->fieldy);
2246 putFile16BitBE(file, level->time);
2247 putFile16BitBE(file, level->gems_needed);
2249 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2250 putFile8Bit(file, level->name[i]);
2252 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2253 putFile8Bit(file, level->score[i]);
2255 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2256 for (y = 0; y < 3; y++)
2257 for (x = 0; x < 3; x++)
2258 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2259 level->yamyam_content[i][x][y]));
2260 putFile8Bit(file, level->amoeba_speed);
2261 putFile8Bit(file, level->time_magic_wall);
2262 putFile8Bit(file, level->time_wheel);
2263 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2264 level->amoeba_content));
2265 putFile8Bit(file, (level->double_speed ? 1 : 0));
2266 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2267 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2268 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2270 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2272 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2273 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2275 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2278 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2282 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2283 putFile8Bit(file, level->author[i]);
2286 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2290 for (y = 0; y < level->fieldy; y++)
2291 for (x = 0; x < level->fieldx; x++)
2292 if (level->encoding_16bit_field)
2293 putFile16BitBE(file, level->field[x][y]);
2295 putFile8Bit(file, level->field[x][y]);
2299 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2303 putFile8Bit(file, EL_YAMYAM);
2304 putFile8Bit(file, level->num_yamyam_contents);
2305 putFile8Bit(file, 0);
2306 putFile8Bit(file, 0);
2308 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2309 for (y = 0; y < 3; y++)
2310 for (x = 0; x < 3; x++)
2311 if (level->encoding_16bit_field)
2312 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2314 putFile8Bit(file, level->yamyam_content[i][x][y]);
2318 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2321 int num_contents, content_xsize, content_ysize;
2322 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2324 if (element == EL_YAMYAM)
2326 num_contents = level->num_yamyam_contents;
2330 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2331 for (y = 0; y < 3; y++)
2332 for (x = 0; x < 3; x++)
2333 content_array[i][x][y] = level->yamyam_content[i][x][y];
2335 else if (element == EL_BD_AMOEBA)
2341 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2342 for (y = 0; y < 3; y++)
2343 for (x = 0; x < 3; x++)
2344 content_array[i][x][y] = EL_EMPTY;
2345 content_array[0][0][0] = level->amoeba_content;
2349 /* chunk header already written -- write empty chunk data */
2350 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2352 Error(ERR_WARN, "cannot save content for element '%d'", element);
2356 putFile16BitBE(file, element);
2357 putFile8Bit(file, num_contents);
2358 putFile8Bit(file, content_xsize);
2359 putFile8Bit(file, content_ysize);
2361 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2363 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2364 for (y = 0; y < 3; y++)
2365 for (x = 0; x < 3; x++)
2366 putFile16BitBE(file, content_array[i][x][y]);
2369 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2372 int envelope_nr = element - EL_ENVELOPE_1;
2373 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2375 putFile16BitBE(file, element);
2376 putFile16BitBE(file, envelope_len);
2377 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2378 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2380 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2382 for (i = 0; i < envelope_len; i++)
2383 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2387 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2388 int num_changed_custom_elements)
2392 putFile16BitBE(file, num_changed_custom_elements);
2394 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2396 int element = EL_CUSTOM_START + i;
2398 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2400 if (check < num_changed_custom_elements)
2402 putFile16BitBE(file, element);
2403 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2410 if (check != num_changed_custom_elements) /* should not happen */
2411 Error(ERR_WARN, "inconsistent number of custom element properties");
2416 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2417 int num_changed_custom_elements)
2421 putFile16BitBE(file, num_changed_custom_elements);
2423 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2425 int element = EL_CUSTOM_START + i;
2427 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2429 if (check < num_changed_custom_elements)
2431 putFile16BitBE(file, element);
2432 putFile16BitBE(file, element_info[element].change->target_element);
2439 if (check != num_changed_custom_elements) /* should not happen */
2440 Error(ERR_WARN, "inconsistent number of custom target elements");
2445 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2446 int num_changed_custom_elements)
2448 int i, j, x, y, check = 0;
2450 putFile16BitBE(file, num_changed_custom_elements);
2452 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2454 int element = EL_CUSTOM_START + i;
2456 if (element_info[element].modified_settings)
2458 if (check < num_changed_custom_elements)
2460 putFile16BitBE(file, element);
2462 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2463 putFile8Bit(file, element_info[element].description[j]);
2465 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2467 /* some free bytes for future properties and padding */
2468 WriteUnusedBytesToFile(file, 7);
2470 putFile8Bit(file, element_info[element].use_gfx_element);
2471 putFile16BitBE(file, element_info[element].gfx_element);
2473 putFile8Bit(file, element_info[element].collect_score);
2474 putFile8Bit(file, element_info[element].collect_count);
2476 putFile16BitBE(file, element_info[element].push_delay_fixed);
2477 putFile16BitBE(file, element_info[element].push_delay_random);
2478 putFile16BitBE(file, element_info[element].move_delay_fixed);
2479 putFile16BitBE(file, element_info[element].move_delay_random);
2481 putFile16BitBE(file, element_info[element].move_pattern);
2482 putFile8Bit(file, element_info[element].move_direction_initial);
2483 putFile8Bit(file, element_info[element].move_stepsize);
2485 for (y = 0; y < 3; y++)
2486 for (x = 0; x < 3; x++)
2487 putFile16BitBE(file, element_info[element].content[x][y]);
2489 putFile32BitBE(file, element_info[element].change->events);
2491 putFile16BitBE(file, element_info[element].change->target_element);
2493 putFile16BitBE(file, element_info[element].change->delay_fixed);
2494 putFile16BitBE(file, element_info[element].change->delay_random);
2495 putFile16BitBE(file, element_info[element].change->delay_frames);
2497 putFile16BitBE(file, element_info[element].change->trigger_element);
2499 putFile8Bit(file, element_info[element].change->explode);
2500 putFile8Bit(file, element_info[element].change->use_content);
2501 putFile8Bit(file, element_info[element].change->only_complete);
2502 putFile8Bit(file, element_info[element].change->use_random_change);
2504 putFile8Bit(file, element_info[element].change->random);
2505 putFile8Bit(file, element_info[element].change->power);
2507 for (y = 0; y < 3; y++)
2508 for (x = 0; x < 3; x++)
2509 putFile16BitBE(file, element_info[element].change->content[x][y]);
2511 putFile8Bit(file, element_info[element].slippery_type);
2513 /* some free bytes for future properties and padding */
2514 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2521 if (check != num_changed_custom_elements) /* should not happen */
2522 Error(ERR_WARN, "inconsistent number of custom element properties");
2526 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2528 struct ElementInfo *ei = &element_info[element];
2531 putFile16BitBE(file, element);
2533 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2534 putFile8Bit(file, ei->description[i]);
2536 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2537 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2539 putFile8Bit(file, ei->num_change_pages);
2541 /* some free bytes for future base property values and padding */
2542 WriteUnusedBytesToFile(file, 5);
2544 /* write custom property values */
2546 putFile8Bit(file, ei->use_gfx_element);
2547 putFile16BitBE(file, ei->gfx_element);
2549 putFile8Bit(file, ei->collect_score);
2550 putFile8Bit(file, ei->collect_count);
2552 putFile16BitBE(file, ei->push_delay_fixed);
2553 putFile16BitBE(file, ei->push_delay_random);
2554 putFile16BitBE(file, ei->move_delay_fixed);
2555 putFile16BitBE(file, ei->move_delay_random);
2557 putFile16BitBE(file, ei->move_pattern);
2558 putFile8Bit(file, ei->move_direction_initial);
2559 putFile8Bit(file, ei->move_stepsize);
2561 putFile8Bit(file, ei->slippery_type);
2563 for (y = 0; y < 3; y++)
2564 for (x = 0; x < 3; x++)
2565 putFile16BitBE(file, ei->content[x][y]);
2567 putFile16BitBE(file, ei->move_enter_element);
2568 putFile16BitBE(file, ei->move_leave_element);
2569 putFile8Bit(file, ei->move_leave_type);
2571 /* some free bytes for future custom property values and padding */
2572 WriteUnusedBytesToFile(file, 7);
2574 /* write change property values */
2576 for (i = 0; i < ei->num_change_pages; i++)
2578 struct ElementChangeInfo *change = &ei->change_page[i];
2580 putFile32BitBE(file, change->events);
2582 putFile16BitBE(file, change->target_element);
2584 putFile16BitBE(file, change->delay_fixed);
2585 putFile16BitBE(file, change->delay_random);
2586 putFile16BitBE(file, change->delay_frames);
2588 putFile16BitBE(file, change->trigger_element);
2590 putFile8Bit(file, change->explode);
2591 putFile8Bit(file, change->use_content);
2592 putFile8Bit(file, change->only_complete);
2593 putFile8Bit(file, change->use_random_change);
2595 putFile8Bit(file, change->random);
2596 putFile8Bit(file, change->power);
2598 for (y = 0; y < 3; y++)
2599 for (x = 0; x < 3; x++)
2600 putFile16BitBE(file, change->content[x][y]);
2602 putFile8Bit(file, change->can_change);
2604 putFile8Bit(file, change->sides);
2606 /* some free bytes for future change property values and padding */
2607 WriteUnusedBytesToFile(file, 8);
2611 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2613 struct ElementInfo *ei = &element_info[element];
2614 struct ElementGroupInfo *group = ei->group;
2617 putFile16BitBE(file, element);
2619 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2620 putFile8Bit(file, ei->description[i]);
2622 putFile8Bit(file, group->num_elements);
2624 putFile8Bit(file, ei->use_gfx_element);
2625 putFile16BitBE(file, ei->gfx_element);
2627 putFile8Bit(file, group->choice_mode);
2629 /* some free bytes for future values and padding */
2630 WriteUnusedBytesToFile(file, 3);
2632 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2633 putFile16BitBE(file, group->element[i]);
2636 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2638 int body_chunk_size;
2642 if (!(file = fopen(filename, MODE_WRITE)))
2644 Error(ERR_WARN, "cannot save level file '%s'", filename);
2648 level->file_version = FILE_VERSION_ACTUAL;
2649 level->game_version = GAME_VERSION_ACTUAL;
2651 /* check level field for 16-bit elements */
2652 level->encoding_16bit_field = FALSE;
2653 for (y = 0; y < level->fieldy; y++)
2654 for (x = 0; x < level->fieldx; x++)
2655 if (level->field[x][y] > 255)
2656 level->encoding_16bit_field = TRUE;
2658 /* check yamyam content for 16-bit elements */
2659 level->encoding_16bit_yamyam = FALSE;
2660 for (i = 0; i < level->num_yamyam_contents; i++)
2661 for (y = 0; y < 3; y++)
2662 for (x = 0; x < 3; x++)
2663 if (level->yamyam_content[i][x][y] > 255)
2664 level->encoding_16bit_yamyam = TRUE;
2666 /* check amoeba content for 16-bit elements */
2667 level->encoding_16bit_amoeba = FALSE;
2668 if (level->amoeba_content > 255)
2669 level->encoding_16bit_amoeba = TRUE;
2671 /* calculate size of "BODY" chunk */
2673 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2675 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2676 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2678 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2679 SaveLevel_VERS(file, level);
2681 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2682 SaveLevel_HEAD(file, level);
2684 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2685 SaveLevel_AUTH(file, level);
2687 putFileChunkBE(file, "BODY", body_chunk_size);
2688 SaveLevel_BODY(file, level);
2690 if (level->encoding_16bit_yamyam ||
2691 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2693 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2694 SaveLevel_CNT2(file, level, EL_YAMYAM);
2697 if (level->encoding_16bit_amoeba)
2699 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2700 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2703 /* check for envelope content */
2704 for (i = 0; i < 4; i++)
2706 if (strlen(level->envelope_text[i]) > 0)
2708 int envelope_len = strlen(level->envelope_text[i]) + 1;
2710 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2711 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2715 /* check for non-default custom elements (unless using template level) */
2716 if (!level->use_custom_template)
2718 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2720 int element = EL_CUSTOM_START + i;
2722 if (element_info[element].modified_settings)
2724 int num_change_pages = element_info[element].num_change_pages;
2726 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2727 SaveLevel_CUS4(file, level, element);
2732 /* check for non-default group elements (unless using template level) */
2733 if (!level->use_custom_template)
2735 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2737 int element = EL_GROUP_START + i;
2739 if (element_info[element].modified_settings)
2741 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2742 SaveLevel_GRP1(file, level, element);
2749 SetFilePermissions(filename, PERMS_PRIVATE);
2752 void SaveLevel(int nr)
2754 char *filename = getDefaultLevelFilename(nr);
2756 SaveLevelFromFilename(&level, filename);
2759 void SaveLevelTemplate()
2761 char *filename = getDefaultLevelFilename(-1);
2763 SaveLevelFromFilename(&level, filename);
2766 void DumpLevel(struct LevelInfo *level)
2768 printf_line("-", 79);
2769 printf("Level xxx (file version %08d, game version %08d)\n",
2770 level->file_version, level->game_version);
2771 printf_line("-", 79);
2773 printf("Level author: '%s'\n", level->author);
2774 printf("Level title: '%s'\n", level->name);
2776 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2778 printf("Level time: %d seconds\n", level->time);
2779 printf("Gems needed: %d\n", level->gems_needed);
2781 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2782 printf("Time for wheel: %d seconds\n", level->time_wheel);
2783 printf("Time for light: %d seconds\n", level->time_light);
2784 printf("Time for timegate: %d seconds\n", level->time_timegate);
2786 printf("Amoeba speed: %d\n", level->amoeba_speed);
2788 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2789 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2790 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2791 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2792 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2794 printf_line("-", 79);
2798 /* ========================================================================= */
2799 /* tape file functions */
2800 /* ========================================================================= */
2802 static void setTapeInfoToDefaults()
2806 /* always start with reliable default values (empty tape) */
2809 /* default values (also for pre-1.2 tapes) with only the first player */
2810 tape.player_participates[0] = TRUE;
2811 for (i = 1; i < MAX_PLAYERS; i++)
2812 tape.player_participates[i] = FALSE;
2814 /* at least one (default: the first) player participates in every tape */
2815 tape.num_participating_players = 1;
2817 tape.level_nr = level_nr;
2819 tape.changed = FALSE;
2821 tape.recording = FALSE;
2822 tape.playing = FALSE;
2823 tape.pausing = FALSE;
2826 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2828 tape->file_version = getFileVersion(file);
2829 tape->game_version = getFileVersion(file);
2834 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2838 tape->random_seed = getFile32BitBE(file);
2839 tape->date = getFile32BitBE(file);
2840 tape->length = getFile32BitBE(file);
2842 /* read header fields that are new since version 1.2 */
2843 if (tape->file_version >= FILE_VERSION_1_2)
2845 byte store_participating_players = getFile8Bit(file);
2848 /* since version 1.2, tapes store which players participate in the tape */
2849 tape->num_participating_players = 0;
2850 for (i = 0; i < MAX_PLAYERS; i++)
2852 tape->player_participates[i] = FALSE;
2854 if (store_participating_players & (1 << i))
2856 tape->player_participates[i] = TRUE;
2857 tape->num_participating_players++;
2861 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2863 engine_version = getFileVersion(file);
2864 if (engine_version > 0)
2865 tape->engine_version = engine_version;
2867 tape->engine_version = tape->game_version;
2873 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2875 int level_identifier_size;
2878 level_identifier_size = getFile16BitBE(file);
2880 tape->level_identifier =
2881 checked_realloc(tape->level_identifier, level_identifier_size);
2883 for (i = 0; i < level_identifier_size; i++)
2884 tape->level_identifier[i] = getFile8Bit(file);
2886 tape->level_nr = getFile16BitBE(file);
2888 chunk_size = 2 + level_identifier_size + 2;
2893 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2896 int chunk_size_expected =
2897 (tape->num_participating_players + 1) * tape->length;
2899 if (chunk_size_expected != chunk_size)
2901 ReadUnusedBytesFromFile(file, chunk_size);
2902 return chunk_size_expected;
2905 for (i = 0; i < tape->length; i++)
2907 if (i >= MAX_TAPELEN)
2910 for (j = 0; j < MAX_PLAYERS; j++)
2912 tape->pos[i].action[j] = MV_NO_MOVING;
2914 if (tape->player_participates[j])
2915 tape->pos[i].action[j] = getFile8Bit(file);
2918 tape->pos[i].delay = getFile8Bit(file);
2920 if (tape->file_version == FILE_VERSION_1_0)
2922 /* eliminate possible diagonal moves in old tapes */
2923 /* this is only for backward compatibility */
2925 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2926 byte action = tape->pos[i].action[0];
2927 int k, num_moves = 0;
2929 for (k = 0; k<4; k++)
2931 if (action & joy_dir[k])
2933 tape->pos[i + num_moves].action[0] = joy_dir[k];
2935 tape->pos[i + num_moves].delay = 0;
2944 tape->length += num_moves;
2947 else if (tape->file_version < FILE_VERSION_2_0)
2949 /* convert pre-2.0 tapes to new tape format */
2951 if (tape->pos[i].delay > 1)
2954 tape->pos[i + 1] = tape->pos[i];
2955 tape->pos[i + 1].delay = 1;
2958 for (j = 0; j < MAX_PLAYERS; j++)
2959 tape->pos[i].action[j] = MV_NO_MOVING;
2960 tape->pos[i].delay--;
2971 if (i != tape->length)
2972 chunk_size = (tape->num_participating_players + 1) * i;
2977 void LoadTapeFromFilename(char *filename)
2979 char cookie[MAX_LINE_LEN];
2980 char chunk_name[CHUNK_ID_LEN + 1];
2984 /* always start with reliable default values */
2985 setTapeInfoToDefaults();
2987 if (!(file = fopen(filename, MODE_READ)))
2990 getFileChunkBE(file, chunk_name, NULL);
2991 if (strcmp(chunk_name, "RND1") == 0)
2993 getFile32BitBE(file); /* not used */
2995 getFileChunkBE(file, chunk_name, NULL);
2996 if (strcmp(chunk_name, "TAPE") != 0)
2998 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3003 else /* check for pre-2.0 file format with cookie string */
3005 strcpy(cookie, chunk_name);
3006 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3007 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3008 cookie[strlen(cookie) - 1] = '\0';
3010 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3012 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3017 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3019 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3024 /* pre-2.0 tape files have no game version, so use file version here */
3025 tape.game_version = tape.file_version;
3028 if (tape.file_version < FILE_VERSION_1_2)
3030 /* tape files from versions before 1.2.0 without chunk structure */
3031 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3032 LoadTape_BODY(file, 2 * tape.length, &tape);
3040 int (*loader)(FILE *, int, struct TapeInfo *);
3044 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3045 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3046 { "INFO", -1, LoadTape_INFO },
3047 { "BODY", -1, LoadTape_BODY },
3051 while (getFileChunkBE(file, chunk_name, &chunk_size))
3055 while (chunk_info[i].name != NULL &&
3056 strcmp(chunk_name, chunk_info[i].name) != 0)
3059 if (chunk_info[i].name == NULL)
3061 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3062 chunk_name, filename);
3063 ReadUnusedBytesFromFile(file, chunk_size);
3065 else if (chunk_info[i].size != -1 &&
3066 chunk_info[i].size != chunk_size)
3068 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3069 chunk_size, chunk_name, filename);
3070 ReadUnusedBytesFromFile(file, chunk_size);
3074 /* call function to load this tape chunk */
3075 int chunk_size_expected =
3076 (chunk_info[i].loader)(file, chunk_size, &tape);
3078 /* the size of some chunks cannot be checked before reading other
3079 chunks first (like "HEAD" and "BODY") that contain some header
3080 information, so check them here */
3081 if (chunk_size_expected != chunk_size)
3083 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3084 chunk_size, chunk_name, filename);
3092 tape.length_seconds = GetTapeLength();
3095 printf("::: tape game version: %d\n", tape.game_version);
3096 printf("::: tape engine version: %d\n", tape.engine_version);
3100 void LoadTape(int nr)
3102 char *filename = getTapeFilename(nr);
3104 LoadTapeFromFilename(filename);
3107 void LoadSolutionTape(int nr)
3109 char *filename = getSolutionTapeFilename(nr);
3111 LoadTapeFromFilename(filename);
3114 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3116 putFileVersion(file, tape->file_version);
3117 putFileVersion(file, tape->game_version);
3120 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3123 byte store_participating_players = 0;
3125 /* set bits for participating players for compact storage */
3126 for (i = 0; i < MAX_PLAYERS; i++)
3127 if (tape->player_participates[i])
3128 store_participating_players |= (1 << i);
3130 putFile32BitBE(file, tape->random_seed);
3131 putFile32BitBE(file, tape->date);
3132 putFile32BitBE(file, tape->length);
3134 putFile8Bit(file, store_participating_players);
3136 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3137 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3139 putFileVersion(file, tape->engine_version);
3142 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3144 int level_identifier_size = strlen(tape->level_identifier) + 1;
3147 putFile16BitBE(file, level_identifier_size);
3149 for (i = 0; i < level_identifier_size; i++)
3150 putFile8Bit(file, tape->level_identifier[i]);
3152 putFile16BitBE(file, tape->level_nr);
3155 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3159 for (i = 0; i < tape->length; i++)
3161 for (j = 0; j < MAX_PLAYERS; j++)
3162 if (tape->player_participates[j])
3163 putFile8Bit(file, tape->pos[i].action[j]);
3165 putFile8Bit(file, tape->pos[i].delay);
3169 void SaveTape(int nr)
3171 char *filename = getTapeFilename(nr);
3173 boolean new_tape = TRUE;
3174 int num_participating_players = 0;
3175 int info_chunk_size;
3176 int body_chunk_size;
3179 InitTapeDirectory(leveldir_current->subdir);
3181 /* if a tape still exists, ask to overwrite it */
3182 if (access(filename, F_OK) == 0)
3185 if (!Request("Replace old tape ?", REQ_ASK))
3189 if (!(file = fopen(filename, MODE_WRITE)))
3191 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3195 tape.file_version = FILE_VERSION_ACTUAL;
3196 tape.game_version = GAME_VERSION_ACTUAL;
3198 /* count number of participating players */
3199 for (i = 0; i < MAX_PLAYERS; i++)
3200 if (tape.player_participates[i])
3201 num_participating_players++;
3203 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3204 body_chunk_size = (num_participating_players + 1) * tape.length;
3206 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3207 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3209 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3210 SaveTape_VERS(file, &tape);
3212 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3213 SaveTape_HEAD(file, &tape);
3215 putFileChunkBE(file, "INFO", info_chunk_size);
3216 SaveTape_INFO(file, &tape);
3218 putFileChunkBE(file, "BODY", body_chunk_size);
3219 SaveTape_BODY(file, &tape);
3223 SetFilePermissions(filename, PERMS_PRIVATE);
3225 tape.changed = FALSE;
3228 Request("tape saved !", REQ_CONFIRM);
3231 void DumpTape(struct TapeInfo *tape)
3235 if (TAPE_IS_EMPTY(*tape))
3237 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3241 printf_line("-", 79);
3242 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3243 tape->level_nr, tape->file_version, tape->game_version);
3244 printf("Level series identifier: '%s'\n", tape->level_identifier);
3245 printf_line("-", 79);
3247 for (i = 0; i < tape->length; i++)
3249 if (i >= MAX_TAPELEN)
3252 printf("%03d: ", i);
3254 for (j = 0; j < MAX_PLAYERS; j++)
3256 if (tape->player_participates[j])
3258 int action = tape->pos[i].action[j];
3260 printf("%d:%02x ", j, action);
3261 printf("[%c%c%c%c|%c%c] - ",
3262 (action & JOY_LEFT ? '<' : ' '),
3263 (action & JOY_RIGHT ? '>' : ' '),
3264 (action & JOY_UP ? '^' : ' '),
3265 (action & JOY_DOWN ? 'v' : ' '),
3266 (action & JOY_BUTTON_1 ? '1' : ' '),
3267 (action & JOY_BUTTON_2 ? '2' : ' '));
3271 printf("(%03d)\n", tape->pos[i].delay);
3274 printf_line("-", 79);
3278 /* ========================================================================= */
3279 /* score file functions */
3280 /* ========================================================================= */
3282 void LoadScore(int nr)
3285 char *filename = getScoreFilename(nr);
3286 char cookie[MAX_LINE_LEN];
3287 char line[MAX_LINE_LEN];
3291 /* always start with reliable default values */
3292 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3294 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3295 highscore[i].Score = 0;
3298 if (!(file = fopen(filename, MODE_READ)))
3301 /* check file identifier */
3302 fgets(cookie, MAX_LINE_LEN, file);
3303 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3304 cookie[strlen(cookie) - 1] = '\0';
3306 if (!checkCookieString(cookie, SCORE_COOKIE))
3308 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3313 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3315 fscanf(file, "%d", &highscore[i].Score);
3316 fgets(line, MAX_LINE_LEN, file);
3318 if (line[strlen(line) - 1] == '\n')
3319 line[strlen(line) - 1] = '\0';
3321 for (line_ptr = line; *line_ptr; line_ptr++)
3323 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3325 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3326 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3335 void SaveScore(int nr)
3338 char *filename = getScoreFilename(nr);
3341 InitScoreDirectory(leveldir_current->subdir);
3343 if (!(file = fopen(filename, MODE_WRITE)))
3345 Error(ERR_WARN, "cannot save score for level %d", nr);
3349 fprintf(file, "%s\n\n", SCORE_COOKIE);
3351 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3352 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3356 SetFilePermissions(filename, PERMS_PUBLIC);
3360 /* ========================================================================= */
3361 /* setup file functions */
3362 /* ========================================================================= */
3364 #define TOKEN_STR_PLAYER_PREFIX "player_"
3367 #define SETUP_TOKEN_PLAYER_NAME 0
3368 #define SETUP_TOKEN_SOUND 1
3369 #define SETUP_TOKEN_SOUND_LOOPS 2
3370 #define SETUP_TOKEN_SOUND_MUSIC 3
3371 #define SETUP_TOKEN_SOUND_SIMPLE 4
3372 #define SETUP_TOKEN_TOONS 5
3373 #define SETUP_TOKEN_SCROLL_DELAY 6
3374 #define SETUP_TOKEN_SOFT_SCROLLING 7
3375 #define SETUP_TOKEN_FADING 8
3376 #define SETUP_TOKEN_AUTORECORD 9
3377 #define SETUP_TOKEN_QUICK_DOORS 10
3378 #define SETUP_TOKEN_TEAM_MODE 11
3379 #define SETUP_TOKEN_HANDICAP 12
3380 #define SETUP_TOKEN_TIME_LIMIT 13
3381 #define SETUP_TOKEN_FULLSCREEN 14
3382 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3383 #define SETUP_TOKEN_GRAPHICS_SET 16
3384 #define SETUP_TOKEN_SOUNDS_SET 17
3385 #define SETUP_TOKEN_MUSIC_SET 18
3386 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3387 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3388 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3390 #define NUM_GLOBAL_SETUP_TOKENS 22
3393 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3394 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3395 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3396 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3397 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3398 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3399 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3400 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3401 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3402 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3403 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3404 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3406 #define NUM_EDITOR_SETUP_TOKENS 12
3408 /* shortcut setup */
3409 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3410 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3411 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3413 #define NUM_SHORTCUT_SETUP_TOKENS 3
3416 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3417 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3418 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3419 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3420 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3421 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3422 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3423 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3424 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3425 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3426 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3427 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3428 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3429 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3430 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3431 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3433 #define NUM_PLAYER_SETUP_TOKENS 16
3436 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3437 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3439 #define NUM_SYSTEM_SETUP_TOKENS 2
3442 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3444 #define NUM_OPTIONS_SETUP_TOKENS 1
3447 static struct SetupInfo si;
3448 static struct SetupEditorInfo sei;
3449 static struct SetupShortcutInfo ssi;
3450 static struct SetupInputInfo sii;
3451 static struct SetupSystemInfo syi;
3452 static struct OptionInfo soi;
3454 static struct TokenInfo global_setup_tokens[] =
3456 { TYPE_STRING, &si.player_name, "player_name" },
3457 { TYPE_SWITCH, &si.sound, "sound" },
3458 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3459 { TYPE_SWITCH, &si.sound_music, "background_music" },
3460 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3461 { TYPE_SWITCH, &si.toons, "toons" },
3462 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3463 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3464 { TYPE_SWITCH, &si.fading, "screen_fading" },
3465 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3466 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3467 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3468 { TYPE_SWITCH, &si.handicap, "handicap" },
3469 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3470 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3471 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3472 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3473 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3474 { TYPE_STRING, &si.music_set, "music_set" },
3475 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3476 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3477 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3480 static struct TokenInfo editor_setup_tokens[] =
3482 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3483 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3484 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3485 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3486 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3487 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3488 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3489 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3490 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3491 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3492 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3493 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3496 static struct TokenInfo shortcut_setup_tokens[] =
3498 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3499 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3500 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3503 static struct TokenInfo player_setup_tokens[] =
3505 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3506 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3507 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3508 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3509 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3510 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3511 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3512 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3513 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3514 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3515 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3516 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3517 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3518 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3519 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3520 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3523 static struct TokenInfo system_setup_tokens[] =
3525 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3526 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3529 static struct TokenInfo options_setup_tokens[] =
3531 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3534 static char *get_corrected_login_name(char *login_name)
3536 /* needed because player name must be a fixed length string */
3537 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3539 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3540 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3542 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3543 if (strchr(login_name_new, ' '))
3544 *strchr(login_name_new, ' ') = '\0';
3546 return login_name_new;
3549 static void setSetupInfoToDefaults(struct SetupInfo *si)
3553 si->player_name = get_corrected_login_name(getLoginName());
3556 si->sound_loops = TRUE;
3557 si->sound_music = TRUE;
3558 si->sound_simple = TRUE;
3560 si->double_buffering = TRUE;
3561 si->direct_draw = !si->double_buffering;
3562 si->scroll_delay = TRUE;
3563 si->soft_scrolling = TRUE;
3565 si->autorecord = TRUE;
3566 si->quick_doors = FALSE;
3567 si->team_mode = FALSE;
3568 si->handicap = TRUE;
3569 si->time_limit = TRUE;
3570 si->fullscreen = FALSE;
3571 si->ask_on_escape = TRUE;
3573 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3574 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3575 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3576 si->override_level_graphics = FALSE;
3577 si->override_level_sounds = FALSE;
3578 si->override_level_music = FALSE;
3580 si->editor.el_boulderdash = TRUE;
3581 si->editor.el_emerald_mine = TRUE;
3582 si->editor.el_more = TRUE;
3583 si->editor.el_sokoban = TRUE;
3584 si->editor.el_supaplex = TRUE;
3585 si->editor.el_diamond_caves = TRUE;
3586 si->editor.el_dx_boulderdash = TRUE;
3587 si->editor.el_chars = TRUE;
3588 si->editor.el_custom = TRUE;
3589 si->editor.el_custom_more = FALSE;
3591 si->editor.el_headlines = TRUE;
3592 si->editor.el_user_defined = FALSE;
3594 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3595 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3596 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3598 for (i = 0; i < MAX_PLAYERS; i++)
3600 si->input[i].use_joystick = FALSE;
3601 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3602 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3603 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3604 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3605 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3606 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3607 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3608 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3609 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3610 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3611 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3612 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3613 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3614 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3615 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3618 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3619 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3621 si->options.verbose = FALSE;
3624 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3628 if (!setup_file_hash)
3633 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3634 setSetupInfo(global_setup_tokens, i,
3635 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3640 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3641 setSetupInfo(editor_setup_tokens, i,
3642 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3645 /* shortcut setup */
3646 ssi = setup.shortcut;
3647 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3648 setSetupInfo(shortcut_setup_tokens, i,
3649 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3650 setup.shortcut = ssi;
3653 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3657 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3659 sii = setup.input[pnr];
3660 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3662 char full_token[100];
3664 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3665 setSetupInfo(player_setup_tokens, i,
3666 getHashEntry(setup_file_hash, full_token));
3668 setup.input[pnr] = sii;
3673 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3674 setSetupInfo(system_setup_tokens, i,
3675 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3679 soi = setup.options;
3680 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3681 setSetupInfo(options_setup_tokens, i,
3682 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3683 setup.options = soi;
3688 char *filename = getSetupFilename();
3689 SetupFileHash *setup_file_hash = NULL;
3691 /* always start with reliable default values */
3692 setSetupInfoToDefaults(&setup);
3694 setup_file_hash = loadSetupFileHash(filename);
3696 if (setup_file_hash)
3698 char *player_name_new;
3700 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3701 decodeSetupFileHash(setup_file_hash);
3703 setup.direct_draw = !setup.double_buffering;
3705 freeSetupFileHash(setup_file_hash);
3707 /* needed to work around problems with fixed length strings */
3708 player_name_new = get_corrected_login_name(setup.player_name);
3709 free(setup.player_name);
3710 setup.player_name = player_name_new;
3713 Error(ERR_WARN, "using default setup values");
3718 char *filename = getSetupFilename();
3722 InitUserDataDirectory();
3724 if (!(file = fopen(filename, MODE_WRITE)))
3726 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3730 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3731 getCookie("SETUP")));
3732 fprintf(file, "\n");
3736 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3738 /* just to make things nicer :) */
3739 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3740 i == SETUP_TOKEN_GRAPHICS_SET)
3741 fprintf(file, "\n");
3743 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3748 fprintf(file, "\n");
3749 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3750 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3752 /* shortcut setup */
3753 ssi = setup.shortcut;
3754 fprintf(file, "\n");
3755 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3756 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3759 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3763 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3764 fprintf(file, "\n");
3766 sii = setup.input[pnr];
3767 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3768 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3773 fprintf(file, "\n");
3774 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3775 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3778 soi = setup.options;
3779 fprintf(file, "\n");
3780 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3781 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3785 SetFilePermissions(filename, PERMS_PRIVATE);
3788 void LoadCustomElementDescriptions()
3790 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3791 SetupFileHash *setup_file_hash;
3794 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3796 if (element_info[i].custom_description != NULL)
3798 free(element_info[i].custom_description);
3799 element_info[i].custom_description = NULL;
3803 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3806 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3808 char *token = getStringCat2(element_info[i].token_name, ".name");
3809 char *value = getHashEntry(setup_file_hash, token);
3812 element_info[i].custom_description = getStringCopy(value);
3817 freeSetupFileHash(setup_file_hash);
3820 void LoadSpecialMenuDesignSettings()
3822 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3823 SetupFileHash *setup_file_hash;
3826 /* always start with reliable default values from default config */
3827 for (i = 0; image_config_vars[i].token != NULL; i++)
3828 for (j = 0; image_config[j].token != NULL; j++)
3829 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3830 *image_config_vars[i].value =
3831 get_auto_parameter_value(image_config_vars[i].token,
3832 image_config[j].value);
3834 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3837 /* special case: initialize with default values that may be overwritten */
3838 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3840 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3841 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3842 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3844 if (value_x != NULL)
3845 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3846 if (value_y != NULL)
3847 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3848 if (list_size != NULL)
3849 menu.list_size[i] = get_integer_from_string(list_size);
3852 /* read (and overwrite with) values that may be specified in config file */
3853 for (i = 0; image_config_vars[i].token != NULL; i++)
3855 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3858 *image_config_vars[i].value =
3859 get_auto_parameter_value(image_config_vars[i].token, value);
3862 freeSetupFileHash(setup_file_hash);
3865 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3867 char *filename = getEditorSetupFilename();
3868 SetupFileList *setup_file_list, *list;
3869 SetupFileHash *element_hash;
3870 int num_unknown_tokens = 0;
3873 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3876 element_hash = newSetupFileHash();
3878 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3879 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3881 /* determined size may be larger than needed (due to unknown elements) */
3883 for (list = setup_file_list; list != NULL; list = list->next)
3886 /* add space for up to 3 more elements for padding that may be needed */
3889 *elements = checked_malloc(*num_elements * sizeof(int));
3892 for (list = setup_file_list; list != NULL; list = list->next)
3894 char *value = getHashEntry(element_hash, list->token);
3898 (*elements)[(*num_elements)++] = atoi(value);
3902 if (num_unknown_tokens == 0)
3904 Error(ERR_RETURN_LINE, "-");
3905 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3906 Error(ERR_RETURN, "- config file: '%s'", filename);
3908 num_unknown_tokens++;
3911 Error(ERR_RETURN, "- token: '%s'", list->token);
3915 if (num_unknown_tokens > 0)
3916 Error(ERR_RETURN_LINE, "-");
3918 while (*num_elements % 4) /* pad with empty elements, if needed */
3919 (*elements)[(*num_elements)++] = EL_EMPTY;
3921 freeSetupFileList(setup_file_list);
3922 freeSetupFileHash(element_hash);
3926 for (i = 0; i < *num_elements; i++)
3927 printf("editor: element '%s' [%d]\n",
3928 element_info[(*elements)[i]].token_name, (*elements)[i]);
3932 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3935 SetupFileHash *setup_file_hash = NULL;
3936 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3937 char *filename_music, *filename_prefix, *filename_info;
3943 token_to_value_ptr[] =
3945 { "title_header", &tmp_music_file_info.title_header },
3946 { "artist_header", &tmp_music_file_info.artist_header },
3947 { "album_header", &tmp_music_file_info.album_header },
3948 { "year_header", &tmp_music_file_info.year_header },
3950 { "title", &tmp_music_file_info.title },
3951 { "artist", &tmp_music_file_info.artist },
3952 { "album", &tmp_music_file_info.album },
3953 { "year", &tmp_music_file_info.year },
3959 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3960 getCustomMusicFilename(basename));
3962 if (filename_music == NULL)
3965 /* ---------- try to replace file extension ---------- */
3967 filename_prefix = getStringCopy(filename_music);
3968 if (strrchr(filename_prefix, '.') != NULL)
3969 *strrchr(filename_prefix, '.') = '\0';
3970 filename_info = getStringCat2(filename_prefix, ".txt");
3973 printf("trying to load file '%s'...\n", filename_info);
3976 if (fileExists(filename_info))
3977 setup_file_hash = loadSetupFileHash(filename_info);
3979 free(filename_prefix);
3980 free(filename_info);
3982 if (setup_file_hash == NULL)
3984 /* ---------- try to add file extension ---------- */
3986 filename_prefix = getStringCopy(filename_music);
3987 filename_info = getStringCat2(filename_prefix, ".txt");
3990 printf("trying to load file '%s'...\n", filename_info);
3993 if (fileExists(filename_info))
3994 setup_file_hash = loadSetupFileHash(filename_info);
3996 free(filename_prefix);
3997 free(filename_info);
4000 if (setup_file_hash == NULL)
4003 /* ---------- music file info found ---------- */
4005 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4007 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4009 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4011 *token_to_value_ptr[i].value_ptr =
4012 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4015 tmp_music_file_info.basename = getStringCopy(basename);
4016 tmp_music_file_info.music = music;
4017 tmp_music_file_info.is_sound = is_sound;
4019 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4020 *new_music_file_info = tmp_music_file_info;
4022 return new_music_file_info;
4025 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4027 return get_music_file_info_ext(basename, music, FALSE);
4030 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4032 return get_music_file_info_ext(basename, sound, TRUE);
4035 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4036 char *basename, boolean is_sound)
4038 for (; list != NULL; list = list->next)
4039 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4045 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4047 return music_info_listed_ext(list, basename, FALSE);
4050 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4052 return music_info_listed_ext(list, basename, TRUE);
4055 void LoadMusicInfo()
4057 char *music_directory = getCustomMusicDirectory();
4058 int num_music = getMusicListSize();
4059 int num_music_noconf = 0;
4060 int num_sounds = getSoundListSize();
4062 struct dirent *dir_entry;
4063 struct FileInfo *music, *sound;
4064 struct MusicFileInfo *next, **new;
4067 while (music_file_info != NULL)
4069 next = music_file_info->next;
4071 checked_free(music_file_info->basename);
4073 checked_free(music_file_info->title_header);
4074 checked_free(music_file_info->artist_header);
4075 checked_free(music_file_info->album_header);
4076 checked_free(music_file_info->year_header);
4078 checked_free(music_file_info->title);
4079 checked_free(music_file_info->artist);
4080 checked_free(music_file_info->album);
4081 checked_free(music_file_info->year);
4083 free(music_file_info);
4085 music_file_info = next;
4088 new = &music_file_info;
4091 printf("::: num_music == %d\n", num_music);
4094 for (i = 0; i < num_music; i++)
4096 music = getMusicListEntry(i);
4099 printf("::: %d [%08x]\n", i, music->filename);
4102 if (music->filename == NULL)
4105 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4108 /* a configured file may be not recognized as music */
4109 if (!FileIsMusic(music->filename))
4113 printf("::: -> '%s' (configured)\n", music->filename);
4116 if (!music_info_listed(music_file_info, music->filename))
4118 *new = get_music_file_info(music->filename, i);
4120 new = &(*new)->next;
4124 if ((dir = opendir(music_directory)) == NULL)
4126 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4130 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4132 char *basename = dir_entry->d_name;
4133 boolean music_already_used = FALSE;
4136 /* skip all music files that are configured in music config file */
4137 for (i = 0; i < num_music; i++)
4139 music = getMusicListEntry(i);
4141 if (music->filename == NULL)
4144 if (strcmp(basename, music->filename) == 0)
4146 music_already_used = TRUE;
4151 if (music_already_used)
4154 if (!FileIsMusic(basename))
4158 printf("::: -> '%s' (found in directory)\n", basename);
4161 if (!music_info_listed(music_file_info, basename))
4163 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4165 new = &(*new)->next;
4173 for (i = 0; i < num_sounds; i++)
4175 sound = getSoundListEntry(i);
4177 if (sound->filename == NULL)
4180 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4183 /* a configured file may be not recognized as sound */
4184 if (!FileIsSound(sound->filename))
4188 printf("::: -> '%s' (configured)\n", sound->filename);
4191 if (!sound_info_listed(music_file_info, sound->filename))
4193 *new = get_sound_file_info(sound->filename, i);
4195 new = &(*new)->next;
4201 for (next = music_file_info; next != NULL; next = next->next)
4202 printf("::: title == '%s'\n", next->title);
4206 void add_helpanim_entry(int element, int action, int direction, int delay,
4207 int *num_list_entries)
4209 struct HelpAnimInfo *new_list_entry;
4210 (*num_list_entries)++;
4213 checked_realloc(helpanim_info,
4214 *num_list_entries * sizeof(struct HelpAnimInfo));
4215 new_list_entry = &helpanim_info[*num_list_entries - 1];
4217 new_list_entry->element = element;
4218 new_list_entry->action = action;
4219 new_list_entry->direction = direction;
4220 new_list_entry->delay = delay;
4223 void print_unknown_token(char *filename, char *token, int token_nr)
4227 Error(ERR_RETURN_LINE, "-");
4228 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4229 Error(ERR_RETURN, "- config file: '%s'", filename);
4232 Error(ERR_RETURN, "- token: '%s'", token);
4235 void print_unknown_token_end(int token_nr)
4238 Error(ERR_RETURN_LINE, "-");
4241 void LoadHelpAnimInfo()
4243 char *filename = getHelpAnimFilename();
4244 SetupFileList *setup_file_list = NULL, *list;
4245 SetupFileHash *element_hash, *action_hash, *direction_hash;
4246 int num_list_entries = 0;
4247 int num_unknown_tokens = 0;
4250 if (fileExists(filename))
4251 setup_file_list = loadSetupFileList(filename);
4253 if (setup_file_list == NULL)
4255 /* use reliable default values from static configuration */
4256 SetupFileList *insert_ptr;
4258 insert_ptr = setup_file_list =
4259 newSetupFileList(helpanim_config[0].token,
4260 helpanim_config[0].value);
4262 for (i = 1; helpanim_config[i].token; i++)
4263 insert_ptr = addListEntry(insert_ptr,
4264 helpanim_config[i].token,
4265 helpanim_config[i].value);
4268 element_hash = newSetupFileHash();
4269 action_hash = newSetupFileHash();
4270 direction_hash = newSetupFileHash();
4272 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4273 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4275 for (i = 0; i < NUM_ACTIONS; i++)
4276 setHashEntry(action_hash, element_action_info[i].suffix,
4277 i_to_a(element_action_info[i].value));
4279 /* do not store direction index (bit) here, but direction value! */
4280 for (i = 0; i < NUM_DIRECTIONS; i++)
4281 setHashEntry(direction_hash, element_direction_info[i].suffix,
4282 i_to_a(1 << element_direction_info[i].value));
4284 for (list = setup_file_list; list != NULL; list = list->next)
4286 char *element_token, *action_token, *direction_token;
4287 char *element_value, *action_value, *direction_value;
4288 int delay = atoi(list->value);
4290 if (strcmp(list->token, "end") == 0)
4292 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4297 /* first try to break element into element/action/direction parts;
4298 if this does not work, also accept combined "element[.act][.dir]"
4299 elements (like "dynamite.active"), which are unique elements */
4301 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4303 element_value = getHashEntry(element_hash, list->token);
4304 if (element_value != NULL) /* element found */
4305 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4309 /* no further suffixes found -- this is not an element */
4310 print_unknown_token(filename, list->token, num_unknown_tokens++);
4316 /* token has format "<prefix>.<something>" */
4318 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4319 direction_token = action_token; /* ... or direction */
4321 element_token = getStringCopy(list->token);
4322 *strchr(element_token, '.') = '\0';
4324 element_value = getHashEntry(element_hash, element_token);
4326 if (element_value == NULL) /* this is no element */
4328 element_value = getHashEntry(element_hash, list->token);
4329 if (element_value != NULL) /* combined element found */
4330 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4333 print_unknown_token(filename, list->token, num_unknown_tokens++);
4335 free(element_token);
4340 action_value = getHashEntry(action_hash, action_token);
4342 if (action_value != NULL) /* action found */
4344 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4347 free(element_token);
4352 direction_value = getHashEntry(direction_hash, direction_token);
4354 if (direction_value != NULL) /* direction found */
4356 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4359 free(element_token);
4364 if (strchr(action_token + 1, '.') == NULL)
4366 /* no further suffixes found -- this is not an action nor direction */
4368 element_value = getHashEntry(element_hash, list->token);
4369 if (element_value != NULL) /* combined element found */
4370 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4373 print_unknown_token(filename, list->token, num_unknown_tokens++);
4375 free(element_token);
4380 /* token has format "<prefix>.<suffix>.<something>" */
4382 direction_token = strchr(action_token + 1, '.');
4384 action_token = getStringCopy(action_token);
4385 *strchr(action_token + 1, '.') = '\0';
4387 action_value = getHashEntry(action_hash, action_token);
4389 if (action_value == NULL) /* this is no action */
4391 element_value = getHashEntry(element_hash, list->token);
4392 if (element_value != NULL) /* combined element found */
4393 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4396 print_unknown_token(filename, list->token, num_unknown_tokens++);
4398 free(element_token);
4404 direction_value = getHashEntry(direction_hash, direction_token);
4406 if (direction_value != NULL) /* direction found */
4408 add_helpanim_entry(atoi(element_value), atoi(action_value),
4409 atoi(direction_value), delay, &num_list_entries);
4411 free(element_token);
4417 /* this is no direction */
4419 element_value = getHashEntry(element_hash, list->token);
4420 if (element_value != NULL) /* combined element found */
4421 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4424 print_unknown_token(filename, list->token, num_unknown_tokens++);
4426 free(element_token);
4430 print_unknown_token_end(num_unknown_tokens);
4432 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4433 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4435 freeSetupFileList(setup_file_list);
4436 freeSetupFileHash(element_hash);
4437 freeSetupFileHash(action_hash);
4438 freeSetupFileHash(direction_hash);
4442 for (i = 0; i < num_list_entries; i++)
4443 printf("::: %d, %d, %d => %d\n",
4444 helpanim_info[i].element,
4445 helpanim_info[i].action,
4446 helpanim_info[i].direction,
4447 helpanim_info[i].delay);
4451 void LoadHelpTextInfo()
4453 char *filename = getHelpTextFilename();
4456 if (helptext_info != NULL)
4458 freeSetupFileHash(helptext_info);
4459 helptext_info = NULL;
4462 if (fileExists(filename))
4463 helptext_info = loadSetupFileHash(filename);
4465 if (helptext_info == NULL)
4467 /* use reliable default values from static configuration */
4468 helptext_info = newSetupFileHash();
4470 for (i = 0; helptext_config[i].token; i++)
4471 setHashEntry(helptext_info,
4472 helptext_config[i].token,
4473 helptext_config[i].value);
4478 BEGIN_HASH_ITERATION(helptext_info, itr)
4480 printf("::: '%s' => '%s'\n",
4481 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4483 END_HASH_ITERATION(hash, itr)