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 4 /* 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->can_move_into_acid_bits = ~0; /* everything can move into acid */
164 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
166 level->use_spring_bug = FALSE;
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_valid_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);
683 level->can_move_into_acid_bits = getFile32BitBE(file);
684 level->dont_collide_with_bits = getFile8Bit(file);
686 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
687 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
689 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
694 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
698 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
699 level->author[i] = getFile8Bit(file);
700 level->author[MAX_LEVEL_NAME_LEN] = 0;
705 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
708 int chunk_size_expected = level->fieldx * level->fieldy;
710 /* Note: "chunk_size" was wrong before version 2.0 when elements are
711 stored with 16-bit encoding (and should be twice as big then).
712 Even worse, playfield data was stored 16-bit when only yamyam content
713 contained 16-bit elements and vice versa. */
715 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
716 chunk_size_expected *= 2;
718 if (chunk_size_expected != chunk_size)
720 ReadUnusedBytesFromFile(file, chunk_size);
721 return chunk_size_expected;
724 for (y = 0; y < level->fieldy; y++)
725 for (x = 0; x < level->fieldx; x++)
727 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
732 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
736 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
737 int chunk_size_expected = header_size + content_size;
739 /* Note: "chunk_size" was wrong before version 2.0 when elements are
740 stored with 16-bit encoding (and should be twice as big then).
741 Even worse, playfield data was stored 16-bit when only yamyam content
742 contained 16-bit elements and vice versa. */
744 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
745 chunk_size_expected += content_size;
747 if (chunk_size_expected != chunk_size)
749 ReadUnusedBytesFromFile(file, chunk_size);
750 return chunk_size_expected;
754 level->num_yamyam_contents = getFile8Bit(file);
758 /* correct invalid number of content fields -- should never happen */
759 if (level->num_yamyam_contents < 1 ||
760 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
761 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
763 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
764 for (y = 0; y < 3; y++)
765 for (x = 0; x < 3; x++)
766 level->yamyam_content[i][x][y] =
767 getMappedElement(level->encoding_16bit_field ?
768 getFile16BitBE(file) : getFile8Bit(file));
772 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
776 int num_contents, content_xsize, content_ysize;
777 int content_array[MAX_ELEMENT_CONTENTS][3][3];
779 element = getMappedElement(getFile16BitBE(file));
780 num_contents = getFile8Bit(file);
781 content_xsize = getFile8Bit(file);
782 content_ysize = getFile8Bit(file);
784 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
786 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
787 for (y = 0; y < 3; y++)
788 for (x = 0; x < 3; x++)
789 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
791 /* correct invalid number of content fields -- should never happen */
792 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
793 num_contents = STD_ELEMENT_CONTENTS;
795 if (element == EL_YAMYAM)
797 level->num_yamyam_contents = num_contents;
799 for (i = 0; i < num_contents; i++)
800 for (y = 0; y < 3; y++)
801 for (x = 0; x < 3; x++)
802 level->yamyam_content[i][x][y] = content_array[i][x][y];
804 else if (element == EL_BD_AMOEBA)
806 level->amoeba_content = content_array[0][0][0];
810 Error(ERR_WARN, "cannot load content for element '%d'", element);
816 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
822 int chunk_size_expected;
824 element = getMappedElement(getFile16BitBE(file));
825 if (!IS_ENVELOPE(element))
826 element = EL_ENVELOPE_1;
828 envelope_nr = element - EL_ENVELOPE_1;
830 envelope_len = getFile16BitBE(file);
832 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
833 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
835 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
837 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
838 if (chunk_size_expected != chunk_size)
840 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
841 return chunk_size_expected;
844 for (i = 0; i < envelope_len; i++)
845 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
850 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
852 int num_changed_custom_elements = getFile16BitBE(file);
853 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
856 if (chunk_size_expected != chunk_size)
858 ReadUnusedBytesFromFile(file, chunk_size - 2);
859 return chunk_size_expected;
862 for (i = 0; i < num_changed_custom_elements; i++)
864 int element = getFile16BitBE(file);
865 int properties = getFile32BitBE(file);
867 if (IS_CUSTOM_ELEMENT(element))
868 Properties[element][EP_BITFIELD_BASE] = properties;
870 Error(ERR_WARN, "invalid custom element number %d", element);
876 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
878 int num_changed_custom_elements = getFile16BitBE(file);
879 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
882 if (chunk_size_expected != chunk_size)
884 ReadUnusedBytesFromFile(file, chunk_size - 2);
885 return chunk_size_expected;
888 for (i = 0; i < num_changed_custom_elements; i++)
890 int element = getFile16BitBE(file);
891 int custom_target_element = getFile16BitBE(file);
893 if (IS_CUSTOM_ELEMENT(element))
894 element_info[element].change->target_element = custom_target_element;
896 Error(ERR_WARN, "invalid custom element number %d", element);
902 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
904 int num_changed_custom_elements = getFile16BitBE(file);
905 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
908 if (chunk_size_expected != chunk_size)
910 ReadUnusedBytesFromFile(file, chunk_size - 2);
911 return chunk_size_expected;
914 for (i = 0; i < num_changed_custom_elements; i++)
916 int element = getFile16BitBE(file);
918 if (!IS_CUSTOM_ELEMENT(element))
920 Error(ERR_WARN, "invalid custom element number %d", element);
922 element = EL_INTERNAL_DUMMY;
925 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
926 element_info[element].description[j] = getFile8Bit(file);
927 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
929 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
931 /* some free bytes for future properties and padding */
932 ReadUnusedBytesFromFile(file, 7);
934 element_info[element].use_gfx_element = getFile8Bit(file);
935 element_info[element].gfx_element =
936 getMappedElement(getFile16BitBE(file));
938 element_info[element].collect_score = getFile8Bit(file);
939 element_info[element].collect_count = getFile8Bit(file);
941 element_info[element].push_delay_fixed = getFile16BitBE(file);
942 element_info[element].push_delay_random = getFile16BitBE(file);
943 element_info[element].move_delay_fixed = getFile16BitBE(file);
944 element_info[element].move_delay_random = getFile16BitBE(file);
946 element_info[element].move_pattern = getFile16BitBE(file);
947 element_info[element].move_direction_initial = getFile8Bit(file);
948 element_info[element].move_stepsize = getFile8Bit(file);
950 for (y = 0; y < 3; y++)
951 for (x = 0; x < 3; x++)
952 element_info[element].content[x][y] =
953 getMappedElement(getFile16BitBE(file));
955 element_info[element].change->events = getFile32BitBE(file);
957 element_info[element].change->target_element =
958 getMappedElement(getFile16BitBE(file));
960 element_info[element].change->delay_fixed = getFile16BitBE(file);
961 element_info[element].change->delay_random = getFile16BitBE(file);
962 element_info[element].change->delay_frames = getFile16BitBE(file);
964 element_info[element].change->trigger_element =
965 getMappedElement(getFile16BitBE(file));
967 element_info[element].change->explode = getFile8Bit(file);
968 element_info[element].change->use_content = getFile8Bit(file);
969 element_info[element].change->only_complete = getFile8Bit(file);
970 element_info[element].change->use_random_change = getFile8Bit(file);
972 element_info[element].change->random = getFile8Bit(file);
973 element_info[element].change->power = getFile8Bit(file);
975 for (y = 0; y < 3; y++)
976 for (x = 0; x < 3; x++)
977 element_info[element].change->content[x][y] =
978 getMappedElement(getFile16BitBE(file));
980 element_info[element].slippery_type = getFile8Bit(file);
982 /* some free bytes for future properties and padding */
983 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
985 /* mark that this custom element has been modified */
986 element_info[element].modified_settings = TRUE;
992 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
994 struct ElementInfo *ei;
995 int chunk_size_expected;
999 element = getFile16BitBE(file);
1001 if (!IS_CUSTOM_ELEMENT(element))
1003 Error(ERR_WARN, "invalid custom element number %d", element);
1005 ReadUnusedBytesFromFile(file, chunk_size - 2);
1009 ei = &element_info[element];
1011 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1012 ei->description[i] = getFile8Bit(file);
1013 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1015 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1016 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1018 ei->num_change_pages = getFile8Bit(file);
1020 /* some free bytes for future base property values and padding */
1021 ReadUnusedBytesFromFile(file, 5);
1023 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1024 if (chunk_size_expected != chunk_size)
1026 ReadUnusedBytesFromFile(file, chunk_size - 48);
1027 return chunk_size_expected;
1030 /* read custom property values */
1032 ei->use_gfx_element = getFile8Bit(file);
1033 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1035 ei->collect_score = getFile8Bit(file);
1036 ei->collect_count = getFile8Bit(file);
1038 ei->push_delay_fixed = getFile16BitBE(file);
1039 ei->push_delay_random = getFile16BitBE(file);
1040 ei->move_delay_fixed = getFile16BitBE(file);
1041 ei->move_delay_random = getFile16BitBE(file);
1043 /* bits 0 - 15 of "move_pattern" ... */
1044 ei->move_pattern = getFile16BitBE(file);
1045 ei->move_direction_initial = getFile8Bit(file);
1046 ei->move_stepsize = getFile8Bit(file);
1048 ei->slippery_type = getFile8Bit(file);
1050 for (y = 0; y < 3; y++)
1051 for (x = 0; x < 3; x++)
1052 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1054 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1055 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1056 ei->move_leave_type = getFile8Bit(file);
1058 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1059 ei->move_pattern |= (getFile16BitBE(file) << 16);
1061 ei->access_direction = getFile8Bit(file);
1063 ei->explosion_delay = getFile8Bit(file);
1064 ei->ignition_delay = getFile8Bit(file);
1066 /* some free bytes for future custom property values and padding */
1067 ReadUnusedBytesFromFile(file, 2);
1069 /* read change property values */
1071 setElementChangePages(ei, ei->num_change_pages);
1073 for (i = 0; i < ei->num_change_pages; i++)
1075 struct ElementChangeInfo *change = &ei->change_page[i];
1077 /* always start with reliable default values */
1078 setElementChangeInfoToDefaults(change);
1080 change->events = getFile32BitBE(file);
1082 change->target_element = getMappedElement(getFile16BitBE(file));
1084 change->delay_fixed = getFile16BitBE(file);
1085 change->delay_random = getFile16BitBE(file);
1086 change->delay_frames = getFile16BitBE(file);
1088 change->trigger_element = getMappedElement(getFile16BitBE(file));
1090 change->explode = getFile8Bit(file);
1091 change->use_content = getFile8Bit(file);
1092 change->only_complete = getFile8Bit(file);
1093 change->use_random_change = getFile8Bit(file);
1095 change->random = getFile8Bit(file);
1096 change->power = getFile8Bit(file);
1098 for (y = 0; y < 3; y++)
1099 for (x = 0; x < 3; x++)
1100 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1102 change->can_change = getFile8Bit(file);
1104 change->trigger_side = getFile8Bit(file);
1107 change->trigger_player = getFile8Bit(file);
1108 change->trigger_page = getFile8Bit(file);
1110 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1111 CH_PAGE_ANY : (1 << change->trigger_page));
1113 /* some free bytes for future change property values and padding */
1114 ReadUnusedBytesFromFile(file, 6);
1118 /* some free bytes for future change property values and padding */
1119 ReadUnusedBytesFromFile(file, 8);
1123 /* mark this custom element as modified */
1124 ei->modified_settings = TRUE;
1129 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1131 struct ElementInfo *ei;
1132 struct ElementGroupInfo *group;
1136 element = getFile16BitBE(file);
1138 if (!IS_GROUP_ELEMENT(element))
1140 Error(ERR_WARN, "invalid group element number %d", element);
1142 ReadUnusedBytesFromFile(file, chunk_size - 2);
1146 ei = &element_info[element];
1148 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1149 ei->description[i] = getFile8Bit(file);
1150 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1152 group = element_info[element].group;
1154 group->num_elements = getFile8Bit(file);
1156 ei->use_gfx_element = getFile8Bit(file);
1157 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1159 group->choice_mode = getFile8Bit(file);
1161 /* some free bytes for future values and padding */
1162 ReadUnusedBytesFromFile(file, 3);
1164 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1165 group->element[i] = getMappedElement(getFile16BitBE(file));
1167 /* mark this group element as modified */
1168 element_info[element].modified_settings = TRUE;
1173 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1174 struct LevelFileInfo *level_file_info)
1176 char *filename = level_file_info->filename;
1177 char cookie[MAX_LINE_LEN];
1178 char chunk_name[CHUNK_ID_LEN + 1];
1182 if (!(file = fopen(filename, MODE_READ)))
1184 level->no_valid_file = TRUE;
1186 if (level != &level_template)
1187 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1192 getFileChunkBE(file, chunk_name, NULL);
1193 if (strcmp(chunk_name, "RND1") == 0)
1195 getFile32BitBE(file); /* not used */
1197 getFileChunkBE(file, chunk_name, NULL);
1198 if (strcmp(chunk_name, "CAVE") != 0)
1200 level->no_valid_file = TRUE;
1202 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1207 else /* check for pre-2.0 file format with cookie string */
1209 strcpy(cookie, chunk_name);
1210 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1211 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1212 cookie[strlen(cookie) - 1] = '\0';
1214 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1216 level->no_valid_file = TRUE;
1218 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1223 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1225 level->no_valid_file = TRUE;
1227 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1232 /* pre-2.0 level files have no game version, so use file version here */
1233 level->game_version = level->file_version;
1236 if (level->file_version < FILE_VERSION_1_2)
1238 /* level files from versions before 1.2.0 without chunk structure */
1239 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1240 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1248 int (*loader)(FILE *, int, struct LevelInfo *);
1252 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1253 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1254 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1255 { "BODY", -1, LoadLevel_BODY },
1256 { "CONT", -1, LoadLevel_CONT },
1257 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1258 { "CNT3", -1, LoadLevel_CNT3 },
1259 { "CUS1", -1, LoadLevel_CUS1 },
1260 { "CUS2", -1, LoadLevel_CUS2 },
1261 { "CUS3", -1, LoadLevel_CUS3 },
1262 { "CUS4", -1, LoadLevel_CUS4 },
1263 { "GRP1", -1, LoadLevel_GRP1 },
1267 while (getFileChunkBE(file, chunk_name, &chunk_size))
1271 while (chunk_info[i].name != NULL &&
1272 strcmp(chunk_name, chunk_info[i].name) != 0)
1275 if (chunk_info[i].name == NULL)
1277 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1278 chunk_name, filename);
1279 ReadUnusedBytesFromFile(file, chunk_size);
1281 else if (chunk_info[i].size != -1 &&
1282 chunk_info[i].size != chunk_size)
1284 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1285 chunk_size, chunk_name, filename);
1286 ReadUnusedBytesFromFile(file, chunk_size);
1290 /* call function to load this level chunk */
1291 int chunk_size_expected =
1292 (chunk_info[i].loader)(file, chunk_size, level);
1294 /* the size of some chunks cannot be checked before reading other
1295 chunks first (like "HEAD" and "BODY") that contain some header
1296 information, so check them here */
1297 if (chunk_size_expected != chunk_size)
1299 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1300 chunk_size, chunk_name, filename);
1309 /* ------------------------------------------------------------------------- */
1310 /* functions for loading EM level */
1311 /* ------------------------------------------------------------------------- */
1313 static int map_em_element_yam(int element)
1317 case 0x00: return EL_EMPTY;
1318 case 0x01: return EL_EMERALD;
1319 case 0x02: return EL_DIAMOND;
1320 case 0x03: return EL_ROCK;
1321 case 0x04: return EL_ROBOT;
1322 case 0x05: return EL_SPACESHIP_UP;
1323 case 0x06: return EL_BOMB;
1324 case 0x07: return EL_BUG_UP;
1325 case 0x08: return EL_AMOEBA_DROP;
1326 case 0x09: return EL_NUT;
1327 case 0x0a: return EL_YAMYAM;
1328 case 0x0b: return EL_QUICKSAND_FULL;
1329 case 0x0c: return EL_SAND;
1330 case 0x0d: return EL_WALL_SLIPPERY;
1331 case 0x0e: return EL_STEELWALL;
1332 case 0x0f: return EL_WALL;
1333 case 0x10: return EL_EM_KEY_1;
1334 case 0x11: return EL_EM_KEY_2;
1335 case 0x12: return EL_EM_KEY_4;
1336 case 0x13: return EL_EM_KEY_3;
1337 case 0x14: return EL_MAGIC_WALL;
1338 case 0x15: return EL_ROBOT_WHEEL;
1339 case 0x16: return EL_DYNAMITE;
1341 case 0x17: return EL_EM_KEY_1; /* EMC */
1342 case 0x18: return EL_BUG_UP; /* EMC */
1343 case 0x1a: return EL_DIAMOND; /* EMC */
1344 case 0x1b: return EL_EMERALD; /* EMC */
1345 case 0x25: return EL_NUT; /* EMC */
1346 case 0x80: return EL_EMPTY; /* EMC */
1347 case 0x85: return EL_EM_KEY_1; /* EMC */
1348 case 0x86: return EL_EM_KEY_2; /* EMC */
1349 case 0x87: return EL_EM_KEY_4; /* EMC */
1350 case 0x88: return EL_EM_KEY_3; /* EMC */
1351 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1352 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1353 case 0xaf: return EL_DYNAMITE; /* EMC */
1354 case 0xbd: return EL_SAND; /* EMC */
1357 Error(ERR_WARN, "invalid level element %d", element);
1362 static int map_em_element_field(int element)
1364 if (element >= 0xc8 && element <= 0xe1)
1365 return EL_CHAR_A + (element - 0xc8);
1366 else if (element >= 0xe2 && element <= 0xeb)
1367 return EL_CHAR_0 + (element - 0xe2);
1371 case 0x00: return EL_ROCK;
1372 case 0x01: return EL_ROCK; /* EMC */
1373 case 0x02: return EL_DIAMOND;
1374 case 0x03: return EL_DIAMOND;
1375 case 0x04: return EL_ROBOT;
1376 case 0x05: return EL_ROBOT; /* EMC */
1377 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1378 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1379 case 0x08: return EL_SPACESHIP_UP;
1380 case 0x09: return EL_SPACESHIP_RIGHT;
1381 case 0x0a: return EL_SPACESHIP_DOWN;
1382 case 0x0b: return EL_SPACESHIP_LEFT;
1383 case 0x0c: return EL_SPACESHIP_UP;
1384 case 0x0d: return EL_SPACESHIP_RIGHT;
1385 case 0x0e: return EL_SPACESHIP_DOWN;
1386 case 0x0f: return EL_SPACESHIP_LEFT;
1388 case 0x10: return EL_BOMB;
1389 case 0x11: return EL_BOMB; /* EMC */
1390 case 0x12: return EL_EMERALD;
1391 case 0x13: return EL_EMERALD;
1392 case 0x14: return EL_BUG_UP;
1393 case 0x15: return EL_BUG_RIGHT;
1394 case 0x16: return EL_BUG_DOWN;
1395 case 0x17: return EL_BUG_LEFT;
1396 case 0x18: return EL_BUG_UP;
1397 case 0x19: return EL_BUG_RIGHT;
1398 case 0x1a: return EL_BUG_DOWN;
1399 case 0x1b: return EL_BUG_LEFT;
1400 case 0x1c: return EL_AMOEBA_DROP;
1401 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1402 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1403 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1405 case 0x20: return EL_ROCK;
1406 case 0x21: return EL_BOMB; /* EMC */
1407 case 0x22: return EL_DIAMOND; /* EMC */
1408 case 0x23: return EL_EMERALD; /* EMC */
1409 case 0x24: return EL_MAGIC_WALL;
1410 case 0x25: return EL_NUT;
1411 case 0x26: return EL_NUT; /* EMC */
1412 case 0x27: return EL_NUT; /* EMC */
1414 /* looks like magic wheel, but is _always_ activated */
1415 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1417 case 0x29: return EL_YAMYAM; /* up */
1418 case 0x2a: return EL_YAMYAM; /* down */
1419 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1420 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1421 case 0x2d: return EL_QUICKSAND_FULL;
1422 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1423 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1425 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1426 case 0x31: return EL_SAND; /* EMC */
1427 case 0x32: return EL_SAND; /* EMC */
1428 case 0x33: return EL_SAND; /* EMC */
1429 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1430 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1431 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1432 case 0x37: return EL_SAND; /* EMC */
1433 case 0x38: return EL_ROCK; /* EMC */
1434 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1435 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1436 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1437 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1438 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1439 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1440 case 0x3f: return EL_ACID_POOL_BOTTOM;
1442 case 0x40: return EL_EXIT_OPEN; /* 1 */
1443 case 0x41: return EL_EXIT_OPEN; /* 2 */
1444 case 0x42: return EL_EXIT_OPEN; /* 3 */
1445 case 0x43: return EL_BALLOON; /* EMC */
1446 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1447 case 0x45: return EL_SPRING; /* EMC */
1448 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1449 case 0x47: return EL_SPRING; /* left */ /* EMC */
1450 case 0x48: return EL_SPRING; /* right */ /* EMC */
1451 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1452 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1453 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1454 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1455 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1456 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1457 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1459 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1460 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1461 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1462 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1463 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1464 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1465 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1466 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1467 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1468 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1469 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1470 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1471 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1472 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1473 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1474 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1476 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1477 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1478 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1479 case 0x63: return EL_SPRING; /* left */ /* EMC */
1480 case 0x64: return EL_SPRING; /* right */ /* EMC */
1481 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1482 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1483 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1484 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1485 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1486 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1487 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1488 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1489 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1490 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1491 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1493 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1494 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1495 case 0x72: return EL_NUT; /* left */ /* EMC */
1496 case 0x73: return EL_SAND; /* EMC (? "nut") */
1497 case 0x74: return EL_STEELWALL;
1498 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1499 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1500 case 0x77: return EL_BOMB; /* left */ /* EMC */
1501 case 0x78: return EL_BOMB; /* right */ /* EMC */
1502 case 0x79: return EL_ROCK; /* left */ /* EMC */
1503 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1504 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1505 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1506 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1507 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1508 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1510 case 0x80: return EL_EMPTY;
1511 case 0x81: return EL_WALL_SLIPPERY;
1512 case 0x82: return EL_SAND;
1513 case 0x83: return EL_STEELWALL;
1514 case 0x84: return EL_WALL;
1515 case 0x85: return EL_EM_KEY_1;
1516 case 0x86: return EL_EM_KEY_2;
1517 case 0x87: return EL_EM_KEY_4;
1518 case 0x88: return EL_EM_KEY_3;
1519 case 0x89: return EL_EM_GATE_1;
1520 case 0x8a: return EL_EM_GATE_2;
1521 case 0x8b: return EL_EM_GATE_4;
1522 case 0x8c: return EL_EM_GATE_3;
1523 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1524 case 0x8e: return EL_EM_GATE_1_GRAY;
1525 case 0x8f: return EL_EM_GATE_2_GRAY;
1527 case 0x90: return EL_EM_GATE_4_GRAY;
1528 case 0x91: return EL_EM_GATE_3_GRAY;
1529 case 0x92: return EL_MAGIC_WALL;
1530 case 0x93: return EL_ROBOT_WHEEL;
1531 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1532 case 0x95: return EL_ACID_POOL_TOPLEFT;
1533 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1534 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1535 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1536 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1537 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1538 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1539 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1540 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1541 case 0x9e: return EL_EXIT_CLOSED;
1542 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1544 /* looks like normal sand, but behaves like wall */
1545 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1546 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1547 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1548 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1549 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1550 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1551 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1552 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1553 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1554 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1555 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1556 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1557 case 0xac: return EL_CHAR_COMMA; /* EMC */
1558 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1559 case 0xae: return EL_CHAR_MINUS; /* EMC */
1560 case 0xaf: return EL_DYNAMITE;
1562 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1563 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1564 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1565 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1566 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1567 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1568 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1569 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1570 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1571 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1572 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1573 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1574 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1575 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1576 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1577 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1579 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1580 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1581 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1582 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1583 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1584 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1585 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1586 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1588 /* characters: see above */
1590 case 0xec: return EL_CHAR_PERIOD;
1591 case 0xed: return EL_CHAR_EXCLAM;
1592 case 0xee: return EL_CHAR_COLON;
1593 case 0xef: return EL_CHAR_QUESTION;
1595 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1596 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1597 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1598 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1599 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1600 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1601 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1602 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1604 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1605 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1606 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1607 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1608 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1609 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1611 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1612 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1615 /* should never happen (all 8-bit value cases should be handled) */
1616 Error(ERR_WARN, "invalid level element %d", element);
1621 #define EM_LEVEL_SIZE 2106
1622 #define EM_LEVEL_XSIZE 64
1623 #define EM_LEVEL_YSIZE 32
1625 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1626 struct LevelFileInfo *level_file_info)
1628 char *filename = level_file_info->filename;
1630 unsigned char leveldata[EM_LEVEL_SIZE];
1631 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1632 unsigned char code0 = 0x65;
1633 unsigned char code1 = 0x11;
1634 boolean level_is_crypted = FALSE;
1635 int nr = level_file_info->nr;
1638 if (!(file = fopen(filename, MODE_READ)))
1640 level->no_valid_file = TRUE;
1642 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1647 for(i = 0; i < EM_LEVEL_SIZE; i++)
1648 leveldata[i] = fgetc(file);
1652 /* check if level data is crypted by testing against known starting bytes
1653 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1655 if ((leveldata[0] == 0xf1 ||
1656 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1658 level_is_crypted = TRUE;
1660 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1661 leveldata[0] = 0xf1;
1664 if (level_is_crypted) /* decode crypted level data */
1666 for(i = 0; i < EM_LEVEL_SIZE; i++)
1668 leveldata[i] ^= code0;
1669 leveldata[i] -= code1;
1671 code0 = (code0 + 7) & 0xff;
1675 level->fieldx = EM_LEVEL_XSIZE;
1676 level->fieldy = EM_LEVEL_YSIZE;
1678 level->time = header[46] * 10;
1679 level->gems_needed = header[47];
1681 /* The original Emerald Mine levels have their level number stored
1682 at the second byte of the level file...
1683 Do not trust this information at other level files, e.g. EMC,
1684 but correct it anyway (normally the first row is completely
1685 steel wall, so the correction does not hurt anyway). */
1687 if (leveldata[1] == nr)
1688 leveldata[1] = leveldata[2]; /* correct level number field */
1690 sprintf(level->name, "Level %d", nr); /* set level name */
1692 level->score[SC_EMERALD] = header[36];
1693 level->score[SC_DIAMOND] = header[37];
1694 level->score[SC_ROBOT] = header[38];
1695 level->score[SC_SPACESHIP] = header[39];
1696 level->score[SC_BUG] = header[40];
1697 level->score[SC_YAMYAM] = header[41];
1698 level->score[SC_NUT] = header[42];
1699 level->score[SC_DYNAMITE] = header[43];
1700 level->score[SC_TIME_BONUS] = header[44];
1702 level->num_yamyam_contents = 4;
1704 for(i = 0; i < level->num_yamyam_contents; i++)
1705 for(y = 0; y < 3; y++)
1706 for(x = 0; x < 3; x++)
1707 level->yamyam_content[i][x][y] =
1708 map_em_element_yam(header[i * 9 + y * 3 + x]);
1710 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1711 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1712 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1713 level->amoeba_content = EL_DIAMOND;
1715 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1717 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1719 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1720 new_element = EL_AMOEBA_WET;
1722 level->field[x][y] = new_element;
1725 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1726 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1727 level->field[x][y] = EL_PLAYER_1;
1729 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1730 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1731 level->field[x][y] = EL_PLAYER_2;
1734 /* ------------------------------------------------------------------------- */
1735 /* functions for loading SP level */
1736 /* ------------------------------------------------------------------------- */
1738 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1739 #define SP_LEVEL_SIZE 1536
1740 #define SP_LEVEL_XSIZE 60
1741 #define SP_LEVEL_YSIZE 24
1742 #define SP_LEVEL_NAME_LEN 23
1744 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1749 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1750 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1752 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1754 int element_old = fgetc(file);
1757 if (element_old <= 0x27)
1758 element_new = getMappedElement(EL_SP_START + element_old);
1759 else if (element_old == 0x28)
1760 element_new = EL_INVISIBLE_WALL;
1763 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1764 Error(ERR_WARN, "invalid level element %d", element_old);
1766 element_new = EL_UNKNOWN;
1769 level->field[x][y] = element_new;
1773 ReadUnusedBytesFromFile(file, 4);
1775 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1776 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1778 ReadUnusedBytesFromFile(file, 1);
1780 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1781 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1782 level->name[i] = fgetc(file);
1783 level->name[SP_LEVEL_NAME_LEN] = '\0';
1785 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1786 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1788 /* number of infotrons needed; 0 means that Supaplex will count the total
1789 amount of infotrons in the level and use the low byte of that number.
1790 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1791 level->gems_needed = fgetc(file);
1793 /* information about special gravity port entries */
1794 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1796 level->fieldx = SP_LEVEL_XSIZE;
1797 level->fieldy = SP_LEVEL_YSIZE;
1799 level->time = 0; /* no time limit */
1800 level->amoeba_speed = 0;
1801 level->time_magic_wall = 0;
1802 level->time_wheel = 0;
1803 level->amoeba_content = EL_EMPTY;
1805 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1806 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1808 /* there are no yamyams in supaplex levels */
1809 for(i = 0; i < level->num_yamyam_contents; i++)
1810 for(y = 0; y < 3; y++)
1811 for(x = 0; x < 3; x++)
1812 level->yamyam_content[i][x][y] = EL_EMPTY;
1815 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1816 struct LevelFileInfo *level_file_info)
1818 char *filename = level_file_info->filename;
1820 int nr = level_file_info->nr - leveldir_current->first_level;
1822 char name_first, name_last;
1823 struct LevelInfo multipart_level;
1824 int multipart_xpos, multipart_ypos;
1825 boolean is_multipart_level;
1826 boolean is_first_part;
1827 boolean reading_multipart_level = FALSE;
1828 boolean use_empty_level = FALSE;
1830 if (!(file = fopen(filename, MODE_READ)))
1832 level->no_valid_file = TRUE;
1834 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1839 /* position file stream to the requested level inside the level package */
1840 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1842 level->no_valid_file = TRUE;
1844 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1849 /* there exist Supaplex level package files with multi-part levels which
1850 can be detected as follows: instead of leading and trailing dashes ('-')
1851 to pad the level name, they have leading and trailing numbers which are
1852 the x and y coordinations of the current part of the multi-part level;
1853 if there are '?' characters instead of numbers on the left or right side
1854 of the level name, the multi-part level consists of only horizontal or
1857 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1859 LoadLevelFromFileStream_SP(file, level, l);
1861 /* check if this level is a part of a bigger multi-part level */
1863 name_first = level->name[0];
1864 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1866 is_multipart_level =
1867 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1868 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1871 ((name_first == '?' || name_first == '1') &&
1872 (name_last == '?' || name_last == '1'));
1874 /* correct leading multipart level meta information in level name */
1875 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1876 level->name[i] = '-';
1878 /* correct trailing multipart level meta information in level name */
1879 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1880 level->name[i] = '-';
1882 /* ---------- check for normal single level ---------- */
1884 if (!reading_multipart_level && !is_multipart_level)
1886 /* the current level is simply a normal single-part level, and we are
1887 not reading a multi-part level yet, so return the level as it is */
1892 /* ---------- check for empty level (unused multi-part) ---------- */
1894 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1896 /* this is a part of a multi-part level, but not the first part
1897 (and we are not already reading parts of a multi-part level);
1898 in this case, use an empty level instead of the single part */
1900 use_empty_level = TRUE;
1905 /* ---------- check for finished multi-part level ---------- */
1907 if (reading_multipart_level &&
1908 (!is_multipart_level ||
1909 strcmp(level->name, multipart_level.name) != 0))
1911 /* we are already reading parts of a multi-part level, but this level is
1912 either not a multi-part level, or a part of a different multi-part
1913 level; in both cases, the multi-part level seems to be complete */
1918 /* ---------- here we have one part of a multi-part level ---------- */
1920 reading_multipart_level = TRUE;
1922 if (is_first_part) /* start with first part of new multi-part level */
1924 /* copy level info structure from first part */
1925 multipart_level = *level;
1927 /* clear playfield of new multi-part level */
1928 for (y = 0; y < MAX_LEV_FIELDY; y++)
1929 for (x = 0; x < MAX_LEV_FIELDX; x++)
1930 multipart_level.field[x][y] = EL_EMPTY;
1933 if (name_first == '?')
1935 if (name_last == '?')
1938 multipart_xpos = (int)(name_first - '0');
1939 multipart_ypos = (int)(name_last - '0');
1942 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1943 multipart_xpos, multipart_ypos, multipart_level.name);
1946 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1947 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1949 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1954 multipart_level.fieldx = MAX(multipart_level.fieldx,
1955 multipart_xpos * SP_LEVEL_XSIZE);
1956 multipart_level.fieldy = MAX(multipart_level.fieldy,
1957 multipart_ypos * SP_LEVEL_YSIZE);
1959 /* copy level part at the right position of multi-part level */
1960 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1962 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1964 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1965 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1967 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1974 if (use_empty_level)
1976 setLevelInfoToDefaults(level);
1978 level->fieldx = SP_LEVEL_XSIZE;
1979 level->fieldy = SP_LEVEL_YSIZE;
1981 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1982 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1983 level->field[x][y] = EL_EMPTY;
1985 strcpy(level->name, "-------- EMPTY --------");
1987 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1990 if (reading_multipart_level)
1991 *level = multipart_level;
1994 /* ------------------------------------------------------------------------- */
1995 /* functions for loading generic level */
1996 /* ------------------------------------------------------------------------- */
1998 void LoadLevelFromFileInfo(struct LevelInfo *level,
1999 struct LevelFileInfo *level_file_info)
2001 /* always start with reliable default values */
2002 setLevelInfoToDefaults(level);
2004 switch (level_file_info->type)
2006 case LEVEL_FILE_TYPE_RND:
2007 LoadLevelFromFileInfo_RND(level, level_file_info);
2010 case LEVEL_FILE_TYPE_EM:
2011 LoadLevelFromFileInfo_EM(level, level_file_info);
2014 case LEVEL_FILE_TYPE_SP:
2015 LoadLevelFromFileInfo_SP(level, level_file_info);
2019 LoadLevelFromFileInfo_RND(level, level_file_info);
2024 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2026 static struct LevelFileInfo level_file_info;
2028 /* always start with reliable default values */
2029 setFileInfoToDefaults(&level_file_info);
2031 level_file_info.nr = 0; /* unknown level number */
2032 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2033 level_file_info.filename = filename;
2035 LoadLevelFromFileInfo(level, &level_file_info);
2038 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2040 if (leveldir_current == NULL) /* only when dumping level */
2044 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2047 /* determine correct game engine version of current level */
2049 if (!leveldir_current->latest_engine)
2051 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2052 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2053 IS_LEVELCLASS_UNDEFINED(leveldir_current))
2057 printf("\n::: This level is private or contributed: '%s'\n", filename);
2061 printf("\n::: Use the stored game engine version for this level\n");
2064 /* For all levels which are not forced to use the latest game engine
2065 version (normally user contributed, private and undefined levels),
2066 use the version of the game engine the levels were created for.
2068 Since 2.0.1, the game engine version is now directly stored
2069 in the level file (chunk "VERS"), so there is no need anymore
2070 to set the game version from the file version (except for old,
2071 pre-2.0 levels, where the game version is still taken from the
2072 file format version used to store the level -- see above). */
2074 /* do some special adjustments to support older level versions */
2075 if (level->file_version == FILE_VERSION_1_0)
2077 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2078 Error(ERR_WARN, "using high speed movement for player");
2080 /* player was faster than monsters in (pre-)1.0 levels */
2081 level->double_speed = TRUE;
2084 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2085 if (level->game_version == VERSION_IDENT(2,0,1,0))
2086 level->em_slippery_gems = TRUE;
2088 if (level->game_version < VERSION_IDENT(2,2,0,0))
2089 level->use_spring_bug = TRUE;
2091 if (level->game_version < VERSION_IDENT(3,1,0,0))
2095 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2096 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
2098 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
2099 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2100 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
2101 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
2103 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2104 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2106 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2108 int element = EL_CUSTOM_START + i;
2109 struct ElementInfo *ei = &element_info[element];
2111 for (j = 0; j < ei->num_change_pages; j++)
2113 struct ElementChangeInfo *change = &ei->change_page[j];
2115 change->trigger_player = CH_PLAYER_ANY;
2116 change->trigger_page = CH_PAGE_ANY;
2124 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2125 leveldir_current->sort_priority, filename);
2129 printf("\n::: Use latest game engine version for this level.\n");
2132 /* For all levels which are forced to use the latest game engine version
2133 (normally all but user contributed, private and undefined levels), set
2134 the game engine version to the actual version; this allows for actual
2135 corrections in the game engine to take effect for existing, converted
2136 levels (from "classic" or other existing games) to make the emulation
2137 of the corresponding game more accurate, while (hopefully) not breaking
2138 existing levels created from other players. */
2141 printf("::: changing engine from %d to %d\n",
2142 level->game_version, GAME_VERSION_ACTUAL);
2145 level->game_version = GAME_VERSION_ACTUAL;
2147 /* Set special EM style gems behaviour: EM style gems slip down from
2148 normal, steel and growing wall. As this is a more fundamental change,
2149 it seems better to set the default behaviour to "off" (as it is more
2150 natural) and make it configurable in the level editor (as a property
2151 of gem style elements). Already existing converted levels (neither
2152 private nor contributed levels) are changed to the new behaviour. */
2154 if (level->file_version < FILE_VERSION_2_0)
2155 level->em_slippery_gems = TRUE;
2159 printf("::: => %d\n", level->game_version);
2163 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2167 /* map custom element change events that have changed in newer versions
2168 (these following values were accidentally changed in version 3.0.1) */
2169 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2171 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2173 int element = EL_CUSTOM_START + i;
2175 /* order of checking and copying events to be mapped is important */
2176 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2178 if (HAS_CHANGE_EVENT(element, j - 2))
2180 SET_CHANGE_EVENT(element, j - 2, FALSE);
2181 SET_CHANGE_EVENT(element, j, TRUE);
2185 /* order of checking and copying events to be mapped is important */
2186 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2188 if (HAS_CHANGE_EVENT(element, j - 1))
2190 SET_CHANGE_EVENT(element, j - 1, FALSE);
2191 SET_CHANGE_EVENT(element, j, TRUE);
2197 /* some custom element change events get mapped since version 3.0.3 */
2198 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2200 int element = EL_CUSTOM_START + i;
2202 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2203 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2205 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2206 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2208 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2212 /* initialize "can_change" field for old levels with only one change page */
2213 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2215 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2217 int element = EL_CUSTOM_START + i;
2219 if (CAN_CHANGE(element))
2220 element_info[element].change->can_change = TRUE;
2224 /* correct custom element values (for old levels without these options) */
2225 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2227 int element = EL_CUSTOM_START + i;
2228 struct ElementInfo *ei = &element_info[element];
2230 if (ei->access_direction == MV_NO_MOVING)
2231 ei->access_direction = MV_ALL_DIRECTIONS;
2233 for (j = 0; j < ei->num_change_pages; j++)
2235 struct ElementChangeInfo *change = &ei->change_page[j];
2237 if (change->trigger_side == CH_SIDE_NONE)
2238 change->trigger_side = CH_SIDE_ANY;
2243 /* set default push delay values (corrected since version 3.0.7-1) */
2244 if (level->game_version < VERSION_IDENT(3,0,7,1))
2246 game.default_push_delay_fixed = 2;
2247 game.default_push_delay_random = 8;
2251 game.default_push_delay_fixed = 8;
2252 game.default_push_delay_random = 8;
2255 /* set uninitialized push delay values of custom elements in older levels */
2256 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2258 int element = EL_CUSTOM_START + i;
2260 if (element_info[element].push_delay_fixed == -1)
2261 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2262 if (element_info[element].push_delay_random == -1)
2263 element_info[element].push_delay_random = game.default_push_delay_random;
2267 /* map elements that have changed in newer versions */
2268 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2269 level->game_version);
2270 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2271 for (x = 0; x < 3; x++)
2272 for (y = 0; y < 3; y++)
2273 level->yamyam_content[i][x][y] =
2274 getMappedElementByVersion(level->yamyam_content[i][x][y],
2275 level->game_version);
2277 /* initialize element properties for level editor etc. */
2278 InitElementPropertiesEngine(level->game_version);
2281 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2285 /* map elements that have changed in newer versions */
2286 for (y = 0; y < level->fieldy; y++)
2288 for (x = 0; x < level->fieldx; x++)
2290 int element = level->field[x][y];
2293 element = getMappedElementByVersion(element, level->game_version);
2295 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2297 /* map game font elements */
2298 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2299 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2300 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2301 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2304 if (level->game_version < VERSION_IDENT(3,0,0,0))
2306 /* map Supaplex gravity tube elements */
2307 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2308 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2309 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2310 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2315 level->field[x][y] = element;
2319 /* copy elements to runtime playfield array */
2320 for (x = 0; x < MAX_LEV_FIELDX; x++)
2321 for (y = 0; y < MAX_LEV_FIELDY; y++)
2322 Feld[x][y] = level->field[x][y];
2324 /* initialize level size variables for faster access */
2325 lev_fieldx = level->fieldx;
2326 lev_fieldy = level->fieldy;
2328 /* determine border element for this level */
2332 void LoadLevelTemplate(int nr)
2335 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2336 char *filename = level_file_info->filename;
2338 LoadLevelFromFileInfo(&level_template, level_file_info);
2340 char *filename = getDefaultLevelFilename(nr);
2342 LoadLevelFromFilename_RND(&level_template, filename);
2345 LoadLevel_InitVersion(&level, filename);
2346 LoadLevel_InitElements(&level, filename);
2348 ActivateLevelTemplate();
2351 void LoadLevel(int nr)
2354 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2355 char *filename = level_file_info->filename;
2357 LoadLevelFromFileInfo(&level, level_file_info);
2359 char *filename = getLevelFilename(nr);
2361 LoadLevelFromFilename_RND(&level, filename);
2364 if (level.use_custom_template)
2365 LoadLevelTemplate(-1);
2368 LoadLevel_InitVersion(&level, filename);
2369 LoadLevel_InitElements(&level, filename);
2370 LoadLevel_InitPlayfield(&level, filename);
2372 LoadLevel_InitLevel(&level, filename);
2376 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2378 putFileVersion(file, level->file_version);
2379 putFileVersion(file, level->game_version);
2382 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2386 putFile8Bit(file, level->fieldx);
2387 putFile8Bit(file, level->fieldy);
2389 putFile16BitBE(file, level->time);
2390 putFile16BitBE(file, level->gems_needed);
2392 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2393 putFile8Bit(file, level->name[i]);
2395 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2396 putFile8Bit(file, level->score[i]);
2398 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2399 for (y = 0; y < 3; y++)
2400 for (x = 0; x < 3; x++)
2401 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2402 level->yamyam_content[i][x][y]));
2403 putFile8Bit(file, level->amoeba_speed);
2404 putFile8Bit(file, level->time_magic_wall);
2405 putFile8Bit(file, level->time_wheel);
2406 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2407 level->amoeba_content));
2408 putFile8Bit(file, (level->double_speed ? 1 : 0));
2409 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2410 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2411 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2413 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2415 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2416 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2417 putFile32BitBE(file, level->can_move_into_acid_bits);
2418 putFile8Bit(file, level->dont_collide_with_bits);
2420 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2421 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
2423 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2426 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2430 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2431 putFile8Bit(file, level->author[i]);
2434 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2438 for (y = 0; y < level->fieldy; y++)
2439 for (x = 0; x < level->fieldx; x++)
2440 if (level->encoding_16bit_field)
2441 putFile16BitBE(file, level->field[x][y]);
2443 putFile8Bit(file, level->field[x][y]);
2447 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2451 putFile8Bit(file, EL_YAMYAM);
2452 putFile8Bit(file, level->num_yamyam_contents);
2453 putFile8Bit(file, 0);
2454 putFile8Bit(file, 0);
2456 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2457 for (y = 0; y < 3; y++)
2458 for (x = 0; x < 3; x++)
2459 if (level->encoding_16bit_field)
2460 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2462 putFile8Bit(file, level->yamyam_content[i][x][y]);
2466 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2469 int num_contents, content_xsize, content_ysize;
2470 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2472 if (element == EL_YAMYAM)
2474 num_contents = level->num_yamyam_contents;
2478 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2479 for (y = 0; y < 3; y++)
2480 for (x = 0; x < 3; x++)
2481 content_array[i][x][y] = level->yamyam_content[i][x][y];
2483 else if (element == EL_BD_AMOEBA)
2489 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2490 for (y = 0; y < 3; y++)
2491 for (x = 0; x < 3; x++)
2492 content_array[i][x][y] = EL_EMPTY;
2493 content_array[0][0][0] = level->amoeba_content;
2497 /* chunk header already written -- write empty chunk data */
2498 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2500 Error(ERR_WARN, "cannot save content for element '%d'", element);
2504 putFile16BitBE(file, element);
2505 putFile8Bit(file, num_contents);
2506 putFile8Bit(file, content_xsize);
2507 putFile8Bit(file, content_ysize);
2509 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2511 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2512 for (y = 0; y < 3; y++)
2513 for (x = 0; x < 3; x++)
2514 putFile16BitBE(file, content_array[i][x][y]);
2517 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2520 int envelope_nr = element - EL_ENVELOPE_1;
2521 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2523 putFile16BitBE(file, element);
2524 putFile16BitBE(file, envelope_len);
2525 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2526 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2528 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2530 for (i = 0; i < envelope_len; i++)
2531 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2535 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2536 int num_changed_custom_elements)
2540 putFile16BitBE(file, num_changed_custom_elements);
2542 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2544 int element = EL_CUSTOM_START + i;
2546 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2548 if (check < num_changed_custom_elements)
2550 putFile16BitBE(file, element);
2551 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2558 if (check != num_changed_custom_elements) /* should not happen */
2559 Error(ERR_WARN, "inconsistent number of custom element properties");
2564 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2565 int num_changed_custom_elements)
2569 putFile16BitBE(file, num_changed_custom_elements);
2571 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2573 int element = EL_CUSTOM_START + i;
2575 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2577 if (check < num_changed_custom_elements)
2579 putFile16BitBE(file, element);
2580 putFile16BitBE(file, element_info[element].change->target_element);
2587 if (check != num_changed_custom_elements) /* should not happen */
2588 Error(ERR_WARN, "inconsistent number of custom target elements");
2593 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2594 int num_changed_custom_elements)
2596 int i, j, x, y, check = 0;
2598 putFile16BitBE(file, num_changed_custom_elements);
2600 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2602 int element = EL_CUSTOM_START + i;
2604 if (element_info[element].modified_settings)
2606 if (check < num_changed_custom_elements)
2608 putFile16BitBE(file, element);
2610 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2611 putFile8Bit(file, element_info[element].description[j]);
2613 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2615 /* some free bytes for future properties and padding */
2616 WriteUnusedBytesToFile(file, 7);
2618 putFile8Bit(file, element_info[element].use_gfx_element);
2619 putFile16BitBE(file, element_info[element].gfx_element);
2621 putFile8Bit(file, element_info[element].collect_score);
2622 putFile8Bit(file, element_info[element].collect_count);
2624 putFile16BitBE(file, element_info[element].push_delay_fixed);
2625 putFile16BitBE(file, element_info[element].push_delay_random);
2626 putFile16BitBE(file, element_info[element].move_delay_fixed);
2627 putFile16BitBE(file, element_info[element].move_delay_random);
2629 putFile16BitBE(file, element_info[element].move_pattern);
2630 putFile8Bit(file, element_info[element].move_direction_initial);
2631 putFile8Bit(file, element_info[element].move_stepsize);
2633 for (y = 0; y < 3; y++)
2634 for (x = 0; x < 3; x++)
2635 putFile16BitBE(file, element_info[element].content[x][y]);
2637 putFile32BitBE(file, element_info[element].change->events);
2639 putFile16BitBE(file, element_info[element].change->target_element);
2641 putFile16BitBE(file, element_info[element].change->delay_fixed);
2642 putFile16BitBE(file, element_info[element].change->delay_random);
2643 putFile16BitBE(file, element_info[element].change->delay_frames);
2645 putFile16BitBE(file, element_info[element].change->trigger_element);
2647 putFile8Bit(file, element_info[element].change->explode);
2648 putFile8Bit(file, element_info[element].change->use_content);
2649 putFile8Bit(file, element_info[element].change->only_complete);
2650 putFile8Bit(file, element_info[element].change->use_random_change);
2652 putFile8Bit(file, element_info[element].change->random);
2653 putFile8Bit(file, element_info[element].change->power);
2655 for (y = 0; y < 3; y++)
2656 for (x = 0; x < 3; x++)
2657 putFile16BitBE(file, element_info[element].change->content[x][y]);
2659 putFile8Bit(file, element_info[element].slippery_type);
2661 /* some free bytes for future properties and padding */
2662 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2669 if (check != num_changed_custom_elements) /* should not happen */
2670 Error(ERR_WARN, "inconsistent number of custom element properties");
2674 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2676 struct ElementInfo *ei = &element_info[element];
2679 putFile16BitBE(file, element);
2681 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2682 putFile8Bit(file, ei->description[i]);
2684 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2685 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2687 putFile8Bit(file, ei->num_change_pages);
2689 /* some free bytes for future base property values and padding */
2690 WriteUnusedBytesToFile(file, 5);
2692 /* write custom property values */
2694 putFile8Bit(file, ei->use_gfx_element);
2695 putFile16BitBE(file, ei->gfx_element);
2697 putFile8Bit(file, ei->collect_score);
2698 putFile8Bit(file, ei->collect_count);
2700 putFile16BitBE(file, ei->push_delay_fixed);
2701 putFile16BitBE(file, ei->push_delay_random);
2702 putFile16BitBE(file, ei->move_delay_fixed);
2703 putFile16BitBE(file, ei->move_delay_random);
2705 /* bits 0 - 15 of "move_pattern" ... */
2706 putFile16BitBE(file, ei->move_pattern & 0xffff);
2707 putFile8Bit(file, ei->move_direction_initial);
2708 putFile8Bit(file, ei->move_stepsize);
2710 putFile8Bit(file, ei->slippery_type);
2712 for (y = 0; y < 3; y++)
2713 for (x = 0; x < 3; x++)
2714 putFile16BitBE(file, ei->content[x][y]);
2716 putFile16BitBE(file, ei->move_enter_element);
2717 putFile16BitBE(file, ei->move_leave_element);
2718 putFile8Bit(file, ei->move_leave_type);
2720 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2721 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2723 putFile8Bit(file, ei->access_direction);
2725 putFile8Bit(file, ei->explosion_delay);
2726 putFile8Bit(file, ei->ignition_delay);
2728 /* some free bytes for future custom property values and padding */
2729 WriteUnusedBytesToFile(file, 2);
2731 /* write change property values */
2733 for (i = 0; i < ei->num_change_pages; i++)
2735 struct ElementChangeInfo *change = &ei->change_page[i];
2737 putFile32BitBE(file, change->events);
2739 putFile16BitBE(file, change->target_element);
2741 putFile16BitBE(file, change->delay_fixed);
2742 putFile16BitBE(file, change->delay_random);
2743 putFile16BitBE(file, change->delay_frames);
2745 putFile16BitBE(file, change->trigger_element);
2747 putFile8Bit(file, change->explode);
2748 putFile8Bit(file, change->use_content);
2749 putFile8Bit(file, change->only_complete);
2750 putFile8Bit(file, change->use_random_change);
2752 putFile8Bit(file, change->random);
2753 putFile8Bit(file, change->power);
2755 for (y = 0; y < 3; y++)
2756 for (x = 0; x < 3; x++)
2757 putFile16BitBE(file, change->content[x][y]);
2759 putFile8Bit(file, change->can_change);
2761 putFile8Bit(file, change->trigger_side);
2764 putFile8Bit(file, change->trigger_player);
2765 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
2766 log_2(change->trigger_page)));
2768 /* some free bytes for future change property values and padding */
2769 WriteUnusedBytesToFile(file, 6);
2773 /* some free bytes for future change property values and padding */
2774 WriteUnusedBytesToFile(file, 8);
2779 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2781 struct ElementInfo *ei = &element_info[element];
2782 struct ElementGroupInfo *group = ei->group;
2785 putFile16BitBE(file, element);
2787 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2788 putFile8Bit(file, ei->description[i]);
2790 putFile8Bit(file, group->num_elements);
2792 putFile8Bit(file, ei->use_gfx_element);
2793 putFile16BitBE(file, ei->gfx_element);
2795 putFile8Bit(file, group->choice_mode);
2797 /* some free bytes for future values and padding */
2798 WriteUnusedBytesToFile(file, 3);
2800 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2801 putFile16BitBE(file, group->element[i]);
2804 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2806 int body_chunk_size;
2810 if (!(file = fopen(filename, MODE_WRITE)))
2812 Error(ERR_WARN, "cannot save level file '%s'", filename);
2816 level->file_version = FILE_VERSION_ACTUAL;
2817 level->game_version = GAME_VERSION_ACTUAL;
2819 /* check level field for 16-bit elements */
2820 level->encoding_16bit_field = FALSE;
2821 for (y = 0; y < level->fieldy; y++)
2822 for (x = 0; x < level->fieldx; x++)
2823 if (level->field[x][y] > 255)
2824 level->encoding_16bit_field = TRUE;
2826 /* check yamyam content for 16-bit elements */
2827 level->encoding_16bit_yamyam = FALSE;
2828 for (i = 0; i < level->num_yamyam_contents; i++)
2829 for (y = 0; y < 3; y++)
2830 for (x = 0; x < 3; x++)
2831 if (level->yamyam_content[i][x][y] > 255)
2832 level->encoding_16bit_yamyam = TRUE;
2834 /* check amoeba content for 16-bit elements */
2835 level->encoding_16bit_amoeba = FALSE;
2836 if (level->amoeba_content > 255)
2837 level->encoding_16bit_amoeba = TRUE;
2839 /* calculate size of "BODY" chunk */
2841 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2843 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2844 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2846 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2847 SaveLevel_VERS(file, level);
2849 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2850 SaveLevel_HEAD(file, level);
2852 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2853 SaveLevel_AUTH(file, level);
2855 putFileChunkBE(file, "BODY", body_chunk_size);
2856 SaveLevel_BODY(file, level);
2858 if (level->encoding_16bit_yamyam ||
2859 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2861 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2862 SaveLevel_CNT2(file, level, EL_YAMYAM);
2865 if (level->encoding_16bit_amoeba)
2867 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2868 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2871 /* check for envelope content */
2872 for (i = 0; i < 4; i++)
2874 if (strlen(level->envelope_text[i]) > 0)
2876 int envelope_len = strlen(level->envelope_text[i]) + 1;
2878 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2879 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2883 /* check for non-default custom elements (unless using template level) */
2884 if (!level->use_custom_template)
2886 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2888 int element = EL_CUSTOM_START + i;
2890 if (element_info[element].modified_settings)
2892 int num_change_pages = element_info[element].num_change_pages;
2894 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2895 SaveLevel_CUS4(file, level, element);
2900 /* check for non-default group elements (unless using template level) */
2901 if (!level->use_custom_template)
2903 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2905 int element = EL_GROUP_START + i;
2907 if (element_info[element].modified_settings)
2909 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2910 SaveLevel_GRP1(file, level, element);
2917 SetFilePermissions(filename, PERMS_PRIVATE);
2920 void SaveLevel(int nr)
2922 char *filename = getDefaultLevelFilename(nr);
2924 SaveLevelFromFilename(&level, filename);
2927 void SaveLevelTemplate()
2929 char *filename = getDefaultLevelFilename(-1);
2931 SaveLevelFromFilename(&level, filename);
2934 void DumpLevel(struct LevelInfo *level)
2936 if (level->no_valid_file)
2938 Error(ERR_WARN, "cannot dump -- no valid level file found");
2943 printf_line("-", 79);
2944 printf("Level xxx (file version %08d, game version %08d)\n",
2945 level->file_version, level->game_version);
2946 printf_line("-", 79);
2948 printf("Level author: '%s'\n", level->author);
2949 printf("Level title: '%s'\n", level->name);
2951 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2953 printf("Level time: %d seconds\n", level->time);
2954 printf("Gems needed: %d\n", level->gems_needed);
2956 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2957 printf("Time for wheel: %d seconds\n", level->time_wheel);
2958 printf("Time for light: %d seconds\n", level->time_light);
2959 printf("Time for timegate: %d seconds\n", level->time_timegate);
2961 printf("Amoeba speed: %d\n", level->amoeba_speed);
2963 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2964 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2965 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2966 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2967 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2968 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
2969 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
2971 printf_line("-", 79);
2975 /* ========================================================================= */
2976 /* tape file functions */
2977 /* ========================================================================= */
2979 static void setTapeInfoToDefaults()
2983 /* always start with reliable default values (empty tape) */
2986 /* default values (also for pre-1.2 tapes) with only the first player */
2987 tape.player_participates[0] = TRUE;
2988 for (i = 1; i < MAX_PLAYERS; i++)
2989 tape.player_participates[i] = FALSE;
2991 /* at least one (default: the first) player participates in every tape */
2992 tape.num_participating_players = 1;
2994 tape.level_nr = level_nr;
2996 tape.changed = FALSE;
2998 tape.recording = FALSE;
2999 tape.playing = FALSE;
3000 tape.pausing = FALSE;
3002 tape.no_valid_file = FALSE;
3005 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3007 tape->file_version = getFileVersion(file);
3008 tape->game_version = getFileVersion(file);
3013 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3017 tape->random_seed = getFile32BitBE(file);
3018 tape->date = getFile32BitBE(file);
3019 tape->length = getFile32BitBE(file);
3021 /* read header fields that are new since version 1.2 */
3022 if (tape->file_version >= FILE_VERSION_1_2)
3024 byte store_participating_players = getFile8Bit(file);
3027 /* since version 1.2, tapes store which players participate in the tape */
3028 tape->num_participating_players = 0;
3029 for (i = 0; i < MAX_PLAYERS; i++)
3031 tape->player_participates[i] = FALSE;
3033 if (store_participating_players & (1 << i))
3035 tape->player_participates[i] = TRUE;
3036 tape->num_participating_players++;
3040 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3042 engine_version = getFileVersion(file);
3043 if (engine_version > 0)
3044 tape->engine_version = engine_version;
3046 tape->engine_version = tape->game_version;
3052 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3054 int level_identifier_size;
3057 level_identifier_size = getFile16BitBE(file);
3059 tape->level_identifier =
3060 checked_realloc(tape->level_identifier, level_identifier_size);
3062 for (i = 0; i < level_identifier_size; i++)
3063 tape->level_identifier[i] = getFile8Bit(file);
3065 tape->level_nr = getFile16BitBE(file);
3067 chunk_size = 2 + level_identifier_size + 2;
3072 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3075 int chunk_size_expected =
3076 (tape->num_participating_players + 1) * tape->length;
3078 if (chunk_size_expected != chunk_size)
3080 ReadUnusedBytesFromFile(file, chunk_size);
3081 return chunk_size_expected;
3084 for (i = 0; i < tape->length; i++)
3086 if (i >= MAX_TAPELEN)
3089 for (j = 0; j < MAX_PLAYERS; j++)
3091 tape->pos[i].action[j] = MV_NO_MOVING;
3093 if (tape->player_participates[j])
3094 tape->pos[i].action[j] = getFile8Bit(file);
3097 tape->pos[i].delay = getFile8Bit(file);
3099 if (tape->file_version == FILE_VERSION_1_0)
3101 /* eliminate possible diagonal moves in old tapes */
3102 /* this is only for backward compatibility */
3104 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3105 byte action = tape->pos[i].action[0];
3106 int k, num_moves = 0;
3108 for (k = 0; k<4; k++)
3110 if (action & joy_dir[k])
3112 tape->pos[i + num_moves].action[0] = joy_dir[k];
3114 tape->pos[i + num_moves].delay = 0;
3123 tape->length += num_moves;
3126 else if (tape->file_version < FILE_VERSION_2_0)
3128 /* convert pre-2.0 tapes to new tape format */
3130 if (tape->pos[i].delay > 1)
3133 tape->pos[i + 1] = tape->pos[i];
3134 tape->pos[i + 1].delay = 1;
3137 for (j = 0; j < MAX_PLAYERS; j++)
3138 tape->pos[i].action[j] = MV_NO_MOVING;
3139 tape->pos[i].delay--;
3150 if (i != tape->length)
3151 chunk_size = (tape->num_participating_players + 1) * i;
3156 void LoadTapeFromFilename(char *filename)
3158 char cookie[MAX_LINE_LEN];
3159 char chunk_name[CHUNK_ID_LEN + 1];
3163 /* always start with reliable default values */
3164 setTapeInfoToDefaults();
3166 if (!(file = fopen(filename, MODE_READ)))
3168 tape.no_valid_file = TRUE;
3171 Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
3177 getFileChunkBE(file, chunk_name, NULL);
3178 if (strcmp(chunk_name, "RND1") == 0)
3180 getFile32BitBE(file); /* not used */
3182 getFileChunkBE(file, chunk_name, NULL);
3183 if (strcmp(chunk_name, "TAPE") != 0)
3185 tape.no_valid_file = TRUE;
3187 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3192 else /* check for pre-2.0 file format with cookie string */
3194 strcpy(cookie, chunk_name);
3195 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3196 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3197 cookie[strlen(cookie) - 1] = '\0';
3199 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3201 tape.no_valid_file = TRUE;
3203 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3208 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3210 tape.no_valid_file = TRUE;
3212 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3217 /* pre-2.0 tape files have no game version, so use file version here */
3218 tape.game_version = tape.file_version;
3221 if (tape.file_version < FILE_VERSION_1_2)
3223 /* tape files from versions before 1.2.0 without chunk structure */
3224 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3225 LoadTape_BODY(file, 2 * tape.length, &tape);
3233 int (*loader)(FILE *, int, struct TapeInfo *);
3237 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3238 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3239 { "INFO", -1, LoadTape_INFO },
3240 { "BODY", -1, LoadTape_BODY },
3244 while (getFileChunkBE(file, chunk_name, &chunk_size))
3248 while (chunk_info[i].name != NULL &&
3249 strcmp(chunk_name, chunk_info[i].name) != 0)
3252 if (chunk_info[i].name == NULL)
3254 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3255 chunk_name, filename);
3256 ReadUnusedBytesFromFile(file, chunk_size);
3258 else if (chunk_info[i].size != -1 &&
3259 chunk_info[i].size != chunk_size)
3261 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3262 chunk_size, chunk_name, filename);
3263 ReadUnusedBytesFromFile(file, chunk_size);
3267 /* call function to load this tape chunk */
3268 int chunk_size_expected =
3269 (chunk_info[i].loader)(file, chunk_size, &tape);
3271 /* the size of some chunks cannot be checked before reading other
3272 chunks first (like "HEAD" and "BODY") that contain some header
3273 information, so check them here */
3274 if (chunk_size_expected != chunk_size)
3276 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3277 chunk_size, chunk_name, filename);
3285 tape.length_seconds = GetTapeLength();
3288 printf("::: tape game version: %d\n", tape.game_version);
3289 printf("::: tape engine version: %d\n", tape.engine_version);
3293 void LoadTape(int nr)
3295 char *filename = getTapeFilename(nr);
3297 LoadTapeFromFilename(filename);
3300 void LoadSolutionTape(int nr)
3302 char *filename = getSolutionTapeFilename(nr);
3304 LoadTapeFromFilename(filename);
3307 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3309 putFileVersion(file, tape->file_version);
3310 putFileVersion(file, tape->game_version);
3313 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3316 byte store_participating_players = 0;
3318 /* set bits for participating players for compact storage */
3319 for (i = 0; i < MAX_PLAYERS; i++)
3320 if (tape->player_participates[i])
3321 store_participating_players |= (1 << i);
3323 putFile32BitBE(file, tape->random_seed);
3324 putFile32BitBE(file, tape->date);
3325 putFile32BitBE(file, tape->length);
3327 putFile8Bit(file, store_participating_players);
3329 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3330 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3332 putFileVersion(file, tape->engine_version);
3335 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3337 int level_identifier_size = strlen(tape->level_identifier) + 1;
3340 putFile16BitBE(file, level_identifier_size);
3342 for (i = 0; i < level_identifier_size; i++)
3343 putFile8Bit(file, tape->level_identifier[i]);
3345 putFile16BitBE(file, tape->level_nr);
3348 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3352 for (i = 0; i < tape->length; i++)
3354 for (j = 0; j < MAX_PLAYERS; j++)
3355 if (tape->player_participates[j])
3356 putFile8Bit(file, tape->pos[i].action[j]);
3358 putFile8Bit(file, tape->pos[i].delay);
3362 void SaveTape(int nr)
3364 char *filename = getTapeFilename(nr);
3366 boolean new_tape = TRUE;
3367 int num_participating_players = 0;
3368 int info_chunk_size;
3369 int body_chunk_size;
3372 InitTapeDirectory(leveldir_current->subdir);
3374 /* if a tape still exists, ask to overwrite it */
3375 if (access(filename, F_OK) == 0)
3378 if (!Request("Replace old tape ?", REQ_ASK))
3382 if (!(file = fopen(filename, MODE_WRITE)))
3384 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3388 tape.file_version = FILE_VERSION_ACTUAL;
3389 tape.game_version = GAME_VERSION_ACTUAL;
3391 /* count number of participating players */
3392 for (i = 0; i < MAX_PLAYERS; i++)
3393 if (tape.player_participates[i])
3394 num_participating_players++;
3396 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3397 body_chunk_size = (num_participating_players + 1) * tape.length;
3399 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3400 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3402 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3403 SaveTape_VERS(file, &tape);
3405 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3406 SaveTape_HEAD(file, &tape);
3408 putFileChunkBE(file, "INFO", info_chunk_size);
3409 SaveTape_INFO(file, &tape);
3411 putFileChunkBE(file, "BODY", body_chunk_size);
3412 SaveTape_BODY(file, &tape);
3416 SetFilePermissions(filename, PERMS_PRIVATE);
3418 tape.changed = FALSE;
3421 Request("tape saved !", REQ_CONFIRM);
3424 void DumpTape(struct TapeInfo *tape)
3429 if (tape->no_valid_file)
3431 Error(ERR_WARN, "cannot dump -- no valid tape file found");
3436 if (TAPE_IS_EMPTY(*tape))
3438 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3444 printf_line("-", 79);
3445 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3446 tape->level_nr, tape->file_version, tape->game_version);
3447 printf("Level series identifier: '%s'\n", tape->level_identifier);
3448 printf_line("-", 79);
3450 for (i = 0; i < tape->length; i++)
3452 if (i >= MAX_TAPELEN)
3455 printf("%03d: ", i);
3457 for (j = 0; j < MAX_PLAYERS; j++)
3459 if (tape->player_participates[j])
3461 int action = tape->pos[i].action[j];
3463 printf("%d:%02x ", j, action);
3464 printf("[%c%c%c%c|%c%c] - ",
3465 (action & JOY_LEFT ? '<' : ' '),
3466 (action & JOY_RIGHT ? '>' : ' '),
3467 (action & JOY_UP ? '^' : ' '),
3468 (action & JOY_DOWN ? 'v' : ' '),
3469 (action & JOY_BUTTON_1 ? '1' : ' '),
3470 (action & JOY_BUTTON_2 ? '2' : ' '));
3474 printf("(%03d)\n", tape->pos[i].delay);
3477 printf_line("-", 79);
3481 /* ========================================================================= */
3482 /* score file functions */
3483 /* ========================================================================= */
3485 void LoadScore(int nr)
3488 char *filename = getScoreFilename(nr);
3489 char cookie[MAX_LINE_LEN];
3490 char line[MAX_LINE_LEN];
3494 /* always start with reliable default values */
3495 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3497 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3498 highscore[i].Score = 0;
3501 if (!(file = fopen(filename, MODE_READ)))
3504 /* check file identifier */
3505 fgets(cookie, MAX_LINE_LEN, file);
3506 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3507 cookie[strlen(cookie) - 1] = '\0';
3509 if (!checkCookieString(cookie, SCORE_COOKIE))
3511 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3516 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3518 fscanf(file, "%d", &highscore[i].Score);
3519 fgets(line, MAX_LINE_LEN, file);
3521 if (line[strlen(line) - 1] == '\n')
3522 line[strlen(line) - 1] = '\0';
3524 for (line_ptr = line; *line_ptr; line_ptr++)
3526 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3528 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3529 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3538 void SaveScore(int nr)
3541 char *filename = getScoreFilename(nr);
3544 InitScoreDirectory(leveldir_current->subdir);
3546 if (!(file = fopen(filename, MODE_WRITE)))
3548 Error(ERR_WARN, "cannot save score for level %d", nr);
3552 fprintf(file, "%s\n\n", SCORE_COOKIE);
3554 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3555 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3559 SetFilePermissions(filename, PERMS_PUBLIC);
3563 /* ========================================================================= */
3564 /* setup file functions */
3565 /* ========================================================================= */
3567 #define TOKEN_STR_PLAYER_PREFIX "player_"
3570 #define SETUP_TOKEN_PLAYER_NAME 0
3571 #define SETUP_TOKEN_SOUND 1
3572 #define SETUP_TOKEN_SOUND_LOOPS 2
3573 #define SETUP_TOKEN_SOUND_MUSIC 3
3574 #define SETUP_TOKEN_SOUND_SIMPLE 4
3575 #define SETUP_TOKEN_TOONS 5
3576 #define SETUP_TOKEN_SCROLL_DELAY 6
3577 #define SETUP_TOKEN_SOFT_SCROLLING 7
3578 #define SETUP_TOKEN_FADING 8
3579 #define SETUP_TOKEN_AUTORECORD 9
3580 #define SETUP_TOKEN_QUICK_DOORS 10
3581 #define SETUP_TOKEN_TEAM_MODE 11
3582 #define SETUP_TOKEN_HANDICAP 12
3583 #define SETUP_TOKEN_TIME_LIMIT 13
3584 #define SETUP_TOKEN_FULLSCREEN 14
3585 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3586 #define SETUP_TOKEN_GRAPHICS_SET 16
3587 #define SETUP_TOKEN_SOUNDS_SET 17
3588 #define SETUP_TOKEN_MUSIC_SET 18
3589 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3590 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3591 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3593 #define NUM_GLOBAL_SETUP_TOKENS 22
3596 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3597 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3598 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3599 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3600 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3601 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3602 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3603 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3604 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3605 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3606 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3607 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3609 #define NUM_EDITOR_SETUP_TOKENS 12
3611 /* shortcut setup */
3612 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3613 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3614 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3616 #define NUM_SHORTCUT_SETUP_TOKENS 3
3619 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3620 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3621 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3622 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3623 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3624 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3625 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3626 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3627 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3628 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3629 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3630 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3631 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3632 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3633 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3634 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3636 #define NUM_PLAYER_SETUP_TOKENS 16
3639 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3640 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3642 #define NUM_SYSTEM_SETUP_TOKENS 2
3645 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3647 #define NUM_OPTIONS_SETUP_TOKENS 1
3650 static struct SetupInfo si;
3651 static struct SetupEditorInfo sei;
3652 static struct SetupShortcutInfo ssi;
3653 static struct SetupInputInfo sii;
3654 static struct SetupSystemInfo syi;
3655 static struct OptionInfo soi;
3657 static struct TokenInfo global_setup_tokens[] =
3659 { TYPE_STRING, &si.player_name, "player_name" },
3660 { TYPE_SWITCH, &si.sound, "sound" },
3661 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3662 { TYPE_SWITCH, &si.sound_music, "background_music" },
3663 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3664 { TYPE_SWITCH, &si.toons, "toons" },
3665 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3666 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3667 { TYPE_SWITCH, &si.fading, "screen_fading" },
3668 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3669 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3670 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3671 { TYPE_SWITCH, &si.handicap, "handicap" },
3672 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3673 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3674 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3675 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3676 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3677 { TYPE_STRING, &si.music_set, "music_set" },
3678 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3679 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3680 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3683 static struct TokenInfo editor_setup_tokens[] =
3685 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3686 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3687 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3688 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3689 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3690 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3691 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3692 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3693 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3694 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3695 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3696 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3699 static struct TokenInfo shortcut_setup_tokens[] =
3701 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3702 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3703 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3706 static struct TokenInfo player_setup_tokens[] =
3708 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3709 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3710 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3711 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3712 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3713 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3714 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3715 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3716 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3717 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3718 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3719 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3720 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3721 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3722 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3723 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3726 static struct TokenInfo system_setup_tokens[] =
3728 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3729 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3732 static struct TokenInfo options_setup_tokens[] =
3734 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3737 static char *get_corrected_login_name(char *login_name)
3739 /* needed because player name must be a fixed length string */
3740 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3742 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3743 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3745 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3746 if (strchr(login_name_new, ' '))
3747 *strchr(login_name_new, ' ') = '\0';
3749 return login_name_new;
3752 static void setSetupInfoToDefaults(struct SetupInfo *si)
3756 si->player_name = get_corrected_login_name(getLoginName());
3759 si->sound_loops = TRUE;
3760 si->sound_music = TRUE;
3761 si->sound_simple = TRUE;
3763 si->double_buffering = TRUE;
3764 si->direct_draw = !si->double_buffering;
3765 si->scroll_delay = TRUE;
3766 si->soft_scrolling = TRUE;
3768 si->autorecord = TRUE;
3769 si->quick_doors = FALSE;
3770 si->team_mode = FALSE;
3771 si->handicap = TRUE;
3772 si->time_limit = TRUE;
3773 si->fullscreen = FALSE;
3774 si->ask_on_escape = TRUE;
3776 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3777 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3778 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3779 si->override_level_graphics = FALSE;
3780 si->override_level_sounds = FALSE;
3781 si->override_level_music = FALSE;
3783 si->editor.el_boulderdash = TRUE;
3784 si->editor.el_emerald_mine = TRUE;
3785 si->editor.el_more = TRUE;
3786 si->editor.el_sokoban = TRUE;
3787 si->editor.el_supaplex = TRUE;
3788 si->editor.el_diamond_caves = TRUE;
3789 si->editor.el_dx_boulderdash = TRUE;
3790 si->editor.el_chars = TRUE;
3791 si->editor.el_custom = TRUE;
3792 si->editor.el_custom_more = FALSE;
3794 si->editor.el_headlines = TRUE;
3795 si->editor.el_user_defined = FALSE;
3797 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3798 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3799 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3801 for (i = 0; i < MAX_PLAYERS; i++)
3803 si->input[i].use_joystick = FALSE;
3804 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3805 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3806 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3807 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3808 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3809 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3810 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3811 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3812 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3813 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3814 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3815 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3816 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3817 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3818 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3821 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3822 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3824 si->options.verbose = FALSE;
3827 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3831 if (!setup_file_hash)
3836 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3837 setSetupInfo(global_setup_tokens, i,
3838 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3843 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3844 setSetupInfo(editor_setup_tokens, i,
3845 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3848 /* shortcut setup */
3849 ssi = setup.shortcut;
3850 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3851 setSetupInfo(shortcut_setup_tokens, i,
3852 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3853 setup.shortcut = ssi;
3856 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3860 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3862 sii = setup.input[pnr];
3863 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3865 char full_token[100];
3867 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3868 setSetupInfo(player_setup_tokens, i,
3869 getHashEntry(setup_file_hash, full_token));
3871 setup.input[pnr] = sii;
3876 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3877 setSetupInfo(system_setup_tokens, i,
3878 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3882 soi = setup.options;
3883 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3884 setSetupInfo(options_setup_tokens, i,
3885 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3886 setup.options = soi;
3891 char *filename = getSetupFilename();
3892 SetupFileHash *setup_file_hash = NULL;
3894 /* always start with reliable default values */
3895 setSetupInfoToDefaults(&setup);
3897 setup_file_hash = loadSetupFileHash(filename);
3899 if (setup_file_hash)
3901 char *player_name_new;
3903 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3904 decodeSetupFileHash(setup_file_hash);
3906 setup.direct_draw = !setup.double_buffering;
3908 freeSetupFileHash(setup_file_hash);
3910 /* needed to work around problems with fixed length strings */
3911 player_name_new = get_corrected_login_name(setup.player_name);
3912 free(setup.player_name);
3913 setup.player_name = player_name_new;
3916 Error(ERR_WARN, "using default setup values");
3921 char *filename = getSetupFilename();
3925 InitUserDataDirectory();
3927 if (!(file = fopen(filename, MODE_WRITE)))
3929 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3933 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3934 getCookie("SETUP")));
3935 fprintf(file, "\n");
3939 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3941 /* just to make things nicer :) */
3942 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3943 i == SETUP_TOKEN_GRAPHICS_SET)
3944 fprintf(file, "\n");
3946 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3951 fprintf(file, "\n");
3952 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3953 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3955 /* shortcut setup */
3956 ssi = setup.shortcut;
3957 fprintf(file, "\n");
3958 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3959 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3962 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3966 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3967 fprintf(file, "\n");
3969 sii = setup.input[pnr];
3970 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3971 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3976 fprintf(file, "\n");
3977 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3978 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3981 soi = setup.options;
3982 fprintf(file, "\n");
3983 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3984 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3988 SetFilePermissions(filename, PERMS_PRIVATE);
3991 void LoadCustomElementDescriptions()
3993 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3994 SetupFileHash *setup_file_hash;
3997 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3999 if (element_info[i].custom_description != NULL)
4001 free(element_info[i].custom_description);
4002 element_info[i].custom_description = NULL;
4006 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4009 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4011 char *token = getStringCat2(element_info[i].token_name, ".name");
4012 char *value = getHashEntry(setup_file_hash, token);
4015 element_info[i].custom_description = getStringCopy(value);
4020 freeSetupFileHash(setup_file_hash);
4023 void LoadSpecialMenuDesignSettings()
4025 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4026 SetupFileHash *setup_file_hash;
4029 /* always start with reliable default values from default config */
4030 for (i = 0; image_config_vars[i].token != NULL; i++)
4031 for (j = 0; image_config[j].token != NULL; j++)
4032 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4033 *image_config_vars[i].value =
4034 get_auto_parameter_value(image_config_vars[i].token,
4035 image_config[j].value);
4037 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4040 /* special case: initialize with default values that may be overwritten */
4041 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4043 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4044 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4045 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4047 if (value_x != NULL)
4048 menu.draw_xoffset[i] = get_integer_from_string(value_x);
4049 if (value_y != NULL)
4050 menu.draw_yoffset[i] = get_integer_from_string(value_y);
4051 if (list_size != NULL)
4052 menu.list_size[i] = get_integer_from_string(list_size);
4055 /* read (and overwrite with) values that may be specified in config file */
4056 for (i = 0; image_config_vars[i].token != NULL; i++)
4058 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4061 *image_config_vars[i].value =
4062 get_auto_parameter_value(image_config_vars[i].token, value);
4065 freeSetupFileHash(setup_file_hash);
4068 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4070 char *filename = getEditorSetupFilename();
4071 SetupFileList *setup_file_list, *list;
4072 SetupFileHash *element_hash;
4073 int num_unknown_tokens = 0;
4076 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4079 element_hash = newSetupFileHash();
4081 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4082 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4084 /* determined size may be larger than needed (due to unknown elements) */
4086 for (list = setup_file_list; list != NULL; list = list->next)
4089 /* add space for up to 3 more elements for padding that may be needed */
4092 *elements = checked_malloc(*num_elements * sizeof(int));
4095 for (list = setup_file_list; list != NULL; list = list->next)
4097 char *value = getHashEntry(element_hash, list->token);
4101 (*elements)[(*num_elements)++] = atoi(value);
4105 if (num_unknown_tokens == 0)
4107 Error(ERR_RETURN_LINE, "-");
4108 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4109 Error(ERR_RETURN, "- config file: '%s'", filename);
4111 num_unknown_tokens++;
4114 Error(ERR_RETURN, "- token: '%s'", list->token);
4118 if (num_unknown_tokens > 0)
4119 Error(ERR_RETURN_LINE, "-");
4121 while (*num_elements % 4) /* pad with empty elements, if needed */
4122 (*elements)[(*num_elements)++] = EL_EMPTY;
4124 freeSetupFileList(setup_file_list);
4125 freeSetupFileHash(element_hash);
4129 for (i = 0; i < *num_elements; i++)
4130 printf("editor: element '%s' [%d]\n",
4131 element_info[(*elements)[i]].token_name, (*elements)[i]);
4135 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4138 SetupFileHash *setup_file_hash = NULL;
4139 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4140 char *filename_music, *filename_prefix, *filename_info;
4146 token_to_value_ptr[] =
4148 { "title_header", &tmp_music_file_info.title_header },
4149 { "artist_header", &tmp_music_file_info.artist_header },
4150 { "album_header", &tmp_music_file_info.album_header },
4151 { "year_header", &tmp_music_file_info.year_header },
4153 { "title", &tmp_music_file_info.title },
4154 { "artist", &tmp_music_file_info.artist },
4155 { "album", &tmp_music_file_info.album },
4156 { "year", &tmp_music_file_info.year },
4162 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4163 getCustomMusicFilename(basename));
4165 if (filename_music == NULL)
4168 /* ---------- try to replace file extension ---------- */
4170 filename_prefix = getStringCopy(filename_music);
4171 if (strrchr(filename_prefix, '.') != NULL)
4172 *strrchr(filename_prefix, '.') = '\0';
4173 filename_info = getStringCat2(filename_prefix, ".txt");
4176 printf("trying to load file '%s'...\n", filename_info);
4179 if (fileExists(filename_info))
4180 setup_file_hash = loadSetupFileHash(filename_info);
4182 free(filename_prefix);
4183 free(filename_info);
4185 if (setup_file_hash == NULL)
4187 /* ---------- try to add file extension ---------- */
4189 filename_prefix = getStringCopy(filename_music);
4190 filename_info = getStringCat2(filename_prefix, ".txt");
4193 printf("trying to load file '%s'...\n", filename_info);
4196 if (fileExists(filename_info))
4197 setup_file_hash = loadSetupFileHash(filename_info);
4199 free(filename_prefix);
4200 free(filename_info);
4203 if (setup_file_hash == NULL)
4206 /* ---------- music file info found ---------- */
4208 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4210 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4212 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4214 *token_to_value_ptr[i].value_ptr =
4215 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4218 tmp_music_file_info.basename = getStringCopy(basename);
4219 tmp_music_file_info.music = music;
4220 tmp_music_file_info.is_sound = is_sound;
4222 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4223 *new_music_file_info = tmp_music_file_info;
4225 return new_music_file_info;
4228 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4230 return get_music_file_info_ext(basename, music, FALSE);
4233 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4235 return get_music_file_info_ext(basename, sound, TRUE);
4238 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4239 char *basename, boolean is_sound)
4241 for (; list != NULL; list = list->next)
4242 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4248 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4250 return music_info_listed_ext(list, basename, FALSE);
4253 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4255 return music_info_listed_ext(list, basename, TRUE);
4258 void LoadMusicInfo()
4260 char *music_directory = getCustomMusicDirectory();
4261 int num_music = getMusicListSize();
4262 int num_music_noconf = 0;
4263 int num_sounds = getSoundListSize();
4265 struct dirent *dir_entry;
4266 struct FileInfo *music, *sound;
4267 struct MusicFileInfo *next, **new;
4270 while (music_file_info != NULL)
4272 next = music_file_info->next;
4274 checked_free(music_file_info->basename);
4276 checked_free(music_file_info->title_header);
4277 checked_free(music_file_info->artist_header);
4278 checked_free(music_file_info->album_header);
4279 checked_free(music_file_info->year_header);
4281 checked_free(music_file_info->title);
4282 checked_free(music_file_info->artist);
4283 checked_free(music_file_info->album);
4284 checked_free(music_file_info->year);
4286 free(music_file_info);
4288 music_file_info = next;
4291 new = &music_file_info;
4294 printf("::: num_music == %d\n", num_music);
4297 for (i = 0; i < num_music; i++)
4299 music = getMusicListEntry(i);
4302 printf("::: %d [%08x]\n", i, music->filename);
4305 if (music->filename == NULL)
4308 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4311 /* a configured file may be not recognized as music */
4312 if (!FileIsMusic(music->filename))
4316 printf("::: -> '%s' (configured)\n", music->filename);
4319 if (!music_info_listed(music_file_info, music->filename))
4321 *new = get_music_file_info(music->filename, i);
4323 new = &(*new)->next;
4327 if ((dir = opendir(music_directory)) == NULL)
4329 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4333 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4335 char *basename = dir_entry->d_name;
4336 boolean music_already_used = FALSE;
4339 /* skip all music files that are configured in music config file */
4340 for (i = 0; i < num_music; i++)
4342 music = getMusicListEntry(i);
4344 if (music->filename == NULL)
4347 if (strcmp(basename, music->filename) == 0)
4349 music_already_used = TRUE;
4354 if (music_already_used)
4357 if (!FileIsMusic(basename))
4361 printf("::: -> '%s' (found in directory)\n", basename);
4364 if (!music_info_listed(music_file_info, basename))
4366 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4368 new = &(*new)->next;
4376 for (i = 0; i < num_sounds; i++)
4378 sound = getSoundListEntry(i);
4380 if (sound->filename == NULL)
4383 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4386 /* a configured file may be not recognized as sound */
4387 if (!FileIsSound(sound->filename))
4391 printf("::: -> '%s' (configured)\n", sound->filename);
4394 if (!sound_info_listed(music_file_info, sound->filename))
4396 *new = get_sound_file_info(sound->filename, i);
4398 new = &(*new)->next;
4404 for (next = music_file_info; next != NULL; next = next->next)
4405 printf("::: title == '%s'\n", next->title);
4409 void add_helpanim_entry(int element, int action, int direction, int delay,
4410 int *num_list_entries)
4412 struct HelpAnimInfo *new_list_entry;
4413 (*num_list_entries)++;
4416 checked_realloc(helpanim_info,
4417 *num_list_entries * sizeof(struct HelpAnimInfo));
4418 new_list_entry = &helpanim_info[*num_list_entries - 1];
4420 new_list_entry->element = element;
4421 new_list_entry->action = action;
4422 new_list_entry->direction = direction;
4423 new_list_entry->delay = delay;
4426 void print_unknown_token(char *filename, char *token, int token_nr)
4430 Error(ERR_RETURN_LINE, "-");
4431 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4432 Error(ERR_RETURN, "- config file: '%s'", filename);
4435 Error(ERR_RETURN, "- token: '%s'", token);
4438 void print_unknown_token_end(int token_nr)
4441 Error(ERR_RETURN_LINE, "-");
4444 void LoadHelpAnimInfo()
4446 char *filename = getHelpAnimFilename();
4447 SetupFileList *setup_file_list = NULL, *list;
4448 SetupFileHash *element_hash, *action_hash, *direction_hash;
4449 int num_list_entries = 0;
4450 int num_unknown_tokens = 0;
4453 if (fileExists(filename))
4454 setup_file_list = loadSetupFileList(filename);
4456 if (setup_file_list == NULL)
4458 /* use reliable default values from static configuration */
4459 SetupFileList *insert_ptr;
4461 insert_ptr = setup_file_list =
4462 newSetupFileList(helpanim_config[0].token,
4463 helpanim_config[0].value);
4465 for (i = 1; helpanim_config[i].token; i++)
4466 insert_ptr = addListEntry(insert_ptr,
4467 helpanim_config[i].token,
4468 helpanim_config[i].value);
4471 element_hash = newSetupFileHash();
4472 action_hash = newSetupFileHash();
4473 direction_hash = newSetupFileHash();
4475 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4476 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4478 for (i = 0; i < NUM_ACTIONS; i++)
4479 setHashEntry(action_hash, element_action_info[i].suffix,
4480 i_to_a(element_action_info[i].value));
4482 /* do not store direction index (bit) here, but direction value! */
4483 for (i = 0; i < NUM_DIRECTIONS; i++)
4484 setHashEntry(direction_hash, element_direction_info[i].suffix,
4485 i_to_a(1 << element_direction_info[i].value));
4487 for (list = setup_file_list; list != NULL; list = list->next)
4489 char *element_token, *action_token, *direction_token;
4490 char *element_value, *action_value, *direction_value;
4491 int delay = atoi(list->value);
4493 if (strcmp(list->token, "end") == 0)
4495 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4500 /* first try to break element into element/action/direction parts;
4501 if this does not work, also accept combined "element[.act][.dir]"
4502 elements (like "dynamite.active"), which are unique elements */
4504 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4506 element_value = getHashEntry(element_hash, list->token);
4507 if (element_value != NULL) /* element found */
4508 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4512 /* no further suffixes found -- this is not an element */
4513 print_unknown_token(filename, list->token, num_unknown_tokens++);
4519 /* token has format "<prefix>.<something>" */
4521 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4522 direction_token = action_token; /* ... or direction */
4524 element_token = getStringCopy(list->token);
4525 *strchr(element_token, '.') = '\0';
4527 element_value = getHashEntry(element_hash, element_token);
4529 if (element_value == NULL) /* this is no element */
4531 element_value = getHashEntry(element_hash, list->token);
4532 if (element_value != NULL) /* combined element found */
4533 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4536 print_unknown_token(filename, list->token, num_unknown_tokens++);
4538 free(element_token);
4543 action_value = getHashEntry(action_hash, action_token);
4545 if (action_value != NULL) /* action found */
4547 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4550 free(element_token);
4555 direction_value = getHashEntry(direction_hash, direction_token);
4557 if (direction_value != NULL) /* direction found */
4559 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4562 free(element_token);
4567 if (strchr(action_token + 1, '.') == NULL)
4569 /* no further suffixes found -- this is not an action nor direction */
4571 element_value = getHashEntry(element_hash, list->token);
4572 if (element_value != NULL) /* combined element found */
4573 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4576 print_unknown_token(filename, list->token, num_unknown_tokens++);
4578 free(element_token);
4583 /* token has format "<prefix>.<suffix>.<something>" */
4585 direction_token = strchr(action_token + 1, '.');
4587 action_token = getStringCopy(action_token);
4588 *strchr(action_token + 1, '.') = '\0';
4590 action_value = getHashEntry(action_hash, action_token);
4592 if (action_value == NULL) /* this is no action */
4594 element_value = getHashEntry(element_hash, list->token);
4595 if (element_value != NULL) /* combined element found */
4596 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4599 print_unknown_token(filename, list->token, num_unknown_tokens++);
4601 free(element_token);
4607 direction_value = getHashEntry(direction_hash, direction_token);
4609 if (direction_value != NULL) /* direction found */
4611 add_helpanim_entry(atoi(element_value), atoi(action_value),
4612 atoi(direction_value), delay, &num_list_entries);
4614 free(element_token);
4620 /* this is no direction */
4622 element_value = getHashEntry(element_hash, list->token);
4623 if (element_value != NULL) /* combined element found */
4624 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4627 print_unknown_token(filename, list->token, num_unknown_tokens++);
4629 free(element_token);
4633 print_unknown_token_end(num_unknown_tokens);
4635 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4636 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4638 freeSetupFileList(setup_file_list);
4639 freeSetupFileHash(element_hash);
4640 freeSetupFileHash(action_hash);
4641 freeSetupFileHash(direction_hash);
4645 for (i = 0; i < num_list_entries; i++)
4646 printf("::: %d, %d, %d => %d\n",
4647 helpanim_info[i].element,
4648 helpanim_info[i].action,
4649 helpanim_info[i].direction,
4650 helpanim_info[i].delay);
4654 void LoadHelpTextInfo()
4656 char *filename = getHelpTextFilename();
4659 if (helptext_info != NULL)
4661 freeSetupFileHash(helptext_info);
4662 helptext_info = NULL;
4665 if (fileExists(filename))
4666 helptext_info = loadSetupFileHash(filename);
4668 if (helptext_info == NULL)
4670 /* use reliable default values from static configuration */
4671 helptext_info = newSetupFileHash();
4673 for (i = 0; helptext_config[i].token; i++)
4674 setHashEntry(helptext_info,
4675 helptext_config[i].token,
4676 helptext_config[i].value);
4681 BEGIN_HASH_ITERATION(helptext_info, itr)
4683 printf("::: '%s' => '%s'\n",
4684 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4686 END_HASH_ITERATION(hash, itr)