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].access_protected = 0;
235 element_info[element].walk_to_action = 0;
236 element_info[element].smash_targets = 0;
237 element_info[element].deadliness = 0;
238 element_info[element].consistency = 0;
240 element_info[element].can_explode_by_fire = FALSE;
241 element_info[element].can_explode_smashed = FALSE;
242 element_info[element].can_explode_impact = FALSE;
244 element_info[element].current_change_page = 0;
246 /* start with no properties at all */
247 for (j = 0; j < NUM_EP_BITFIELDS; j++)
248 Properties[element][j] = EP_BITMASK_DEFAULT;
250 element_info[element].modified_settings = FALSE;
252 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
254 /* initialize memory for list of elements in group */
255 if (element_info[element].group == NULL)
256 element_info[element].group =
257 checked_malloc(sizeof(struct ElementGroupInfo));
259 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
260 element_info[element].group->element[j] = EL_EMPTY_SPACE;
262 /* default: only one element in group */
263 element_info[element].group->num_elements = 1;
265 element_info[element].group->choice_mode = ANIM_RANDOM;
269 BorderElement = EL_STEELWALL;
271 level->no_level_file = FALSE;
273 if (leveldir_current == NULL) /* only when dumping level */
276 /* try to determine better author name than 'anonymous' */
277 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
279 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
280 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
284 switch (LEVELCLASS(leveldir_current))
286 case LEVELCLASS_TUTORIAL:
287 strcpy(level->author, PROGRAM_AUTHOR_STRING);
290 case LEVELCLASS_CONTRIB:
291 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
292 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
295 case LEVELCLASS_PRIVATE:
296 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
297 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
301 /* keep default value */
307 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
309 level_file_info->nr = 0;
310 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
311 level_file_info->packed = FALSE;
312 level_file_info->basename = NULL;
313 level_file_info->filename = NULL;
316 static void ActivateLevelTemplate()
318 /* Currently there is no special action needed to activate the template
319 data, because 'element_info' and 'Properties' overwrite the original
320 level data, while all other variables do not change. */
323 static char *getLevelFilenameFromBasename(char *basename)
325 static char *filename = NULL;
327 checked_free(filename);
329 filename = getPath2(getCurrentLevelDir(), basename);
334 static int getFileTypeFromBasename(char *basename)
336 static char *filename = NULL;
337 struct stat file_status;
339 /* ---------- try to determine file type from filename ---------- */
341 /* check for typical filename of a Supaplex level package file */
342 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
343 strncmp(basename, "LEVELS.D", 8) == 0))
344 return LEVEL_FILE_TYPE_SP;
346 /* ---------- try to determine file type from filesize ---------- */
348 checked_free(filename);
349 filename = getPath2(getCurrentLevelDir(), basename);
351 if (stat(filename, &file_status) == 0)
353 /* check for typical filesize of a Supaplex level package file */
354 if (file_status.st_size == 170496)
355 return LEVEL_FILE_TYPE_SP;
358 return LEVEL_FILE_TYPE_UNKNOWN;
361 static char *getSingleLevelBasename(int nr, int type)
363 static char basename[MAX_FILENAME_LEN];
364 char *level_filename = getStringCopy(leveldir_current->level_filename);
366 if (level_filename == NULL)
367 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
371 case LEVEL_FILE_TYPE_RND:
373 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
375 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
378 case LEVEL_FILE_TYPE_EM:
379 sprintf(basename, "%d", nr);
382 case LEVEL_FILE_TYPE_UNKNOWN:
384 sprintf(basename, level_filename, nr);
388 free(level_filename);
393 static char *getPackedLevelBasename(int type)
395 static char basename[MAX_FILENAME_LEN];
396 char *directory = getCurrentLevelDir();
398 struct dirent *dir_entry;
400 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
402 if ((dir = opendir(directory)) == NULL)
404 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
409 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
411 char *entry_basename = dir_entry->d_name;
412 int entry_type = getFileTypeFromBasename(entry_basename);
414 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
416 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
419 strcpy(basename, entry_basename);
431 static char *getSingleLevelFilename(int nr, int type)
433 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
437 static char *getPackedLevelFilename(int type)
439 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
443 char *getDefaultLevelFilename(int nr)
445 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
448 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
453 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
454 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
457 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
462 lfi->basename = getPackedLevelBasename(lfi->type);
463 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
466 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
468 /* special case: level number is negative => check for level template file */
471 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
476 if (leveldir_current->level_filename != NULL)
478 /* check for file name/pattern specified in "levelinfo.conf" */
479 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
480 if (fileExists(lfi->filename))
484 /* check for native Rocks'n'Diamonds level file */
485 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
486 if (fileExists(lfi->filename))
489 /* check for classic Emerald Mine level file */
490 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
491 if (fileExists(lfi->filename))
494 /* check for various packed level file formats */
495 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
496 if (fileExists(lfi->filename))
499 /* no known level file found -- try to use default values */
500 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
503 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
505 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
506 lfi->type = getFileTypeFromBasename(lfi->basename);
509 static struct LevelFileInfo *getLevelFileInfo(int nr)
511 static struct LevelFileInfo level_file_info;
513 /* always start with reliable default values */
514 setFileInfoToDefaults(&level_file_info);
516 level_file_info.nr = nr; /* set requested level number */
518 determineLevelFileInfo_Filename(&level_file_info);
519 determineLevelFileInfo_Filetype(&level_file_info);
521 return &level_file_info;
525 /* ------------------------------------------------------------------------- */
526 /* functions for loading R'n'D level */
527 /* ------------------------------------------------------------------------- */
529 int getMappedElement(int element)
531 /* map some (historic, now obsolete) elements */
536 case EL_PLAYER_OBSOLETE:
537 element = EL_PLAYER_1;
540 case EL_KEY_OBSOLETE:
543 case EL_EM_KEY_1_FILE_OBSOLETE:
544 element = EL_EM_KEY_1;
547 case EL_EM_KEY_2_FILE_OBSOLETE:
548 element = EL_EM_KEY_2;
551 case EL_EM_KEY_3_FILE_OBSOLETE:
552 element = EL_EM_KEY_3;
555 case EL_EM_KEY_4_FILE_OBSOLETE:
556 element = EL_EM_KEY_4;
559 case EL_ENVELOPE_OBSOLETE:
560 element = EL_ENVELOPE_1;
568 if (element >= NUM_FILE_ELEMENTS)
570 Error(ERR_WARN, "invalid level element %d", element);
572 element = EL_UNKNOWN;
577 if (element >= NUM_FILE_ELEMENTS)
579 Error(ERR_WARN, "invalid level element %d", element);
581 element = EL_UNKNOWN;
583 else if (element == EL_PLAYER_OBSOLETE)
584 element = EL_PLAYER_1;
585 else if (element == EL_KEY_OBSOLETE)
592 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
594 level->file_version = getFileVersion(file);
595 level->game_version = getFileVersion(file);
600 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
604 level->fieldx = getFile8Bit(file);
605 level->fieldy = getFile8Bit(file);
607 level->time = getFile16BitBE(file);
608 level->gems_needed = getFile16BitBE(file);
610 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
611 level->name[i] = getFile8Bit(file);
612 level->name[MAX_LEVEL_NAME_LEN] = 0;
614 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
615 level->score[i] = getFile8Bit(file);
617 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
618 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
619 for (y = 0; y < 3; y++)
620 for (x = 0; x < 3; x++)
621 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
623 level->amoeba_speed = getFile8Bit(file);
624 level->time_magic_wall = getFile8Bit(file);
625 level->time_wheel = getFile8Bit(file);
626 level->amoeba_content = getMappedElement(getFile8Bit(file));
627 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
628 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
629 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
630 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
632 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
634 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
635 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
637 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
642 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
646 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
647 level->author[i] = getFile8Bit(file);
648 level->author[MAX_LEVEL_NAME_LEN] = 0;
653 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
656 int chunk_size_expected = level->fieldx * level->fieldy;
658 /* Note: "chunk_size" was wrong before version 2.0 when elements are
659 stored with 16-bit encoding (and should be twice as big then).
660 Even worse, playfield data was stored 16-bit when only yamyam content
661 contained 16-bit elements and vice versa. */
663 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
664 chunk_size_expected *= 2;
666 if (chunk_size_expected != chunk_size)
668 ReadUnusedBytesFromFile(file, chunk_size);
669 return chunk_size_expected;
672 for (y = 0; y < level->fieldy; y++)
673 for (x = 0; x < level->fieldx; x++)
675 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
680 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
684 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
685 int chunk_size_expected = header_size + content_size;
687 /* Note: "chunk_size" was wrong before version 2.0 when elements are
688 stored with 16-bit encoding (and should be twice as big then).
689 Even worse, playfield data was stored 16-bit when only yamyam content
690 contained 16-bit elements and vice versa. */
692 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
693 chunk_size_expected += content_size;
695 if (chunk_size_expected != chunk_size)
697 ReadUnusedBytesFromFile(file, chunk_size);
698 return chunk_size_expected;
702 level->num_yamyam_contents = getFile8Bit(file);
706 /* correct invalid number of content fields -- should never happen */
707 if (level->num_yamyam_contents < 1 ||
708 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
709 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
711 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
712 for (y = 0; y < 3; y++)
713 for (x = 0; x < 3; x++)
714 level->yamyam_content[i][x][y] =
715 getMappedElement(level->encoding_16bit_field ?
716 getFile16BitBE(file) : getFile8Bit(file));
720 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
724 int num_contents, content_xsize, content_ysize;
725 int content_array[MAX_ELEMENT_CONTENTS][3][3];
727 element = getMappedElement(getFile16BitBE(file));
728 num_contents = getFile8Bit(file);
729 content_xsize = getFile8Bit(file);
730 content_ysize = getFile8Bit(file);
732 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
734 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
735 for (y = 0; y < 3; y++)
736 for (x = 0; x < 3; x++)
737 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
739 /* correct invalid number of content fields -- should never happen */
740 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
741 num_contents = STD_ELEMENT_CONTENTS;
743 if (element == EL_YAMYAM)
745 level->num_yamyam_contents = num_contents;
747 for (i = 0; i < num_contents; i++)
748 for (y = 0; y < 3; y++)
749 for (x = 0; x < 3; x++)
750 level->yamyam_content[i][x][y] = content_array[i][x][y];
752 else if (element == EL_BD_AMOEBA)
754 level->amoeba_content = content_array[0][0][0];
758 Error(ERR_WARN, "cannot load content for element '%d'", element);
764 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
770 int chunk_size_expected;
772 element = getMappedElement(getFile16BitBE(file));
773 if (!IS_ENVELOPE(element))
774 element = EL_ENVELOPE_1;
776 envelope_nr = element - EL_ENVELOPE_1;
778 envelope_len = getFile16BitBE(file);
780 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
781 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
783 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
785 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
786 if (chunk_size_expected != chunk_size)
788 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
789 return chunk_size_expected;
792 for (i = 0; i < envelope_len; i++)
793 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
798 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
800 int num_changed_custom_elements = getFile16BitBE(file);
801 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
804 if (chunk_size_expected != chunk_size)
806 ReadUnusedBytesFromFile(file, chunk_size - 2);
807 return chunk_size_expected;
810 for (i = 0; i < num_changed_custom_elements; i++)
812 int element = getFile16BitBE(file);
813 int properties = getFile32BitBE(file);
815 if (IS_CUSTOM_ELEMENT(element))
816 Properties[element][EP_BITFIELD_BASE] = properties;
818 Error(ERR_WARN, "invalid custom element number %d", element);
824 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
826 int num_changed_custom_elements = getFile16BitBE(file);
827 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
830 if (chunk_size_expected != chunk_size)
832 ReadUnusedBytesFromFile(file, chunk_size - 2);
833 return chunk_size_expected;
836 for (i = 0; i < num_changed_custom_elements; i++)
838 int element = getFile16BitBE(file);
839 int custom_target_element = getFile16BitBE(file);
841 if (IS_CUSTOM_ELEMENT(element))
842 element_info[element].change->target_element = custom_target_element;
844 Error(ERR_WARN, "invalid custom element number %d", element);
850 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
852 int num_changed_custom_elements = getFile16BitBE(file);
853 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
856 if (chunk_size_expected != chunk_size)
858 ReadUnusedBytesFromFile(file, chunk_size - 2);
859 return chunk_size_expected;
862 for (i = 0; i < num_changed_custom_elements; i++)
864 int element = getFile16BitBE(file);
866 if (!IS_CUSTOM_ELEMENT(element))
868 Error(ERR_WARN, "invalid custom element number %d", element);
870 element = EL_INTERNAL_DUMMY;
873 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
874 element_info[element].description[j] = getFile8Bit(file);
875 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
877 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
879 /* some free bytes for future properties and padding */
880 ReadUnusedBytesFromFile(file, 7);
882 element_info[element].use_gfx_element = getFile8Bit(file);
883 element_info[element].gfx_element =
884 getMappedElement(getFile16BitBE(file));
886 element_info[element].collect_score = getFile8Bit(file);
887 element_info[element].collect_count = getFile8Bit(file);
889 element_info[element].push_delay_fixed = getFile16BitBE(file);
890 element_info[element].push_delay_random = getFile16BitBE(file);
891 element_info[element].move_delay_fixed = getFile16BitBE(file);
892 element_info[element].move_delay_random = getFile16BitBE(file);
894 element_info[element].move_pattern = getFile16BitBE(file);
895 element_info[element].move_direction_initial = getFile8Bit(file);
896 element_info[element].move_stepsize = getFile8Bit(file);
898 for (y = 0; y < 3; y++)
899 for (x = 0; x < 3; x++)
900 element_info[element].content[x][y] =
901 getMappedElement(getFile16BitBE(file));
903 element_info[element].change->events = getFile32BitBE(file);
905 element_info[element].change->target_element =
906 getMappedElement(getFile16BitBE(file));
908 element_info[element].change->delay_fixed = getFile16BitBE(file);
909 element_info[element].change->delay_random = getFile16BitBE(file);
910 element_info[element].change->delay_frames = getFile16BitBE(file);
912 element_info[element].change->trigger_element =
913 getMappedElement(getFile16BitBE(file));
915 element_info[element].change->explode = getFile8Bit(file);
916 element_info[element].change->use_content = getFile8Bit(file);
917 element_info[element].change->only_complete = getFile8Bit(file);
918 element_info[element].change->use_random_change = getFile8Bit(file);
920 element_info[element].change->random = getFile8Bit(file);
921 element_info[element].change->power = getFile8Bit(file);
923 for (y = 0; y < 3; y++)
924 for (x = 0; x < 3; x++)
925 element_info[element].change->content[x][y] =
926 getMappedElement(getFile16BitBE(file));
928 element_info[element].slippery_type = getFile8Bit(file);
930 /* some free bytes for future properties and padding */
931 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
933 /* mark that this custom element has been modified */
934 element_info[element].modified_settings = TRUE;
940 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
942 struct ElementInfo *ei;
943 int chunk_size_expected;
947 element = getFile16BitBE(file);
949 if (!IS_CUSTOM_ELEMENT(element))
951 Error(ERR_WARN, "invalid custom element number %d", element);
953 ReadUnusedBytesFromFile(file, chunk_size - 2);
957 ei = &element_info[element];
959 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
960 ei->description[i] = getFile8Bit(file);
961 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
963 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
964 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
966 ei->num_change_pages = getFile8Bit(file);
968 /* some free bytes for future base property values and padding */
969 ReadUnusedBytesFromFile(file, 5);
971 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
972 if (chunk_size_expected != chunk_size)
974 ReadUnusedBytesFromFile(file, chunk_size - 48);
975 return chunk_size_expected;
978 /* read custom property values */
980 ei->use_gfx_element = getFile8Bit(file);
981 ei->gfx_element = getMappedElement(getFile16BitBE(file));
983 ei->collect_score = getFile8Bit(file);
984 ei->collect_count = getFile8Bit(file);
986 ei->push_delay_fixed = getFile16BitBE(file);
987 ei->push_delay_random = getFile16BitBE(file);
988 ei->move_delay_fixed = getFile16BitBE(file);
989 ei->move_delay_random = getFile16BitBE(file);
991 ei->move_pattern = getFile16BitBE(file);
992 ei->move_direction_initial = getFile8Bit(file);
993 ei->move_stepsize = getFile8Bit(file);
995 ei->slippery_type = getFile8Bit(file);
997 for (y = 0; y < 3; y++)
998 for (x = 0; x < 3; x++)
999 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1001 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1002 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1003 ei->move_leave_type = getFile8Bit(file);
1005 /* some free bytes for future custom property values and padding */
1006 ReadUnusedBytesFromFile(file, 7);
1008 /* read change property values */
1010 setElementChangePages(ei, ei->num_change_pages);
1012 for (i = 0; i < ei->num_change_pages; i++)
1014 struct ElementChangeInfo *change = &ei->change_page[i];
1016 /* always start with reliable default values */
1017 setElementChangeInfoToDefaults(change);
1019 change->events = getFile32BitBE(file);
1021 change->target_element = getMappedElement(getFile16BitBE(file));
1023 change->delay_fixed = getFile16BitBE(file);
1024 change->delay_random = getFile16BitBE(file);
1025 change->delay_frames = getFile16BitBE(file);
1027 change->trigger_element = getMappedElement(getFile16BitBE(file));
1029 change->explode = getFile8Bit(file);
1030 change->use_content = getFile8Bit(file);
1031 change->only_complete = getFile8Bit(file);
1032 change->use_random_change = getFile8Bit(file);
1034 change->random = getFile8Bit(file);
1035 change->power = getFile8Bit(file);
1037 for (y = 0; y < 3; y++)
1038 for (x = 0; x < 3; x++)
1039 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1041 change->can_change = getFile8Bit(file);
1043 change->sides = getFile8Bit(file);
1045 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
1046 change->sides = CH_SIDE_ANY;
1048 /* some free bytes for future change property values and padding */
1049 ReadUnusedBytesFromFile(file, 8);
1052 /* mark this custom element as modified */
1053 ei->modified_settings = TRUE;
1058 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1060 struct ElementInfo *ei;
1061 struct ElementGroupInfo *group;
1065 element = getFile16BitBE(file);
1067 if (!IS_GROUP_ELEMENT(element))
1069 Error(ERR_WARN, "invalid group element number %d", element);
1071 ReadUnusedBytesFromFile(file, chunk_size - 2);
1075 ei = &element_info[element];
1077 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1078 ei->description[i] = getFile8Bit(file);
1079 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1081 group = element_info[element].group;
1083 group->num_elements = getFile8Bit(file);
1085 ei->use_gfx_element = getFile8Bit(file);
1086 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1088 group->choice_mode = getFile8Bit(file);
1090 /* some free bytes for future values and padding */
1091 ReadUnusedBytesFromFile(file, 3);
1093 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1094 group->element[i] = getMappedElement(getFile16BitBE(file));
1096 /* mark this group element as modified */
1097 element_info[element].modified_settings = TRUE;
1102 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1103 struct LevelFileInfo *level_file_info)
1105 char *filename = level_file_info->filename;
1106 char cookie[MAX_LINE_LEN];
1107 char chunk_name[CHUNK_ID_LEN + 1];
1111 if (!(file = fopen(filename, MODE_READ)))
1113 level->no_level_file = TRUE;
1115 if (level != &level_template)
1116 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1121 getFileChunkBE(file, chunk_name, NULL);
1122 if (strcmp(chunk_name, "RND1") == 0)
1124 getFile32BitBE(file); /* not used */
1126 getFileChunkBE(file, chunk_name, NULL);
1127 if (strcmp(chunk_name, "CAVE") != 0)
1129 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1134 else /* check for pre-2.0 file format with cookie string */
1136 strcpy(cookie, chunk_name);
1137 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1138 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1139 cookie[strlen(cookie) - 1] = '\0';
1141 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1143 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1148 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1150 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1155 /* pre-2.0 level files have no game version, so use file version here */
1156 level->game_version = level->file_version;
1159 if (level->file_version < FILE_VERSION_1_2)
1161 /* level files from versions before 1.2.0 without chunk structure */
1162 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1163 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1171 int (*loader)(FILE *, int, struct LevelInfo *);
1175 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1176 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1177 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1178 { "BODY", -1, LoadLevel_BODY },
1179 { "CONT", -1, LoadLevel_CONT },
1180 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1181 { "CNT3", -1, LoadLevel_CNT3 },
1182 { "CUS1", -1, LoadLevel_CUS1 },
1183 { "CUS2", -1, LoadLevel_CUS2 },
1184 { "CUS3", -1, LoadLevel_CUS3 },
1185 { "CUS4", -1, LoadLevel_CUS4 },
1186 { "GRP1", -1, LoadLevel_GRP1 },
1190 while (getFileChunkBE(file, chunk_name, &chunk_size))
1194 while (chunk_info[i].name != NULL &&
1195 strcmp(chunk_name, chunk_info[i].name) != 0)
1198 if (chunk_info[i].name == NULL)
1200 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1201 chunk_name, filename);
1202 ReadUnusedBytesFromFile(file, chunk_size);
1204 else if (chunk_info[i].size != -1 &&
1205 chunk_info[i].size != chunk_size)
1207 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1208 chunk_size, chunk_name, filename);
1209 ReadUnusedBytesFromFile(file, chunk_size);
1213 /* call function to load this level chunk */
1214 int chunk_size_expected =
1215 (chunk_info[i].loader)(file, chunk_size, level);
1217 /* the size of some chunks cannot be checked before reading other
1218 chunks first (like "HEAD" and "BODY") that contain some header
1219 information, so check them here */
1220 if (chunk_size_expected != chunk_size)
1222 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1223 chunk_size, chunk_name, filename);
1232 /* ------------------------------------------------------------------------- */
1233 /* functions for loading EM level */
1234 /* ------------------------------------------------------------------------- */
1236 static int map_em_element_yam(int element)
1240 case 0x00: return EL_EMPTY;
1241 case 0x01: return EL_EMERALD;
1242 case 0x02: return EL_DIAMOND;
1243 case 0x03: return EL_ROCK;
1244 case 0x04: return EL_ROBOT;
1245 case 0x05: return EL_SPACESHIP_UP;
1246 case 0x06: return EL_BOMB;
1247 case 0x07: return EL_BUG_UP;
1248 case 0x08: return EL_AMOEBA_DROP;
1249 case 0x09: return EL_NUT;
1250 case 0x0a: return EL_YAMYAM;
1251 case 0x0b: return EL_QUICKSAND_FULL;
1252 case 0x0c: return EL_SAND;
1253 case 0x0d: return EL_WALL_SLIPPERY;
1254 case 0x0e: return EL_STEELWALL;
1255 case 0x0f: return EL_WALL;
1256 case 0x10: return EL_EM_KEY_1;
1257 case 0x11: return EL_EM_KEY_2;
1258 case 0x12: return EL_EM_KEY_4;
1259 case 0x13: return EL_EM_KEY_3;
1260 case 0x14: return EL_MAGIC_WALL;
1261 case 0x15: return EL_ROBOT_WHEEL;
1262 case 0x16: return EL_DYNAMITE;
1264 case 0x17: return EL_EM_KEY_1; /* EMC */
1265 case 0x18: return EL_BUG_UP; /* EMC */
1266 case 0x1a: return EL_DIAMOND; /* EMC */
1267 case 0x1b: return EL_EMERALD; /* EMC */
1268 case 0x25: return EL_NUT; /* EMC */
1269 case 0x80: return EL_EMPTY; /* EMC */
1270 case 0x85: return EL_EM_KEY_1; /* EMC */
1271 case 0x86: return EL_EM_KEY_2; /* EMC */
1272 case 0x87: return EL_EM_KEY_4; /* EMC */
1273 case 0x88: return EL_EM_KEY_3; /* EMC */
1274 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1275 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1276 case 0xaf: return EL_DYNAMITE; /* EMC */
1277 case 0xbd: return EL_SAND; /* EMC */
1280 Error(ERR_WARN, "invalid level element %d", element);
1285 static int map_em_element_field(int element)
1287 if (element >= 0xc8 && element <= 0xe1)
1288 return EL_CHAR_A + (element - 0xc8);
1289 else if (element >= 0xe2 && element <= 0xeb)
1290 return EL_CHAR_0 + (element - 0xe2);
1294 case 0x00: return EL_ROCK;
1295 case 0x01: return EL_ROCK; /* EMC */
1296 case 0x02: return EL_DIAMOND;
1297 case 0x03: return EL_DIAMOND;
1298 case 0x04: return EL_ROBOT;
1299 case 0x05: return EL_ROBOT; /* EMC */
1300 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1301 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1302 case 0x08: return EL_SPACESHIP_UP;
1303 case 0x09: return EL_SPACESHIP_RIGHT;
1304 case 0x0a: return EL_SPACESHIP_DOWN;
1305 case 0x0b: return EL_SPACESHIP_LEFT;
1306 case 0x0c: return EL_SPACESHIP_UP;
1307 case 0x0d: return EL_SPACESHIP_RIGHT;
1308 case 0x0e: return EL_SPACESHIP_DOWN;
1309 case 0x0f: return EL_SPACESHIP_LEFT;
1311 case 0x10: return EL_BOMB;
1312 case 0x11: return EL_BOMB; /* EMC */
1313 case 0x12: return EL_EMERALD;
1314 case 0x13: return EL_EMERALD;
1315 case 0x14: return EL_BUG_UP;
1316 case 0x15: return EL_BUG_RIGHT;
1317 case 0x16: return EL_BUG_DOWN;
1318 case 0x17: return EL_BUG_LEFT;
1319 case 0x18: return EL_BUG_UP;
1320 case 0x19: return EL_BUG_RIGHT;
1321 case 0x1a: return EL_BUG_DOWN;
1322 case 0x1b: return EL_BUG_LEFT;
1323 case 0x1c: return EL_AMOEBA_DROP;
1324 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1325 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1326 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1328 case 0x20: return EL_ROCK;
1329 case 0x21: return EL_BOMB; /* EMC */
1330 case 0x22: return EL_DIAMOND; /* EMC */
1331 case 0x23: return EL_EMERALD; /* EMC */
1332 case 0x24: return EL_MAGIC_WALL;
1333 case 0x25: return EL_NUT;
1334 case 0x26: return EL_NUT; /* EMC */
1335 case 0x27: return EL_NUT; /* EMC */
1337 /* looks like magic wheel, but is _always_ activated */
1338 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1340 case 0x29: return EL_YAMYAM; /* up */
1341 case 0x2a: return EL_YAMYAM; /* down */
1342 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1343 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1344 case 0x2d: return EL_QUICKSAND_FULL;
1345 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1346 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1348 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1349 case 0x31: return EL_SAND; /* EMC */
1350 case 0x32: return EL_SAND; /* EMC */
1351 case 0x33: return EL_SAND; /* EMC */
1352 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1353 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1354 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1355 case 0x37: return EL_SAND; /* EMC */
1356 case 0x38: return EL_ROCK; /* EMC */
1357 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1358 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1359 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1360 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1361 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1362 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1363 case 0x3f: return EL_ACID_POOL_BOTTOM;
1365 case 0x40: return EL_EXIT_OPEN; /* 1 */
1366 case 0x41: return EL_EXIT_OPEN; /* 2 */
1367 case 0x42: return EL_EXIT_OPEN; /* 3 */
1368 case 0x43: return EL_BALLOON; /* EMC */
1369 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1370 case 0x45: return EL_SPRING; /* EMC */
1371 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1372 case 0x47: return EL_SPRING; /* left */ /* EMC */
1373 case 0x48: return EL_SPRING; /* right */ /* EMC */
1374 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1375 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1376 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1377 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1378 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1379 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1380 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1382 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1383 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1384 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1385 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1386 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1387 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1388 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1389 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1390 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1391 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1392 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1393 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1394 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1395 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1396 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1397 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1399 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1400 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1401 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1402 case 0x63: return EL_SPRING; /* left */ /* EMC */
1403 case 0x64: return EL_SPRING; /* right */ /* EMC */
1404 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1405 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1406 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1407 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1408 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1409 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1410 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1411 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1412 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1413 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1414 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1416 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1417 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1418 case 0x72: return EL_NUT; /* left */ /* EMC */
1419 case 0x73: return EL_SAND; /* EMC (? "nut") */
1420 case 0x74: return EL_STEELWALL;
1421 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1422 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1423 case 0x77: return EL_BOMB; /* left */ /* EMC */
1424 case 0x78: return EL_BOMB; /* right */ /* EMC */
1425 case 0x79: return EL_ROCK; /* left */ /* EMC */
1426 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1427 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1428 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1429 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1430 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1431 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1433 case 0x80: return EL_EMPTY;
1434 case 0x81: return EL_WALL_SLIPPERY;
1435 case 0x82: return EL_SAND;
1436 case 0x83: return EL_STEELWALL;
1437 case 0x84: return EL_WALL;
1438 case 0x85: return EL_EM_KEY_1;
1439 case 0x86: return EL_EM_KEY_2;
1440 case 0x87: return EL_EM_KEY_4;
1441 case 0x88: return EL_EM_KEY_3;
1442 case 0x89: return EL_EM_GATE_1;
1443 case 0x8a: return EL_EM_GATE_2;
1444 case 0x8b: return EL_EM_GATE_4;
1445 case 0x8c: return EL_EM_GATE_3;
1446 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1447 case 0x8e: return EL_EM_GATE_1_GRAY;
1448 case 0x8f: return EL_EM_GATE_2_GRAY;
1450 case 0x90: return EL_EM_GATE_4_GRAY;
1451 case 0x91: return EL_EM_GATE_3_GRAY;
1452 case 0x92: return EL_MAGIC_WALL;
1453 case 0x93: return EL_ROBOT_WHEEL;
1454 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1455 case 0x95: return EL_ACID_POOL_TOPLEFT;
1456 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1457 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1458 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1459 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1460 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1461 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1462 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1463 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1464 case 0x9e: return EL_EXIT_CLOSED;
1465 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1467 /* looks like normal sand, but behaves like wall */
1468 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1469 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1470 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1471 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1472 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1473 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1474 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1475 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1476 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1477 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1478 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1479 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1480 case 0xac: return EL_CHAR_COMMA; /* EMC */
1481 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1482 case 0xae: return EL_CHAR_MINUS; /* EMC */
1483 case 0xaf: return EL_DYNAMITE;
1485 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1486 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1487 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1488 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1489 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1490 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1491 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1492 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1493 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1494 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1495 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1496 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1497 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1498 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1499 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1500 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1502 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1503 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1504 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1505 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1506 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1507 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1508 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1509 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1511 /* characters: see above */
1513 case 0xec: return EL_CHAR_PERIOD;
1514 case 0xed: return EL_CHAR_EXCLAM;
1515 case 0xee: return EL_CHAR_COLON;
1516 case 0xef: return EL_CHAR_QUESTION;
1518 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1519 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1520 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1521 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1522 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1523 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1524 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1525 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1527 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1528 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1529 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1530 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1531 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1532 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1534 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1535 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1538 /* should never happen (all 8-bit value cases should be handled) */
1539 Error(ERR_WARN, "invalid level element %d", element);
1544 #define EM_LEVEL_SIZE 2106
1545 #define EM_LEVEL_XSIZE 64
1546 #define EM_LEVEL_YSIZE 32
1548 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1549 struct LevelFileInfo *level_file_info)
1551 char *filename = level_file_info->filename;
1553 unsigned char leveldata[EM_LEVEL_SIZE];
1554 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1555 unsigned char code0 = 0x65;
1556 unsigned char code1 = 0x11;
1557 boolean level_is_crypted = FALSE;
1558 int nr = level_file_info->nr;
1561 if (!(file = fopen(filename, MODE_READ)))
1563 level->no_level_file = TRUE;
1565 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1570 for(i = 0; i < EM_LEVEL_SIZE; i++)
1571 leveldata[i] = fgetc(file);
1575 /* check if level data is crypted by testing against known starting bytes
1576 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1578 if ((leveldata[0] == 0xf1 ||
1579 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1581 level_is_crypted = TRUE;
1583 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1584 leveldata[0] = 0xf1;
1587 if (level_is_crypted) /* decode crypted level data */
1589 for(i = 0; i < EM_LEVEL_SIZE; i++)
1591 leveldata[i] ^= code0;
1592 leveldata[i] -= code1;
1594 code0 = (code0 + 7) & 0xff;
1598 level->fieldx = EM_LEVEL_XSIZE;
1599 level->fieldy = EM_LEVEL_YSIZE;
1601 level->time = header[46] * 10;
1602 level->gems_needed = header[47];
1604 /* The original Emerald Mine levels have their level number stored
1605 at the second byte of the level file...
1606 Do not trust this information at other level files, e.g. EMC,
1607 but correct it anyway (normally the first row is completely
1608 steel wall, so the correction does not hurt anyway). */
1610 if (leveldata[1] == nr)
1611 leveldata[1] = leveldata[2]; /* correct level number field */
1613 sprintf(level->name, "Level %d", nr); /* set level name */
1615 level->score[SC_EMERALD] = header[36];
1616 level->score[SC_DIAMOND] = header[37];
1617 level->score[SC_ROBOT] = header[38];
1618 level->score[SC_SPACESHIP] = header[39];
1619 level->score[SC_BUG] = header[40];
1620 level->score[SC_YAMYAM] = header[41];
1621 level->score[SC_NUT] = header[42];
1622 level->score[SC_DYNAMITE] = header[43];
1623 level->score[SC_TIME_BONUS] = header[44];
1625 level->num_yamyam_contents = 4;
1627 for(i = 0; i < level->num_yamyam_contents; i++)
1628 for(y = 0; y < 3; y++)
1629 for(x = 0; x < 3; x++)
1630 level->yamyam_content[i][x][y] =
1631 map_em_element_yam(header[i * 9 + y * 3 + x]);
1633 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1634 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1635 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1636 level->amoeba_content = EL_DIAMOND;
1638 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1640 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1642 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1643 new_element = EL_AMOEBA_WET;
1645 level->field[x][y] = new_element;
1648 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1649 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1650 level->field[x][y] = EL_PLAYER_1;
1652 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1653 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1654 level->field[x][y] = EL_PLAYER_2;
1657 /* ------------------------------------------------------------------------- */
1658 /* functions for loading SP level */
1659 /* ------------------------------------------------------------------------- */
1661 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1662 #define SP_LEVEL_SIZE 1536
1663 #define SP_LEVEL_XSIZE 60
1664 #define SP_LEVEL_YSIZE 24
1665 #define SP_LEVEL_NAME_LEN 23
1667 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1672 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1673 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1675 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1677 int element_old = fgetc(file);
1680 if (element_old <= 0x27)
1681 element_new = getMappedElement(EL_SP_START + element_old);
1682 else if (element_old == 0x28)
1683 element_new = EL_INVISIBLE_WALL;
1686 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1687 Error(ERR_WARN, "invalid level element %d", element_old);
1689 element_new = EL_UNKNOWN;
1692 level->field[x][y] = element_new;
1696 ReadUnusedBytesFromFile(file, 4);
1698 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1699 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1701 ReadUnusedBytesFromFile(file, 1);
1703 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1704 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1705 level->name[i] = fgetc(file);
1706 level->name[SP_LEVEL_NAME_LEN] = '\0';
1708 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1709 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1711 /* number of infotrons needed; 0 means that Supaplex will count the total
1712 amount of infotrons in the level and use the low byte of that number.
1713 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1714 level->gems_needed = fgetc(file);
1716 /* information about special gravity port entries */
1717 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1719 level->fieldx = SP_LEVEL_XSIZE;
1720 level->fieldy = SP_LEVEL_YSIZE;
1722 level->time = 0; /* no time limit */
1723 level->amoeba_speed = 0;
1724 level->time_magic_wall = 0;
1725 level->time_wheel = 0;
1726 level->amoeba_content = EL_EMPTY;
1728 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1729 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1731 /* there are no yamyams in supaplex levels */
1732 for(i = 0; i < level->num_yamyam_contents; i++)
1733 for(y = 0; y < 3; y++)
1734 for(x = 0; x < 3; x++)
1735 level->yamyam_content[i][x][y] = EL_EMPTY;
1738 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1739 struct LevelFileInfo *level_file_info)
1741 char *filename = level_file_info->filename;
1743 int nr = level_file_info->nr - leveldir_current->first_level;
1745 char name_first, name_last;
1746 struct LevelInfo multipart_level;
1747 int multipart_xpos, multipart_ypos;
1748 boolean is_multipart_level;
1749 boolean is_first_part;
1750 boolean reading_multipart_level = FALSE;
1751 boolean use_empty_level = FALSE;
1753 if (!(file = fopen(filename, MODE_READ)))
1755 level->no_level_file = TRUE;
1757 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1762 /* position file stream to the requested level inside the level package */
1763 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1765 level->no_level_file = TRUE;
1767 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1772 /* there exist Supaplex level package files with multi-part levels which
1773 can be detected as follows: instead of leading and trailing dashes ('-')
1774 to pad the level name, they have leading and trailing numbers which are
1775 the x and y coordinations of the current part of the multi-part level;
1776 if there are '?' characters instead of numbers on the left or right side
1777 of the level name, the multi-part level consists of only horizontal or
1780 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1782 LoadLevelFromFileStream_SP(file, level, l);
1784 /* check if this level is a part of a bigger multi-part level */
1786 name_first = level->name[0];
1787 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1789 is_multipart_level =
1790 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1791 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1794 ((name_first == '?' || name_first == '1') &&
1795 (name_last == '?' || name_last == '1'));
1797 /* correct leading multipart level meta information in level name */
1798 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1799 level->name[i] = '-';
1801 /* correct trailing multipart level meta information in level name */
1802 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1803 level->name[i] = '-';
1805 /* ---------- check for normal single level ---------- */
1807 if (!reading_multipart_level && !is_multipart_level)
1809 /* the current level is simply a normal single-part level, and we are
1810 not reading a multi-part level yet, so return the level as it is */
1815 /* ---------- check for empty level (unused multi-part) ---------- */
1817 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1819 /* this is a part of a multi-part level, but not the first part
1820 (and we are not already reading parts of a multi-part level);
1821 in this case, use an empty level instead of the single part */
1823 use_empty_level = TRUE;
1828 /* ---------- check for finished multi-part level ---------- */
1830 if (reading_multipart_level &&
1831 (!is_multipart_level ||
1832 strcmp(level->name, multipart_level.name) != 0))
1834 /* we are already reading parts of a multi-part level, but this level is
1835 either not a multi-part level, or a part of a different multi-part
1836 level; in both cases, the multi-part level seems to be complete */
1841 /* ---------- here we have one part of a multi-part level ---------- */
1843 reading_multipart_level = TRUE;
1845 if (is_first_part) /* start with first part of new multi-part level */
1847 /* copy level info structure from first part */
1848 multipart_level = *level;
1850 /* clear playfield of new multi-part level */
1851 for (y = 0; y < MAX_LEV_FIELDY; y++)
1852 for (x = 0; x < MAX_LEV_FIELDX; x++)
1853 multipart_level.field[x][y] = EL_EMPTY;
1856 if (name_first == '?')
1858 if (name_last == '?')
1861 multipart_xpos = (int)(name_first - '0');
1862 multipart_ypos = (int)(name_last - '0');
1865 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1866 multipart_xpos, multipart_ypos, multipart_level.name);
1869 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1870 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1872 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1877 multipart_level.fieldx = MAX(multipart_level.fieldx,
1878 multipart_xpos * SP_LEVEL_XSIZE);
1879 multipart_level.fieldy = MAX(multipart_level.fieldy,
1880 multipart_ypos * SP_LEVEL_YSIZE);
1882 /* copy level part at the right position of multi-part level */
1883 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1885 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1887 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1888 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1890 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1897 if (use_empty_level)
1899 setLevelInfoToDefaults(level);
1901 level->fieldx = SP_LEVEL_XSIZE;
1902 level->fieldy = SP_LEVEL_YSIZE;
1904 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1905 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1906 level->field[x][y] = EL_EMPTY;
1908 strcpy(level->name, "-------- EMPTY --------");
1910 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1913 if (reading_multipart_level)
1914 *level = multipart_level;
1917 /* ------------------------------------------------------------------------- */
1918 /* functions for loading generic level */
1919 /* ------------------------------------------------------------------------- */
1921 void LoadLevelFromFileInfo(struct LevelInfo *level,
1922 struct LevelFileInfo *level_file_info)
1924 /* always start with reliable default values */
1925 setLevelInfoToDefaults(level);
1927 switch (level_file_info->type)
1929 case LEVEL_FILE_TYPE_RND:
1930 LoadLevelFromFileInfo_RND(level, level_file_info);
1933 case LEVEL_FILE_TYPE_EM:
1934 LoadLevelFromFileInfo_EM(level, level_file_info);
1937 case LEVEL_FILE_TYPE_SP:
1938 LoadLevelFromFileInfo_SP(level, level_file_info);
1942 LoadLevelFromFileInfo_RND(level, level_file_info);
1947 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1949 static struct LevelFileInfo level_file_info;
1951 /* always start with reliable default values */
1952 setFileInfoToDefaults(&level_file_info);
1954 level_file_info.nr = 0; /* unknown level number */
1955 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1956 level_file_info.filename = filename;
1958 LoadLevelFromFileInfo(level, &level_file_info);
1961 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1963 if (leveldir_current == NULL) /* only when dumping level */
1967 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1970 /* determine correct game engine version of current level */
1972 if (!leveldir_current->latest_engine)
1974 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1975 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1976 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1980 printf("\n::: This level is private or contributed: '%s'\n", filename);
1984 printf("\n::: Use the stored game engine version for this level\n");
1987 /* For all levels which are not forced to use the latest game engine
1988 version (normally user contributed, private and undefined levels),
1989 use the version of the game engine the levels were created for.
1991 Since 2.0.1, the game engine version is now directly stored
1992 in the level file (chunk "VERS"), so there is no need anymore
1993 to set the game version from the file version (except for old,
1994 pre-2.0 levels, where the game version is still taken from the
1995 file format version used to store the level -- see above). */
1997 /* do some special adjustments to support older level versions */
1998 if (level->file_version == FILE_VERSION_1_0)
2000 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2001 Error(ERR_WARN, "using high speed movement for player");
2003 /* player was faster than monsters in (pre-)1.0 levels */
2004 level->double_speed = TRUE;
2007 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2008 if (level->game_version == VERSION_IDENT(2,0,1,0))
2009 level->em_slippery_gems = TRUE;
2014 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2015 leveldir_current->sort_priority, filename);
2019 printf("\n::: Use latest game engine version for this level.\n");
2022 /* For all levels which are forced to use the latest game engine version
2023 (normally all but user contributed, private and undefined levels), set
2024 the game engine version to the actual version; this allows for actual
2025 corrections in the game engine to take effect for existing, converted
2026 levels (from "classic" or other existing games) to make the emulation
2027 of the corresponding game more accurate, while (hopefully) not breaking
2028 existing levels created from other players. */
2031 printf("::: changing engine from %d to %d\n",
2032 level->game_version, GAME_VERSION_ACTUAL);
2035 level->game_version = GAME_VERSION_ACTUAL;
2037 /* Set special EM style gems behaviour: EM style gems slip down from
2038 normal, steel and growing wall. As this is a more fundamental change,
2039 it seems better to set the default behaviour to "off" (as it is more
2040 natural) and make it configurable in the level editor (as a property
2041 of gem style elements). Already existing converted levels (neither
2042 private nor contributed levels) are changed to the new behaviour. */
2044 if (level->file_version < FILE_VERSION_2_0)
2045 level->em_slippery_gems = TRUE;
2049 printf("::: => %d\n", level->game_version);
2053 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2057 /* map custom element change events that have changed in newer versions
2058 (these following values were accidentally changed in version 3.0.1) */
2059 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2061 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2063 int element = EL_CUSTOM_START + i;
2065 /* order of checking and copying events to be mapped is important */
2066 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2068 if (HAS_CHANGE_EVENT(element, j - 2))
2070 SET_CHANGE_EVENT(element, j - 2, FALSE);
2071 SET_CHANGE_EVENT(element, j, TRUE);
2075 /* order of checking and copying events to be mapped is important */
2076 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2078 if (HAS_CHANGE_EVENT(element, j - 1))
2080 SET_CHANGE_EVENT(element, j - 1, FALSE);
2081 SET_CHANGE_EVENT(element, j, TRUE);
2087 /* some custom element change events get mapped since version 3.0.3 */
2088 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2090 int element = EL_CUSTOM_START + i;
2092 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2093 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2095 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2096 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2098 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2102 /* initialize "can_change" field for old levels with only one change page */
2103 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2105 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2107 int element = EL_CUSTOM_START + i;
2109 if (CAN_CHANGE(element))
2110 element_info[element].change->can_change = TRUE;
2115 /* set default push delay values (corrected since version 3.0.7-1) */
2116 if (level->game_version < VERSION_IDENT(3,0,7,1))
2118 game.default_push_delay_fixed = 2;
2119 game.default_push_delay_random = 8;
2123 game.default_push_delay_fixed = 8;
2124 game.default_push_delay_random = 8;
2127 /* set uninitialized push delay values of custom elements in older levels */
2128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2130 int element = EL_CUSTOM_START + i;
2132 if (element_info[element].push_delay_fixed == -1)
2133 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2134 if (element_info[element].push_delay_random == -1)
2135 element_info[element].push_delay_random = game.default_push_delay_random;
2139 /* initialize element properties for level editor etc. */
2140 InitElementPropertiesEngine(level->game_version);
2143 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2147 /* map elements that have changed in newer versions */
2148 for (y = 0; y < level->fieldy; y++)
2150 for (x = 0; x < level->fieldx; x++)
2152 int element = level->field[x][y];
2154 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2156 /* map game font elements */
2157 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2158 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2159 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2160 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2163 if (level->game_version < VERSION_IDENT(3,0,0,0))
2165 /* map Supaplex gravity tube elements */
2166 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2167 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2168 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2169 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2173 level->field[x][y] = element;
2177 /* copy elements to runtime playfield array */
2178 for (x = 0; x < MAX_LEV_FIELDX; x++)
2179 for (y = 0; y < MAX_LEV_FIELDY; y++)
2180 Feld[x][y] = level->field[x][y];
2182 /* initialize level size variables for faster access */
2183 lev_fieldx = level->fieldx;
2184 lev_fieldy = level->fieldy;
2186 /* determine border element for this level */
2190 void LoadLevelTemplate(int nr)
2193 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2194 char *filename = level_file_info->filename;
2196 LoadLevelFromFileInfo(&level_template, level_file_info);
2198 char *filename = getDefaultLevelFilename(nr);
2200 LoadLevelFromFilename_RND(&level_template, filename);
2203 LoadLevel_InitVersion(&level, filename);
2204 LoadLevel_InitElements(&level, filename);
2206 ActivateLevelTemplate();
2209 void LoadLevel(int nr)
2212 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2213 char *filename = level_file_info->filename;
2215 LoadLevelFromFileInfo(&level, level_file_info);
2217 char *filename = getLevelFilename(nr);
2219 LoadLevelFromFilename_RND(&level, filename);
2222 if (level.use_custom_template)
2223 LoadLevelTemplate(-1);
2226 LoadLevel_InitVersion(&level, filename);
2227 LoadLevel_InitElements(&level, filename);
2228 LoadLevel_InitPlayfield(&level, filename);
2230 LoadLevel_InitLevel(&level, filename);
2234 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2236 putFileVersion(file, level->file_version);
2237 putFileVersion(file, level->game_version);
2240 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2244 putFile8Bit(file, level->fieldx);
2245 putFile8Bit(file, level->fieldy);
2247 putFile16BitBE(file, level->time);
2248 putFile16BitBE(file, level->gems_needed);
2250 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2251 putFile8Bit(file, level->name[i]);
2253 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2254 putFile8Bit(file, level->score[i]);
2256 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2257 for (y = 0; y < 3; y++)
2258 for (x = 0; x < 3; x++)
2259 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2260 level->yamyam_content[i][x][y]));
2261 putFile8Bit(file, level->amoeba_speed);
2262 putFile8Bit(file, level->time_magic_wall);
2263 putFile8Bit(file, level->time_wheel);
2264 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2265 level->amoeba_content));
2266 putFile8Bit(file, (level->double_speed ? 1 : 0));
2267 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2268 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2269 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2271 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2273 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2274 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2276 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2279 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2283 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2284 putFile8Bit(file, level->author[i]);
2287 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2291 for (y = 0; y < level->fieldy; y++)
2292 for (x = 0; x < level->fieldx; x++)
2293 if (level->encoding_16bit_field)
2294 putFile16BitBE(file, level->field[x][y]);
2296 putFile8Bit(file, level->field[x][y]);
2300 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2304 putFile8Bit(file, EL_YAMYAM);
2305 putFile8Bit(file, level->num_yamyam_contents);
2306 putFile8Bit(file, 0);
2307 putFile8Bit(file, 0);
2309 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2310 for (y = 0; y < 3; y++)
2311 for (x = 0; x < 3; x++)
2312 if (level->encoding_16bit_field)
2313 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2315 putFile8Bit(file, level->yamyam_content[i][x][y]);
2319 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2322 int num_contents, content_xsize, content_ysize;
2323 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2325 if (element == EL_YAMYAM)
2327 num_contents = level->num_yamyam_contents;
2331 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2332 for (y = 0; y < 3; y++)
2333 for (x = 0; x < 3; x++)
2334 content_array[i][x][y] = level->yamyam_content[i][x][y];
2336 else if (element == EL_BD_AMOEBA)
2342 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2343 for (y = 0; y < 3; y++)
2344 for (x = 0; x < 3; x++)
2345 content_array[i][x][y] = EL_EMPTY;
2346 content_array[0][0][0] = level->amoeba_content;
2350 /* chunk header already written -- write empty chunk data */
2351 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2353 Error(ERR_WARN, "cannot save content for element '%d'", element);
2357 putFile16BitBE(file, element);
2358 putFile8Bit(file, num_contents);
2359 putFile8Bit(file, content_xsize);
2360 putFile8Bit(file, content_ysize);
2362 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2364 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2365 for (y = 0; y < 3; y++)
2366 for (x = 0; x < 3; x++)
2367 putFile16BitBE(file, content_array[i][x][y]);
2370 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2373 int envelope_nr = element - EL_ENVELOPE_1;
2374 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2376 putFile16BitBE(file, element);
2377 putFile16BitBE(file, envelope_len);
2378 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2379 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2381 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2383 for (i = 0; i < envelope_len; i++)
2384 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2388 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2389 int num_changed_custom_elements)
2393 putFile16BitBE(file, num_changed_custom_elements);
2395 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2397 int element = EL_CUSTOM_START + i;
2399 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2401 if (check < num_changed_custom_elements)
2403 putFile16BitBE(file, element);
2404 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2411 if (check != num_changed_custom_elements) /* should not happen */
2412 Error(ERR_WARN, "inconsistent number of custom element properties");
2417 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2418 int num_changed_custom_elements)
2422 putFile16BitBE(file, num_changed_custom_elements);
2424 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2426 int element = EL_CUSTOM_START + i;
2428 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2430 if (check < num_changed_custom_elements)
2432 putFile16BitBE(file, element);
2433 putFile16BitBE(file, element_info[element].change->target_element);
2440 if (check != num_changed_custom_elements) /* should not happen */
2441 Error(ERR_WARN, "inconsistent number of custom target elements");
2446 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2447 int num_changed_custom_elements)
2449 int i, j, x, y, check = 0;
2451 putFile16BitBE(file, num_changed_custom_elements);
2453 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2455 int element = EL_CUSTOM_START + i;
2457 if (element_info[element].modified_settings)
2459 if (check < num_changed_custom_elements)
2461 putFile16BitBE(file, element);
2463 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2464 putFile8Bit(file, element_info[element].description[j]);
2466 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2468 /* some free bytes for future properties and padding */
2469 WriteUnusedBytesToFile(file, 7);
2471 putFile8Bit(file, element_info[element].use_gfx_element);
2472 putFile16BitBE(file, element_info[element].gfx_element);
2474 putFile8Bit(file, element_info[element].collect_score);
2475 putFile8Bit(file, element_info[element].collect_count);
2477 putFile16BitBE(file, element_info[element].push_delay_fixed);
2478 putFile16BitBE(file, element_info[element].push_delay_random);
2479 putFile16BitBE(file, element_info[element].move_delay_fixed);
2480 putFile16BitBE(file, element_info[element].move_delay_random);
2482 putFile16BitBE(file, element_info[element].move_pattern);
2483 putFile8Bit(file, element_info[element].move_direction_initial);
2484 putFile8Bit(file, element_info[element].move_stepsize);
2486 for (y = 0; y < 3; y++)
2487 for (x = 0; x < 3; x++)
2488 putFile16BitBE(file, element_info[element].content[x][y]);
2490 putFile32BitBE(file, element_info[element].change->events);
2492 putFile16BitBE(file, element_info[element].change->target_element);
2494 putFile16BitBE(file, element_info[element].change->delay_fixed);
2495 putFile16BitBE(file, element_info[element].change->delay_random);
2496 putFile16BitBE(file, element_info[element].change->delay_frames);
2498 putFile16BitBE(file, element_info[element].change->trigger_element);
2500 putFile8Bit(file, element_info[element].change->explode);
2501 putFile8Bit(file, element_info[element].change->use_content);
2502 putFile8Bit(file, element_info[element].change->only_complete);
2503 putFile8Bit(file, element_info[element].change->use_random_change);
2505 putFile8Bit(file, element_info[element].change->random);
2506 putFile8Bit(file, element_info[element].change->power);
2508 for (y = 0; y < 3; y++)
2509 for (x = 0; x < 3; x++)
2510 putFile16BitBE(file, element_info[element].change->content[x][y]);
2512 putFile8Bit(file, element_info[element].slippery_type);
2514 /* some free bytes for future properties and padding */
2515 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2522 if (check != num_changed_custom_elements) /* should not happen */
2523 Error(ERR_WARN, "inconsistent number of custom element properties");
2527 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2529 struct ElementInfo *ei = &element_info[element];
2532 putFile16BitBE(file, element);
2534 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2535 putFile8Bit(file, ei->description[i]);
2537 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2538 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2540 putFile8Bit(file, ei->num_change_pages);
2542 /* some free bytes for future base property values and padding */
2543 WriteUnusedBytesToFile(file, 5);
2545 /* write custom property values */
2547 putFile8Bit(file, ei->use_gfx_element);
2548 putFile16BitBE(file, ei->gfx_element);
2550 putFile8Bit(file, ei->collect_score);
2551 putFile8Bit(file, ei->collect_count);
2553 putFile16BitBE(file, ei->push_delay_fixed);
2554 putFile16BitBE(file, ei->push_delay_random);
2555 putFile16BitBE(file, ei->move_delay_fixed);
2556 putFile16BitBE(file, ei->move_delay_random);
2558 putFile16BitBE(file, ei->move_pattern);
2559 putFile8Bit(file, ei->move_direction_initial);
2560 putFile8Bit(file, ei->move_stepsize);
2562 putFile8Bit(file, ei->slippery_type);
2564 for (y = 0; y < 3; y++)
2565 for (x = 0; x < 3; x++)
2566 putFile16BitBE(file, ei->content[x][y]);
2568 putFile16BitBE(file, ei->move_enter_element);
2569 putFile16BitBE(file, ei->move_leave_element);
2570 putFile8Bit(file, ei->move_leave_type);
2572 /* some free bytes for future custom property values and padding */
2573 WriteUnusedBytesToFile(file, 7);
2575 /* write change property values */
2577 for (i = 0; i < ei->num_change_pages; i++)
2579 struct ElementChangeInfo *change = &ei->change_page[i];
2581 putFile32BitBE(file, change->events);
2583 putFile16BitBE(file, change->target_element);
2585 putFile16BitBE(file, change->delay_fixed);
2586 putFile16BitBE(file, change->delay_random);
2587 putFile16BitBE(file, change->delay_frames);
2589 putFile16BitBE(file, change->trigger_element);
2591 putFile8Bit(file, change->explode);
2592 putFile8Bit(file, change->use_content);
2593 putFile8Bit(file, change->only_complete);
2594 putFile8Bit(file, change->use_random_change);
2596 putFile8Bit(file, change->random);
2597 putFile8Bit(file, change->power);
2599 for (y = 0; y < 3; y++)
2600 for (x = 0; x < 3; x++)
2601 putFile16BitBE(file, change->content[x][y]);
2603 putFile8Bit(file, change->can_change);
2605 putFile8Bit(file, change->sides);
2607 /* some free bytes for future change property values and padding */
2608 WriteUnusedBytesToFile(file, 8);
2612 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2614 struct ElementInfo *ei = &element_info[element];
2615 struct ElementGroupInfo *group = ei->group;
2618 putFile16BitBE(file, element);
2620 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2621 putFile8Bit(file, ei->description[i]);
2623 putFile8Bit(file, group->num_elements);
2625 putFile8Bit(file, ei->use_gfx_element);
2626 putFile16BitBE(file, ei->gfx_element);
2628 putFile8Bit(file, group->choice_mode);
2630 /* some free bytes for future values and padding */
2631 WriteUnusedBytesToFile(file, 3);
2633 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2634 putFile16BitBE(file, group->element[i]);
2637 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2639 int body_chunk_size;
2643 if (!(file = fopen(filename, MODE_WRITE)))
2645 Error(ERR_WARN, "cannot save level file '%s'", filename);
2649 level->file_version = FILE_VERSION_ACTUAL;
2650 level->game_version = GAME_VERSION_ACTUAL;
2652 /* check level field for 16-bit elements */
2653 level->encoding_16bit_field = FALSE;
2654 for (y = 0; y < level->fieldy; y++)
2655 for (x = 0; x < level->fieldx; x++)
2656 if (level->field[x][y] > 255)
2657 level->encoding_16bit_field = TRUE;
2659 /* check yamyam content for 16-bit elements */
2660 level->encoding_16bit_yamyam = FALSE;
2661 for (i = 0; i < level->num_yamyam_contents; i++)
2662 for (y = 0; y < 3; y++)
2663 for (x = 0; x < 3; x++)
2664 if (level->yamyam_content[i][x][y] > 255)
2665 level->encoding_16bit_yamyam = TRUE;
2667 /* check amoeba content for 16-bit elements */
2668 level->encoding_16bit_amoeba = FALSE;
2669 if (level->amoeba_content > 255)
2670 level->encoding_16bit_amoeba = TRUE;
2672 /* calculate size of "BODY" chunk */
2674 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2676 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2677 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2679 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2680 SaveLevel_VERS(file, level);
2682 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2683 SaveLevel_HEAD(file, level);
2685 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2686 SaveLevel_AUTH(file, level);
2688 putFileChunkBE(file, "BODY", body_chunk_size);
2689 SaveLevel_BODY(file, level);
2691 if (level->encoding_16bit_yamyam ||
2692 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2694 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2695 SaveLevel_CNT2(file, level, EL_YAMYAM);
2698 if (level->encoding_16bit_amoeba)
2700 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2701 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2704 /* check for envelope content */
2705 for (i = 0; i < 4; i++)
2707 if (strlen(level->envelope_text[i]) > 0)
2709 int envelope_len = strlen(level->envelope_text[i]) + 1;
2711 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2712 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2716 /* check for non-default custom elements (unless using template level) */
2717 if (!level->use_custom_template)
2719 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2721 int element = EL_CUSTOM_START + i;
2723 if (element_info[element].modified_settings)
2725 int num_change_pages = element_info[element].num_change_pages;
2727 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2728 SaveLevel_CUS4(file, level, element);
2733 /* check for non-default group elements (unless using template level) */
2734 if (!level->use_custom_template)
2736 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2738 int element = EL_GROUP_START + i;
2740 if (element_info[element].modified_settings)
2742 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2743 SaveLevel_GRP1(file, level, element);
2750 SetFilePermissions(filename, PERMS_PRIVATE);
2753 void SaveLevel(int nr)
2755 char *filename = getDefaultLevelFilename(nr);
2757 SaveLevelFromFilename(&level, filename);
2760 void SaveLevelTemplate()
2762 char *filename = getDefaultLevelFilename(-1);
2764 SaveLevelFromFilename(&level, filename);
2767 void DumpLevel(struct LevelInfo *level)
2769 printf_line("-", 79);
2770 printf("Level xxx (file version %08d, game version %08d)\n",
2771 level->file_version, level->game_version);
2772 printf_line("-", 79);
2774 printf("Level author: '%s'\n", level->author);
2775 printf("Level title: '%s'\n", level->name);
2777 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2779 printf("Level time: %d seconds\n", level->time);
2780 printf("Gems needed: %d\n", level->gems_needed);
2782 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2783 printf("Time for wheel: %d seconds\n", level->time_wheel);
2784 printf("Time for light: %d seconds\n", level->time_light);
2785 printf("Time for timegate: %d seconds\n", level->time_timegate);
2787 printf("Amoeba speed: %d\n", level->amoeba_speed);
2789 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2790 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2791 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2792 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2793 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2795 printf_line("-", 79);
2799 /* ========================================================================= */
2800 /* tape file functions */
2801 /* ========================================================================= */
2803 static void setTapeInfoToDefaults()
2807 /* always start with reliable default values (empty tape) */
2810 /* default values (also for pre-1.2 tapes) with only the first player */
2811 tape.player_participates[0] = TRUE;
2812 for (i = 1; i < MAX_PLAYERS; i++)
2813 tape.player_participates[i] = FALSE;
2815 /* at least one (default: the first) player participates in every tape */
2816 tape.num_participating_players = 1;
2818 tape.level_nr = level_nr;
2820 tape.changed = FALSE;
2822 tape.recording = FALSE;
2823 tape.playing = FALSE;
2824 tape.pausing = FALSE;
2827 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2829 tape->file_version = getFileVersion(file);
2830 tape->game_version = getFileVersion(file);
2835 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2839 tape->random_seed = getFile32BitBE(file);
2840 tape->date = getFile32BitBE(file);
2841 tape->length = getFile32BitBE(file);
2843 /* read header fields that are new since version 1.2 */
2844 if (tape->file_version >= FILE_VERSION_1_2)
2846 byte store_participating_players = getFile8Bit(file);
2849 /* since version 1.2, tapes store which players participate in the tape */
2850 tape->num_participating_players = 0;
2851 for (i = 0; i < MAX_PLAYERS; i++)
2853 tape->player_participates[i] = FALSE;
2855 if (store_participating_players & (1 << i))
2857 tape->player_participates[i] = TRUE;
2858 tape->num_participating_players++;
2862 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2864 engine_version = getFileVersion(file);
2865 if (engine_version > 0)
2866 tape->engine_version = engine_version;
2868 tape->engine_version = tape->game_version;
2874 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2876 int level_identifier_size;
2879 level_identifier_size = getFile16BitBE(file);
2881 tape->level_identifier =
2882 checked_realloc(tape->level_identifier, level_identifier_size);
2884 for (i = 0; i < level_identifier_size; i++)
2885 tape->level_identifier[i] = getFile8Bit(file);
2887 tape->level_nr = getFile16BitBE(file);
2889 chunk_size = 2 + level_identifier_size + 2;
2894 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2897 int chunk_size_expected =
2898 (tape->num_participating_players + 1) * tape->length;
2900 if (chunk_size_expected != chunk_size)
2902 ReadUnusedBytesFromFile(file, chunk_size);
2903 return chunk_size_expected;
2906 for (i = 0; i < tape->length; i++)
2908 if (i >= MAX_TAPELEN)
2911 for (j = 0; j < MAX_PLAYERS; j++)
2913 tape->pos[i].action[j] = MV_NO_MOVING;
2915 if (tape->player_participates[j])
2916 tape->pos[i].action[j] = getFile8Bit(file);
2919 tape->pos[i].delay = getFile8Bit(file);
2921 if (tape->file_version == FILE_VERSION_1_0)
2923 /* eliminate possible diagonal moves in old tapes */
2924 /* this is only for backward compatibility */
2926 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2927 byte action = tape->pos[i].action[0];
2928 int k, num_moves = 0;
2930 for (k = 0; k<4; k++)
2932 if (action & joy_dir[k])
2934 tape->pos[i + num_moves].action[0] = joy_dir[k];
2936 tape->pos[i + num_moves].delay = 0;
2945 tape->length += num_moves;
2948 else if (tape->file_version < FILE_VERSION_2_0)
2950 /* convert pre-2.0 tapes to new tape format */
2952 if (tape->pos[i].delay > 1)
2955 tape->pos[i + 1] = tape->pos[i];
2956 tape->pos[i + 1].delay = 1;
2959 for (j = 0; j < MAX_PLAYERS; j++)
2960 tape->pos[i].action[j] = MV_NO_MOVING;
2961 tape->pos[i].delay--;
2972 if (i != tape->length)
2973 chunk_size = (tape->num_participating_players + 1) * i;
2978 void LoadTapeFromFilename(char *filename)
2980 char cookie[MAX_LINE_LEN];
2981 char chunk_name[CHUNK_ID_LEN + 1];
2985 /* always start with reliable default values */
2986 setTapeInfoToDefaults();
2988 if (!(file = fopen(filename, MODE_READ)))
2991 getFileChunkBE(file, chunk_name, NULL);
2992 if (strcmp(chunk_name, "RND1") == 0)
2994 getFile32BitBE(file); /* not used */
2996 getFileChunkBE(file, chunk_name, NULL);
2997 if (strcmp(chunk_name, "TAPE") != 0)
2999 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3004 else /* check for pre-2.0 file format with cookie string */
3006 strcpy(cookie, chunk_name);
3007 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3008 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3009 cookie[strlen(cookie) - 1] = '\0';
3011 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3013 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3018 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3020 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3025 /* pre-2.0 tape files have no game version, so use file version here */
3026 tape.game_version = tape.file_version;
3029 if (tape.file_version < FILE_VERSION_1_2)
3031 /* tape files from versions before 1.2.0 without chunk structure */
3032 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3033 LoadTape_BODY(file, 2 * tape.length, &tape);
3041 int (*loader)(FILE *, int, struct TapeInfo *);
3045 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3046 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3047 { "INFO", -1, LoadTape_INFO },
3048 { "BODY", -1, LoadTape_BODY },
3052 while (getFileChunkBE(file, chunk_name, &chunk_size))
3056 while (chunk_info[i].name != NULL &&
3057 strcmp(chunk_name, chunk_info[i].name) != 0)
3060 if (chunk_info[i].name == NULL)
3062 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3063 chunk_name, filename);
3064 ReadUnusedBytesFromFile(file, chunk_size);
3066 else if (chunk_info[i].size != -1 &&
3067 chunk_info[i].size != chunk_size)
3069 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3070 chunk_size, chunk_name, filename);
3071 ReadUnusedBytesFromFile(file, chunk_size);
3075 /* call function to load this tape chunk */
3076 int chunk_size_expected =
3077 (chunk_info[i].loader)(file, chunk_size, &tape);
3079 /* the size of some chunks cannot be checked before reading other
3080 chunks first (like "HEAD" and "BODY") that contain some header
3081 information, so check them here */
3082 if (chunk_size_expected != chunk_size)
3084 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3085 chunk_size, chunk_name, filename);
3093 tape.length_seconds = GetTapeLength();
3096 printf("::: tape game version: %d\n", tape.game_version);
3097 printf("::: tape engine version: %d\n", tape.engine_version);
3101 void LoadTape(int nr)
3103 char *filename = getTapeFilename(nr);
3105 LoadTapeFromFilename(filename);
3108 void LoadSolutionTape(int nr)
3110 char *filename = getSolutionTapeFilename(nr);
3112 LoadTapeFromFilename(filename);
3115 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3117 putFileVersion(file, tape->file_version);
3118 putFileVersion(file, tape->game_version);
3121 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3124 byte store_participating_players = 0;
3126 /* set bits for participating players for compact storage */
3127 for (i = 0; i < MAX_PLAYERS; i++)
3128 if (tape->player_participates[i])
3129 store_participating_players |= (1 << i);
3131 putFile32BitBE(file, tape->random_seed);
3132 putFile32BitBE(file, tape->date);
3133 putFile32BitBE(file, tape->length);
3135 putFile8Bit(file, store_participating_players);
3137 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3138 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3140 putFileVersion(file, tape->engine_version);
3143 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3145 int level_identifier_size = strlen(tape->level_identifier) + 1;
3148 putFile16BitBE(file, level_identifier_size);
3150 for (i = 0; i < level_identifier_size; i++)
3151 putFile8Bit(file, tape->level_identifier[i]);
3153 putFile16BitBE(file, tape->level_nr);
3156 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3160 for (i = 0; i < tape->length; i++)
3162 for (j = 0; j < MAX_PLAYERS; j++)
3163 if (tape->player_participates[j])
3164 putFile8Bit(file, tape->pos[i].action[j]);
3166 putFile8Bit(file, tape->pos[i].delay);
3170 void SaveTape(int nr)
3172 char *filename = getTapeFilename(nr);
3174 boolean new_tape = TRUE;
3175 int num_participating_players = 0;
3176 int info_chunk_size;
3177 int body_chunk_size;
3180 InitTapeDirectory(leveldir_current->subdir);
3182 /* if a tape still exists, ask to overwrite it */
3183 if (access(filename, F_OK) == 0)
3186 if (!Request("Replace old tape ?", REQ_ASK))
3190 if (!(file = fopen(filename, MODE_WRITE)))
3192 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3196 tape.file_version = FILE_VERSION_ACTUAL;
3197 tape.game_version = GAME_VERSION_ACTUAL;
3199 /* count number of participating players */
3200 for (i = 0; i < MAX_PLAYERS; i++)
3201 if (tape.player_participates[i])
3202 num_participating_players++;
3204 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3205 body_chunk_size = (num_participating_players + 1) * tape.length;
3207 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3208 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3210 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3211 SaveTape_VERS(file, &tape);
3213 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3214 SaveTape_HEAD(file, &tape);
3216 putFileChunkBE(file, "INFO", info_chunk_size);
3217 SaveTape_INFO(file, &tape);
3219 putFileChunkBE(file, "BODY", body_chunk_size);
3220 SaveTape_BODY(file, &tape);
3224 SetFilePermissions(filename, PERMS_PRIVATE);
3226 tape.changed = FALSE;
3229 Request("tape saved !", REQ_CONFIRM);
3232 void DumpTape(struct TapeInfo *tape)
3236 if (TAPE_IS_EMPTY(*tape))
3238 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3242 printf_line("-", 79);
3243 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3244 tape->level_nr, tape->file_version, tape->game_version);
3245 printf("Level series identifier: '%s'\n", tape->level_identifier);
3246 printf_line("-", 79);
3248 for (i = 0; i < tape->length; i++)
3250 if (i >= MAX_TAPELEN)
3253 printf("%03d: ", i);
3255 for (j = 0; j < MAX_PLAYERS; j++)
3257 if (tape->player_participates[j])
3259 int action = tape->pos[i].action[j];
3261 printf("%d:%02x ", j, action);
3262 printf("[%c%c%c%c|%c%c] - ",
3263 (action & JOY_LEFT ? '<' : ' '),
3264 (action & JOY_RIGHT ? '>' : ' '),
3265 (action & JOY_UP ? '^' : ' '),
3266 (action & JOY_DOWN ? 'v' : ' '),
3267 (action & JOY_BUTTON_1 ? '1' : ' '),
3268 (action & JOY_BUTTON_2 ? '2' : ' '));
3272 printf("(%03d)\n", tape->pos[i].delay);
3275 printf_line("-", 79);
3279 /* ========================================================================= */
3280 /* score file functions */
3281 /* ========================================================================= */
3283 void LoadScore(int nr)
3286 char *filename = getScoreFilename(nr);
3287 char cookie[MAX_LINE_LEN];
3288 char line[MAX_LINE_LEN];
3292 /* always start with reliable default values */
3293 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3295 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3296 highscore[i].Score = 0;
3299 if (!(file = fopen(filename, MODE_READ)))
3302 /* check file identifier */
3303 fgets(cookie, MAX_LINE_LEN, file);
3304 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3305 cookie[strlen(cookie) - 1] = '\0';
3307 if (!checkCookieString(cookie, SCORE_COOKIE))
3309 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3314 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3316 fscanf(file, "%d", &highscore[i].Score);
3317 fgets(line, MAX_LINE_LEN, file);
3319 if (line[strlen(line) - 1] == '\n')
3320 line[strlen(line) - 1] = '\0';
3322 for (line_ptr = line; *line_ptr; line_ptr++)
3324 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3326 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3327 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3336 void SaveScore(int nr)
3339 char *filename = getScoreFilename(nr);
3342 InitScoreDirectory(leveldir_current->subdir);
3344 if (!(file = fopen(filename, MODE_WRITE)))
3346 Error(ERR_WARN, "cannot save score for level %d", nr);
3350 fprintf(file, "%s\n\n", SCORE_COOKIE);
3352 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3353 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3357 SetFilePermissions(filename, PERMS_PUBLIC);
3361 /* ========================================================================= */
3362 /* setup file functions */
3363 /* ========================================================================= */
3365 #define TOKEN_STR_PLAYER_PREFIX "player_"
3368 #define SETUP_TOKEN_PLAYER_NAME 0
3369 #define SETUP_TOKEN_SOUND 1
3370 #define SETUP_TOKEN_SOUND_LOOPS 2
3371 #define SETUP_TOKEN_SOUND_MUSIC 3
3372 #define SETUP_TOKEN_SOUND_SIMPLE 4
3373 #define SETUP_TOKEN_TOONS 5
3374 #define SETUP_TOKEN_SCROLL_DELAY 6
3375 #define SETUP_TOKEN_SOFT_SCROLLING 7
3376 #define SETUP_TOKEN_FADING 8
3377 #define SETUP_TOKEN_AUTORECORD 9
3378 #define SETUP_TOKEN_QUICK_DOORS 10
3379 #define SETUP_TOKEN_TEAM_MODE 11
3380 #define SETUP_TOKEN_HANDICAP 12
3381 #define SETUP_TOKEN_TIME_LIMIT 13
3382 #define SETUP_TOKEN_FULLSCREEN 14
3383 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3384 #define SETUP_TOKEN_GRAPHICS_SET 16
3385 #define SETUP_TOKEN_SOUNDS_SET 17
3386 #define SETUP_TOKEN_MUSIC_SET 18
3387 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3388 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3389 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3391 #define NUM_GLOBAL_SETUP_TOKENS 22
3394 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3395 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3396 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3397 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3398 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3399 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3400 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3401 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3402 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3403 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3404 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3405 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3407 #define NUM_EDITOR_SETUP_TOKENS 12
3409 /* shortcut setup */
3410 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3411 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3412 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3414 #define NUM_SHORTCUT_SETUP_TOKENS 3
3417 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3418 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3419 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3420 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3421 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3422 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3423 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3424 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3425 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3426 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3427 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3428 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3429 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3430 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3431 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3432 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3434 #define NUM_PLAYER_SETUP_TOKENS 16
3437 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3438 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3440 #define NUM_SYSTEM_SETUP_TOKENS 2
3443 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3445 #define NUM_OPTIONS_SETUP_TOKENS 1
3448 static struct SetupInfo si;
3449 static struct SetupEditorInfo sei;
3450 static struct SetupShortcutInfo ssi;
3451 static struct SetupInputInfo sii;
3452 static struct SetupSystemInfo syi;
3453 static struct OptionInfo soi;
3455 static struct TokenInfo global_setup_tokens[] =
3457 { TYPE_STRING, &si.player_name, "player_name" },
3458 { TYPE_SWITCH, &si.sound, "sound" },
3459 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3460 { TYPE_SWITCH, &si.sound_music, "background_music" },
3461 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3462 { TYPE_SWITCH, &si.toons, "toons" },
3463 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3464 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3465 { TYPE_SWITCH, &si.fading, "screen_fading" },
3466 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3467 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3468 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3469 { TYPE_SWITCH, &si.handicap, "handicap" },
3470 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3471 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3472 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3473 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3474 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3475 { TYPE_STRING, &si.music_set, "music_set" },
3476 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3477 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3478 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3481 static struct TokenInfo editor_setup_tokens[] =
3483 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3484 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3485 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3486 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3487 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3488 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3489 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3490 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3491 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3492 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3493 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3494 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3497 static struct TokenInfo shortcut_setup_tokens[] =
3499 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3500 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3501 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3504 static struct TokenInfo player_setup_tokens[] =
3506 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3507 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3508 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3509 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3510 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3511 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3512 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3513 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3514 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3515 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3516 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3517 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3518 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3519 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3520 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3521 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3524 static struct TokenInfo system_setup_tokens[] =
3526 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3527 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3530 static struct TokenInfo options_setup_tokens[] =
3532 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3535 static char *get_corrected_login_name(char *login_name)
3537 /* needed because player name must be a fixed length string */
3538 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3540 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3541 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3543 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3544 if (strchr(login_name_new, ' '))
3545 *strchr(login_name_new, ' ') = '\0';
3547 return login_name_new;
3550 static void setSetupInfoToDefaults(struct SetupInfo *si)
3554 si->player_name = get_corrected_login_name(getLoginName());
3557 si->sound_loops = TRUE;
3558 si->sound_music = TRUE;
3559 si->sound_simple = TRUE;
3561 si->double_buffering = TRUE;
3562 si->direct_draw = !si->double_buffering;
3563 si->scroll_delay = TRUE;
3564 si->soft_scrolling = TRUE;
3566 si->autorecord = TRUE;
3567 si->quick_doors = FALSE;
3568 si->team_mode = FALSE;
3569 si->handicap = TRUE;
3570 si->time_limit = TRUE;
3571 si->fullscreen = FALSE;
3572 si->ask_on_escape = TRUE;
3574 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3575 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3576 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3577 si->override_level_graphics = FALSE;
3578 si->override_level_sounds = FALSE;
3579 si->override_level_music = FALSE;
3581 si->editor.el_boulderdash = TRUE;
3582 si->editor.el_emerald_mine = TRUE;
3583 si->editor.el_more = TRUE;
3584 si->editor.el_sokoban = TRUE;
3585 si->editor.el_supaplex = TRUE;
3586 si->editor.el_diamond_caves = TRUE;
3587 si->editor.el_dx_boulderdash = TRUE;
3588 si->editor.el_chars = TRUE;
3589 si->editor.el_custom = TRUE;
3590 si->editor.el_custom_more = FALSE;
3592 si->editor.el_headlines = TRUE;
3593 si->editor.el_user_defined = FALSE;
3595 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3596 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3597 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3599 for (i = 0; i < MAX_PLAYERS; i++)
3601 si->input[i].use_joystick = FALSE;
3602 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3603 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3604 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3605 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3606 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3607 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3608 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3609 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3610 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3611 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3612 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3613 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3614 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3615 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3616 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3619 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3620 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3622 si->options.verbose = FALSE;
3625 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3629 if (!setup_file_hash)
3634 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3635 setSetupInfo(global_setup_tokens, i,
3636 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3641 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3642 setSetupInfo(editor_setup_tokens, i,
3643 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3646 /* shortcut setup */
3647 ssi = setup.shortcut;
3648 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3649 setSetupInfo(shortcut_setup_tokens, i,
3650 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3651 setup.shortcut = ssi;
3654 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3658 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3660 sii = setup.input[pnr];
3661 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3663 char full_token[100];
3665 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3666 setSetupInfo(player_setup_tokens, i,
3667 getHashEntry(setup_file_hash, full_token));
3669 setup.input[pnr] = sii;
3674 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3675 setSetupInfo(system_setup_tokens, i,
3676 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3680 soi = setup.options;
3681 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3682 setSetupInfo(options_setup_tokens, i,
3683 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3684 setup.options = soi;
3689 char *filename = getSetupFilename();
3690 SetupFileHash *setup_file_hash = NULL;
3692 /* always start with reliable default values */
3693 setSetupInfoToDefaults(&setup);
3695 setup_file_hash = loadSetupFileHash(filename);
3697 if (setup_file_hash)
3699 char *player_name_new;
3701 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3702 decodeSetupFileHash(setup_file_hash);
3704 setup.direct_draw = !setup.double_buffering;
3706 freeSetupFileHash(setup_file_hash);
3708 /* needed to work around problems with fixed length strings */
3709 player_name_new = get_corrected_login_name(setup.player_name);
3710 free(setup.player_name);
3711 setup.player_name = player_name_new;
3714 Error(ERR_WARN, "using default setup values");
3719 char *filename = getSetupFilename();
3723 InitUserDataDirectory();
3725 if (!(file = fopen(filename, MODE_WRITE)))
3727 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3731 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3732 getCookie("SETUP")));
3733 fprintf(file, "\n");
3737 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3739 /* just to make things nicer :) */
3740 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3741 i == SETUP_TOKEN_GRAPHICS_SET)
3742 fprintf(file, "\n");
3744 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3749 fprintf(file, "\n");
3750 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3751 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3753 /* shortcut setup */
3754 ssi = setup.shortcut;
3755 fprintf(file, "\n");
3756 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3757 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3760 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3764 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3765 fprintf(file, "\n");
3767 sii = setup.input[pnr];
3768 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3769 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3774 fprintf(file, "\n");
3775 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3776 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3779 soi = setup.options;
3780 fprintf(file, "\n");
3781 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3782 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3786 SetFilePermissions(filename, PERMS_PRIVATE);
3789 void LoadCustomElementDescriptions()
3791 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3792 SetupFileHash *setup_file_hash;
3795 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3797 if (element_info[i].custom_description != NULL)
3799 free(element_info[i].custom_description);
3800 element_info[i].custom_description = NULL;
3804 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3807 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3809 char *token = getStringCat2(element_info[i].token_name, ".name");
3810 char *value = getHashEntry(setup_file_hash, token);
3813 element_info[i].custom_description = getStringCopy(value);
3818 freeSetupFileHash(setup_file_hash);
3821 void LoadSpecialMenuDesignSettings()
3823 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3824 SetupFileHash *setup_file_hash;
3827 /* always start with reliable default values from default config */
3828 for (i = 0; image_config_vars[i].token != NULL; i++)
3829 for (j = 0; image_config[j].token != NULL; j++)
3830 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3831 *image_config_vars[i].value =
3832 get_auto_parameter_value(image_config_vars[i].token,
3833 image_config[j].value);
3835 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3838 /* special case: initialize with default values that may be overwritten */
3839 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3841 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3842 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3843 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3845 if (value_x != NULL)
3846 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3847 if (value_y != NULL)
3848 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3849 if (list_size != NULL)
3850 menu.list_size[i] = get_integer_from_string(list_size);
3853 /* read (and overwrite with) values that may be specified in config file */
3854 for (i = 0; image_config_vars[i].token != NULL; i++)
3856 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3859 *image_config_vars[i].value =
3860 get_auto_parameter_value(image_config_vars[i].token, value);
3863 freeSetupFileHash(setup_file_hash);
3866 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3868 char *filename = getEditorSetupFilename();
3869 SetupFileList *setup_file_list, *list;
3870 SetupFileHash *element_hash;
3871 int num_unknown_tokens = 0;
3874 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3877 element_hash = newSetupFileHash();
3879 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3880 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3882 /* determined size may be larger than needed (due to unknown elements) */
3884 for (list = setup_file_list; list != NULL; list = list->next)
3887 /* add space for up to 3 more elements for padding that may be needed */
3890 *elements = checked_malloc(*num_elements * sizeof(int));
3893 for (list = setup_file_list; list != NULL; list = list->next)
3895 char *value = getHashEntry(element_hash, list->token);
3899 (*elements)[(*num_elements)++] = atoi(value);
3903 if (num_unknown_tokens == 0)
3905 Error(ERR_RETURN_LINE, "-");
3906 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3907 Error(ERR_RETURN, "- config file: '%s'", filename);
3909 num_unknown_tokens++;
3912 Error(ERR_RETURN, "- token: '%s'", list->token);
3916 if (num_unknown_tokens > 0)
3917 Error(ERR_RETURN_LINE, "-");
3919 while (*num_elements % 4) /* pad with empty elements, if needed */
3920 (*elements)[(*num_elements)++] = EL_EMPTY;
3922 freeSetupFileList(setup_file_list);
3923 freeSetupFileHash(element_hash);
3927 for (i = 0; i < *num_elements; i++)
3928 printf("editor: element '%s' [%d]\n",
3929 element_info[(*elements)[i]].token_name, (*elements)[i]);
3933 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3936 SetupFileHash *setup_file_hash = NULL;
3937 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3938 char *filename_music, *filename_prefix, *filename_info;
3944 token_to_value_ptr[] =
3946 { "title_header", &tmp_music_file_info.title_header },
3947 { "artist_header", &tmp_music_file_info.artist_header },
3948 { "album_header", &tmp_music_file_info.album_header },
3949 { "year_header", &tmp_music_file_info.year_header },
3951 { "title", &tmp_music_file_info.title },
3952 { "artist", &tmp_music_file_info.artist },
3953 { "album", &tmp_music_file_info.album },
3954 { "year", &tmp_music_file_info.year },
3960 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3961 getCustomMusicFilename(basename));
3963 if (filename_music == NULL)
3966 /* ---------- try to replace file extension ---------- */
3968 filename_prefix = getStringCopy(filename_music);
3969 if (strrchr(filename_prefix, '.') != NULL)
3970 *strrchr(filename_prefix, '.') = '\0';
3971 filename_info = getStringCat2(filename_prefix, ".txt");
3974 printf("trying to load file '%s'...\n", filename_info);
3977 if (fileExists(filename_info))
3978 setup_file_hash = loadSetupFileHash(filename_info);
3980 free(filename_prefix);
3981 free(filename_info);
3983 if (setup_file_hash == NULL)
3985 /* ---------- try to add file extension ---------- */
3987 filename_prefix = getStringCopy(filename_music);
3988 filename_info = getStringCat2(filename_prefix, ".txt");
3991 printf("trying to load file '%s'...\n", filename_info);
3994 if (fileExists(filename_info))
3995 setup_file_hash = loadSetupFileHash(filename_info);
3997 free(filename_prefix);
3998 free(filename_info);
4001 if (setup_file_hash == NULL)
4004 /* ---------- music file info found ---------- */
4006 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4008 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4010 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4012 *token_to_value_ptr[i].value_ptr =
4013 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4016 tmp_music_file_info.basename = getStringCopy(basename);
4017 tmp_music_file_info.music = music;
4018 tmp_music_file_info.is_sound = is_sound;
4020 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4021 *new_music_file_info = tmp_music_file_info;
4023 return new_music_file_info;
4026 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4028 return get_music_file_info_ext(basename, music, FALSE);
4031 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4033 return get_music_file_info_ext(basename, sound, TRUE);
4036 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4037 char *basename, boolean is_sound)
4039 for (; list != NULL; list = list->next)
4040 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4046 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4048 return music_info_listed_ext(list, basename, FALSE);
4051 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4053 return music_info_listed_ext(list, basename, TRUE);
4056 void LoadMusicInfo()
4058 char *music_directory = getCustomMusicDirectory();
4059 int num_music = getMusicListSize();
4060 int num_music_noconf = 0;
4061 int num_sounds = getSoundListSize();
4063 struct dirent *dir_entry;
4064 struct FileInfo *music, *sound;
4065 struct MusicFileInfo *next, **new;
4068 while (music_file_info != NULL)
4070 next = music_file_info->next;
4072 checked_free(music_file_info->basename);
4074 checked_free(music_file_info->title_header);
4075 checked_free(music_file_info->artist_header);
4076 checked_free(music_file_info->album_header);
4077 checked_free(music_file_info->year_header);
4079 checked_free(music_file_info->title);
4080 checked_free(music_file_info->artist);
4081 checked_free(music_file_info->album);
4082 checked_free(music_file_info->year);
4084 free(music_file_info);
4086 music_file_info = next;
4089 new = &music_file_info;
4092 printf("::: num_music == %d\n", num_music);
4095 for (i = 0; i < num_music; i++)
4097 music = getMusicListEntry(i);
4100 printf("::: %d [%08x]\n", i, music->filename);
4103 if (music->filename == NULL)
4106 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4109 /* a configured file may be not recognized as music */
4110 if (!FileIsMusic(music->filename))
4114 printf("::: -> '%s' (configured)\n", music->filename);
4117 if (!music_info_listed(music_file_info, music->filename))
4119 *new = get_music_file_info(music->filename, i);
4121 new = &(*new)->next;
4125 if ((dir = opendir(music_directory)) == NULL)
4127 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4131 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4133 char *basename = dir_entry->d_name;
4134 boolean music_already_used = FALSE;
4137 /* skip all music files that are configured in music config file */
4138 for (i = 0; i < num_music; i++)
4140 music = getMusicListEntry(i);
4142 if (music->filename == NULL)
4145 if (strcmp(basename, music->filename) == 0)
4147 music_already_used = TRUE;
4152 if (music_already_used)
4155 if (!FileIsMusic(basename))
4159 printf("::: -> '%s' (found in directory)\n", basename);
4162 if (!music_info_listed(music_file_info, basename))
4164 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4166 new = &(*new)->next;
4174 for (i = 0; i < num_sounds; i++)
4176 sound = getSoundListEntry(i);
4178 if (sound->filename == NULL)
4181 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4184 /* a configured file may be not recognized as sound */
4185 if (!FileIsSound(sound->filename))
4189 printf("::: -> '%s' (configured)\n", sound->filename);
4192 if (!sound_info_listed(music_file_info, sound->filename))
4194 *new = get_sound_file_info(sound->filename, i);
4196 new = &(*new)->next;
4202 for (next = music_file_info; next != NULL; next = next->next)
4203 printf("::: title == '%s'\n", next->title);
4207 void add_helpanim_entry(int element, int action, int direction, int delay,
4208 int *num_list_entries)
4210 struct HelpAnimInfo *new_list_entry;
4211 (*num_list_entries)++;
4214 checked_realloc(helpanim_info,
4215 *num_list_entries * sizeof(struct HelpAnimInfo));
4216 new_list_entry = &helpanim_info[*num_list_entries - 1];
4218 new_list_entry->element = element;
4219 new_list_entry->action = action;
4220 new_list_entry->direction = direction;
4221 new_list_entry->delay = delay;
4224 void print_unknown_token(char *filename, char *token, int token_nr)
4228 Error(ERR_RETURN_LINE, "-");
4229 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4230 Error(ERR_RETURN, "- config file: '%s'", filename);
4233 Error(ERR_RETURN, "- token: '%s'", token);
4236 void print_unknown_token_end(int token_nr)
4239 Error(ERR_RETURN_LINE, "-");
4242 void LoadHelpAnimInfo()
4244 char *filename = getHelpAnimFilename();
4245 SetupFileList *setup_file_list = NULL, *list;
4246 SetupFileHash *element_hash, *action_hash, *direction_hash;
4247 int num_list_entries = 0;
4248 int num_unknown_tokens = 0;
4251 if (fileExists(filename))
4252 setup_file_list = loadSetupFileList(filename);
4254 if (setup_file_list == NULL)
4256 /* use reliable default values from static configuration */
4257 SetupFileList *insert_ptr;
4259 insert_ptr = setup_file_list =
4260 newSetupFileList(helpanim_config[0].token,
4261 helpanim_config[0].value);
4263 for (i = 1; helpanim_config[i].token; i++)
4264 insert_ptr = addListEntry(insert_ptr,
4265 helpanim_config[i].token,
4266 helpanim_config[i].value);
4269 element_hash = newSetupFileHash();
4270 action_hash = newSetupFileHash();
4271 direction_hash = newSetupFileHash();
4273 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4274 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4276 for (i = 0; i < NUM_ACTIONS; i++)
4277 setHashEntry(action_hash, element_action_info[i].suffix,
4278 i_to_a(element_action_info[i].value));
4280 /* do not store direction index (bit) here, but direction value! */
4281 for (i = 0; i < NUM_DIRECTIONS; i++)
4282 setHashEntry(direction_hash, element_direction_info[i].suffix,
4283 i_to_a(1 << element_direction_info[i].value));
4285 for (list = setup_file_list; list != NULL; list = list->next)
4287 char *element_token, *action_token, *direction_token;
4288 char *element_value, *action_value, *direction_value;
4289 int delay = atoi(list->value);
4291 if (strcmp(list->token, "end") == 0)
4293 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4298 /* first try to break element into element/action/direction parts;
4299 if this does not work, also accept combined "element[.act][.dir]"
4300 elements (like "dynamite.active"), which are unique elements */
4302 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4304 element_value = getHashEntry(element_hash, list->token);
4305 if (element_value != NULL) /* element found */
4306 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4310 /* no further suffixes found -- this is not an element */
4311 print_unknown_token(filename, list->token, num_unknown_tokens++);
4317 /* token has format "<prefix>.<something>" */
4319 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4320 direction_token = action_token; /* ... or direction */
4322 element_token = getStringCopy(list->token);
4323 *strchr(element_token, '.') = '\0';
4325 element_value = getHashEntry(element_hash, element_token);
4327 if (element_value == NULL) /* this is no element */
4329 element_value = getHashEntry(element_hash, list->token);
4330 if (element_value != NULL) /* combined element found */
4331 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4334 print_unknown_token(filename, list->token, num_unknown_tokens++);
4336 free(element_token);
4341 action_value = getHashEntry(action_hash, action_token);
4343 if (action_value != NULL) /* action found */
4345 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4348 free(element_token);
4353 direction_value = getHashEntry(direction_hash, direction_token);
4355 if (direction_value != NULL) /* direction found */
4357 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4360 free(element_token);
4365 if (strchr(action_token + 1, '.') == NULL)
4367 /* no further suffixes found -- this is not an action nor direction */
4369 element_value = getHashEntry(element_hash, list->token);
4370 if (element_value != NULL) /* combined element found */
4371 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4374 print_unknown_token(filename, list->token, num_unknown_tokens++);
4376 free(element_token);
4381 /* token has format "<prefix>.<suffix>.<something>" */
4383 direction_token = strchr(action_token + 1, '.');
4385 action_token = getStringCopy(action_token);
4386 *strchr(action_token + 1, '.') = '\0';
4388 action_value = getHashEntry(action_hash, action_token);
4390 if (action_value == NULL) /* this is no action */
4392 element_value = getHashEntry(element_hash, list->token);
4393 if (element_value != NULL) /* combined element found */
4394 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4397 print_unknown_token(filename, list->token, num_unknown_tokens++);
4399 free(element_token);
4405 direction_value = getHashEntry(direction_hash, direction_token);
4407 if (direction_value != NULL) /* direction found */
4409 add_helpanim_entry(atoi(element_value), atoi(action_value),
4410 atoi(direction_value), delay, &num_list_entries);
4412 free(element_token);
4418 /* this is no direction */
4420 element_value = getHashEntry(element_hash, list->token);
4421 if (element_value != NULL) /* combined element found */
4422 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4425 print_unknown_token(filename, list->token, num_unknown_tokens++);
4427 free(element_token);
4431 print_unknown_token_end(num_unknown_tokens);
4433 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4434 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4436 freeSetupFileList(setup_file_list);
4437 freeSetupFileHash(element_hash);
4438 freeSetupFileHash(action_hash);
4439 freeSetupFileHash(direction_hash);
4443 for (i = 0; i < num_list_entries; i++)
4444 printf("::: %d, %d, %d => %d\n",
4445 helpanim_info[i].element,
4446 helpanim_info[i].action,
4447 helpanim_info[i].direction,
4448 helpanim_info[i].delay);
4452 void LoadHelpTextInfo()
4454 char *filename = getHelpTextFilename();
4457 if (helptext_info != NULL)
4459 freeSetupFileHash(helptext_info);
4460 helptext_info = NULL;
4463 if (fileExists(filename))
4464 helptext_info = loadSetupFileHash(filename);
4466 if (helptext_info == NULL)
4468 /* use reliable default values from static configuration */
4469 helptext_info = newSetupFileHash();
4471 for (i = 0; helptext_config[i].token; i++)
4472 setHashEntry(helptext_info,
4473 helptext_config[i].token,
4474 helptext_config[i].value);
4479 BEGIN_HASH_ITERATION(helptext_info, itr)
4481 printf("::: '%s' => '%s'\n",
4482 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4484 END_HASH_ITERATION(hash, itr)