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 7 /* 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;
96 change->trigger_player = CH_PLAYER_ANY;
97 change->trigger_side = CH_SIDE_ANY;
98 change->trigger_page = CH_PAGE_ANY;
100 change->target_element = EL_EMPTY_SPACE;
102 change->delay_fixed = 0;
103 change->delay_random = 0;
104 change->delay_frames = 1;
106 change->trigger_element = EL_EMPTY_SPACE;
108 change->explode = FALSE;
109 change->use_content = FALSE;
110 change->only_complete = FALSE;
111 change->use_random_change = FALSE;
112 change->random = 100;
113 change->power = CP_NON_DESTRUCTIVE;
115 for (x = 0; x < 3; x++)
116 for (y = 0; y < 3; y++)
117 change->content[x][y] = EL_EMPTY_SPACE;
119 change->direct_action = 0;
120 change->other_action = 0;
122 change->pre_change_function = NULL;
123 change->change_function = NULL;
124 change->post_change_function = NULL;
127 static void setLevelInfoToDefaults(struct LevelInfo *level)
131 level->file_version = FILE_VERSION_ACTUAL;
132 level->game_version = GAME_VERSION_ACTUAL;
134 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
135 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
136 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
138 level->fieldx = STD_LEV_FIELDX;
139 level->fieldy = STD_LEV_FIELDY;
141 for (x = 0; x < MAX_LEV_FIELDX; x++)
142 for (y = 0; y < MAX_LEV_FIELDY; y++)
143 level->field[x][y] = EL_SAND;
146 level->gems_needed = 0;
148 level->amoeba_speed = 10;
150 level->time_magic_wall = 10;
151 level->time_wheel = 10;
152 level->time_light = 10;
153 level->time_timegate = 10;
155 level->amoeba_content = EL_DIAMOND;
157 level->double_speed = FALSE;
158 level->initial_gravity = FALSE;
159 level->em_slippery_gems = FALSE;
160 level->block_last_field = FALSE;
161 level->sp_block_last_field = TRUE;
163 level->use_spring_bug = FALSE;
165 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
167 level->use_step_counter = FALSE;
169 level->use_custom_template = FALSE;
171 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
172 level->name[i] = '\0';
173 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
174 level->author[i] = '\0';
176 strcpy(level->name, NAMELESS_LEVEL_NAME);
177 strcpy(level->author, ANONYMOUS_NAME);
179 for (i = 0; i < 4; i++)
181 level->envelope_text[i][0] = '\0';
182 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
183 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
186 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
187 level->score[i] = 10;
189 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
190 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
191 for (x = 0; x < 3; x++)
192 for (y = 0; y < 3; y++)
193 level->yamyam_content[i][x][y] =
194 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
196 level->field[0][0] = EL_PLAYER_1;
197 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
199 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
203 setElementChangePages(&element_info[element], 1);
204 setElementChangeInfoToDefaults(element_info[element].change);
206 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
208 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
209 element_info[element].description[j] = '\0';
211 if (element_info[element].custom_description != NULL)
212 strncpy(element_info[element].description,
213 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
215 strcpy(element_info[element].description,
216 element_info[element].editor_description);
218 element_info[element].use_gfx_element = FALSE;
219 element_info[element].gfx_element = EL_EMPTY_SPACE;
222 if (IS_CUSTOM_ELEMENT(element))
224 element_info[element].access_direction = MV_ALL_DIRECTIONS;
226 element_info[element].collect_score = 10; /* special default */
227 element_info[element].collect_count = 1; /* special default */
229 element_info[element].push_delay_fixed = -1; /* initialize later */
230 element_info[element].push_delay_random = -1; /* initialize later */
231 element_info[element].move_delay_fixed = 0;
232 element_info[element].move_delay_random = 0;
234 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
235 element_info[element].move_direction_initial = MV_START_AUTOMATIC;
236 element_info[element].move_stepsize = TILEX / 8;
237 element_info[element].move_enter_element = EL_EMPTY_SPACE;
238 element_info[element].move_leave_element = EL_EMPTY_SPACE;
239 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
241 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
243 element_info[element].explosion_delay = 18;
244 element_info[element].ignition_delay = 8;
246 for (x = 0; x < 3; x++)
247 for (y = 0; y < 3; y++)
248 element_info[element].content[x][y] = EL_EMPTY_SPACE;
250 element_info[element].access_type = 0;
251 element_info[element].access_layer = 0;
252 element_info[element].access_protected = 0;
253 element_info[element].walk_to_action = 0;
254 element_info[element].smash_targets = 0;
255 element_info[element].deadliness = 0;
256 element_info[element].consistency = 0;
258 element_info[element].can_explode_by_fire = FALSE;
259 element_info[element].can_explode_smashed = FALSE;
260 element_info[element].can_explode_impact = FALSE;
262 element_info[element].current_change_page = 0;
264 /* start with no properties at all */
265 for (j = 0; j < NUM_EP_BITFIELDS; j++)
266 Properties[element][j] = EP_BITMASK_DEFAULT;
268 /* now set default properties */
269 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
271 element_info[element].modified_settings = FALSE;
273 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
275 /* initialize memory for list of elements in group */
276 if (element_info[element].group == NULL)
277 element_info[element].group =
278 checked_malloc(sizeof(struct ElementGroupInfo));
280 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
281 element_info[element].group->element[j] = EL_EMPTY_SPACE;
283 /* default: only one element in group */
284 element_info[element].group->num_elements = 1;
286 element_info[element].group->choice_mode = ANIM_RANDOM;
290 BorderElement = EL_STEELWALL;
292 level->no_level_file = FALSE;
294 if (leveldir_current == NULL) /* only when dumping level */
297 /* try to determine better author name than 'anonymous' */
298 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
300 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
301 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
305 switch (LEVELCLASS(leveldir_current))
307 case LEVELCLASS_TUTORIAL:
308 strcpy(level->author, PROGRAM_AUTHOR_STRING);
311 case LEVELCLASS_CONTRIB:
312 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
313 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
316 case LEVELCLASS_PRIVATE:
317 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
318 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
322 /* keep default value */
328 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
330 level_file_info->nr = 0;
331 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
332 level_file_info->packed = FALSE;
333 level_file_info->basename = NULL;
334 level_file_info->filename = NULL;
337 static void ActivateLevelTemplate()
339 /* Currently there is no special action needed to activate the template
340 data, because 'element_info' and 'Properties' overwrite the original
341 level data, while all other variables do not change. */
344 static char *getLevelFilenameFromBasename(char *basename)
346 static char *filename = NULL;
348 checked_free(filename);
350 filename = getPath2(getCurrentLevelDir(), basename);
355 static int getFileTypeFromBasename(char *basename)
357 static char *filename = NULL;
358 struct stat file_status;
360 /* ---------- try to determine file type from filename ---------- */
362 /* check for typical filename of a Supaplex level package file */
363 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
364 strncmp(basename, "LEVELS.D", 8) == 0))
365 return LEVEL_FILE_TYPE_SP;
367 /* ---------- try to determine file type from filesize ---------- */
369 checked_free(filename);
370 filename = getPath2(getCurrentLevelDir(), basename);
372 if (stat(filename, &file_status) == 0)
374 /* check for typical filesize of a Supaplex level package file */
375 if (file_status.st_size == 170496)
376 return LEVEL_FILE_TYPE_SP;
379 return LEVEL_FILE_TYPE_UNKNOWN;
382 static char *getSingleLevelBasename(int nr, int type)
384 static char basename[MAX_FILENAME_LEN];
385 char *level_filename = getStringCopy(leveldir_current->level_filename);
387 if (level_filename == NULL)
388 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
392 case LEVEL_FILE_TYPE_RND:
394 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
396 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
399 case LEVEL_FILE_TYPE_EM:
400 sprintf(basename, "%d", nr);
403 case LEVEL_FILE_TYPE_UNKNOWN:
405 sprintf(basename, level_filename, nr);
409 free(level_filename);
414 static char *getPackedLevelBasename(int type)
416 static char basename[MAX_FILENAME_LEN];
417 char *directory = getCurrentLevelDir();
419 struct dirent *dir_entry;
421 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
423 if ((dir = opendir(directory)) == NULL)
425 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
430 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
432 char *entry_basename = dir_entry->d_name;
433 int entry_type = getFileTypeFromBasename(entry_basename);
435 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
437 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
440 strcpy(basename, entry_basename);
452 static char *getSingleLevelFilename(int nr, int type)
454 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
458 static char *getPackedLevelFilename(int type)
460 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
464 char *getDefaultLevelFilename(int nr)
466 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
469 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
474 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
475 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
478 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
483 lfi->basename = getPackedLevelBasename(lfi->type);
484 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
487 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
489 /* special case: level number is negative => check for level template file */
492 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
497 if (leveldir_current->level_filename != NULL)
499 /* check for file name/pattern specified in "levelinfo.conf" */
500 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
501 if (fileExists(lfi->filename))
505 /* check for native Rocks'n'Diamonds level file */
506 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
507 if (fileExists(lfi->filename))
510 /* check for classic Emerald Mine level file */
511 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
512 if (fileExists(lfi->filename))
515 /* check for various packed level file formats */
516 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
517 if (fileExists(lfi->filename))
520 /* no known level file found -- try to use default values */
521 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
524 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
526 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
527 lfi->type = getFileTypeFromBasename(lfi->basename);
530 static struct LevelFileInfo *getLevelFileInfo(int nr)
532 static struct LevelFileInfo level_file_info;
534 /* always start with reliable default values */
535 setFileInfoToDefaults(&level_file_info);
537 level_file_info.nr = nr; /* set requested level number */
539 determineLevelFileInfo_Filename(&level_file_info);
540 determineLevelFileInfo_Filetype(&level_file_info);
542 return &level_file_info;
546 /* ------------------------------------------------------------------------- */
547 /* functions for loading R'n'D level */
548 /* ------------------------------------------------------------------------- */
550 int getMappedElement(int element)
552 /* remap some (historic, now obsolete) elements */
557 case EL_PLAYER_OBSOLETE:
558 element = EL_PLAYER_1;
561 case EL_KEY_OBSOLETE:
564 case EL_EM_KEY_1_FILE_OBSOLETE:
565 element = EL_EM_KEY_1;
568 case EL_EM_KEY_2_FILE_OBSOLETE:
569 element = EL_EM_KEY_2;
572 case EL_EM_KEY_3_FILE_OBSOLETE:
573 element = EL_EM_KEY_3;
576 case EL_EM_KEY_4_FILE_OBSOLETE:
577 element = EL_EM_KEY_4;
580 case EL_ENVELOPE_OBSOLETE:
581 element = EL_ENVELOPE_1;
589 if (element >= NUM_FILE_ELEMENTS)
591 Error(ERR_WARN, "invalid level element %d", element);
593 element = EL_UNKNOWN;
598 if (element >= NUM_FILE_ELEMENTS)
600 Error(ERR_WARN, "invalid level element %d", element);
602 element = EL_UNKNOWN;
604 else if (element == EL_PLAYER_OBSOLETE)
605 element = EL_PLAYER_1;
606 else if (element == EL_KEY_OBSOLETE)
613 int getMappedElementByVersion(int element, int game_version)
615 /* remap some elements due to certain game version */
617 if (game_version <= VERSION_IDENT(2,2,0,0))
619 /* map game font elements */
620 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
621 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
622 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
623 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
626 if (game_version < VERSION_IDENT(3,0,0,0))
628 /* map Supaplex gravity tube elements */
629 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
630 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
631 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
632 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
639 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
641 level->file_version = getFileVersion(file);
642 level->game_version = getFileVersion(file);
647 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
651 level->fieldx = getFile8Bit(file);
652 level->fieldy = getFile8Bit(file);
654 level->time = getFile16BitBE(file);
655 level->gems_needed = getFile16BitBE(file);
657 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
658 level->name[i] = getFile8Bit(file);
659 level->name[MAX_LEVEL_NAME_LEN] = 0;
661 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
662 level->score[i] = getFile8Bit(file);
664 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
665 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
666 for (y = 0; y < 3; y++)
667 for (x = 0; x < 3; x++)
668 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
670 level->amoeba_speed = getFile8Bit(file);
671 level->time_magic_wall = getFile8Bit(file);
672 level->time_wheel = getFile8Bit(file);
673 level->amoeba_content = getMappedElement(getFile8Bit(file));
674 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
675 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
676 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
677 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
679 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
681 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
682 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
684 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
686 level->can_move_into_acid_bits = getFile16BitBE(file);
688 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
690 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
695 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
699 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
700 level->author[i] = getFile8Bit(file);
701 level->author[MAX_LEVEL_NAME_LEN] = 0;
706 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
709 int chunk_size_expected = level->fieldx * level->fieldy;
711 /* Note: "chunk_size" was wrong before version 2.0 when elements are
712 stored with 16-bit encoding (and should be twice as big then).
713 Even worse, playfield data was stored 16-bit when only yamyam content
714 contained 16-bit elements and vice versa. */
716 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
717 chunk_size_expected *= 2;
719 if (chunk_size_expected != chunk_size)
721 ReadUnusedBytesFromFile(file, chunk_size);
722 return chunk_size_expected;
725 for (y = 0; y < level->fieldy; y++)
726 for (x = 0; x < level->fieldx; x++)
728 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
733 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
737 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
738 int chunk_size_expected = header_size + content_size;
740 /* Note: "chunk_size" was wrong before version 2.0 when elements are
741 stored with 16-bit encoding (and should be twice as big then).
742 Even worse, playfield data was stored 16-bit when only yamyam content
743 contained 16-bit elements and vice versa. */
745 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
746 chunk_size_expected += content_size;
748 if (chunk_size_expected != chunk_size)
750 ReadUnusedBytesFromFile(file, chunk_size);
751 return chunk_size_expected;
755 level->num_yamyam_contents = getFile8Bit(file);
759 /* correct invalid number of content fields -- should never happen */
760 if (level->num_yamyam_contents < 1 ||
761 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
762 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
764 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
765 for (y = 0; y < 3; y++)
766 for (x = 0; x < 3; x++)
767 level->yamyam_content[i][x][y] =
768 getMappedElement(level->encoding_16bit_field ?
769 getFile16BitBE(file) : getFile8Bit(file));
773 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
777 int num_contents, content_xsize, content_ysize;
778 int content_array[MAX_ELEMENT_CONTENTS][3][3];
780 element = getMappedElement(getFile16BitBE(file));
781 num_contents = getFile8Bit(file);
782 content_xsize = getFile8Bit(file);
783 content_ysize = getFile8Bit(file);
785 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
787 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
788 for (y = 0; y < 3; y++)
789 for (x = 0; x < 3; x++)
790 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
792 /* correct invalid number of content fields -- should never happen */
793 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
794 num_contents = STD_ELEMENT_CONTENTS;
796 if (element == EL_YAMYAM)
798 level->num_yamyam_contents = num_contents;
800 for (i = 0; i < num_contents; i++)
801 for (y = 0; y < 3; y++)
802 for (x = 0; x < 3; x++)
803 level->yamyam_content[i][x][y] = content_array[i][x][y];
805 else if (element == EL_BD_AMOEBA)
807 level->amoeba_content = content_array[0][0][0];
811 Error(ERR_WARN, "cannot load content for element '%d'", element);
817 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
823 int chunk_size_expected;
825 element = getMappedElement(getFile16BitBE(file));
826 if (!IS_ENVELOPE(element))
827 element = EL_ENVELOPE_1;
829 envelope_nr = element - EL_ENVELOPE_1;
831 envelope_len = getFile16BitBE(file);
833 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
834 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
836 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
838 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
839 if (chunk_size_expected != chunk_size)
841 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
842 return chunk_size_expected;
845 for (i = 0; i < envelope_len; i++)
846 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
851 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
853 int num_changed_custom_elements = getFile16BitBE(file);
854 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
857 if (chunk_size_expected != chunk_size)
859 ReadUnusedBytesFromFile(file, chunk_size - 2);
860 return chunk_size_expected;
863 for (i = 0; i < num_changed_custom_elements; i++)
865 int element = getFile16BitBE(file);
866 int properties = getFile32BitBE(file);
868 if (IS_CUSTOM_ELEMENT(element))
869 Properties[element][EP_BITFIELD_BASE] = properties;
871 Error(ERR_WARN, "invalid custom element number %d", element);
877 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
879 int num_changed_custom_elements = getFile16BitBE(file);
880 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
883 if (chunk_size_expected != chunk_size)
885 ReadUnusedBytesFromFile(file, chunk_size - 2);
886 return chunk_size_expected;
889 for (i = 0; i < num_changed_custom_elements; i++)
891 int element = getFile16BitBE(file);
892 int custom_target_element = getFile16BitBE(file);
894 if (IS_CUSTOM_ELEMENT(element))
895 element_info[element].change->target_element = custom_target_element;
897 Error(ERR_WARN, "invalid custom element number %d", element);
903 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
905 int num_changed_custom_elements = getFile16BitBE(file);
906 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
909 if (chunk_size_expected != chunk_size)
911 ReadUnusedBytesFromFile(file, chunk_size - 2);
912 return chunk_size_expected;
915 for (i = 0; i < num_changed_custom_elements; i++)
917 int element = getFile16BitBE(file);
919 if (!IS_CUSTOM_ELEMENT(element))
921 Error(ERR_WARN, "invalid custom element number %d", element);
923 element = EL_INTERNAL_DUMMY;
926 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
927 element_info[element].description[j] = getFile8Bit(file);
928 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
930 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
932 /* some free bytes for future properties and padding */
933 ReadUnusedBytesFromFile(file, 7);
935 element_info[element].use_gfx_element = getFile8Bit(file);
936 element_info[element].gfx_element =
937 getMappedElement(getFile16BitBE(file));
939 element_info[element].collect_score = getFile8Bit(file);
940 element_info[element].collect_count = getFile8Bit(file);
942 element_info[element].push_delay_fixed = getFile16BitBE(file);
943 element_info[element].push_delay_random = getFile16BitBE(file);
944 element_info[element].move_delay_fixed = getFile16BitBE(file);
945 element_info[element].move_delay_random = getFile16BitBE(file);
947 element_info[element].move_pattern = getFile16BitBE(file);
948 element_info[element].move_direction_initial = getFile8Bit(file);
949 element_info[element].move_stepsize = getFile8Bit(file);
951 for (y = 0; y < 3; y++)
952 for (x = 0; x < 3; x++)
953 element_info[element].content[x][y] =
954 getMappedElement(getFile16BitBE(file));
956 element_info[element].change->events = getFile32BitBE(file);
958 element_info[element].change->target_element =
959 getMappedElement(getFile16BitBE(file));
961 element_info[element].change->delay_fixed = getFile16BitBE(file);
962 element_info[element].change->delay_random = getFile16BitBE(file);
963 element_info[element].change->delay_frames = getFile16BitBE(file);
965 element_info[element].change->trigger_element =
966 getMappedElement(getFile16BitBE(file));
968 element_info[element].change->explode = getFile8Bit(file);
969 element_info[element].change->use_content = getFile8Bit(file);
970 element_info[element].change->only_complete = getFile8Bit(file);
971 element_info[element].change->use_random_change = getFile8Bit(file);
973 element_info[element].change->random = getFile8Bit(file);
974 element_info[element].change->power = getFile8Bit(file);
976 for (y = 0; y < 3; y++)
977 for (x = 0; x < 3; x++)
978 element_info[element].change->content[x][y] =
979 getMappedElement(getFile16BitBE(file));
981 element_info[element].slippery_type = getFile8Bit(file);
983 /* some free bytes for future properties and padding */
984 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
986 /* mark that this custom element has been modified */
987 element_info[element].modified_settings = TRUE;
993 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
995 struct ElementInfo *ei;
996 int chunk_size_expected;
1000 element = getFile16BitBE(file);
1002 if (!IS_CUSTOM_ELEMENT(element))
1004 Error(ERR_WARN, "invalid custom element number %d", element);
1006 ReadUnusedBytesFromFile(file, chunk_size - 2);
1010 ei = &element_info[element];
1012 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1013 ei->description[i] = getFile8Bit(file);
1014 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1016 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1017 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1019 ei->num_change_pages = getFile8Bit(file);
1021 /* some free bytes for future base property values and padding */
1022 ReadUnusedBytesFromFile(file, 5);
1024 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1025 if (chunk_size_expected != chunk_size)
1027 ReadUnusedBytesFromFile(file, chunk_size - 48);
1028 return chunk_size_expected;
1031 /* read custom property values */
1033 ei->use_gfx_element = getFile8Bit(file);
1034 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1036 ei->collect_score = getFile8Bit(file);
1037 ei->collect_count = getFile8Bit(file);
1039 ei->push_delay_fixed = getFile16BitBE(file);
1040 ei->push_delay_random = getFile16BitBE(file);
1041 ei->move_delay_fixed = getFile16BitBE(file);
1042 ei->move_delay_random = getFile16BitBE(file);
1044 /* bits 0 - 15 of "move_pattern" ... */
1045 ei->move_pattern = getFile16BitBE(file);
1046 ei->move_direction_initial = getFile8Bit(file);
1047 ei->move_stepsize = getFile8Bit(file);
1049 ei->slippery_type = getFile8Bit(file);
1051 for (y = 0; y < 3; y++)
1052 for (x = 0; x < 3; x++)
1053 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1055 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1056 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1057 ei->move_leave_type = getFile8Bit(file);
1059 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1060 ei->move_pattern |= (getFile16BitBE(file) << 16);
1062 ei->access_direction = getFile8Bit(file);
1064 ei->explosion_delay = getFile8Bit(file);
1065 ei->ignition_delay = getFile8Bit(file);
1067 /* some free bytes for future custom property values and padding */
1068 ReadUnusedBytesFromFile(file, 2);
1070 /* read change property values */
1072 setElementChangePages(ei, ei->num_change_pages);
1074 for (i = 0; i < ei->num_change_pages; i++)
1076 struct ElementChangeInfo *change = &ei->change_page[i];
1078 /* always start with reliable default values */
1079 setElementChangeInfoToDefaults(change);
1081 change->events = getFile32BitBE(file);
1083 change->target_element = getMappedElement(getFile16BitBE(file));
1085 change->delay_fixed = getFile16BitBE(file);
1086 change->delay_random = getFile16BitBE(file);
1087 change->delay_frames = getFile16BitBE(file);
1089 change->trigger_element = getMappedElement(getFile16BitBE(file));
1091 change->explode = getFile8Bit(file);
1092 change->use_content = getFile8Bit(file);
1093 change->only_complete = getFile8Bit(file);
1094 change->use_random_change = getFile8Bit(file);
1096 change->random = getFile8Bit(file);
1097 change->power = getFile8Bit(file);
1099 for (y = 0; y < 3; y++)
1100 for (x = 0; x < 3; x++)
1101 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1103 change->can_change = getFile8Bit(file);
1105 change->trigger_side = getFile8Bit(file);
1108 change->trigger_player = getFile8Bit(file);
1109 change->trigger_page = getFile8Bit(file);
1111 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1112 CH_PAGE_ANY : (1 << change->trigger_page));
1114 /* some free bytes for future change property values and padding */
1115 ReadUnusedBytesFromFile(file, 6);
1119 /* some free bytes for future change property values and padding */
1120 ReadUnusedBytesFromFile(file, 8);
1124 /* mark this custom element as modified */
1125 ei->modified_settings = TRUE;
1130 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1132 struct ElementInfo *ei;
1133 struct ElementGroupInfo *group;
1137 element = getFile16BitBE(file);
1139 if (!IS_GROUP_ELEMENT(element))
1141 Error(ERR_WARN, "invalid group element number %d", element);
1143 ReadUnusedBytesFromFile(file, chunk_size - 2);
1147 ei = &element_info[element];
1149 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1150 ei->description[i] = getFile8Bit(file);
1151 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1153 group = element_info[element].group;
1155 group->num_elements = getFile8Bit(file);
1157 ei->use_gfx_element = getFile8Bit(file);
1158 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1160 group->choice_mode = getFile8Bit(file);
1162 /* some free bytes for future values and padding */
1163 ReadUnusedBytesFromFile(file, 3);
1165 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1166 group->element[i] = getMappedElement(getFile16BitBE(file));
1168 /* mark this group element as modified */
1169 element_info[element].modified_settings = TRUE;
1174 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1175 struct LevelFileInfo *level_file_info)
1177 char *filename = level_file_info->filename;
1178 char cookie[MAX_LINE_LEN];
1179 char chunk_name[CHUNK_ID_LEN + 1];
1183 if (!(file = fopen(filename, MODE_READ)))
1185 level->no_level_file = TRUE;
1187 if (level != &level_template)
1188 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1193 getFileChunkBE(file, chunk_name, NULL);
1194 if (strcmp(chunk_name, "RND1") == 0)
1196 getFile32BitBE(file); /* not used */
1198 getFileChunkBE(file, chunk_name, NULL);
1199 if (strcmp(chunk_name, "CAVE") != 0)
1201 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1206 else /* check for pre-2.0 file format with cookie string */
1208 strcpy(cookie, chunk_name);
1209 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1210 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1211 cookie[strlen(cookie) - 1] = '\0';
1213 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1215 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1220 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1222 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1227 /* pre-2.0 level files have no game version, so use file version here */
1228 level->game_version = level->file_version;
1231 if (level->file_version < FILE_VERSION_1_2)
1233 /* level files from versions before 1.2.0 without chunk structure */
1234 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1235 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1243 int (*loader)(FILE *, int, struct LevelInfo *);
1247 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1248 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1249 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1250 { "BODY", -1, LoadLevel_BODY },
1251 { "CONT", -1, LoadLevel_CONT },
1252 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1253 { "CNT3", -1, LoadLevel_CNT3 },
1254 { "CUS1", -1, LoadLevel_CUS1 },
1255 { "CUS2", -1, LoadLevel_CUS2 },
1256 { "CUS3", -1, LoadLevel_CUS3 },
1257 { "CUS4", -1, LoadLevel_CUS4 },
1258 { "GRP1", -1, LoadLevel_GRP1 },
1262 while (getFileChunkBE(file, chunk_name, &chunk_size))
1266 while (chunk_info[i].name != NULL &&
1267 strcmp(chunk_name, chunk_info[i].name) != 0)
1270 if (chunk_info[i].name == NULL)
1272 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1273 chunk_name, filename);
1274 ReadUnusedBytesFromFile(file, chunk_size);
1276 else if (chunk_info[i].size != -1 &&
1277 chunk_info[i].size != chunk_size)
1279 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1280 chunk_size, chunk_name, filename);
1281 ReadUnusedBytesFromFile(file, chunk_size);
1285 /* call function to load this level chunk */
1286 int chunk_size_expected =
1287 (chunk_info[i].loader)(file, chunk_size, level);
1289 /* the size of some chunks cannot be checked before reading other
1290 chunks first (like "HEAD" and "BODY") that contain some header
1291 information, so check them here */
1292 if (chunk_size_expected != chunk_size)
1294 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1295 chunk_size, chunk_name, filename);
1304 /* ------------------------------------------------------------------------- */
1305 /* functions for loading EM level */
1306 /* ------------------------------------------------------------------------- */
1308 static int map_em_element_yam(int element)
1312 case 0x00: return EL_EMPTY;
1313 case 0x01: return EL_EMERALD;
1314 case 0x02: return EL_DIAMOND;
1315 case 0x03: return EL_ROCK;
1316 case 0x04: return EL_ROBOT;
1317 case 0x05: return EL_SPACESHIP_UP;
1318 case 0x06: return EL_BOMB;
1319 case 0x07: return EL_BUG_UP;
1320 case 0x08: return EL_AMOEBA_DROP;
1321 case 0x09: return EL_NUT;
1322 case 0x0a: return EL_YAMYAM;
1323 case 0x0b: return EL_QUICKSAND_FULL;
1324 case 0x0c: return EL_SAND;
1325 case 0x0d: return EL_WALL_SLIPPERY;
1326 case 0x0e: return EL_STEELWALL;
1327 case 0x0f: return EL_WALL;
1328 case 0x10: return EL_EM_KEY_1;
1329 case 0x11: return EL_EM_KEY_2;
1330 case 0x12: return EL_EM_KEY_4;
1331 case 0x13: return EL_EM_KEY_3;
1332 case 0x14: return EL_MAGIC_WALL;
1333 case 0x15: return EL_ROBOT_WHEEL;
1334 case 0x16: return EL_DYNAMITE;
1336 case 0x17: return EL_EM_KEY_1; /* EMC */
1337 case 0x18: return EL_BUG_UP; /* EMC */
1338 case 0x1a: return EL_DIAMOND; /* EMC */
1339 case 0x1b: return EL_EMERALD; /* EMC */
1340 case 0x25: return EL_NUT; /* EMC */
1341 case 0x80: return EL_EMPTY; /* EMC */
1342 case 0x85: return EL_EM_KEY_1; /* EMC */
1343 case 0x86: return EL_EM_KEY_2; /* EMC */
1344 case 0x87: return EL_EM_KEY_4; /* EMC */
1345 case 0x88: return EL_EM_KEY_3; /* EMC */
1346 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1347 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1348 case 0xaf: return EL_DYNAMITE; /* EMC */
1349 case 0xbd: return EL_SAND; /* EMC */
1352 Error(ERR_WARN, "invalid level element %d", element);
1357 static int map_em_element_field(int element)
1359 if (element >= 0xc8 && element <= 0xe1)
1360 return EL_CHAR_A + (element - 0xc8);
1361 else if (element >= 0xe2 && element <= 0xeb)
1362 return EL_CHAR_0 + (element - 0xe2);
1366 case 0x00: return EL_ROCK;
1367 case 0x01: return EL_ROCK; /* EMC */
1368 case 0x02: return EL_DIAMOND;
1369 case 0x03: return EL_DIAMOND;
1370 case 0x04: return EL_ROBOT;
1371 case 0x05: return EL_ROBOT; /* EMC */
1372 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1373 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1374 case 0x08: return EL_SPACESHIP_UP;
1375 case 0x09: return EL_SPACESHIP_RIGHT;
1376 case 0x0a: return EL_SPACESHIP_DOWN;
1377 case 0x0b: return EL_SPACESHIP_LEFT;
1378 case 0x0c: return EL_SPACESHIP_UP;
1379 case 0x0d: return EL_SPACESHIP_RIGHT;
1380 case 0x0e: return EL_SPACESHIP_DOWN;
1381 case 0x0f: return EL_SPACESHIP_LEFT;
1383 case 0x10: return EL_BOMB;
1384 case 0x11: return EL_BOMB; /* EMC */
1385 case 0x12: return EL_EMERALD;
1386 case 0x13: return EL_EMERALD;
1387 case 0x14: return EL_BUG_UP;
1388 case 0x15: return EL_BUG_RIGHT;
1389 case 0x16: return EL_BUG_DOWN;
1390 case 0x17: return EL_BUG_LEFT;
1391 case 0x18: return EL_BUG_UP;
1392 case 0x19: return EL_BUG_RIGHT;
1393 case 0x1a: return EL_BUG_DOWN;
1394 case 0x1b: return EL_BUG_LEFT;
1395 case 0x1c: return EL_AMOEBA_DROP;
1396 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1397 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1398 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1400 case 0x20: return EL_ROCK;
1401 case 0x21: return EL_BOMB; /* EMC */
1402 case 0x22: return EL_DIAMOND; /* EMC */
1403 case 0x23: return EL_EMERALD; /* EMC */
1404 case 0x24: return EL_MAGIC_WALL;
1405 case 0x25: return EL_NUT;
1406 case 0x26: return EL_NUT; /* EMC */
1407 case 0x27: return EL_NUT; /* EMC */
1409 /* looks like magic wheel, but is _always_ activated */
1410 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1412 case 0x29: return EL_YAMYAM; /* up */
1413 case 0x2a: return EL_YAMYAM; /* down */
1414 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1415 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1416 case 0x2d: return EL_QUICKSAND_FULL;
1417 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1418 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1420 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1421 case 0x31: return EL_SAND; /* EMC */
1422 case 0x32: return EL_SAND; /* EMC */
1423 case 0x33: return EL_SAND; /* EMC */
1424 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1425 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1426 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1427 case 0x37: return EL_SAND; /* EMC */
1428 case 0x38: return EL_ROCK; /* EMC */
1429 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1430 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1431 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1432 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1433 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1434 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1435 case 0x3f: return EL_ACID_POOL_BOTTOM;
1437 case 0x40: return EL_EXIT_OPEN; /* 1 */
1438 case 0x41: return EL_EXIT_OPEN; /* 2 */
1439 case 0x42: return EL_EXIT_OPEN; /* 3 */
1440 case 0x43: return EL_BALLOON; /* EMC */
1441 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1442 case 0x45: return EL_SPRING; /* EMC */
1443 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1444 case 0x47: return EL_SPRING; /* left */ /* EMC */
1445 case 0x48: return EL_SPRING; /* right */ /* EMC */
1446 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1447 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1448 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1449 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1450 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1451 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1452 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1454 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1455 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1456 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1457 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1458 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1459 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1460 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1461 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1462 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1463 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1464 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1465 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1466 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1467 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1468 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1469 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1471 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1472 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1473 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1474 case 0x63: return EL_SPRING; /* left */ /* EMC */
1475 case 0x64: return EL_SPRING; /* right */ /* EMC */
1476 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1477 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1478 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1479 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1480 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1481 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1482 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1483 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1484 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1485 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1486 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1488 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1489 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1490 case 0x72: return EL_NUT; /* left */ /* EMC */
1491 case 0x73: return EL_SAND; /* EMC (? "nut") */
1492 case 0x74: return EL_STEELWALL;
1493 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1494 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1495 case 0x77: return EL_BOMB; /* left */ /* EMC */
1496 case 0x78: return EL_BOMB; /* right */ /* EMC */
1497 case 0x79: return EL_ROCK; /* left */ /* EMC */
1498 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1499 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1500 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1501 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1502 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1503 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1505 case 0x80: return EL_EMPTY;
1506 case 0x81: return EL_WALL_SLIPPERY;
1507 case 0x82: return EL_SAND;
1508 case 0x83: return EL_STEELWALL;
1509 case 0x84: return EL_WALL;
1510 case 0x85: return EL_EM_KEY_1;
1511 case 0x86: return EL_EM_KEY_2;
1512 case 0x87: return EL_EM_KEY_4;
1513 case 0x88: return EL_EM_KEY_3;
1514 case 0x89: return EL_EM_GATE_1;
1515 case 0x8a: return EL_EM_GATE_2;
1516 case 0x8b: return EL_EM_GATE_4;
1517 case 0x8c: return EL_EM_GATE_3;
1518 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1519 case 0x8e: return EL_EM_GATE_1_GRAY;
1520 case 0x8f: return EL_EM_GATE_2_GRAY;
1522 case 0x90: return EL_EM_GATE_4_GRAY;
1523 case 0x91: return EL_EM_GATE_3_GRAY;
1524 case 0x92: return EL_MAGIC_WALL;
1525 case 0x93: return EL_ROBOT_WHEEL;
1526 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1527 case 0x95: return EL_ACID_POOL_TOPLEFT;
1528 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1529 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1530 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1531 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1532 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1533 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1534 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1535 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1536 case 0x9e: return EL_EXIT_CLOSED;
1537 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1539 /* looks like normal sand, but behaves like wall */
1540 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1541 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1542 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1543 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1544 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1545 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1546 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1547 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1548 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1549 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1550 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1551 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1552 case 0xac: return EL_CHAR_COMMA; /* EMC */
1553 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1554 case 0xae: return EL_CHAR_MINUS; /* EMC */
1555 case 0xaf: return EL_DYNAMITE;
1557 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1558 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1559 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1560 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1561 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1562 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1563 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1564 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1565 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1566 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1567 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1568 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1569 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1570 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1571 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1572 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1574 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1575 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1576 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1577 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1578 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1579 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1580 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1581 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1583 /* characters: see above */
1585 case 0xec: return EL_CHAR_PERIOD;
1586 case 0xed: return EL_CHAR_EXCLAM;
1587 case 0xee: return EL_CHAR_COLON;
1588 case 0xef: return EL_CHAR_QUESTION;
1590 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1591 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1592 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1593 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1594 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1595 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1596 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1597 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1599 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1600 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1601 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1602 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1603 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1604 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1606 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1607 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1610 /* should never happen (all 8-bit value cases should be handled) */
1611 Error(ERR_WARN, "invalid level element %d", element);
1616 #define EM_LEVEL_SIZE 2106
1617 #define EM_LEVEL_XSIZE 64
1618 #define EM_LEVEL_YSIZE 32
1620 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1621 struct LevelFileInfo *level_file_info)
1623 char *filename = level_file_info->filename;
1625 unsigned char leveldata[EM_LEVEL_SIZE];
1626 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1627 unsigned char code0 = 0x65;
1628 unsigned char code1 = 0x11;
1629 boolean level_is_crypted = FALSE;
1630 int nr = level_file_info->nr;
1633 if (!(file = fopen(filename, MODE_READ)))
1635 level->no_level_file = TRUE;
1637 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1642 for(i = 0; i < EM_LEVEL_SIZE; i++)
1643 leveldata[i] = fgetc(file);
1647 /* check if level data is crypted by testing against known starting bytes
1648 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1650 if ((leveldata[0] == 0xf1 ||
1651 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1653 level_is_crypted = TRUE;
1655 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1656 leveldata[0] = 0xf1;
1659 if (level_is_crypted) /* decode crypted level data */
1661 for(i = 0; i < EM_LEVEL_SIZE; i++)
1663 leveldata[i] ^= code0;
1664 leveldata[i] -= code1;
1666 code0 = (code0 + 7) & 0xff;
1670 level->fieldx = EM_LEVEL_XSIZE;
1671 level->fieldy = EM_LEVEL_YSIZE;
1673 level->time = header[46] * 10;
1674 level->gems_needed = header[47];
1676 /* The original Emerald Mine levels have their level number stored
1677 at the second byte of the level file...
1678 Do not trust this information at other level files, e.g. EMC,
1679 but correct it anyway (normally the first row is completely
1680 steel wall, so the correction does not hurt anyway). */
1682 if (leveldata[1] == nr)
1683 leveldata[1] = leveldata[2]; /* correct level number field */
1685 sprintf(level->name, "Level %d", nr); /* set level name */
1687 level->score[SC_EMERALD] = header[36];
1688 level->score[SC_DIAMOND] = header[37];
1689 level->score[SC_ROBOT] = header[38];
1690 level->score[SC_SPACESHIP] = header[39];
1691 level->score[SC_BUG] = header[40];
1692 level->score[SC_YAMYAM] = header[41];
1693 level->score[SC_NUT] = header[42];
1694 level->score[SC_DYNAMITE] = header[43];
1695 level->score[SC_TIME_BONUS] = header[44];
1697 level->num_yamyam_contents = 4;
1699 for(i = 0; i < level->num_yamyam_contents; i++)
1700 for(y = 0; y < 3; y++)
1701 for(x = 0; x < 3; x++)
1702 level->yamyam_content[i][x][y] =
1703 map_em_element_yam(header[i * 9 + y * 3 + x]);
1705 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1706 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1707 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1708 level->amoeba_content = EL_DIAMOND;
1710 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1712 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1714 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1715 new_element = EL_AMOEBA_WET;
1717 level->field[x][y] = new_element;
1720 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1721 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1722 level->field[x][y] = EL_PLAYER_1;
1724 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1725 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1726 level->field[x][y] = EL_PLAYER_2;
1729 /* ------------------------------------------------------------------------- */
1730 /* functions for loading SP level */
1731 /* ------------------------------------------------------------------------- */
1733 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1734 #define SP_LEVEL_SIZE 1536
1735 #define SP_LEVEL_XSIZE 60
1736 #define SP_LEVEL_YSIZE 24
1737 #define SP_LEVEL_NAME_LEN 23
1739 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1744 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1745 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1747 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1749 int element_old = fgetc(file);
1752 if (element_old <= 0x27)
1753 element_new = getMappedElement(EL_SP_START + element_old);
1754 else if (element_old == 0x28)
1755 element_new = EL_INVISIBLE_WALL;
1758 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1759 Error(ERR_WARN, "invalid level element %d", element_old);
1761 element_new = EL_UNKNOWN;
1764 level->field[x][y] = element_new;
1768 ReadUnusedBytesFromFile(file, 4);
1770 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1771 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1773 ReadUnusedBytesFromFile(file, 1);
1775 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1776 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1777 level->name[i] = fgetc(file);
1778 level->name[SP_LEVEL_NAME_LEN] = '\0';
1780 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1781 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1783 /* number of infotrons needed; 0 means that Supaplex will count the total
1784 amount of infotrons in the level and use the low byte of that number.
1785 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1786 level->gems_needed = fgetc(file);
1788 /* information about special gravity port entries */
1789 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1791 level->fieldx = SP_LEVEL_XSIZE;
1792 level->fieldy = SP_LEVEL_YSIZE;
1794 level->time = 0; /* no time limit */
1795 level->amoeba_speed = 0;
1796 level->time_magic_wall = 0;
1797 level->time_wheel = 0;
1798 level->amoeba_content = EL_EMPTY;
1800 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1801 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1803 /* there are no yamyams in supaplex levels */
1804 for(i = 0; i < level->num_yamyam_contents; i++)
1805 for(y = 0; y < 3; y++)
1806 for(x = 0; x < 3; x++)
1807 level->yamyam_content[i][x][y] = EL_EMPTY;
1810 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1811 struct LevelFileInfo *level_file_info)
1813 char *filename = level_file_info->filename;
1815 int nr = level_file_info->nr - leveldir_current->first_level;
1817 char name_first, name_last;
1818 struct LevelInfo multipart_level;
1819 int multipart_xpos, multipart_ypos;
1820 boolean is_multipart_level;
1821 boolean is_first_part;
1822 boolean reading_multipart_level = FALSE;
1823 boolean use_empty_level = FALSE;
1825 if (!(file = fopen(filename, MODE_READ)))
1827 level->no_level_file = TRUE;
1829 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1834 /* position file stream to the requested level inside the level package */
1835 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1837 level->no_level_file = TRUE;
1839 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1844 /* there exist Supaplex level package files with multi-part levels which
1845 can be detected as follows: instead of leading and trailing dashes ('-')
1846 to pad the level name, they have leading and trailing numbers which are
1847 the x and y coordinations of the current part of the multi-part level;
1848 if there are '?' characters instead of numbers on the left or right side
1849 of the level name, the multi-part level consists of only horizontal or
1852 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1854 LoadLevelFromFileStream_SP(file, level, l);
1856 /* check if this level is a part of a bigger multi-part level */
1858 name_first = level->name[0];
1859 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1861 is_multipart_level =
1862 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1863 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1866 ((name_first == '?' || name_first == '1') &&
1867 (name_last == '?' || name_last == '1'));
1869 /* correct leading multipart level meta information in level name */
1870 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1871 level->name[i] = '-';
1873 /* correct trailing multipart level meta information in level name */
1874 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1875 level->name[i] = '-';
1877 /* ---------- check for normal single level ---------- */
1879 if (!reading_multipart_level && !is_multipart_level)
1881 /* the current level is simply a normal single-part level, and we are
1882 not reading a multi-part level yet, so return the level as it is */
1887 /* ---------- check for empty level (unused multi-part) ---------- */
1889 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1891 /* this is a part of a multi-part level, but not the first part
1892 (and we are not already reading parts of a multi-part level);
1893 in this case, use an empty level instead of the single part */
1895 use_empty_level = TRUE;
1900 /* ---------- check for finished multi-part level ---------- */
1902 if (reading_multipart_level &&
1903 (!is_multipart_level ||
1904 strcmp(level->name, multipart_level.name) != 0))
1906 /* we are already reading parts of a multi-part level, but this level is
1907 either not a multi-part level, or a part of a different multi-part
1908 level; in both cases, the multi-part level seems to be complete */
1913 /* ---------- here we have one part of a multi-part level ---------- */
1915 reading_multipart_level = TRUE;
1917 if (is_first_part) /* start with first part of new multi-part level */
1919 /* copy level info structure from first part */
1920 multipart_level = *level;
1922 /* clear playfield of new multi-part level */
1923 for (y = 0; y < MAX_LEV_FIELDY; y++)
1924 for (x = 0; x < MAX_LEV_FIELDX; x++)
1925 multipart_level.field[x][y] = EL_EMPTY;
1928 if (name_first == '?')
1930 if (name_last == '?')
1933 multipart_xpos = (int)(name_first - '0');
1934 multipart_ypos = (int)(name_last - '0');
1937 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1938 multipart_xpos, multipart_ypos, multipart_level.name);
1941 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1942 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1944 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1949 multipart_level.fieldx = MAX(multipart_level.fieldx,
1950 multipart_xpos * SP_LEVEL_XSIZE);
1951 multipart_level.fieldy = MAX(multipart_level.fieldy,
1952 multipart_ypos * SP_LEVEL_YSIZE);
1954 /* copy level part at the right position of multi-part level */
1955 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1957 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1959 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1960 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1962 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1969 if (use_empty_level)
1971 setLevelInfoToDefaults(level);
1973 level->fieldx = SP_LEVEL_XSIZE;
1974 level->fieldy = SP_LEVEL_YSIZE;
1976 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1977 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1978 level->field[x][y] = EL_EMPTY;
1980 strcpy(level->name, "-------- EMPTY --------");
1982 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1985 if (reading_multipart_level)
1986 *level = multipart_level;
1989 /* ------------------------------------------------------------------------- */
1990 /* functions for loading generic level */
1991 /* ------------------------------------------------------------------------- */
1993 void LoadLevelFromFileInfo(struct LevelInfo *level,
1994 struct LevelFileInfo *level_file_info)
1996 /* always start with reliable default values */
1997 setLevelInfoToDefaults(level);
1999 switch (level_file_info->type)
2001 case LEVEL_FILE_TYPE_RND:
2002 LoadLevelFromFileInfo_RND(level, level_file_info);
2005 case LEVEL_FILE_TYPE_EM:
2006 LoadLevelFromFileInfo_EM(level, level_file_info);
2009 case LEVEL_FILE_TYPE_SP:
2010 LoadLevelFromFileInfo_SP(level, level_file_info);
2014 LoadLevelFromFileInfo_RND(level, level_file_info);
2019 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2021 static struct LevelFileInfo level_file_info;
2023 /* always start with reliable default values */
2024 setFileInfoToDefaults(&level_file_info);
2026 level_file_info.nr = 0; /* unknown level number */
2027 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2028 level_file_info.filename = filename;
2030 LoadLevelFromFileInfo(level, &level_file_info);
2033 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2035 if (leveldir_current == NULL) /* only when dumping level */
2039 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2042 /* determine correct game engine version of current level */
2044 if (!leveldir_current->latest_engine)
2046 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2047 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2048 IS_LEVELCLASS_UNDEFINED(leveldir_current))
2052 printf("\n::: This level is private or contributed: '%s'\n", filename);
2056 printf("\n::: Use the stored game engine version for this level\n");
2059 /* For all levels which are not forced to use the latest game engine
2060 version (normally user contributed, private and undefined levels),
2061 use the version of the game engine the levels were created for.
2063 Since 2.0.1, the game engine version is now directly stored
2064 in the level file (chunk "VERS"), so there is no need anymore
2065 to set the game version from the file version (except for old,
2066 pre-2.0 levels, where the game version is still taken from the
2067 file format version used to store the level -- see above). */
2069 /* do some special adjustments to support older level versions */
2070 if (level->file_version == FILE_VERSION_1_0)
2072 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2073 Error(ERR_WARN, "using high speed movement for player");
2075 /* player was faster than monsters in (pre-)1.0 levels */
2076 level->double_speed = TRUE;
2079 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2080 if (level->game_version == VERSION_IDENT(2,0,1,0))
2081 level->em_slippery_gems = TRUE;
2083 if (level->game_version < VERSION_IDENT(2,2,0,0))
2084 level->use_spring_bug = TRUE;
2086 if (level->game_version < VERSION_IDENT(3,0,9,0))
2090 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2092 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
2093 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2094 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
2095 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
2097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2098 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2100 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2102 int element = EL_CUSTOM_START + i;
2103 struct ElementInfo *ei = &element_info[element];
2105 for (j = 0; j < ei->num_change_pages; j++)
2107 struct ElementChangeInfo *change = &ei->change_page[j];
2109 change->trigger_player = CH_PLAYER_ANY;
2110 change->trigger_page = CH_PAGE_ANY;
2118 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2119 leveldir_current->sort_priority, filename);
2123 printf("\n::: Use latest game engine version for this level.\n");
2126 /* For all levels which are forced to use the latest game engine version
2127 (normally all but user contributed, private and undefined levels), set
2128 the game engine version to the actual version; this allows for actual
2129 corrections in the game engine to take effect for existing, converted
2130 levels (from "classic" or other existing games) to make the emulation
2131 of the corresponding game more accurate, while (hopefully) not breaking
2132 existing levels created from other players. */
2135 printf("::: changing engine from %d to %d\n",
2136 level->game_version, GAME_VERSION_ACTUAL);
2139 level->game_version = GAME_VERSION_ACTUAL;
2141 /* Set special EM style gems behaviour: EM style gems slip down from
2142 normal, steel and growing wall. As this is a more fundamental change,
2143 it seems better to set the default behaviour to "off" (as it is more
2144 natural) and make it configurable in the level editor (as a property
2145 of gem style elements). Already existing converted levels (neither
2146 private nor contributed levels) are changed to the new behaviour. */
2148 if (level->file_version < FILE_VERSION_2_0)
2149 level->em_slippery_gems = TRUE;
2153 printf("::: => %d\n", level->game_version);
2157 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2161 /* map custom element change events that have changed in newer versions
2162 (these following values were accidentally changed in version 3.0.1) */
2163 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2165 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2167 int element = EL_CUSTOM_START + i;
2169 /* order of checking and copying events to be mapped is important */
2170 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2172 if (HAS_CHANGE_EVENT(element, j - 2))
2174 SET_CHANGE_EVENT(element, j - 2, FALSE);
2175 SET_CHANGE_EVENT(element, j, TRUE);
2179 /* order of checking and copying events to be mapped is important */
2180 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2182 if (HAS_CHANGE_EVENT(element, j - 1))
2184 SET_CHANGE_EVENT(element, j - 1, FALSE);
2185 SET_CHANGE_EVENT(element, j, TRUE);
2191 /* some custom element change events get mapped since version 3.0.3 */
2192 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2194 int element = EL_CUSTOM_START + i;
2196 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2197 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2199 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2200 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2202 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2206 /* initialize "can_change" field for old levels with only one change page */
2207 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2209 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2211 int element = EL_CUSTOM_START + i;
2213 if (CAN_CHANGE(element))
2214 element_info[element].change->can_change = TRUE;
2218 /* correct custom element values (for old levels without these options) */
2219 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2221 int element = EL_CUSTOM_START + i;
2222 struct ElementInfo *ei = &element_info[element];
2224 if (ei->access_direction == MV_NO_MOVING)
2225 ei->access_direction = MV_ALL_DIRECTIONS;
2227 for (j = 0; j < ei->num_change_pages; j++)
2229 struct ElementChangeInfo *change = &ei->change_page[j];
2231 if (change->trigger_side == CH_SIDE_NONE)
2232 change->trigger_side = CH_SIDE_ANY;
2237 /* set default push delay values (corrected since version 3.0.7-1) */
2238 if (level->game_version < VERSION_IDENT(3,0,7,1))
2240 game.default_push_delay_fixed = 2;
2241 game.default_push_delay_random = 8;
2245 game.default_push_delay_fixed = 8;
2246 game.default_push_delay_random = 8;
2249 /* set uninitialized push delay values of custom elements in older levels */
2250 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2252 int element = EL_CUSTOM_START + i;
2254 if (element_info[element].push_delay_fixed == -1)
2255 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2256 if (element_info[element].push_delay_random == -1)
2257 element_info[element].push_delay_random = game.default_push_delay_random;
2261 /* map elements that have changed in newer versions */
2262 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2263 level->game_version);
2264 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2265 for (x = 0; x < 3; x++)
2266 for (y = 0; y < 3; y++)
2267 level->yamyam_content[i][x][y] =
2268 getMappedElementByVersion(level->yamyam_content[i][x][y],
2269 level->game_version);
2271 /* initialize element properties for level editor etc. */
2272 InitElementPropertiesEngine(level->game_version);
2275 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2279 /* map elements that have changed in newer versions */
2280 for (y = 0; y < level->fieldy; y++)
2282 for (x = 0; x < level->fieldx; x++)
2284 int element = level->field[x][y];
2287 element = getMappedElementByVersion(element, level->game_version);
2289 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2291 /* map game font elements */
2292 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2293 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2294 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2295 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2298 if (level->game_version < VERSION_IDENT(3,0,0,0))
2300 /* map Supaplex gravity tube elements */
2301 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2302 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2303 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2304 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2309 level->field[x][y] = element;
2313 /* copy elements to runtime playfield array */
2314 for (x = 0; x < MAX_LEV_FIELDX; x++)
2315 for (y = 0; y < MAX_LEV_FIELDY; y++)
2316 Feld[x][y] = level->field[x][y];
2318 /* initialize level size variables for faster access */
2319 lev_fieldx = level->fieldx;
2320 lev_fieldy = level->fieldy;
2322 /* determine border element for this level */
2326 void LoadLevelTemplate(int nr)
2329 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2330 char *filename = level_file_info->filename;
2332 LoadLevelFromFileInfo(&level_template, level_file_info);
2334 char *filename = getDefaultLevelFilename(nr);
2336 LoadLevelFromFilename_RND(&level_template, filename);
2339 LoadLevel_InitVersion(&level, filename);
2340 LoadLevel_InitElements(&level, filename);
2342 ActivateLevelTemplate();
2345 void LoadLevel(int nr)
2348 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2349 char *filename = level_file_info->filename;
2351 LoadLevelFromFileInfo(&level, level_file_info);
2353 char *filename = getLevelFilename(nr);
2355 LoadLevelFromFilename_RND(&level, filename);
2358 if (level.use_custom_template)
2359 LoadLevelTemplate(-1);
2362 LoadLevel_InitVersion(&level, filename);
2363 LoadLevel_InitElements(&level, filename);
2364 LoadLevel_InitPlayfield(&level, filename);
2366 LoadLevel_InitLevel(&level, filename);
2370 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2372 putFileVersion(file, level->file_version);
2373 putFileVersion(file, level->game_version);
2376 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2380 putFile8Bit(file, level->fieldx);
2381 putFile8Bit(file, level->fieldy);
2383 putFile16BitBE(file, level->time);
2384 putFile16BitBE(file, level->gems_needed);
2386 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2387 putFile8Bit(file, level->name[i]);
2389 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2390 putFile8Bit(file, level->score[i]);
2392 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2393 for (y = 0; y < 3; y++)
2394 for (x = 0; x < 3; x++)
2395 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2396 level->yamyam_content[i][x][y]));
2397 putFile8Bit(file, level->amoeba_speed);
2398 putFile8Bit(file, level->time_magic_wall);
2399 putFile8Bit(file, level->time_wheel);
2400 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2401 level->amoeba_content));
2402 putFile8Bit(file, (level->double_speed ? 1 : 0));
2403 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2404 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2405 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2407 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2409 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2410 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2412 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2414 putFile16BitBE(file, level->can_move_into_acid_bits);
2416 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
2418 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2421 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2425 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2426 putFile8Bit(file, level->author[i]);
2429 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2433 for (y = 0; y < level->fieldy; y++)
2434 for (x = 0; x < level->fieldx; x++)
2435 if (level->encoding_16bit_field)
2436 putFile16BitBE(file, level->field[x][y]);
2438 putFile8Bit(file, level->field[x][y]);
2442 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2446 putFile8Bit(file, EL_YAMYAM);
2447 putFile8Bit(file, level->num_yamyam_contents);
2448 putFile8Bit(file, 0);
2449 putFile8Bit(file, 0);
2451 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2452 for (y = 0; y < 3; y++)
2453 for (x = 0; x < 3; x++)
2454 if (level->encoding_16bit_field)
2455 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2457 putFile8Bit(file, level->yamyam_content[i][x][y]);
2461 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2464 int num_contents, content_xsize, content_ysize;
2465 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2467 if (element == EL_YAMYAM)
2469 num_contents = level->num_yamyam_contents;
2473 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2474 for (y = 0; y < 3; y++)
2475 for (x = 0; x < 3; x++)
2476 content_array[i][x][y] = level->yamyam_content[i][x][y];
2478 else if (element == EL_BD_AMOEBA)
2484 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2485 for (y = 0; y < 3; y++)
2486 for (x = 0; x < 3; x++)
2487 content_array[i][x][y] = EL_EMPTY;
2488 content_array[0][0][0] = level->amoeba_content;
2492 /* chunk header already written -- write empty chunk data */
2493 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2495 Error(ERR_WARN, "cannot save content for element '%d'", element);
2499 putFile16BitBE(file, element);
2500 putFile8Bit(file, num_contents);
2501 putFile8Bit(file, content_xsize);
2502 putFile8Bit(file, content_ysize);
2504 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2506 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2507 for (y = 0; y < 3; y++)
2508 for (x = 0; x < 3; x++)
2509 putFile16BitBE(file, content_array[i][x][y]);
2512 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2515 int envelope_nr = element - EL_ENVELOPE_1;
2516 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2518 putFile16BitBE(file, element);
2519 putFile16BitBE(file, envelope_len);
2520 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2521 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2523 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2525 for (i = 0; i < envelope_len; i++)
2526 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2530 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2531 int num_changed_custom_elements)
2535 putFile16BitBE(file, num_changed_custom_elements);
2537 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2539 int element = EL_CUSTOM_START + i;
2541 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2543 if (check < num_changed_custom_elements)
2545 putFile16BitBE(file, element);
2546 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2553 if (check != num_changed_custom_elements) /* should not happen */
2554 Error(ERR_WARN, "inconsistent number of custom element properties");
2559 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2560 int num_changed_custom_elements)
2564 putFile16BitBE(file, num_changed_custom_elements);
2566 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2568 int element = EL_CUSTOM_START + i;
2570 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2572 if (check < num_changed_custom_elements)
2574 putFile16BitBE(file, element);
2575 putFile16BitBE(file, element_info[element].change->target_element);
2582 if (check != num_changed_custom_elements) /* should not happen */
2583 Error(ERR_WARN, "inconsistent number of custom target elements");
2588 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2589 int num_changed_custom_elements)
2591 int i, j, x, y, check = 0;
2593 putFile16BitBE(file, num_changed_custom_elements);
2595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2597 int element = EL_CUSTOM_START + i;
2599 if (element_info[element].modified_settings)
2601 if (check < num_changed_custom_elements)
2603 putFile16BitBE(file, element);
2605 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2606 putFile8Bit(file, element_info[element].description[j]);
2608 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2610 /* some free bytes for future properties and padding */
2611 WriteUnusedBytesToFile(file, 7);
2613 putFile8Bit(file, element_info[element].use_gfx_element);
2614 putFile16BitBE(file, element_info[element].gfx_element);
2616 putFile8Bit(file, element_info[element].collect_score);
2617 putFile8Bit(file, element_info[element].collect_count);
2619 putFile16BitBE(file, element_info[element].push_delay_fixed);
2620 putFile16BitBE(file, element_info[element].push_delay_random);
2621 putFile16BitBE(file, element_info[element].move_delay_fixed);
2622 putFile16BitBE(file, element_info[element].move_delay_random);
2624 putFile16BitBE(file, element_info[element].move_pattern);
2625 putFile8Bit(file, element_info[element].move_direction_initial);
2626 putFile8Bit(file, element_info[element].move_stepsize);
2628 for (y = 0; y < 3; y++)
2629 for (x = 0; x < 3; x++)
2630 putFile16BitBE(file, element_info[element].content[x][y]);
2632 putFile32BitBE(file, element_info[element].change->events);
2634 putFile16BitBE(file, element_info[element].change->target_element);
2636 putFile16BitBE(file, element_info[element].change->delay_fixed);
2637 putFile16BitBE(file, element_info[element].change->delay_random);
2638 putFile16BitBE(file, element_info[element].change->delay_frames);
2640 putFile16BitBE(file, element_info[element].change->trigger_element);
2642 putFile8Bit(file, element_info[element].change->explode);
2643 putFile8Bit(file, element_info[element].change->use_content);
2644 putFile8Bit(file, element_info[element].change->only_complete);
2645 putFile8Bit(file, element_info[element].change->use_random_change);
2647 putFile8Bit(file, element_info[element].change->random);
2648 putFile8Bit(file, element_info[element].change->power);
2650 for (y = 0; y < 3; y++)
2651 for (x = 0; x < 3; x++)
2652 putFile16BitBE(file, element_info[element].change->content[x][y]);
2654 putFile8Bit(file, element_info[element].slippery_type);
2656 /* some free bytes for future properties and padding */
2657 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2664 if (check != num_changed_custom_elements) /* should not happen */
2665 Error(ERR_WARN, "inconsistent number of custom element properties");
2669 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2671 struct ElementInfo *ei = &element_info[element];
2674 putFile16BitBE(file, element);
2676 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2677 putFile8Bit(file, ei->description[i]);
2679 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2680 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2682 putFile8Bit(file, ei->num_change_pages);
2684 /* some free bytes for future base property values and padding */
2685 WriteUnusedBytesToFile(file, 5);
2687 /* write custom property values */
2689 putFile8Bit(file, ei->use_gfx_element);
2690 putFile16BitBE(file, ei->gfx_element);
2692 putFile8Bit(file, ei->collect_score);
2693 putFile8Bit(file, ei->collect_count);
2695 putFile16BitBE(file, ei->push_delay_fixed);
2696 putFile16BitBE(file, ei->push_delay_random);
2697 putFile16BitBE(file, ei->move_delay_fixed);
2698 putFile16BitBE(file, ei->move_delay_random);
2700 /* bits 0 - 15 of "move_pattern" ... */
2701 putFile16BitBE(file, ei->move_pattern & 0xffff);
2702 putFile8Bit(file, ei->move_direction_initial);
2703 putFile8Bit(file, ei->move_stepsize);
2705 putFile8Bit(file, ei->slippery_type);
2707 for (y = 0; y < 3; y++)
2708 for (x = 0; x < 3; x++)
2709 putFile16BitBE(file, ei->content[x][y]);
2711 putFile16BitBE(file, ei->move_enter_element);
2712 putFile16BitBE(file, ei->move_leave_element);
2713 putFile8Bit(file, ei->move_leave_type);
2715 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2716 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2718 putFile8Bit(file, ei->access_direction);
2720 putFile8Bit(file, ei->explosion_delay);
2721 putFile8Bit(file, ei->ignition_delay);
2723 /* some free bytes for future custom property values and padding */
2724 WriteUnusedBytesToFile(file, 2);
2726 /* write change property values */
2728 for (i = 0; i < ei->num_change_pages; i++)
2730 struct ElementChangeInfo *change = &ei->change_page[i];
2732 putFile32BitBE(file, change->events);
2734 putFile16BitBE(file, change->target_element);
2736 putFile16BitBE(file, change->delay_fixed);
2737 putFile16BitBE(file, change->delay_random);
2738 putFile16BitBE(file, change->delay_frames);
2740 putFile16BitBE(file, change->trigger_element);
2742 putFile8Bit(file, change->explode);
2743 putFile8Bit(file, change->use_content);
2744 putFile8Bit(file, change->only_complete);
2745 putFile8Bit(file, change->use_random_change);
2747 putFile8Bit(file, change->random);
2748 putFile8Bit(file, change->power);
2750 for (y = 0; y < 3; y++)
2751 for (x = 0; x < 3; x++)
2752 putFile16BitBE(file, change->content[x][y]);
2754 putFile8Bit(file, change->can_change);
2756 putFile8Bit(file, change->trigger_side);
2759 putFile8Bit(file, change->trigger_player);
2760 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
2761 log_2(change->trigger_page)));
2763 /* some free bytes for future change property values and padding */
2764 WriteUnusedBytesToFile(file, 6);
2768 /* some free bytes for future change property values and padding */
2769 WriteUnusedBytesToFile(file, 8);
2774 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2776 struct ElementInfo *ei = &element_info[element];
2777 struct ElementGroupInfo *group = ei->group;
2780 putFile16BitBE(file, element);
2782 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2783 putFile8Bit(file, ei->description[i]);
2785 putFile8Bit(file, group->num_elements);
2787 putFile8Bit(file, ei->use_gfx_element);
2788 putFile16BitBE(file, ei->gfx_element);
2790 putFile8Bit(file, group->choice_mode);
2792 /* some free bytes for future values and padding */
2793 WriteUnusedBytesToFile(file, 3);
2795 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2796 putFile16BitBE(file, group->element[i]);
2799 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2801 int body_chunk_size;
2805 if (!(file = fopen(filename, MODE_WRITE)))
2807 Error(ERR_WARN, "cannot save level file '%s'", filename);
2811 level->file_version = FILE_VERSION_ACTUAL;
2812 level->game_version = GAME_VERSION_ACTUAL;
2814 /* check level field for 16-bit elements */
2815 level->encoding_16bit_field = FALSE;
2816 for (y = 0; y < level->fieldy; y++)
2817 for (x = 0; x < level->fieldx; x++)
2818 if (level->field[x][y] > 255)
2819 level->encoding_16bit_field = TRUE;
2821 /* check yamyam content for 16-bit elements */
2822 level->encoding_16bit_yamyam = FALSE;
2823 for (i = 0; i < level->num_yamyam_contents; i++)
2824 for (y = 0; y < 3; y++)
2825 for (x = 0; x < 3; x++)
2826 if (level->yamyam_content[i][x][y] > 255)
2827 level->encoding_16bit_yamyam = TRUE;
2829 /* check amoeba content for 16-bit elements */
2830 level->encoding_16bit_amoeba = FALSE;
2831 if (level->amoeba_content > 255)
2832 level->encoding_16bit_amoeba = TRUE;
2834 /* calculate size of "BODY" chunk */
2836 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2838 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2839 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2841 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2842 SaveLevel_VERS(file, level);
2844 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2845 SaveLevel_HEAD(file, level);
2847 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2848 SaveLevel_AUTH(file, level);
2850 putFileChunkBE(file, "BODY", body_chunk_size);
2851 SaveLevel_BODY(file, level);
2853 if (level->encoding_16bit_yamyam ||
2854 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2856 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2857 SaveLevel_CNT2(file, level, EL_YAMYAM);
2860 if (level->encoding_16bit_amoeba)
2862 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2863 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2866 /* check for envelope content */
2867 for (i = 0; i < 4; i++)
2869 if (strlen(level->envelope_text[i]) > 0)
2871 int envelope_len = strlen(level->envelope_text[i]) + 1;
2873 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2874 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2878 /* check for non-default custom elements (unless using template level) */
2879 if (!level->use_custom_template)
2881 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2883 int element = EL_CUSTOM_START + i;
2885 if (element_info[element].modified_settings)
2887 int num_change_pages = element_info[element].num_change_pages;
2889 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2890 SaveLevel_CUS4(file, level, element);
2895 /* check for non-default group elements (unless using template level) */
2896 if (!level->use_custom_template)
2898 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2900 int element = EL_GROUP_START + i;
2902 if (element_info[element].modified_settings)
2904 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2905 SaveLevel_GRP1(file, level, element);
2912 SetFilePermissions(filename, PERMS_PRIVATE);
2915 void SaveLevel(int nr)
2917 char *filename = getDefaultLevelFilename(nr);
2919 SaveLevelFromFilename(&level, filename);
2922 void SaveLevelTemplate()
2924 char *filename = getDefaultLevelFilename(-1);
2926 SaveLevelFromFilename(&level, filename);
2929 void DumpLevel(struct LevelInfo *level)
2931 printf_line("-", 79);
2932 printf("Level xxx (file version %08d, game version %08d)\n",
2933 level->file_version, level->game_version);
2934 printf_line("-", 79);
2936 printf("Level author: '%s'\n", level->author);
2937 printf("Level title: '%s'\n", level->name);
2939 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2941 printf("Level time: %d seconds\n", level->time);
2942 printf("Gems needed: %d\n", level->gems_needed);
2944 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2945 printf("Time for wheel: %d seconds\n", level->time_wheel);
2946 printf("Time for light: %d seconds\n", level->time_light);
2947 printf("Time for timegate: %d seconds\n", level->time_timegate);
2949 printf("Amoeba speed: %d\n", level->amoeba_speed);
2951 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2952 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2953 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2954 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2955 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2956 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
2957 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
2959 printf_line("-", 79);
2963 /* ========================================================================= */
2964 /* tape file functions */
2965 /* ========================================================================= */
2967 static void setTapeInfoToDefaults()
2971 /* always start with reliable default values (empty tape) */
2974 /* default values (also for pre-1.2 tapes) with only the first player */
2975 tape.player_participates[0] = TRUE;
2976 for (i = 1; i < MAX_PLAYERS; i++)
2977 tape.player_participates[i] = FALSE;
2979 /* at least one (default: the first) player participates in every tape */
2980 tape.num_participating_players = 1;
2982 tape.level_nr = level_nr;
2984 tape.changed = FALSE;
2986 tape.recording = FALSE;
2987 tape.playing = FALSE;
2988 tape.pausing = FALSE;
2991 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2993 tape->file_version = getFileVersion(file);
2994 tape->game_version = getFileVersion(file);
2999 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3003 tape->random_seed = getFile32BitBE(file);
3004 tape->date = getFile32BitBE(file);
3005 tape->length = getFile32BitBE(file);
3007 /* read header fields that are new since version 1.2 */
3008 if (tape->file_version >= FILE_VERSION_1_2)
3010 byte store_participating_players = getFile8Bit(file);
3013 /* since version 1.2, tapes store which players participate in the tape */
3014 tape->num_participating_players = 0;
3015 for (i = 0; i < MAX_PLAYERS; i++)
3017 tape->player_participates[i] = FALSE;
3019 if (store_participating_players & (1 << i))
3021 tape->player_participates[i] = TRUE;
3022 tape->num_participating_players++;
3026 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3028 engine_version = getFileVersion(file);
3029 if (engine_version > 0)
3030 tape->engine_version = engine_version;
3032 tape->engine_version = tape->game_version;
3038 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3040 int level_identifier_size;
3043 level_identifier_size = getFile16BitBE(file);
3045 tape->level_identifier =
3046 checked_realloc(tape->level_identifier, level_identifier_size);
3048 for (i = 0; i < level_identifier_size; i++)
3049 tape->level_identifier[i] = getFile8Bit(file);
3051 tape->level_nr = getFile16BitBE(file);
3053 chunk_size = 2 + level_identifier_size + 2;
3058 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3061 int chunk_size_expected =
3062 (tape->num_participating_players + 1) * tape->length;
3064 if (chunk_size_expected != chunk_size)
3066 ReadUnusedBytesFromFile(file, chunk_size);
3067 return chunk_size_expected;
3070 for (i = 0; i < tape->length; i++)
3072 if (i >= MAX_TAPELEN)
3075 for (j = 0; j < MAX_PLAYERS; j++)
3077 tape->pos[i].action[j] = MV_NO_MOVING;
3079 if (tape->player_participates[j])
3080 tape->pos[i].action[j] = getFile8Bit(file);
3083 tape->pos[i].delay = getFile8Bit(file);
3085 if (tape->file_version == FILE_VERSION_1_0)
3087 /* eliminate possible diagonal moves in old tapes */
3088 /* this is only for backward compatibility */
3090 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3091 byte action = tape->pos[i].action[0];
3092 int k, num_moves = 0;
3094 for (k = 0; k<4; k++)
3096 if (action & joy_dir[k])
3098 tape->pos[i + num_moves].action[0] = joy_dir[k];
3100 tape->pos[i + num_moves].delay = 0;
3109 tape->length += num_moves;
3112 else if (tape->file_version < FILE_VERSION_2_0)
3114 /* convert pre-2.0 tapes to new tape format */
3116 if (tape->pos[i].delay > 1)
3119 tape->pos[i + 1] = tape->pos[i];
3120 tape->pos[i + 1].delay = 1;
3123 for (j = 0; j < MAX_PLAYERS; j++)
3124 tape->pos[i].action[j] = MV_NO_MOVING;
3125 tape->pos[i].delay--;
3136 if (i != tape->length)
3137 chunk_size = (tape->num_participating_players + 1) * i;
3142 void LoadTapeFromFilename(char *filename)
3144 char cookie[MAX_LINE_LEN];
3145 char chunk_name[CHUNK_ID_LEN + 1];
3149 /* always start with reliable default values */
3150 setTapeInfoToDefaults();
3152 if (!(file = fopen(filename, MODE_READ)))
3155 getFileChunkBE(file, chunk_name, NULL);
3156 if (strcmp(chunk_name, "RND1") == 0)
3158 getFile32BitBE(file); /* not used */
3160 getFileChunkBE(file, chunk_name, NULL);
3161 if (strcmp(chunk_name, "TAPE") != 0)
3163 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3168 else /* check for pre-2.0 file format with cookie string */
3170 strcpy(cookie, chunk_name);
3171 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3172 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3173 cookie[strlen(cookie) - 1] = '\0';
3175 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3177 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3182 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3184 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3189 /* pre-2.0 tape files have no game version, so use file version here */
3190 tape.game_version = tape.file_version;
3193 if (tape.file_version < FILE_VERSION_1_2)
3195 /* tape files from versions before 1.2.0 without chunk structure */
3196 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3197 LoadTape_BODY(file, 2 * tape.length, &tape);
3205 int (*loader)(FILE *, int, struct TapeInfo *);
3209 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3210 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3211 { "INFO", -1, LoadTape_INFO },
3212 { "BODY", -1, LoadTape_BODY },
3216 while (getFileChunkBE(file, chunk_name, &chunk_size))
3220 while (chunk_info[i].name != NULL &&
3221 strcmp(chunk_name, chunk_info[i].name) != 0)
3224 if (chunk_info[i].name == NULL)
3226 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3227 chunk_name, filename);
3228 ReadUnusedBytesFromFile(file, chunk_size);
3230 else if (chunk_info[i].size != -1 &&
3231 chunk_info[i].size != chunk_size)
3233 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3234 chunk_size, chunk_name, filename);
3235 ReadUnusedBytesFromFile(file, chunk_size);
3239 /* call function to load this tape chunk */
3240 int chunk_size_expected =
3241 (chunk_info[i].loader)(file, chunk_size, &tape);
3243 /* the size of some chunks cannot be checked before reading other
3244 chunks first (like "HEAD" and "BODY") that contain some header
3245 information, so check them here */
3246 if (chunk_size_expected != chunk_size)
3248 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3249 chunk_size, chunk_name, filename);
3257 tape.length_seconds = GetTapeLength();
3260 printf("::: tape game version: %d\n", tape.game_version);
3261 printf("::: tape engine version: %d\n", tape.engine_version);
3265 void LoadTape(int nr)
3267 char *filename = getTapeFilename(nr);
3269 LoadTapeFromFilename(filename);
3272 void LoadSolutionTape(int nr)
3274 char *filename = getSolutionTapeFilename(nr);
3276 LoadTapeFromFilename(filename);
3279 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3281 putFileVersion(file, tape->file_version);
3282 putFileVersion(file, tape->game_version);
3285 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3288 byte store_participating_players = 0;
3290 /* set bits for participating players for compact storage */
3291 for (i = 0; i < MAX_PLAYERS; i++)
3292 if (tape->player_participates[i])
3293 store_participating_players |= (1 << i);
3295 putFile32BitBE(file, tape->random_seed);
3296 putFile32BitBE(file, tape->date);
3297 putFile32BitBE(file, tape->length);
3299 putFile8Bit(file, store_participating_players);
3301 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3302 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3304 putFileVersion(file, tape->engine_version);
3307 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3309 int level_identifier_size = strlen(tape->level_identifier) + 1;
3312 putFile16BitBE(file, level_identifier_size);
3314 for (i = 0; i < level_identifier_size; i++)
3315 putFile8Bit(file, tape->level_identifier[i]);
3317 putFile16BitBE(file, tape->level_nr);
3320 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3324 for (i = 0; i < tape->length; i++)
3326 for (j = 0; j < MAX_PLAYERS; j++)
3327 if (tape->player_participates[j])
3328 putFile8Bit(file, tape->pos[i].action[j]);
3330 putFile8Bit(file, tape->pos[i].delay);
3334 void SaveTape(int nr)
3336 char *filename = getTapeFilename(nr);
3338 boolean new_tape = TRUE;
3339 int num_participating_players = 0;
3340 int info_chunk_size;
3341 int body_chunk_size;
3344 InitTapeDirectory(leveldir_current->subdir);
3346 /* if a tape still exists, ask to overwrite it */
3347 if (access(filename, F_OK) == 0)
3350 if (!Request("Replace old tape ?", REQ_ASK))
3354 if (!(file = fopen(filename, MODE_WRITE)))
3356 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3360 tape.file_version = FILE_VERSION_ACTUAL;
3361 tape.game_version = GAME_VERSION_ACTUAL;
3363 /* count number of participating players */
3364 for (i = 0; i < MAX_PLAYERS; i++)
3365 if (tape.player_participates[i])
3366 num_participating_players++;
3368 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3369 body_chunk_size = (num_participating_players + 1) * tape.length;
3371 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3372 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3374 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3375 SaveTape_VERS(file, &tape);
3377 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3378 SaveTape_HEAD(file, &tape);
3380 putFileChunkBE(file, "INFO", info_chunk_size);
3381 SaveTape_INFO(file, &tape);
3383 putFileChunkBE(file, "BODY", body_chunk_size);
3384 SaveTape_BODY(file, &tape);
3388 SetFilePermissions(filename, PERMS_PRIVATE);
3390 tape.changed = FALSE;
3393 Request("tape saved !", REQ_CONFIRM);
3396 void DumpTape(struct TapeInfo *tape)
3400 if (TAPE_IS_EMPTY(*tape))
3402 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3406 printf_line("-", 79);
3407 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3408 tape->level_nr, tape->file_version, tape->game_version);
3409 printf("Level series identifier: '%s'\n", tape->level_identifier);
3410 printf_line("-", 79);
3412 for (i = 0; i < tape->length; i++)
3414 if (i >= MAX_TAPELEN)
3417 printf("%03d: ", i);
3419 for (j = 0; j < MAX_PLAYERS; j++)
3421 if (tape->player_participates[j])
3423 int action = tape->pos[i].action[j];
3425 printf("%d:%02x ", j, action);
3426 printf("[%c%c%c%c|%c%c] - ",
3427 (action & JOY_LEFT ? '<' : ' '),
3428 (action & JOY_RIGHT ? '>' : ' '),
3429 (action & JOY_UP ? '^' : ' '),
3430 (action & JOY_DOWN ? 'v' : ' '),
3431 (action & JOY_BUTTON_1 ? '1' : ' '),
3432 (action & JOY_BUTTON_2 ? '2' : ' '));
3436 printf("(%03d)\n", tape->pos[i].delay);
3439 printf_line("-", 79);
3443 /* ========================================================================= */
3444 /* score file functions */
3445 /* ========================================================================= */
3447 void LoadScore(int nr)
3450 char *filename = getScoreFilename(nr);
3451 char cookie[MAX_LINE_LEN];
3452 char line[MAX_LINE_LEN];
3456 /* always start with reliable default values */
3457 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3459 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3460 highscore[i].Score = 0;
3463 if (!(file = fopen(filename, MODE_READ)))
3466 /* check file identifier */
3467 fgets(cookie, MAX_LINE_LEN, file);
3468 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3469 cookie[strlen(cookie) - 1] = '\0';
3471 if (!checkCookieString(cookie, SCORE_COOKIE))
3473 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3478 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3480 fscanf(file, "%d", &highscore[i].Score);
3481 fgets(line, MAX_LINE_LEN, file);
3483 if (line[strlen(line) - 1] == '\n')
3484 line[strlen(line) - 1] = '\0';
3486 for (line_ptr = line; *line_ptr; line_ptr++)
3488 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3490 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3491 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3500 void SaveScore(int nr)
3503 char *filename = getScoreFilename(nr);
3506 InitScoreDirectory(leveldir_current->subdir);
3508 if (!(file = fopen(filename, MODE_WRITE)))
3510 Error(ERR_WARN, "cannot save score for level %d", nr);
3514 fprintf(file, "%s\n\n", SCORE_COOKIE);
3516 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3517 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3521 SetFilePermissions(filename, PERMS_PUBLIC);
3525 /* ========================================================================= */
3526 /* setup file functions */
3527 /* ========================================================================= */
3529 #define TOKEN_STR_PLAYER_PREFIX "player_"
3532 #define SETUP_TOKEN_PLAYER_NAME 0
3533 #define SETUP_TOKEN_SOUND 1
3534 #define SETUP_TOKEN_SOUND_LOOPS 2
3535 #define SETUP_TOKEN_SOUND_MUSIC 3
3536 #define SETUP_TOKEN_SOUND_SIMPLE 4
3537 #define SETUP_TOKEN_TOONS 5
3538 #define SETUP_TOKEN_SCROLL_DELAY 6
3539 #define SETUP_TOKEN_SOFT_SCROLLING 7
3540 #define SETUP_TOKEN_FADING 8
3541 #define SETUP_TOKEN_AUTORECORD 9
3542 #define SETUP_TOKEN_QUICK_DOORS 10
3543 #define SETUP_TOKEN_TEAM_MODE 11
3544 #define SETUP_TOKEN_HANDICAP 12
3545 #define SETUP_TOKEN_TIME_LIMIT 13
3546 #define SETUP_TOKEN_FULLSCREEN 14
3547 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3548 #define SETUP_TOKEN_GRAPHICS_SET 16
3549 #define SETUP_TOKEN_SOUNDS_SET 17
3550 #define SETUP_TOKEN_MUSIC_SET 18
3551 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3552 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3553 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3555 #define NUM_GLOBAL_SETUP_TOKENS 22
3558 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3559 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3560 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3561 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3562 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3563 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3564 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3565 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3566 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3567 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3568 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3569 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3571 #define NUM_EDITOR_SETUP_TOKENS 12
3573 /* shortcut setup */
3574 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3575 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3576 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3578 #define NUM_SHORTCUT_SETUP_TOKENS 3
3581 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3582 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3583 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3584 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3585 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3586 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3587 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3588 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3589 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3590 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3591 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3592 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3593 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3594 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3595 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3596 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3598 #define NUM_PLAYER_SETUP_TOKENS 16
3601 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3602 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3604 #define NUM_SYSTEM_SETUP_TOKENS 2
3607 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3609 #define NUM_OPTIONS_SETUP_TOKENS 1
3612 static struct SetupInfo si;
3613 static struct SetupEditorInfo sei;
3614 static struct SetupShortcutInfo ssi;
3615 static struct SetupInputInfo sii;
3616 static struct SetupSystemInfo syi;
3617 static struct OptionInfo soi;
3619 static struct TokenInfo global_setup_tokens[] =
3621 { TYPE_STRING, &si.player_name, "player_name" },
3622 { TYPE_SWITCH, &si.sound, "sound" },
3623 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3624 { TYPE_SWITCH, &si.sound_music, "background_music" },
3625 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3626 { TYPE_SWITCH, &si.toons, "toons" },
3627 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3628 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3629 { TYPE_SWITCH, &si.fading, "screen_fading" },
3630 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3631 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3632 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3633 { TYPE_SWITCH, &si.handicap, "handicap" },
3634 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3635 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3636 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3637 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3638 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3639 { TYPE_STRING, &si.music_set, "music_set" },
3640 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3641 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3642 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3645 static struct TokenInfo editor_setup_tokens[] =
3647 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3648 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3649 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3650 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3651 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3652 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3653 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3654 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3655 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3656 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3657 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3658 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3661 static struct TokenInfo shortcut_setup_tokens[] =
3663 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3664 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3665 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3668 static struct TokenInfo player_setup_tokens[] =
3670 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3671 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3672 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3673 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3674 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3675 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3676 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3677 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3678 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3679 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3680 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3681 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3682 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3683 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3684 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3685 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3688 static struct TokenInfo system_setup_tokens[] =
3690 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3691 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3694 static struct TokenInfo options_setup_tokens[] =
3696 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3699 static char *get_corrected_login_name(char *login_name)
3701 /* needed because player name must be a fixed length string */
3702 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3704 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3705 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3707 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3708 if (strchr(login_name_new, ' '))
3709 *strchr(login_name_new, ' ') = '\0';
3711 return login_name_new;
3714 static void setSetupInfoToDefaults(struct SetupInfo *si)
3718 si->player_name = get_corrected_login_name(getLoginName());
3721 si->sound_loops = TRUE;
3722 si->sound_music = TRUE;
3723 si->sound_simple = TRUE;
3725 si->double_buffering = TRUE;
3726 si->direct_draw = !si->double_buffering;
3727 si->scroll_delay = TRUE;
3728 si->soft_scrolling = TRUE;
3730 si->autorecord = TRUE;
3731 si->quick_doors = FALSE;
3732 si->team_mode = FALSE;
3733 si->handicap = TRUE;
3734 si->time_limit = TRUE;
3735 si->fullscreen = FALSE;
3736 si->ask_on_escape = TRUE;
3738 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3739 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3740 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3741 si->override_level_graphics = FALSE;
3742 si->override_level_sounds = FALSE;
3743 si->override_level_music = FALSE;
3745 si->editor.el_boulderdash = TRUE;
3746 si->editor.el_emerald_mine = TRUE;
3747 si->editor.el_more = TRUE;
3748 si->editor.el_sokoban = TRUE;
3749 si->editor.el_supaplex = TRUE;
3750 si->editor.el_diamond_caves = TRUE;
3751 si->editor.el_dx_boulderdash = TRUE;
3752 si->editor.el_chars = TRUE;
3753 si->editor.el_custom = TRUE;
3754 si->editor.el_custom_more = FALSE;
3756 si->editor.el_headlines = TRUE;
3757 si->editor.el_user_defined = FALSE;
3759 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3760 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3761 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3763 for (i = 0; i < MAX_PLAYERS; i++)
3765 si->input[i].use_joystick = FALSE;
3766 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3767 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3768 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3769 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3770 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3771 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3772 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3773 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3774 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3775 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3776 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3777 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3778 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3779 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3780 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3783 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3784 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3786 si->options.verbose = FALSE;
3789 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3793 if (!setup_file_hash)
3798 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3799 setSetupInfo(global_setup_tokens, i,
3800 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3805 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3806 setSetupInfo(editor_setup_tokens, i,
3807 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3810 /* shortcut setup */
3811 ssi = setup.shortcut;
3812 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3813 setSetupInfo(shortcut_setup_tokens, i,
3814 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3815 setup.shortcut = ssi;
3818 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3822 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3824 sii = setup.input[pnr];
3825 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3827 char full_token[100];
3829 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3830 setSetupInfo(player_setup_tokens, i,
3831 getHashEntry(setup_file_hash, full_token));
3833 setup.input[pnr] = sii;
3838 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3839 setSetupInfo(system_setup_tokens, i,
3840 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3844 soi = setup.options;
3845 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3846 setSetupInfo(options_setup_tokens, i,
3847 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3848 setup.options = soi;
3853 char *filename = getSetupFilename();
3854 SetupFileHash *setup_file_hash = NULL;
3856 /* always start with reliable default values */
3857 setSetupInfoToDefaults(&setup);
3859 setup_file_hash = loadSetupFileHash(filename);
3861 if (setup_file_hash)
3863 char *player_name_new;
3865 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3866 decodeSetupFileHash(setup_file_hash);
3868 setup.direct_draw = !setup.double_buffering;
3870 freeSetupFileHash(setup_file_hash);
3872 /* needed to work around problems with fixed length strings */
3873 player_name_new = get_corrected_login_name(setup.player_name);
3874 free(setup.player_name);
3875 setup.player_name = player_name_new;
3878 Error(ERR_WARN, "using default setup values");
3883 char *filename = getSetupFilename();
3887 InitUserDataDirectory();
3889 if (!(file = fopen(filename, MODE_WRITE)))
3891 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3895 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3896 getCookie("SETUP")));
3897 fprintf(file, "\n");
3901 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3903 /* just to make things nicer :) */
3904 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3905 i == SETUP_TOKEN_GRAPHICS_SET)
3906 fprintf(file, "\n");
3908 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3913 fprintf(file, "\n");
3914 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3915 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3917 /* shortcut setup */
3918 ssi = setup.shortcut;
3919 fprintf(file, "\n");
3920 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3921 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3924 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3928 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3929 fprintf(file, "\n");
3931 sii = setup.input[pnr];
3932 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3933 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3938 fprintf(file, "\n");
3939 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3940 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3943 soi = setup.options;
3944 fprintf(file, "\n");
3945 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3946 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3950 SetFilePermissions(filename, PERMS_PRIVATE);
3953 void LoadCustomElementDescriptions()
3955 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3956 SetupFileHash *setup_file_hash;
3959 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3961 if (element_info[i].custom_description != NULL)
3963 free(element_info[i].custom_description);
3964 element_info[i].custom_description = NULL;
3968 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3971 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3973 char *token = getStringCat2(element_info[i].token_name, ".name");
3974 char *value = getHashEntry(setup_file_hash, token);
3977 element_info[i].custom_description = getStringCopy(value);
3982 freeSetupFileHash(setup_file_hash);
3985 void LoadSpecialMenuDesignSettings()
3987 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3988 SetupFileHash *setup_file_hash;
3991 /* always start with reliable default values from default config */
3992 for (i = 0; image_config_vars[i].token != NULL; i++)
3993 for (j = 0; image_config[j].token != NULL; j++)
3994 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3995 *image_config_vars[i].value =
3996 get_auto_parameter_value(image_config_vars[i].token,
3997 image_config[j].value);
3999 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4002 /* special case: initialize with default values that may be overwritten */
4003 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4005 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4006 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4007 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4009 if (value_x != NULL)
4010 menu.draw_xoffset[i] = get_integer_from_string(value_x);
4011 if (value_y != NULL)
4012 menu.draw_yoffset[i] = get_integer_from_string(value_y);
4013 if (list_size != NULL)
4014 menu.list_size[i] = get_integer_from_string(list_size);
4017 /* read (and overwrite with) values that may be specified in config file */
4018 for (i = 0; image_config_vars[i].token != NULL; i++)
4020 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4023 *image_config_vars[i].value =
4024 get_auto_parameter_value(image_config_vars[i].token, value);
4027 freeSetupFileHash(setup_file_hash);
4030 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4032 char *filename = getEditorSetupFilename();
4033 SetupFileList *setup_file_list, *list;
4034 SetupFileHash *element_hash;
4035 int num_unknown_tokens = 0;
4038 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4041 element_hash = newSetupFileHash();
4043 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4044 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4046 /* determined size may be larger than needed (due to unknown elements) */
4048 for (list = setup_file_list; list != NULL; list = list->next)
4051 /* add space for up to 3 more elements for padding that may be needed */
4054 *elements = checked_malloc(*num_elements * sizeof(int));
4057 for (list = setup_file_list; list != NULL; list = list->next)
4059 char *value = getHashEntry(element_hash, list->token);
4063 (*elements)[(*num_elements)++] = atoi(value);
4067 if (num_unknown_tokens == 0)
4069 Error(ERR_RETURN_LINE, "-");
4070 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4071 Error(ERR_RETURN, "- config file: '%s'", filename);
4073 num_unknown_tokens++;
4076 Error(ERR_RETURN, "- token: '%s'", list->token);
4080 if (num_unknown_tokens > 0)
4081 Error(ERR_RETURN_LINE, "-");
4083 while (*num_elements % 4) /* pad with empty elements, if needed */
4084 (*elements)[(*num_elements)++] = EL_EMPTY;
4086 freeSetupFileList(setup_file_list);
4087 freeSetupFileHash(element_hash);
4091 for (i = 0; i < *num_elements; i++)
4092 printf("editor: element '%s' [%d]\n",
4093 element_info[(*elements)[i]].token_name, (*elements)[i]);
4097 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4100 SetupFileHash *setup_file_hash = NULL;
4101 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4102 char *filename_music, *filename_prefix, *filename_info;
4108 token_to_value_ptr[] =
4110 { "title_header", &tmp_music_file_info.title_header },
4111 { "artist_header", &tmp_music_file_info.artist_header },
4112 { "album_header", &tmp_music_file_info.album_header },
4113 { "year_header", &tmp_music_file_info.year_header },
4115 { "title", &tmp_music_file_info.title },
4116 { "artist", &tmp_music_file_info.artist },
4117 { "album", &tmp_music_file_info.album },
4118 { "year", &tmp_music_file_info.year },
4124 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4125 getCustomMusicFilename(basename));
4127 if (filename_music == NULL)
4130 /* ---------- try to replace file extension ---------- */
4132 filename_prefix = getStringCopy(filename_music);
4133 if (strrchr(filename_prefix, '.') != NULL)
4134 *strrchr(filename_prefix, '.') = '\0';
4135 filename_info = getStringCat2(filename_prefix, ".txt");
4138 printf("trying to load file '%s'...\n", filename_info);
4141 if (fileExists(filename_info))
4142 setup_file_hash = loadSetupFileHash(filename_info);
4144 free(filename_prefix);
4145 free(filename_info);
4147 if (setup_file_hash == NULL)
4149 /* ---------- try to add file extension ---------- */
4151 filename_prefix = getStringCopy(filename_music);
4152 filename_info = getStringCat2(filename_prefix, ".txt");
4155 printf("trying to load file '%s'...\n", filename_info);
4158 if (fileExists(filename_info))
4159 setup_file_hash = loadSetupFileHash(filename_info);
4161 free(filename_prefix);
4162 free(filename_info);
4165 if (setup_file_hash == NULL)
4168 /* ---------- music file info found ---------- */
4170 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4172 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4174 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4176 *token_to_value_ptr[i].value_ptr =
4177 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4180 tmp_music_file_info.basename = getStringCopy(basename);
4181 tmp_music_file_info.music = music;
4182 tmp_music_file_info.is_sound = is_sound;
4184 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4185 *new_music_file_info = tmp_music_file_info;
4187 return new_music_file_info;
4190 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4192 return get_music_file_info_ext(basename, music, FALSE);
4195 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4197 return get_music_file_info_ext(basename, sound, TRUE);
4200 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4201 char *basename, boolean is_sound)
4203 for (; list != NULL; list = list->next)
4204 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4210 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4212 return music_info_listed_ext(list, basename, FALSE);
4215 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4217 return music_info_listed_ext(list, basename, TRUE);
4220 void LoadMusicInfo()
4222 char *music_directory = getCustomMusicDirectory();
4223 int num_music = getMusicListSize();
4224 int num_music_noconf = 0;
4225 int num_sounds = getSoundListSize();
4227 struct dirent *dir_entry;
4228 struct FileInfo *music, *sound;
4229 struct MusicFileInfo *next, **new;
4232 while (music_file_info != NULL)
4234 next = music_file_info->next;
4236 checked_free(music_file_info->basename);
4238 checked_free(music_file_info->title_header);
4239 checked_free(music_file_info->artist_header);
4240 checked_free(music_file_info->album_header);
4241 checked_free(music_file_info->year_header);
4243 checked_free(music_file_info->title);
4244 checked_free(music_file_info->artist);
4245 checked_free(music_file_info->album);
4246 checked_free(music_file_info->year);
4248 free(music_file_info);
4250 music_file_info = next;
4253 new = &music_file_info;
4256 printf("::: num_music == %d\n", num_music);
4259 for (i = 0; i < num_music; i++)
4261 music = getMusicListEntry(i);
4264 printf("::: %d [%08x]\n", i, music->filename);
4267 if (music->filename == NULL)
4270 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4273 /* a configured file may be not recognized as music */
4274 if (!FileIsMusic(music->filename))
4278 printf("::: -> '%s' (configured)\n", music->filename);
4281 if (!music_info_listed(music_file_info, music->filename))
4283 *new = get_music_file_info(music->filename, i);
4285 new = &(*new)->next;
4289 if ((dir = opendir(music_directory)) == NULL)
4291 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4295 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4297 char *basename = dir_entry->d_name;
4298 boolean music_already_used = FALSE;
4301 /* skip all music files that are configured in music config file */
4302 for (i = 0; i < num_music; i++)
4304 music = getMusicListEntry(i);
4306 if (music->filename == NULL)
4309 if (strcmp(basename, music->filename) == 0)
4311 music_already_used = TRUE;
4316 if (music_already_used)
4319 if (!FileIsMusic(basename))
4323 printf("::: -> '%s' (found in directory)\n", basename);
4326 if (!music_info_listed(music_file_info, basename))
4328 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4330 new = &(*new)->next;
4338 for (i = 0; i < num_sounds; i++)
4340 sound = getSoundListEntry(i);
4342 if (sound->filename == NULL)
4345 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4348 /* a configured file may be not recognized as sound */
4349 if (!FileIsSound(sound->filename))
4353 printf("::: -> '%s' (configured)\n", sound->filename);
4356 if (!sound_info_listed(music_file_info, sound->filename))
4358 *new = get_sound_file_info(sound->filename, i);
4360 new = &(*new)->next;
4366 for (next = music_file_info; next != NULL; next = next->next)
4367 printf("::: title == '%s'\n", next->title);
4371 void add_helpanim_entry(int element, int action, int direction, int delay,
4372 int *num_list_entries)
4374 struct HelpAnimInfo *new_list_entry;
4375 (*num_list_entries)++;
4378 checked_realloc(helpanim_info,
4379 *num_list_entries * sizeof(struct HelpAnimInfo));
4380 new_list_entry = &helpanim_info[*num_list_entries - 1];
4382 new_list_entry->element = element;
4383 new_list_entry->action = action;
4384 new_list_entry->direction = direction;
4385 new_list_entry->delay = delay;
4388 void print_unknown_token(char *filename, char *token, int token_nr)
4392 Error(ERR_RETURN_LINE, "-");
4393 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4394 Error(ERR_RETURN, "- config file: '%s'", filename);
4397 Error(ERR_RETURN, "- token: '%s'", token);
4400 void print_unknown_token_end(int token_nr)
4403 Error(ERR_RETURN_LINE, "-");
4406 void LoadHelpAnimInfo()
4408 char *filename = getHelpAnimFilename();
4409 SetupFileList *setup_file_list = NULL, *list;
4410 SetupFileHash *element_hash, *action_hash, *direction_hash;
4411 int num_list_entries = 0;
4412 int num_unknown_tokens = 0;
4415 if (fileExists(filename))
4416 setup_file_list = loadSetupFileList(filename);
4418 if (setup_file_list == NULL)
4420 /* use reliable default values from static configuration */
4421 SetupFileList *insert_ptr;
4423 insert_ptr = setup_file_list =
4424 newSetupFileList(helpanim_config[0].token,
4425 helpanim_config[0].value);
4427 for (i = 1; helpanim_config[i].token; i++)
4428 insert_ptr = addListEntry(insert_ptr,
4429 helpanim_config[i].token,
4430 helpanim_config[i].value);
4433 element_hash = newSetupFileHash();
4434 action_hash = newSetupFileHash();
4435 direction_hash = newSetupFileHash();
4437 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4438 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4440 for (i = 0; i < NUM_ACTIONS; i++)
4441 setHashEntry(action_hash, element_action_info[i].suffix,
4442 i_to_a(element_action_info[i].value));
4444 /* do not store direction index (bit) here, but direction value! */
4445 for (i = 0; i < NUM_DIRECTIONS; i++)
4446 setHashEntry(direction_hash, element_direction_info[i].suffix,
4447 i_to_a(1 << element_direction_info[i].value));
4449 for (list = setup_file_list; list != NULL; list = list->next)
4451 char *element_token, *action_token, *direction_token;
4452 char *element_value, *action_value, *direction_value;
4453 int delay = atoi(list->value);
4455 if (strcmp(list->token, "end") == 0)
4457 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4462 /* first try to break element into element/action/direction parts;
4463 if this does not work, also accept combined "element[.act][.dir]"
4464 elements (like "dynamite.active"), which are unique elements */
4466 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4468 element_value = getHashEntry(element_hash, list->token);
4469 if (element_value != NULL) /* element found */
4470 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4474 /* no further suffixes found -- this is not an element */
4475 print_unknown_token(filename, list->token, num_unknown_tokens++);
4481 /* token has format "<prefix>.<something>" */
4483 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4484 direction_token = action_token; /* ... or direction */
4486 element_token = getStringCopy(list->token);
4487 *strchr(element_token, '.') = '\0';
4489 element_value = getHashEntry(element_hash, element_token);
4491 if (element_value == NULL) /* this is no element */
4493 element_value = getHashEntry(element_hash, list->token);
4494 if (element_value != NULL) /* combined element found */
4495 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4498 print_unknown_token(filename, list->token, num_unknown_tokens++);
4500 free(element_token);
4505 action_value = getHashEntry(action_hash, action_token);
4507 if (action_value != NULL) /* action found */
4509 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4512 free(element_token);
4517 direction_value = getHashEntry(direction_hash, direction_token);
4519 if (direction_value != NULL) /* direction found */
4521 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4524 free(element_token);
4529 if (strchr(action_token + 1, '.') == NULL)
4531 /* no further suffixes found -- this is not an action nor direction */
4533 element_value = getHashEntry(element_hash, list->token);
4534 if (element_value != NULL) /* combined element found */
4535 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4538 print_unknown_token(filename, list->token, num_unknown_tokens++);
4540 free(element_token);
4545 /* token has format "<prefix>.<suffix>.<something>" */
4547 direction_token = strchr(action_token + 1, '.');
4549 action_token = getStringCopy(action_token);
4550 *strchr(action_token + 1, '.') = '\0';
4552 action_value = getHashEntry(action_hash, action_token);
4554 if (action_value == NULL) /* this is no action */
4556 element_value = getHashEntry(element_hash, list->token);
4557 if (element_value != NULL) /* combined element found */
4558 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4561 print_unknown_token(filename, list->token, num_unknown_tokens++);
4563 free(element_token);
4569 direction_value = getHashEntry(direction_hash, direction_token);
4571 if (direction_value != NULL) /* direction found */
4573 add_helpanim_entry(atoi(element_value), atoi(action_value),
4574 atoi(direction_value), delay, &num_list_entries);
4576 free(element_token);
4582 /* this is no direction */
4584 element_value = getHashEntry(element_hash, list->token);
4585 if (element_value != NULL) /* combined element found */
4586 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4589 print_unknown_token(filename, list->token, num_unknown_tokens++);
4591 free(element_token);
4595 print_unknown_token_end(num_unknown_tokens);
4597 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4598 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4600 freeSetupFileList(setup_file_list);
4601 freeSetupFileHash(element_hash);
4602 freeSetupFileHash(action_hash);
4603 freeSetupFileHash(direction_hash);
4607 for (i = 0; i < num_list_entries; i++)
4608 printf("::: %d, %d, %d => %d\n",
4609 helpanim_info[i].element,
4610 helpanim_info[i].action,
4611 helpanim_info[i].direction,
4612 helpanim_info[i].delay);
4616 void LoadHelpTextInfo()
4618 char *filename = getHelpTextFilename();
4621 if (helptext_info != NULL)
4623 freeSetupFileHash(helptext_info);
4624 helptext_info = NULL;
4627 if (fileExists(filename))
4628 helptext_info = loadSetupFileHash(filename);
4630 if (helptext_info == NULL)
4632 /* use reliable default values from static configuration */
4633 helptext_info = newSetupFileHash();
4635 for (i = 0; helptext_config[i].token; i++)
4636 setHashEntry(helptext_info,
4637 helptext_config[i].token,
4638 helptext_config[i].value);
4643 BEGIN_HASH_ITERATION(helptext_info, itr)
4645 printf("::: '%s' => '%s'\n",
4646 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4648 END_HASH_ITERATION(hash, itr)