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 0 /* 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"
53 /* ========================================================================= */
54 /* level file functions */
55 /* ========================================================================= */
57 void setElementChangePages(struct ElementInfo *ei, int change_pages)
59 int change_page_size = sizeof(struct ElementChangeInfo);
61 ei->num_change_pages = MAX(1, change_pages);
64 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
66 if (ei->current_change_page >= ei->num_change_pages)
67 ei->current_change_page = ei->num_change_pages - 1;
69 ei->change = &ei->change_page[ei->current_change_page];
72 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
76 change->can_change = FALSE;
78 change->events = CE_BITMASK_DEFAULT;
80 change->trigger_player = CH_PLAYER_ANY;
81 change->trigger_side = CH_SIDE_ANY;
82 change->trigger_page = CH_PAGE_ANY;
84 change->target_element = EL_EMPTY_SPACE;
86 change->delay_fixed = 0;
87 change->delay_random = 0;
88 change->delay_frames = 1;
90 change->trigger_element = EL_EMPTY_SPACE;
92 change->explode = FALSE;
93 change->use_target_content = FALSE;
94 change->only_if_complete = FALSE;
95 change->use_random_replace = FALSE;
96 change->random_percentage = 100;
97 change->replace_when = CP_WHEN_EMPTY;
99 for (x = 0; x < 3; x++)
100 for (y = 0; y < 3; y++)
101 change->target_content[x][y] = EL_EMPTY_SPACE;
103 change->direct_action = 0;
104 change->other_action = 0;
106 change->pre_change_function = NULL;
107 change->change_function = NULL;
108 change->post_change_function = NULL;
111 static void setLevelInfoToDefaults(struct LevelInfo *level)
113 static boolean clipboard_elements_initialized = FALSE;
117 level->native_em_level = &native_em_level;
119 level->game_engine_type = GAME_ENGINE_TYPE_RND;
121 level->file_version = FILE_VERSION_ACTUAL;
122 level->game_version = GAME_VERSION_ACTUAL;
124 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
125 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
126 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
128 level->fieldx = STD_LEV_FIELDX;
129 level->fieldy = STD_LEV_FIELDY;
131 for (x = 0; x < MAX_LEV_FIELDX; x++)
132 for (y = 0; y < MAX_LEV_FIELDY; y++)
133 level->field[x][y] = EL_SAND;
136 level->gems_needed = 0;
138 level->amoeba_speed = 10;
140 level->time_magic_wall = 10;
141 level->time_wheel = 10;
142 level->time_light = 10;
143 level->time_timegate = 10;
145 level->amoeba_content = EL_DIAMOND;
147 level->double_speed = FALSE;
148 level->initial_gravity = FALSE;
149 level->em_slippery_gems = FALSE;
150 level->instant_relocation = FALSE;
151 level->can_pass_to_walkable = FALSE;
152 level->grow_into_diggable = TRUE;
154 level->block_last_field = FALSE; /* EM does not block by default */
155 level->sp_block_last_field = TRUE; /* SP blocks the last field */
156 level->block_delay = 8; /* when blocking, block 8 frames */
157 level->sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
159 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
160 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
162 level->use_spring_bug = FALSE;
163 level->use_step_counter = FALSE;
165 level->use_custom_template = FALSE;
167 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
168 level->name[i] = '\0';
169 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
170 level->author[i] = '\0';
172 strcpy(level->name, NAMELESS_LEVEL_NAME);
173 strcpy(level->author, ANONYMOUS_NAME);
175 for (i = 0; i < 4; i++)
177 level->envelope_text[i][0] = '\0';
178 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
179 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
182 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
183 level->score[i] = 10;
185 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
186 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
187 for (x = 0; x < 3; x++)
188 for (y = 0; y < 3; y++)
189 level->yamyam_content[i][x][y] =
190 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
192 level->field[0][0] = EL_PLAYER_1;
193 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
195 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
199 /* never initialize clipboard elements after the very first time */
200 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
203 setElementChangePages(&element_info[element], 1);
204 setElementChangeInfoToDefaults(element_info[element].change);
206 if (IS_CUSTOM_ELEMENT(element) ||
207 IS_GROUP_ELEMENT(element) ||
208 IS_INTERNAL_ELEMENT(element))
210 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
211 element_info[element].description[j] = '\0';
213 if (element_info[element].custom_description != NULL)
214 strncpy(element_info[element].description,
215 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
217 strcpy(element_info[element].description,
218 element_info[element].editor_description);
220 element_info[element].use_gfx_element = FALSE;
221 element_info[element].gfx_element = EL_EMPTY_SPACE;
223 element_info[element].modified_settings = FALSE;
226 if (IS_CUSTOM_ELEMENT(element) ||
227 IS_INTERNAL_ELEMENT(element))
229 element_info[element].access_direction = MV_ALL_DIRECTIONS;
231 element_info[element].collect_score = 10; /* special default */
232 element_info[element].collect_count = 1; /* special default */
234 element_info[element].push_delay_fixed = -1; /* initialize later */
235 element_info[element].push_delay_random = -1; /* initialize later */
236 element_info[element].drop_delay_fixed = 0;
237 element_info[element].drop_delay_random = 0;
238 element_info[element].move_delay_fixed = 0;
239 element_info[element].move_delay_random = 0;
241 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
242 element_info[element].move_direction_initial = MV_START_AUTOMATIC;
243 element_info[element].move_stepsize = TILEX / 8;
245 element_info[element].move_enter_element = EL_EMPTY_SPACE;
246 element_info[element].move_leave_element = EL_EMPTY_SPACE;
247 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
249 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
251 element_info[element].explosion_type = EXPLODES_3X3;
252 element_info[element].explosion_delay = 16;
253 element_info[element].ignition_delay = 8;
255 for (x = 0; x < 3; x++)
256 for (y = 0; y < 3; y++)
257 element_info[element].content[x][y] = EL_EMPTY_SPACE;
259 element_info[element].access_type = 0;
260 element_info[element].access_layer = 0;
261 element_info[element].access_protected = 0;
262 element_info[element].walk_to_action = 0;
263 element_info[element].smash_targets = 0;
264 element_info[element].deadliness = 0;
266 element_info[element].can_explode_by_fire = FALSE;
267 element_info[element].can_explode_smashed = FALSE;
268 element_info[element].can_explode_impact = FALSE;
270 element_info[element].current_change_page = 0;
272 /* start with no properties at all */
273 for (j = 0; j < NUM_EP_BITFIELDS; j++)
274 Properties[element][j] = EP_BITMASK_DEFAULT;
276 /* now set default properties */
277 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
280 if (IS_GROUP_ELEMENT(element) ||
281 IS_INTERNAL_ELEMENT(element))
283 /* initialize memory for list of elements in group */
284 if (element_info[element].group == NULL)
285 element_info[element].group =
286 checked_malloc(sizeof(struct ElementGroupInfo));
288 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
289 element_info[element].group->element[j] = EL_EMPTY_SPACE;
291 /* default: only one element in group */
292 element_info[element].group->num_elements = 1;
294 element_info[element].group->choice_mode = ANIM_RANDOM;
298 clipboard_elements_initialized = TRUE;
300 BorderElement = EL_STEELWALL;
302 level->no_valid_file = FALSE;
304 level->changed = FALSE;
306 if (leveldir_current == NULL) /* only when dumping level */
309 /* try to determine better author name than 'anonymous' */
310 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
312 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
313 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
317 switch (LEVELCLASS(leveldir_current))
319 case LEVELCLASS_TUTORIAL:
320 strcpy(level->author, PROGRAM_AUTHOR_STRING);
323 case LEVELCLASS_CONTRIB:
324 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
325 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
328 case LEVELCLASS_PRIVATE:
329 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
330 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
334 /* keep default value */
340 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
342 level_file_info->nr = 0;
343 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
344 level_file_info->packed = FALSE;
345 level_file_info->basename = NULL;
346 level_file_info->filename = NULL;
349 static void ActivateLevelTemplate()
351 /* Currently there is no special action needed to activate the template
352 data, because 'element_info' and 'Properties' overwrite the original
353 level data, while all other variables do not change. */
356 static char *getLevelFilenameFromBasename(char *basename)
358 static char *filename = NULL;
360 checked_free(filename);
362 filename = getPath2(getCurrentLevelDir(), basename);
367 static int getFileTypeFromBasename(char *basename)
369 static char *filename = NULL;
370 struct stat file_status;
372 /* ---------- try to determine file type from filename ---------- */
374 /* check for typical filename of a Supaplex level package file */
375 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
376 strncmp(basename, "LEVELS.D", 8) == 0))
377 return LEVEL_FILE_TYPE_SP;
379 /* ---------- try to determine file type from filesize ---------- */
381 checked_free(filename);
382 filename = getPath2(getCurrentLevelDir(), basename);
384 if (stat(filename, &file_status) == 0)
386 /* check for typical filesize of a Supaplex level package file */
387 if (file_status.st_size == 170496)
388 return LEVEL_FILE_TYPE_SP;
391 return LEVEL_FILE_TYPE_UNKNOWN;
394 static char *getSingleLevelBasename(int nr, int type)
396 static char basename[MAX_FILENAME_LEN];
397 char *level_filename = getStringCopy(leveldir_current->level_filename);
399 if (level_filename == NULL)
400 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
404 case LEVEL_FILE_TYPE_RND:
406 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
408 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
411 case LEVEL_FILE_TYPE_EM:
412 sprintf(basename, "%d", nr);
415 case LEVEL_FILE_TYPE_UNKNOWN:
417 sprintf(basename, level_filename, nr);
421 free(level_filename);
426 static char *getPackedLevelBasename(int type)
428 static char basename[MAX_FILENAME_LEN];
429 char *directory = getCurrentLevelDir();
431 struct dirent *dir_entry;
433 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
435 if ((dir = opendir(directory)) == NULL)
437 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
442 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
444 char *entry_basename = dir_entry->d_name;
445 int entry_type = getFileTypeFromBasename(entry_basename);
447 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
449 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
452 strcpy(basename, entry_basename);
464 static char *getSingleLevelFilename(int nr, int type)
466 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
470 static char *getPackedLevelFilename(int type)
472 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
476 char *getDefaultLevelFilename(int nr)
478 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
481 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
486 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
487 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
490 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
495 lfi->basename = getPackedLevelBasename(lfi->type);
496 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
499 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
501 /* special case: level number is negative => check for level template file */
504 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
509 if (leveldir_current->level_filename != NULL)
511 /* check for file name/pattern specified in "levelinfo.conf" */
512 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
513 if (fileExists(lfi->filename))
517 /* check for native Rocks'n'Diamonds level file */
518 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
519 if (fileExists(lfi->filename))
522 /* check for classic Emerald Mine level file */
523 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
524 if (fileExists(lfi->filename))
527 /* check for various packed level file formats */
528 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
529 if (fileExists(lfi->filename))
532 /* no known level file found -- try to use default values */
533 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
536 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
538 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
539 lfi->type = getFileTypeFromBasename(lfi->basename);
543 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
545 /* always start with reliable default values */
546 setFileInfoToDefaults(level_file_info);
548 level_file_info->nr = nr; /* set requested level number */
550 determineLevelFileInfo_Filename(level_file_info);
551 determineLevelFileInfo_Filetype(level_file_info);
556 static struct LevelFileInfo *getLevelFileInfo(int nr)
558 static struct LevelFileInfo level_file_info;
560 /* always start with reliable default values */
561 setFileInfoToDefaults(&level_file_info);
563 level_file_info.nr = nr; /* set requested level number */
565 determineLevelFileInfo_Filename(&level_file_info);
566 determineLevelFileInfo_Filetype(&level_file_info);
568 return &level_file_info;
572 /* ------------------------------------------------------------------------- */
573 /* functions for loading R'n'D level */
574 /* ------------------------------------------------------------------------- */
576 int getMappedElement(int element)
578 /* remap some (historic, now obsolete) elements */
583 case EL_PLAYER_OBSOLETE:
584 element = EL_PLAYER_1;
587 case EL_KEY_OBSOLETE:
590 case EL_EM_KEY_1_FILE_OBSOLETE:
591 element = EL_EM_KEY_1;
594 case EL_EM_KEY_2_FILE_OBSOLETE:
595 element = EL_EM_KEY_2;
598 case EL_EM_KEY_3_FILE_OBSOLETE:
599 element = EL_EM_KEY_3;
602 case EL_EM_KEY_4_FILE_OBSOLETE:
603 element = EL_EM_KEY_4;
606 case EL_ENVELOPE_OBSOLETE:
607 element = EL_ENVELOPE_1;
615 if (element >= NUM_FILE_ELEMENTS)
617 Error(ERR_WARN, "invalid level element %d", element);
619 element = EL_UNKNOWN;
624 if (element >= NUM_FILE_ELEMENTS)
626 Error(ERR_WARN, "invalid level element %d", element);
628 element = EL_UNKNOWN;
630 else if (element == EL_PLAYER_OBSOLETE)
631 element = EL_PLAYER_1;
632 else if (element == EL_KEY_OBSOLETE)
639 int getMappedElementByVersion(int element, int game_version)
641 /* remap some elements due to certain game version */
643 if (game_version <= VERSION_IDENT(2,2,0,0))
645 /* map game font elements */
646 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
647 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
648 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
649 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
652 if (game_version < VERSION_IDENT(3,0,0,0))
654 /* map Supaplex gravity tube elements */
655 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
656 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
657 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
658 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
665 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
667 level->file_version = getFileVersion(file);
668 level->game_version = getFileVersion(file);
673 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
677 level->fieldx = getFile8Bit(file);
678 level->fieldy = getFile8Bit(file);
680 level->time = getFile16BitBE(file);
681 level->gems_needed = getFile16BitBE(file);
683 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
684 level->name[i] = getFile8Bit(file);
685 level->name[MAX_LEVEL_NAME_LEN] = 0;
687 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
688 level->score[i] = getFile8Bit(file);
690 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
691 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
692 for (y = 0; y < 3; y++)
693 for (x = 0; x < 3; x++)
694 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
696 level->amoeba_speed = getFile8Bit(file);
697 level->time_magic_wall = getFile8Bit(file);
698 level->time_wheel = getFile8Bit(file);
699 level->amoeba_content = getMappedElement(getFile8Bit(file));
700 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
701 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
702 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
703 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
705 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
707 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
708 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
709 level->can_move_into_acid_bits = getFile32BitBE(file);
710 level->dont_collide_with_bits = getFile8Bit(file);
712 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
713 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
715 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
716 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
717 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
719 level->game_engine_type = getFile8Bit(file);
721 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
726 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
730 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
731 level->author[i] = getFile8Bit(file);
732 level->author[MAX_LEVEL_NAME_LEN] = 0;
737 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
740 int chunk_size_expected = level->fieldx * level->fieldy;
742 /* Note: "chunk_size" was wrong before version 2.0 when elements are
743 stored with 16-bit encoding (and should be twice as big then).
744 Even worse, playfield data was stored 16-bit when only yamyam content
745 contained 16-bit elements and vice versa. */
747 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
748 chunk_size_expected *= 2;
750 if (chunk_size_expected != chunk_size)
752 ReadUnusedBytesFromFile(file, chunk_size);
753 return chunk_size_expected;
756 for (y = 0; y < level->fieldy; y++)
757 for (x = 0; x < level->fieldx; x++)
759 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
764 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
768 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
769 int chunk_size_expected = header_size + content_size;
771 /* Note: "chunk_size" was wrong before version 2.0 when elements are
772 stored with 16-bit encoding (and should be twice as big then).
773 Even worse, playfield data was stored 16-bit when only yamyam content
774 contained 16-bit elements and vice versa. */
776 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
777 chunk_size_expected += content_size;
779 if (chunk_size_expected != chunk_size)
781 ReadUnusedBytesFromFile(file, chunk_size);
782 return chunk_size_expected;
786 level->num_yamyam_contents = getFile8Bit(file);
790 /* correct invalid number of content fields -- should never happen */
791 if (level->num_yamyam_contents < 1 ||
792 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
793 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
795 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
796 for (y = 0; y < 3; y++)
797 for (x = 0; x < 3; x++)
798 level->yamyam_content[i][x][y] =
799 getMappedElement(level->encoding_16bit_field ?
800 getFile16BitBE(file) : getFile8Bit(file));
804 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
808 int num_contents, content_xsize, content_ysize;
809 int content_array[MAX_ELEMENT_CONTENTS][3][3];
811 element = getMappedElement(getFile16BitBE(file));
812 num_contents = getFile8Bit(file);
813 content_xsize = getFile8Bit(file);
814 content_ysize = getFile8Bit(file);
816 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
818 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
819 for (y = 0; y < 3; y++)
820 for (x = 0; x < 3; x++)
821 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
823 /* correct invalid number of content fields -- should never happen */
824 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
825 num_contents = STD_ELEMENT_CONTENTS;
827 if (element == EL_YAMYAM)
829 level->num_yamyam_contents = num_contents;
831 for (i = 0; i < num_contents; i++)
832 for (y = 0; y < 3; y++)
833 for (x = 0; x < 3; x++)
834 level->yamyam_content[i][x][y] = content_array[i][x][y];
836 else if (element == EL_BD_AMOEBA)
838 level->amoeba_content = content_array[0][0][0];
842 Error(ERR_WARN, "cannot load content for element '%d'", element);
848 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
854 int chunk_size_expected;
856 element = getMappedElement(getFile16BitBE(file));
857 if (!IS_ENVELOPE(element))
858 element = EL_ENVELOPE_1;
860 envelope_nr = element - EL_ENVELOPE_1;
862 envelope_len = getFile16BitBE(file);
864 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
865 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
867 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
869 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
870 if (chunk_size_expected != chunk_size)
872 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
873 return chunk_size_expected;
876 for (i = 0; i < envelope_len; i++)
877 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
882 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
884 int num_changed_custom_elements = getFile16BitBE(file);
885 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
888 if (chunk_size_expected != chunk_size)
890 ReadUnusedBytesFromFile(file, chunk_size - 2);
891 return chunk_size_expected;
894 for (i = 0; i < num_changed_custom_elements; i++)
896 int element = getFile16BitBE(file);
897 int properties = getFile32BitBE(file);
899 if (IS_CUSTOM_ELEMENT(element))
900 Properties[element][EP_BITFIELD_BASE] = properties;
902 Error(ERR_WARN, "invalid custom element number %d", element);
908 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
910 int num_changed_custom_elements = getFile16BitBE(file);
911 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
914 if (chunk_size_expected != chunk_size)
916 ReadUnusedBytesFromFile(file, chunk_size - 2);
917 return chunk_size_expected;
920 for (i = 0; i < num_changed_custom_elements; i++)
922 int element = getFile16BitBE(file);
923 int custom_target_element = getFile16BitBE(file);
925 if (IS_CUSTOM_ELEMENT(element))
926 element_info[element].change->target_element = custom_target_element;
928 Error(ERR_WARN, "invalid custom element number %d", element);
934 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
936 int num_changed_custom_elements = getFile16BitBE(file);
937 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
940 if (chunk_size_expected != chunk_size)
942 ReadUnusedBytesFromFile(file, chunk_size - 2);
943 return chunk_size_expected;
946 for (i = 0; i < num_changed_custom_elements; i++)
948 int element = getFile16BitBE(file);
950 if (!IS_CUSTOM_ELEMENT(element))
952 Error(ERR_WARN, "invalid custom element number %d", element);
954 element = EL_INTERNAL_DUMMY;
957 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
958 element_info[element].description[j] = getFile8Bit(file);
959 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
961 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
963 /* some free bytes for future properties and padding */
964 ReadUnusedBytesFromFile(file, 7);
966 element_info[element].use_gfx_element = getFile8Bit(file);
967 element_info[element].gfx_element =
968 getMappedElement(getFile16BitBE(file));
970 element_info[element].collect_score = getFile8Bit(file);
971 element_info[element].collect_count = getFile8Bit(file);
973 element_info[element].push_delay_fixed = getFile16BitBE(file);
974 element_info[element].push_delay_random = getFile16BitBE(file);
975 element_info[element].move_delay_fixed = getFile16BitBE(file);
976 element_info[element].move_delay_random = getFile16BitBE(file);
978 element_info[element].move_pattern = getFile16BitBE(file);
979 element_info[element].move_direction_initial = getFile8Bit(file);
980 element_info[element].move_stepsize = getFile8Bit(file);
982 for (y = 0; y < 3; y++)
983 for (x = 0; x < 3; x++)
984 element_info[element].content[x][y] =
985 getMappedElement(getFile16BitBE(file));
987 element_info[element].change->events = getFile32BitBE(file);
989 element_info[element].change->target_element =
990 getMappedElement(getFile16BitBE(file));
992 element_info[element].change->delay_fixed = getFile16BitBE(file);
993 element_info[element].change->delay_random = getFile16BitBE(file);
994 element_info[element].change->delay_frames = getFile16BitBE(file);
996 element_info[element].change->trigger_element =
997 getMappedElement(getFile16BitBE(file));
999 element_info[element].change->explode = getFile8Bit(file);
1000 element_info[element].change->use_target_content = getFile8Bit(file);
1001 element_info[element].change->only_if_complete = getFile8Bit(file);
1002 element_info[element].change->use_random_replace = getFile8Bit(file);
1004 element_info[element].change->random_percentage = getFile8Bit(file);
1005 element_info[element].change->replace_when = getFile8Bit(file);
1007 for (y = 0; y < 3; y++)
1008 for (x = 0; x < 3; x++)
1009 element_info[element].change->target_content[x][y] =
1010 getMappedElement(getFile16BitBE(file));
1012 element_info[element].slippery_type = getFile8Bit(file);
1014 /* some free bytes for future properties and padding */
1015 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1017 /* mark that this custom element has been modified */
1018 element_info[element].modified_settings = TRUE;
1024 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1026 struct ElementInfo *ei;
1027 int chunk_size_expected;
1031 element = getFile16BitBE(file);
1033 if (!IS_CUSTOM_ELEMENT(element))
1035 Error(ERR_WARN, "invalid custom element number %d", element);
1037 ReadUnusedBytesFromFile(file, chunk_size - 2);
1041 ei = &element_info[element];
1043 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1044 ei->description[i] = getFile8Bit(file);
1045 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1047 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1048 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1050 ei->num_change_pages = getFile8Bit(file);
1052 /* some free bytes for future base property values and padding */
1053 ReadUnusedBytesFromFile(file, 5);
1055 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1056 if (chunk_size_expected != chunk_size)
1058 ReadUnusedBytesFromFile(file, chunk_size - 48);
1059 return chunk_size_expected;
1062 /* read custom property values */
1064 ei->use_gfx_element = getFile8Bit(file);
1065 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1067 ei->collect_score = getFile8Bit(file);
1068 ei->collect_count = getFile8Bit(file);
1070 ei->drop_delay_fixed = getFile8Bit(file);
1071 ei->push_delay_fixed = getFile8Bit(file);
1072 ei->drop_delay_random = getFile8Bit(file);
1073 ei->push_delay_random = getFile8Bit(file);
1074 ei->move_delay_fixed = getFile16BitBE(file);
1075 ei->move_delay_random = getFile16BitBE(file);
1077 /* bits 0 - 15 of "move_pattern" ... */
1078 ei->move_pattern = getFile16BitBE(file);
1079 ei->move_direction_initial = getFile8Bit(file);
1080 ei->move_stepsize = getFile8Bit(file);
1082 ei->slippery_type = getFile8Bit(file);
1084 for (y = 0; y < 3; y++)
1085 for (x = 0; x < 3; x++)
1086 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1088 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1089 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1090 ei->move_leave_type = getFile8Bit(file);
1092 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1093 ei->move_pattern |= (getFile16BitBE(file) << 16);
1095 ei->access_direction = getFile8Bit(file);
1097 ei->explosion_delay = getFile8Bit(file);
1098 ei->ignition_delay = getFile8Bit(file);
1099 ei->explosion_type = getFile8Bit(file);
1101 /* some free bytes for future custom property values and padding */
1102 ReadUnusedBytesFromFile(file, 1);
1104 /* read change property values */
1106 setElementChangePages(ei, ei->num_change_pages);
1108 for (i = 0; i < ei->num_change_pages; i++)
1110 struct ElementChangeInfo *change = &ei->change_page[i];
1112 /* always start with reliable default values */
1113 setElementChangeInfoToDefaults(change);
1115 change->events = getFile32BitBE(file);
1117 change->target_element = getMappedElement(getFile16BitBE(file));
1119 change->delay_fixed = getFile16BitBE(file);
1120 change->delay_random = getFile16BitBE(file);
1121 change->delay_frames = getFile16BitBE(file);
1123 change->trigger_element = getMappedElement(getFile16BitBE(file));
1125 change->explode = getFile8Bit(file);
1126 change->use_target_content = getFile8Bit(file);
1127 change->only_if_complete = getFile8Bit(file);
1128 change->use_random_replace = getFile8Bit(file);
1130 change->random_percentage = getFile8Bit(file);
1131 change->replace_when = getFile8Bit(file);
1133 for (y = 0; y < 3; y++)
1134 for (x = 0; x < 3; x++)
1135 change->target_content[x][y] = getMappedElement(getFile16BitBE(file));
1137 change->can_change = getFile8Bit(file);
1139 change->trigger_side = getFile8Bit(file);
1142 change->trigger_player = getFile8Bit(file);
1143 change->trigger_page = getFile8Bit(file);
1145 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1146 CH_PAGE_ANY : (1 << change->trigger_page));
1148 /* some free bytes for future change property values and padding */
1149 ReadUnusedBytesFromFile(file, 6);
1153 /* some free bytes for future change property values and padding */
1154 ReadUnusedBytesFromFile(file, 8);
1158 /* mark this custom element as modified */
1159 ei->modified_settings = TRUE;
1164 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1166 struct ElementInfo *ei;
1167 struct ElementGroupInfo *group;
1171 element = getFile16BitBE(file);
1173 if (!IS_GROUP_ELEMENT(element))
1175 Error(ERR_WARN, "invalid group element number %d", element);
1177 ReadUnusedBytesFromFile(file, chunk_size - 2);
1181 ei = &element_info[element];
1183 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1184 ei->description[i] = getFile8Bit(file);
1185 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1187 group = element_info[element].group;
1189 group->num_elements = getFile8Bit(file);
1191 ei->use_gfx_element = getFile8Bit(file);
1192 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1194 group->choice_mode = getFile8Bit(file);
1196 /* some free bytes for future values and padding */
1197 ReadUnusedBytesFromFile(file, 3);
1199 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1200 group->element[i] = getMappedElement(getFile16BitBE(file));
1202 /* mark this group element as modified */
1203 element_info[element].modified_settings = TRUE;
1208 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1209 struct LevelFileInfo *level_file_info)
1211 char *filename = level_file_info->filename;
1212 char cookie[MAX_LINE_LEN];
1213 char chunk_name[CHUNK_ID_LEN + 1];
1217 if (!(file = fopen(filename, MODE_READ)))
1219 level->no_valid_file = TRUE;
1221 if (level != &level_template)
1222 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1227 getFileChunkBE(file, chunk_name, NULL);
1228 if (strcmp(chunk_name, "RND1") == 0)
1230 getFile32BitBE(file); /* not used */
1232 getFileChunkBE(file, chunk_name, NULL);
1233 if (strcmp(chunk_name, "CAVE") != 0)
1235 level->no_valid_file = TRUE;
1237 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1242 else /* check for pre-2.0 file format with cookie string */
1244 strcpy(cookie, chunk_name);
1245 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1246 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1247 cookie[strlen(cookie) - 1] = '\0';
1249 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1251 level->no_valid_file = TRUE;
1253 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1258 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1260 level->no_valid_file = TRUE;
1262 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1267 /* pre-2.0 level files have no game version, so use file version here */
1268 level->game_version = level->file_version;
1271 if (level->file_version < FILE_VERSION_1_2)
1273 /* level files from versions before 1.2.0 without chunk structure */
1274 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1275 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1283 int (*loader)(FILE *, int, struct LevelInfo *);
1287 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1288 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1289 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1290 { "BODY", -1, LoadLevel_BODY },
1291 { "CONT", -1, LoadLevel_CONT },
1292 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1293 { "CNT3", -1, LoadLevel_CNT3 },
1294 { "CUS1", -1, LoadLevel_CUS1 },
1295 { "CUS2", -1, LoadLevel_CUS2 },
1296 { "CUS3", -1, LoadLevel_CUS3 },
1297 { "CUS4", -1, LoadLevel_CUS4 },
1298 { "GRP1", -1, LoadLevel_GRP1 },
1302 while (getFileChunkBE(file, chunk_name, &chunk_size))
1306 while (chunk_info[i].name != NULL &&
1307 strcmp(chunk_name, chunk_info[i].name) != 0)
1310 if (chunk_info[i].name == NULL)
1312 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1313 chunk_name, filename);
1314 ReadUnusedBytesFromFile(file, chunk_size);
1316 else if (chunk_info[i].size != -1 &&
1317 chunk_info[i].size != chunk_size)
1319 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1320 chunk_size, chunk_name, filename);
1321 ReadUnusedBytesFromFile(file, chunk_size);
1325 /* call function to load this level chunk */
1326 int chunk_size_expected =
1327 (chunk_info[i].loader)(file, chunk_size, level);
1329 /* the size of some chunks cannot be checked before reading other
1330 chunks first (like "HEAD" and "BODY") that contain some header
1331 information, so check them here */
1332 if (chunk_size_expected != chunk_size)
1334 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1335 chunk_size, chunk_name, filename);
1344 /* ------------------------------------------------------------------------- */
1345 /* functions for loading EM level */
1346 /* ------------------------------------------------------------------------- */
1350 static int map_em_element_yam(int element)
1354 case 0x00: return EL_EMPTY;
1355 case 0x01: return EL_EMERALD;
1356 case 0x02: return EL_DIAMOND;
1357 case 0x03: return EL_ROCK;
1358 case 0x04: return EL_ROBOT;
1359 case 0x05: return EL_SPACESHIP_UP;
1360 case 0x06: return EL_BOMB;
1361 case 0x07: return EL_BUG_UP;
1362 case 0x08: return EL_AMOEBA_DROP;
1363 case 0x09: return EL_NUT;
1364 case 0x0a: return EL_YAMYAM;
1365 case 0x0b: return EL_QUICKSAND_FULL;
1366 case 0x0c: return EL_SAND;
1367 case 0x0d: return EL_WALL_SLIPPERY;
1368 case 0x0e: return EL_STEELWALL;
1369 case 0x0f: return EL_WALL;
1370 case 0x10: return EL_EM_KEY_1;
1371 case 0x11: return EL_EM_KEY_2;
1372 case 0x12: return EL_EM_KEY_4;
1373 case 0x13: return EL_EM_KEY_3;
1374 case 0x14: return EL_MAGIC_WALL;
1375 case 0x15: return EL_ROBOT_WHEEL;
1376 case 0x16: return EL_DYNAMITE;
1378 case 0x17: return EL_EM_KEY_1; /* EMC */
1379 case 0x18: return EL_BUG_UP; /* EMC */
1380 case 0x1a: return EL_DIAMOND; /* EMC */
1381 case 0x1b: return EL_EMERALD; /* EMC */
1382 case 0x25: return EL_NUT; /* EMC */
1383 case 0x80: return EL_EMPTY; /* EMC */
1384 case 0x85: return EL_EM_KEY_1; /* EMC */
1385 case 0x86: return EL_EM_KEY_2; /* EMC */
1386 case 0x87: return EL_EM_KEY_4; /* EMC */
1387 case 0x88: return EL_EM_KEY_3; /* EMC */
1388 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1389 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1390 case 0xaf: return EL_DYNAMITE; /* EMC */
1391 case 0xbd: return EL_SAND; /* EMC */
1394 Error(ERR_WARN, "invalid level element %d", element);
1399 static int map_em_element_field(int element)
1401 if (element >= 0xc8 && element <= 0xe1)
1402 return EL_CHAR_A + (element - 0xc8);
1403 else if (element >= 0xe2 && element <= 0xeb)
1404 return EL_CHAR_0 + (element - 0xe2);
1408 case 0x00: return EL_ROCK;
1409 case 0x01: return EL_ROCK; /* EMC */
1410 case 0x02: return EL_DIAMOND;
1411 case 0x03: return EL_DIAMOND;
1412 case 0x04: return EL_ROBOT;
1413 case 0x05: return EL_ROBOT; /* EMC */
1414 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1415 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1416 case 0x08: return EL_SPACESHIP_UP;
1417 case 0x09: return EL_SPACESHIP_RIGHT;
1418 case 0x0a: return EL_SPACESHIP_DOWN;
1419 case 0x0b: return EL_SPACESHIP_LEFT;
1420 case 0x0c: return EL_SPACESHIP_UP;
1421 case 0x0d: return EL_SPACESHIP_RIGHT;
1422 case 0x0e: return EL_SPACESHIP_DOWN;
1423 case 0x0f: return EL_SPACESHIP_LEFT;
1425 case 0x10: return EL_BOMB;
1426 case 0x11: return EL_BOMB; /* EMC */
1427 case 0x12: return EL_EMERALD;
1428 case 0x13: return EL_EMERALD;
1429 case 0x14: return EL_BUG_UP;
1430 case 0x15: return EL_BUG_RIGHT;
1431 case 0x16: return EL_BUG_DOWN;
1432 case 0x17: return EL_BUG_LEFT;
1433 case 0x18: return EL_BUG_UP;
1434 case 0x19: return EL_BUG_RIGHT;
1435 case 0x1a: return EL_BUG_DOWN;
1436 case 0x1b: return EL_BUG_LEFT;
1437 case 0x1c: return EL_AMOEBA_DROP;
1438 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1439 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1440 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1442 case 0x20: return EL_ROCK;
1443 case 0x21: return EL_BOMB; /* EMC */
1444 case 0x22: return EL_DIAMOND; /* EMC */
1445 case 0x23: return EL_EMERALD; /* EMC */
1446 case 0x24: return EL_MAGIC_WALL;
1447 case 0x25: return EL_NUT;
1448 case 0x26: return EL_NUT; /* EMC */
1449 case 0x27: return EL_NUT; /* EMC */
1451 /* looks like magic wheel, but is _always_ activated */
1452 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1454 case 0x29: return EL_YAMYAM; /* up */
1455 case 0x2a: return EL_YAMYAM; /* down */
1456 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1457 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1458 case 0x2d: return EL_QUICKSAND_FULL;
1459 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1460 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1462 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1463 case 0x31: return EL_SAND; /* EMC */
1464 case 0x32: return EL_SAND; /* EMC */
1465 case 0x33: return EL_SAND; /* EMC */
1466 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1467 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1468 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1469 case 0x37: return EL_SAND; /* EMC */
1470 case 0x38: return EL_ROCK; /* EMC */
1471 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1472 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1473 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1474 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1475 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1476 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1477 case 0x3f: return EL_ACID_POOL_BOTTOM;
1479 case 0x40: return EL_EXIT_OPEN; /* 1 */
1480 case 0x41: return EL_EXIT_OPEN; /* 2 */
1481 case 0x42: return EL_EXIT_OPEN; /* 3 */
1482 case 0x43: return EL_BALLOON; /* EMC */
1483 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1484 case 0x45: return EL_SPRING; /* EMC */
1485 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1486 case 0x47: return EL_SPRING; /* left */ /* EMC */
1487 case 0x48: return EL_SPRING; /* right */ /* EMC */
1488 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1489 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1490 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1491 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1492 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1493 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1494 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1496 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1497 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1498 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1499 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1500 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1501 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1502 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1503 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1504 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1505 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1506 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1507 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1508 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1509 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1510 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1511 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1513 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1514 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1515 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1516 case 0x63: return EL_SPRING; /* left */ /* EMC */
1517 case 0x64: return EL_SPRING; /* right */ /* EMC */
1518 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1519 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1520 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1521 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1522 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1523 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1524 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1525 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1526 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1527 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1528 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1530 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1531 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1532 case 0x72: return EL_NUT; /* left */ /* EMC */
1533 case 0x73: return EL_SAND; /* EMC (? "nut") */
1534 case 0x74: return EL_STEELWALL;
1535 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1536 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1537 case 0x77: return EL_BOMB; /* left */ /* EMC */
1538 case 0x78: return EL_BOMB; /* right */ /* EMC */
1539 case 0x79: return EL_ROCK; /* left */ /* EMC */
1540 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1541 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1542 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1543 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1544 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1545 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1547 case 0x80: return EL_EMPTY;
1548 case 0x81: return EL_WALL_SLIPPERY;
1549 case 0x82: return EL_SAND;
1550 case 0x83: return EL_STEELWALL;
1551 case 0x84: return EL_WALL;
1552 case 0x85: return EL_EM_KEY_1;
1553 case 0x86: return EL_EM_KEY_2;
1554 case 0x87: return EL_EM_KEY_4;
1555 case 0x88: return EL_EM_KEY_3;
1556 case 0x89: return EL_EM_GATE_1;
1557 case 0x8a: return EL_EM_GATE_2;
1558 case 0x8b: return EL_EM_GATE_4;
1559 case 0x8c: return EL_EM_GATE_3;
1560 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1561 case 0x8e: return EL_EM_GATE_1_GRAY;
1562 case 0x8f: return EL_EM_GATE_2_GRAY;
1564 case 0x90: return EL_EM_GATE_4_GRAY;
1565 case 0x91: return EL_EM_GATE_3_GRAY;
1566 case 0x92: return EL_MAGIC_WALL;
1567 case 0x93: return EL_ROBOT_WHEEL;
1568 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1569 case 0x95: return EL_ACID_POOL_TOPLEFT;
1570 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1571 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1572 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1573 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1574 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1575 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1576 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1577 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1578 case 0x9e: return EL_EXIT_CLOSED;
1579 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1581 /* looks like normal sand, but behaves like wall */
1582 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1583 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1584 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1585 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1586 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1587 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1588 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1589 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1590 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1591 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1592 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1593 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1594 case 0xac: return EL_CHAR_COMMA; /* EMC */
1595 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1596 case 0xae: return EL_CHAR_MINUS; /* EMC */
1597 case 0xaf: return EL_DYNAMITE;
1599 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1600 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1601 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1602 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1603 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1604 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1605 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1606 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1607 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1608 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1609 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1610 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1611 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1612 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1613 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1614 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1616 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1617 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1618 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1619 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1620 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1621 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1622 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1623 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1625 /* characters: see above */
1627 case 0xec: return EL_CHAR_PERIOD;
1628 case 0xed: return EL_CHAR_EXCLAM;
1629 case 0xee: return EL_CHAR_COLON;
1630 case 0xef: return EL_CHAR_QUESTION;
1632 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1633 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1634 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1635 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1636 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1637 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1638 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1639 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1641 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1642 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1643 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1644 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1645 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1646 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1648 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1649 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1652 /* should never happen (all 8-bit value cases should be handled) */
1653 Error(ERR_WARN, "invalid level element %d", element);
1658 #define EM_LEVEL_SIZE 2106
1659 #define EM_LEVEL_XSIZE 64
1660 #define EM_LEVEL_YSIZE 32
1662 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1663 struct LevelFileInfo *level_file_info)
1665 char *filename = level_file_info->filename;
1667 unsigned char leveldata[EM_LEVEL_SIZE];
1668 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1669 int nr = level_file_info->nr;
1672 if (!(file = fopen(filename, MODE_READ)))
1674 level->no_valid_file = TRUE;
1676 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1681 for (i = 0; i < EM_LEVEL_SIZE; i++)
1682 leveldata[i] = fgetc(file);
1686 /* check if level data is crypted by testing against known starting bytes
1687 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1689 if ((leveldata[0] == 0xf1 ||
1690 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1692 unsigned char code0 = 0x65;
1693 unsigned char code1 = 0x11;
1695 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1696 leveldata[0] = 0xf1;
1698 /* decode crypted level data */
1700 for (i = 0; i < EM_LEVEL_SIZE; i++)
1702 leveldata[i] ^= code0;
1703 leveldata[i] -= code1;
1705 code0 = (code0 + 7) & 0xff;
1709 level->fieldx = EM_LEVEL_XSIZE;
1710 level->fieldy = EM_LEVEL_YSIZE;
1712 level->time = header[46] * 10;
1713 level->gems_needed = header[47];
1715 /* The original Emerald Mine levels have their level number stored
1716 at the second byte of the level file...
1717 Do not trust this information at other level files, e.g. EMC,
1718 but correct it anyway (normally the first row is completely
1719 steel wall, so the correction does not hurt anyway). */
1721 if (leveldata[1] == nr)
1722 leveldata[1] = leveldata[2]; /* correct level number field */
1724 sprintf(level->name, "Level %d", nr); /* set level name */
1726 level->score[SC_EMERALD] = header[36];
1727 level->score[SC_DIAMOND] = header[37];
1728 level->score[SC_ROBOT] = header[38];
1729 level->score[SC_SPACESHIP] = header[39];
1730 level->score[SC_BUG] = header[40];
1731 level->score[SC_YAMYAM] = header[41];
1732 level->score[SC_NUT] = header[42];
1733 level->score[SC_DYNAMITE] = header[43];
1734 level->score[SC_TIME_BONUS] = header[44];
1736 level->num_yamyam_contents = 4;
1738 for (i = 0; i < level->num_yamyam_contents; i++)
1739 for (y = 0; y < 3; y++)
1740 for (x = 0; x < 3; x++)
1741 level->yamyam_content[i][x][y] =
1742 map_em_element_yam(header[i * 9 + y * 3 + x]);
1744 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1745 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1746 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1747 level->amoeba_content = EL_DIAMOND;
1749 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1751 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1753 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1754 new_element = EL_AMOEBA_WET;
1756 level->field[x][y] = new_element;
1759 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1760 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1761 level->field[x][y] = EL_PLAYER_1;
1763 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1764 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1765 level->field[x][y] = EL_PLAYER_2;
1770 static int map_element_RND_to_EM(int element_rnd)
1772 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
1773 static boolean mapping_initialized = FALSE;
1780 mapping_RND_to_EM_list[] =
1782 { Xblank, EL_EMPTY },
1783 { Xstone, EL_ROCK },
1785 { Xbug_n, EL_BUG_UP },
1786 { Xbug_e, EL_BUG_RIGHT },
1787 { Xbug_s, EL_BUG_DOWN },
1788 { Xbug_w, EL_BUG_LEFT },
1789 { Xtank_n, EL_SPACESHIP_UP },
1790 { Xtank_e, EL_SPACESHIP_RIGHT },
1791 { Xtank_s, EL_SPACESHIP_DOWN },
1792 { Xtank_w, EL_SPACESHIP_LEFT },
1793 { Xandroid, EL_EMC_ANDROID },
1794 { Xandroid_1_n, EL_EMC_ANDROID_UP },
1795 { Xandroid_1_e, EL_EMC_ANDROID_RIGHT },
1796 { Xandroid_1_w, EL_EMC_ANDROID_LEFT },
1797 { Xandroid_1_s, EL_EMC_ANDROID_DOWN },
1798 { Xspring, EL_SPRING },
1799 { Xeater_n, EL_YAMYAM },
1800 { Xalien, EL_ROBOT },
1801 { Xemerald, EL_EMERALD },
1802 { Xdiamond, EL_DIAMOND },
1803 { Xdrip_fall, EL_AMOEBA_DROP },
1805 { Xballoon, EL_BALLOON },
1806 { Xgrass, EL_EMC_GRASS },
1808 { Xacid_ne, EL_ACID_POOL_TOPRIGHT },
1809 { Xacid_se, EL_ACID_POOL_BOTTOMRIGHT },
1810 { Xacid_s, EL_ACID_POOL_BOTTOM },
1811 { Xacid_sw, EL_ACID_POOL_BOTTOMLEFT },
1812 { Xacid_nw, EL_ACID_POOL_TOPLEFT },
1813 { Xacid_1, EL_ACID },
1814 { Xball_1, EL_EMC_GENERATOR_BALL },
1815 { Xgrow_ns, EL_EMC_GROW },
1816 { Xwonderwall, EL_MAGIC_WALL },
1817 { Xameuba_1, EL_AMOEBA_WET },
1818 { Xdoor_1, EL_EM_GATE_1 },
1819 { Xdoor_2, EL_EM_GATE_2 },
1820 { Xdoor_3, EL_EM_GATE_3 },
1821 { Xdoor_4, EL_EM_GATE_4 },
1822 { Xdoor_5, EL_EMC_GATE_5 },
1823 { Xdoor_6, EL_EMC_GATE_6 },
1824 { Xdoor_7, EL_EMC_GATE_7 },
1825 { Xdoor_8, EL_EMC_GATE_8 },
1826 { Xkey_1, EL_EM_KEY_1 },
1827 { Xkey_2, EL_EM_KEY_2 },
1828 { Xkey_3, EL_EM_KEY_3 },
1829 { Xkey_4, EL_EM_KEY_4 },
1830 { Xkey_5, EL_EMC_KEY_5 },
1831 { Xkey_6, EL_EMC_KEY_6 },
1832 { Xkey_7, EL_EMC_KEY_7 },
1833 { Xkey_8, EL_EMC_KEY_8 },
1834 { Xwind_n, EL_BALLOON_SWITCH_UP },
1835 { Xwind_e, EL_BALLOON_SWITCH_RIGHT },
1836 { Xwind_s, EL_BALLOON_SWITCH_DOWN },
1837 { Xwind_w, EL_BALLOON_SWITCH_LEFT },
1838 { Xwind_nesw, EL_BALLOON_SWITCH_ANY },
1839 { Xwind_stop, EL_BALLOON_SWITCH_NONE },
1840 { Xexit, EL_EXIT_CLOSED },
1841 { Xexit_1, EL_EXIT_OPEN },
1842 { Xdynamite, EL_DYNAMITE },
1843 { Xdynamite_1, EL_DYNAMITE_ACTIVE },
1844 { Xbumper, EL_EMC_BUMPER },
1845 { Xwheel, EL_ROBOT_WHEEL },
1846 { Xswitch, EL_UNKNOWN },
1847 { Xsand, EL_QUICKSAND_EMPTY },
1848 { Xsand_stone, EL_QUICKSAND_FULL },
1849 { Xplant, EL_EMC_PLANT },
1850 { Xlenses, EL_EMC_LENSES },
1851 { Xmagnify, EL_EMC_MAGNIFY },
1852 { Xdripper, EL_UNKNOWN },
1853 { Xfake_blank, EL_INVISIBLE_WALL },
1854 { Xfake_grass, EL_INVISIBLE_SAND },
1855 { Xfake_door_1, EL_EM_GATE_1_GRAY },
1856 { Xfake_door_2, EL_EM_GATE_2_GRAY },
1857 { Xfake_door_3, EL_EM_GATE_3_GRAY },
1858 { Xfake_door_4, EL_EM_GATE_4_GRAY },
1859 { Xfake_door_5, EL_EMC_GATE_5_GRAY },
1860 { Xfake_door_6, EL_EMC_GATE_6_GRAY },
1861 { Xfake_door_7, EL_EMC_GATE_7_GRAY },
1862 { Xfake_door_8, EL_EMC_GATE_8_GRAY },
1863 { Xsteel_1, EL_STEELWALL },
1864 { Xsteel_2, EL_UNKNOWN },
1865 { Xsteel_3, EL_EMC_STEELWALL_1 },
1866 { Xsteel_4, EL_UNKNOWN },
1867 { Xwall_1, EL_WALL },
1868 { Xwall_2, EL_UNKNOWN },
1869 { Xwall_3, EL_UNKNOWN },
1870 { Xwall_4, EL_UNKNOWN },
1871 { Xround_wall_1, EL_WALL_SLIPPERY },
1872 { Xround_wall_2, EL_UNKNOWN },
1873 { Xround_wall_3, EL_UNKNOWN },
1874 { Xround_wall_4, EL_UNKNOWN },
1875 { Xdecor_1, EL_UNKNOWN },
1876 { Xdecor_2, EL_EMC_WALL_6 },
1877 { Xdecor_3, EL_EMC_WALL_4 },
1878 { Xdecor_4, EL_EMC_WALL_5 },
1879 { Xdecor_5, EL_EMC_WALL_7 },
1880 { Xdecor_6, EL_EMC_WALL_8 },
1881 { Xdecor_7, EL_UNKNOWN },
1882 { Xdecor_8, EL_EMC_WALL_1 },
1883 { Xdecor_9, EL_EMC_WALL_2 },
1884 { Xdecor_10, EL_EMC_WALL_3 },
1885 { Xdecor_11, EL_UNKNOWN },
1886 { Xdecor_12, EL_UNKNOWN },
1887 { Xalpha_0, EL_CHAR('0') },
1888 { Xalpha_1, EL_CHAR('1') },
1889 { Xalpha_2, EL_CHAR('2') },
1890 { Xalpha_3, EL_CHAR('3') },
1891 { Xalpha_4, EL_CHAR('4') },
1892 { Xalpha_5, EL_CHAR('5') },
1893 { Xalpha_6, EL_CHAR('6') },
1894 { Xalpha_7, EL_CHAR('7') },
1895 { Xalpha_8, EL_CHAR('8') },
1896 { Xalpha_9, EL_CHAR('9') },
1897 { Xalpha_excla, EL_CHAR('!') },
1898 { Xalpha_quote, EL_CHAR('"') },
1899 { Xalpha_comma, EL_CHAR(',') },
1900 { Xalpha_minus, EL_CHAR('-') },
1901 { Xalpha_perio, EL_CHAR('.') },
1902 { Xalpha_colon, EL_CHAR(':') },
1903 { Xalpha_quest, EL_CHAR('?') },
1904 { Xalpha_a, EL_CHAR('A') },
1905 { Xalpha_b, EL_CHAR('B') },
1906 { Xalpha_c, EL_CHAR('C') },
1907 { Xalpha_d, EL_CHAR('D') },
1908 { Xalpha_e, EL_CHAR('E') },
1909 { Xalpha_f, EL_CHAR('F') },
1910 { Xalpha_g, EL_CHAR('G') },
1911 { Xalpha_h, EL_CHAR('H') },
1912 { Xalpha_i, EL_CHAR('I') },
1913 { Xalpha_j, EL_CHAR('J') },
1914 { Xalpha_k, EL_CHAR('K') },
1915 { Xalpha_l, EL_CHAR('L') },
1916 { Xalpha_m, EL_CHAR('M') },
1917 { Xalpha_n, EL_CHAR('N') },
1918 { Xalpha_o, EL_CHAR('O') },
1919 { Xalpha_p, EL_CHAR('P') },
1920 { Xalpha_q, EL_CHAR('Q') },
1921 { Xalpha_r, EL_CHAR('R') },
1922 { Xalpha_s, EL_CHAR('S') },
1923 { Xalpha_t, EL_CHAR('T') },
1924 { Xalpha_u, EL_CHAR('U') },
1925 { Xalpha_v, EL_CHAR('V') },
1926 { Xalpha_w, EL_CHAR('W') },
1927 { Xalpha_x, EL_CHAR('X') },
1928 { Xalpha_y, EL_CHAR('Y') },
1929 { Xalpha_z, EL_CHAR('Z') },
1930 { Xalpha_arrow_e, EL_CHAR('>') },
1931 { Xalpha_arrow_w, EL_CHAR('<') },
1932 { Xalpha_copyr, EL_CHAR('©') },
1934 { Zplayer, EL_PLAYER_1 },
1935 { Zplayer, EL_PLAYER_2 },
1936 { Zplayer, EL_PLAYER_3 },
1937 { Zplayer, EL_PLAYER_4 },
1939 { ZBORDER, EL_EMC_LEVEL_BORDER },
1944 if (!mapping_initialized)
1948 /* return "Xalpha_quest" for all undefined elements in mapping array */
1949 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
1950 mapping_RND_to_EM[i] = Xalpha_quest;
1952 for (i = 0; mapping_RND_to_EM_list[i].element_rnd != -1; i++)
1953 mapping_RND_to_EM[mapping_RND_to_EM_list[i].element_rnd] =
1954 mapping_RND_to_EM_list[i].element_em;
1956 mapping_initialized = TRUE;
1959 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
1960 return mapping_RND_to_EM[element_rnd];
1962 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
1967 static int map_element_EM_to_RND(int element_em)
1969 static unsigned short mapping_EM_to_RND[TILE_MAX];
1970 static boolean mapping_initialized = FALSE;
1977 mapping_EM_to_RND_list[] =
1979 { Xblank, EL_EMPTY },
1980 { Yacid_splash_eB, EL_EMPTY },
1981 { Yacid_splash_wB, EL_EMPTY },
1983 #ifdef EM_ENGINE_BAD_ROLL
1984 { Xstone_force_e, EL_ROCK },
1985 { Xstone_force_w, EL_ROCK },
1986 { Xnut_force_e, EL_NUT },
1987 { Xnut_force_w, EL_NUT },
1988 { Xspring_force_e, EL_SPRING },
1989 { Xspring_force_w, EL_SPRING },
1990 { Xemerald_force_e, EL_EMERALD },
1991 { Xemerald_force_w, EL_EMERALD },
1992 { Xdiamond_force_e, EL_DIAMOND },
1993 { Xdiamond_force_w, EL_DIAMOND },
1994 { Xbomb_force_e, EL_BOMB },
1995 { Xbomb_force_w, EL_BOMB },
1998 { Xstone, EL_ROCK },
1999 { Xstone_pause, EL_ROCK },
2000 { Xstone_fall, EL_ROCK },
2001 { Ystone_s, EL_ROCK },
2002 { Ystone_sB, EL_ROCK },
2003 { Ystone_e, EL_ROCK },
2004 { Ystone_eB, EL_ROCK },
2005 { Ystone_w, EL_ROCK },
2006 { Ystone_wB, EL_ROCK },
2008 { Xnut_pause, EL_NUT },
2009 { Xnut_fall, EL_NUT },
2011 { Ynut_sB, EL_NUT },
2013 { Ynut_eB, EL_NUT },
2015 { Ynut_wB, EL_NUT },
2016 { Xbug_n, EL_BUG_UP },
2017 { Xbug_e, EL_BUG_RIGHT },
2018 { Xbug_s, EL_BUG_DOWN },
2019 { Xbug_w, EL_BUG_LEFT },
2020 { Xbug_gon, EL_BUG_UP },
2021 { Xbug_goe, EL_BUG_RIGHT },
2022 { Xbug_gos, EL_BUG_DOWN },
2023 { Xbug_gow, EL_BUG_LEFT },
2024 { Ybug_n, EL_BUG_UP },
2025 { Ybug_nB, EL_BUG_UP },
2026 { Ybug_e, EL_BUG_RIGHT },
2027 { Ybug_eB, EL_BUG_RIGHT },
2028 { Ybug_s, EL_BUG_DOWN },
2029 { Ybug_sB, EL_BUG_DOWN },
2030 { Ybug_w, EL_BUG_LEFT },
2031 { Ybug_wB, EL_BUG_LEFT },
2032 { Ybug_w_n, EL_BUG_UP },
2033 { Ybug_n_e, EL_BUG_RIGHT },
2034 { Ybug_e_s, EL_BUG_DOWN },
2035 { Ybug_s_w, EL_BUG_LEFT },
2036 { Ybug_e_n, EL_BUG_UP },
2037 { Ybug_s_e, EL_BUG_RIGHT },
2038 { Ybug_w_s, EL_BUG_DOWN },
2039 { Ybug_n_w, EL_BUG_LEFT },
2040 { Ybug_stone, EL_ROCK },
2041 { Ybug_spring, EL_SPRING },
2042 { Xtank_n, EL_SPACESHIP_UP },
2043 { Xtank_e, EL_SPACESHIP_RIGHT },
2044 { Xtank_s, EL_SPACESHIP_DOWN },
2045 { Xtank_w, EL_SPACESHIP_LEFT },
2046 { Xtank_gon, EL_SPACESHIP_UP },
2047 { Xtank_goe, EL_SPACESHIP_RIGHT },
2048 { Xtank_gos, EL_SPACESHIP_DOWN },
2049 { Xtank_gow, EL_SPACESHIP_LEFT },
2050 { Ytank_n, EL_SPACESHIP_UP },
2051 { Ytank_nB, EL_SPACESHIP_UP },
2052 { Ytank_e, EL_SPACESHIP_RIGHT },
2053 { Ytank_eB, EL_SPACESHIP_RIGHT },
2054 { Ytank_s, EL_SPACESHIP_DOWN },
2055 { Ytank_sB, EL_SPACESHIP_DOWN },
2056 { Ytank_w, EL_SPACESHIP_LEFT },
2057 { Ytank_wB, EL_SPACESHIP_LEFT },
2058 { Ytank_w_n, EL_SPACESHIP_UP },
2059 { Ytank_n_e, EL_SPACESHIP_RIGHT },
2060 { Ytank_e_s, EL_SPACESHIP_DOWN },
2061 { Ytank_s_w, EL_SPACESHIP_LEFT },
2062 { Ytank_e_n, EL_SPACESHIP_UP },
2063 { Ytank_s_e, EL_SPACESHIP_RIGHT },
2064 { Ytank_w_s, EL_SPACESHIP_DOWN },
2065 { Ytank_n_w, EL_SPACESHIP_LEFT },
2066 { Ytank_stone, EL_ROCK },
2067 { Ytank_spring, EL_SPRING },
2068 { Xandroid, EL_EMC_ANDROID },
2069 { Xandroid_1_n, EL_EMC_ANDROID_UP },
2070 { Xandroid_2_n, EL_EMC_ANDROID_UP },
2071 { Xandroid_1_e, EL_EMC_ANDROID_RIGHT },
2072 { Xandroid_2_e, EL_EMC_ANDROID_RIGHT },
2073 { Xandroid_1_w, EL_EMC_ANDROID_LEFT },
2074 { Xandroid_2_w, EL_EMC_ANDROID_LEFT },
2075 { Xandroid_1_s, EL_EMC_ANDROID_DOWN },
2076 { Xandroid_2_s, EL_EMC_ANDROID_DOWN },
2077 { Yandroid_n, EL_EMC_ANDROID_UP },
2078 { Yandroid_nB, EL_EMC_ANDROID_UP },
2079 { Yandroid_ne, EL_EMC_ANDROID_RIGHT_UP },
2080 { Yandroid_neB, EL_EMC_ANDROID_RIGHT_UP },
2081 { Yandroid_e, EL_EMC_ANDROID_RIGHT },
2082 { Yandroid_eB, EL_EMC_ANDROID_RIGHT },
2083 { Yandroid_se, EL_EMC_ANDROID_RIGHT_DOWN },
2084 { Yandroid_seB, EL_EMC_ANDROID_RIGHT_DOWN },
2085 { Yandroid_s, EL_EMC_ANDROID_DOWN },
2086 { Yandroid_sB, EL_EMC_ANDROID_DOWN },
2087 { Yandroid_sw, EL_EMC_ANDROID_LEFT_DOWN },
2088 { Yandroid_swB, EL_EMC_ANDROID_LEFT_DOWN },
2089 { Yandroid_w, EL_EMC_ANDROID_LEFT },
2090 { Yandroid_wB, EL_EMC_ANDROID_LEFT },
2091 { Yandroid_nw, EL_EMC_ANDROID_LEFT_UP },
2092 { Yandroid_nwB, EL_EMC_ANDROID_LEFT_UP },
2093 { Xspring, EL_SPRING },
2094 { Xspring_pause, EL_SPRING },
2095 { Xspring_e, EL_SPRING },
2096 { Xspring_w, EL_SPRING },
2097 { Xspring_fall, EL_SPRING },
2098 { Yspring_s, EL_SPRING },
2099 { Yspring_sB, EL_SPRING },
2100 { Yspring_e, EL_SPRING },
2101 { Yspring_eB, EL_SPRING },
2102 { Yspring_w, EL_SPRING },
2103 { Yspring_wB, EL_SPRING },
2104 { Yspring_kill_e, EL_SPRING },
2105 { Yspring_kill_eB, EL_SPRING },
2106 { Yspring_kill_w, EL_SPRING },
2107 { Yspring_kill_wB, EL_SPRING },
2108 { Xeater_n, EL_YAMYAM },
2109 { Xeater_e, EL_YAMYAM },
2110 { Xeater_w, EL_YAMYAM },
2111 { Xeater_s, EL_YAMYAM },
2112 { Yeater_n, EL_YAMYAM },
2113 { Yeater_nB, EL_YAMYAM },
2114 { Yeater_e, EL_YAMYAM },
2115 { Yeater_eB, EL_YAMYAM },
2116 { Yeater_s, EL_YAMYAM },
2117 { Yeater_sB, EL_YAMYAM },
2118 { Yeater_w, EL_YAMYAM },
2119 { Yeater_wB, EL_YAMYAM },
2120 { Yeater_stone, EL_ROCK },
2121 { Yeater_spring, EL_SPRING },
2122 { Xalien, EL_ROBOT },
2123 { Xalien_pause, EL_ROBOT },
2124 { Yalien_n, EL_ROBOT },
2125 { Yalien_nB, EL_ROBOT },
2126 { Yalien_e, EL_ROBOT },
2127 { Yalien_eB, EL_ROBOT },
2128 { Yalien_s, EL_ROBOT },
2129 { Yalien_sB, EL_ROBOT },
2130 { Yalien_w, EL_ROBOT },
2131 { Yalien_wB, EL_ROBOT },
2132 { Yalien_stone, EL_ROCK },
2133 { Yalien_spring, EL_SPRING },
2134 { Xemerald, EL_EMERALD },
2135 { Xemerald_pause, EL_EMERALD },
2136 { Xemerald_fall, EL_EMERALD },
2137 { Xemerald_shine, EL_EMERALD },
2138 { Yemerald_s, EL_EMERALD },
2139 { Yemerald_sB, EL_EMERALD },
2140 { Yemerald_e, EL_EMERALD },
2141 { Yemerald_eB, EL_EMERALD },
2142 { Yemerald_w, EL_EMERALD },
2143 { Yemerald_wB, EL_EMERALD },
2144 { Yemerald_eat, EL_EMERALD },
2145 { Yemerald_stone, EL_ROCK },
2146 { Xdiamond, EL_DIAMOND },
2147 { Xdiamond_pause, EL_DIAMOND },
2148 { Xdiamond_fall, EL_DIAMOND },
2149 { Xdiamond_shine, EL_DIAMOND },
2150 { Ydiamond_s, EL_DIAMOND },
2151 { Ydiamond_sB, EL_DIAMOND },
2152 { Ydiamond_e, EL_DIAMOND },
2153 { Ydiamond_eB, EL_DIAMOND },
2154 { Ydiamond_w, EL_DIAMOND },
2155 { Ydiamond_wB, EL_DIAMOND },
2156 { Ydiamond_eat, EL_DIAMOND },
2157 { Ydiamond_stone, EL_ROCK },
2158 { Xdrip_fall, EL_AMOEBA_DROP },
2159 { Xdrip_stretch, EL_AMOEBA_DROP },
2160 { Xdrip_stretchB, EL_AMOEBA_DROP },
2161 { Xdrip_eat, EL_AMOEBA_DROP },
2162 { Ydrip_s1, EL_AMOEBA_DROP },
2163 { Ydrip_s1B, EL_AMOEBA_DROP },
2164 { Ydrip_s2, EL_AMOEBA_DROP },
2165 { Ydrip_s2B, EL_AMOEBA_DROP },
2167 { Xbomb_pause, EL_BOMB },
2168 { Xbomb_fall, EL_BOMB },
2169 { Ybomb_s, EL_BOMB },
2170 { Ybomb_sB, EL_BOMB },
2171 { Ybomb_e, EL_BOMB },
2172 { Ybomb_eB, EL_BOMB },
2173 { Ybomb_w, EL_BOMB },
2174 { Ybomb_wB, EL_BOMB },
2175 { Ybomb_eat, EL_BOMB },
2176 { Xballoon, EL_BALLOON },
2177 { Yballoon_n, EL_BALLOON },
2178 { Yballoon_nB, EL_BALLOON },
2179 { Yballoon_e, EL_BALLOON },
2180 { Yballoon_eB, EL_BALLOON },
2181 { Yballoon_s, EL_BALLOON },
2182 { Yballoon_sB, EL_BALLOON },
2183 { Yballoon_w, EL_BALLOON },
2184 { Yballoon_wB, EL_BALLOON },
2185 { Xgrass, EL_SAND },
2186 { Ygrass_nB, EL_SAND },
2187 { Ygrass_eB, EL_SAND },
2188 { Ygrass_sB, EL_SAND },
2189 { Ygrass_wB, EL_SAND },
2191 { Ydirt_nB, EL_SAND },
2192 { Ydirt_eB, EL_SAND },
2193 { Ydirt_sB, EL_SAND },
2194 { Ydirt_wB, EL_SAND },
2195 { Xacid_ne, EL_ACID_POOL_TOPRIGHT },
2196 { Xacid_se, EL_ACID_POOL_BOTTOMRIGHT },
2197 { Xacid_s, EL_ACID_POOL_BOTTOM },
2198 { Xacid_sw, EL_ACID_POOL_BOTTOMLEFT },
2199 { Xacid_nw, EL_ACID_POOL_TOPLEFT },
2200 { Xacid_1, EL_ACID },
2201 { Xacid_2, EL_ACID },
2202 { Xacid_3, EL_ACID },
2203 { Xacid_4, EL_ACID },
2204 { Xacid_5, EL_ACID },
2205 { Xacid_6, EL_ACID },
2206 { Xacid_7, EL_ACID },
2207 { Xacid_8, EL_ACID },
2208 { Xball_1, EL_EMC_GENERATOR_BALL },
2209 { Xball_1B, EL_EMC_GENERATOR_BALL },
2210 { Xball_2, EL_EMC_GENERATOR_BALL },
2211 { Xball_2B, EL_EMC_GENERATOR_BALL },
2212 { Yball_eat, EL_EMC_GENERATOR_BALL },
2213 { Xgrow_ns, EL_EMC_GROW },
2214 { Ygrow_ns_eat, EL_EMC_GROW },
2215 { Xgrow_ew, EL_EMC_GROW },
2216 { Ygrow_ew_eat, EL_EMC_GROW },
2217 { Xwonderwall, EL_MAGIC_WALL },
2218 { XwonderwallB, EL_MAGIC_WALL },
2219 { Xameuba_1, EL_AMOEBA_WET },
2220 { Xameuba_2, EL_AMOEBA_WET },
2221 { Xameuba_3, EL_AMOEBA_WET },
2222 { Xameuba_4, EL_AMOEBA_WET },
2223 { Xameuba_5, EL_AMOEBA_WET },
2224 { Xameuba_6, EL_AMOEBA_WET },
2225 { Xameuba_7, EL_AMOEBA_WET },
2226 { Xameuba_8, EL_AMOEBA_WET },
2227 { Xdoor_1, EL_EM_GATE_1 },
2228 { Xdoor_2, EL_EM_GATE_2 },
2229 { Xdoor_3, EL_EM_GATE_3 },
2230 { Xdoor_4, EL_EM_GATE_4 },
2231 { Xdoor_5, EL_EMC_GATE_5 },
2232 { Xdoor_6, EL_EMC_GATE_6 },
2233 { Xdoor_7, EL_EMC_GATE_7 },
2234 { Xdoor_8, EL_EMC_GATE_8 },
2235 { Xkey_1, EL_EM_KEY_1 },
2236 { Xkey_2, EL_EM_KEY_2 },
2237 { Xkey_3, EL_EM_KEY_3 },
2238 { Xkey_4, EL_EM_KEY_4 },
2239 { Xkey_5, EL_EMC_KEY_5 },
2240 { Xkey_6, EL_EMC_KEY_6 },
2241 { Xkey_7, EL_EMC_KEY_7 },
2242 { Xkey_8, EL_EMC_KEY_8 },
2243 { Xwind_n, EL_BALLOON_SWITCH_UP },
2244 { Xwind_e, EL_BALLOON_SWITCH_RIGHT },
2245 { Xwind_s, EL_BALLOON_SWITCH_DOWN },
2246 { Xwind_w, EL_BALLOON_SWITCH_LEFT },
2247 { Xwind_nesw, EL_BALLOON_SWITCH_ANY },
2248 { Xwind_stop, EL_BALLOON_SWITCH_NONE },
2249 { Xexit, EL_EXIT_CLOSED },
2250 { Xexit_1, EL_EXIT_OPEN },
2251 { Xexit_2, EL_EXIT_OPEN },
2252 { Xexit_3, EL_EXIT_OPEN },
2253 { Xdynamite, EL_DYNAMITE },
2254 { Ydynamite_eat, EL_DYNAMITE },
2255 { Xdynamite_1, EL_DYNAMITE_ACTIVE },
2256 { Xdynamite_2, EL_DYNAMITE_ACTIVE },
2257 { Xdynamite_3, EL_DYNAMITE_ACTIVE },
2258 { Xdynamite_4, EL_DYNAMITE_ACTIVE },
2259 { Xbumper, EL_EMC_BUMPER },
2260 { XbumperB, EL_EMC_BUMPER },
2261 { Xwheel, EL_ROBOT_WHEEL },
2262 { XwheelB, EL_ROBOT_WHEEL },
2263 { Xswitch, EL_UNKNOWN },
2264 { XswitchB, EL_UNKNOWN },
2265 { Xsand, EL_QUICKSAND_EMPTY },
2266 { Xsand_stone, EL_QUICKSAND_FULL },
2267 { Xsand_stonein_1, EL_QUICKSAND_FULL },
2268 { Xsand_stonein_2, EL_QUICKSAND_FULL },
2269 { Xsand_stonein_3, EL_QUICKSAND_FULL },
2270 { Xsand_stonein_4, EL_QUICKSAND_FULL },
2271 { Xsand_stonesand_1, EL_QUICKSAND_FULL },
2272 { Xsand_stonesand_2, EL_QUICKSAND_FULL },
2273 { Xsand_stonesand_3, EL_QUICKSAND_FULL },
2274 { Xsand_stonesand_4, EL_QUICKSAND_FULL },
2275 { Xsand_stoneout_1, EL_QUICKSAND_FULL },
2276 { Xsand_stoneout_2, EL_QUICKSAND_FULL },
2277 { Xsand_sandstone_1, EL_QUICKSAND_FULL },
2278 { Xsand_sandstone_2, EL_QUICKSAND_FULL },
2279 { Xsand_sandstone_3, EL_QUICKSAND_FULL },
2280 { Xsand_sandstone_4, EL_QUICKSAND_FULL },
2281 { Xplant, EL_EMC_PLANT },
2282 { Yplant, EL_EMC_PLANT },
2283 { Xlenses, EL_EMC_LENSES },
2284 { Xmagnify, EL_EMC_MAGNIFY },
2285 { Xdripper, EL_UNKNOWN },
2286 { XdripperB, EL_UNKNOWN },
2287 { Xfake_blank, EL_INVISIBLE_WALL },
2288 { Xfake_blankB, EL_INVISIBLE_WALL },
2289 { Xfake_grass, EL_INVISIBLE_SAND },
2290 { Xfake_grassB, EL_INVISIBLE_SAND },
2291 { Xfake_door_1, EL_EM_GATE_1_GRAY },
2292 { Xfake_door_2, EL_EM_GATE_2_GRAY },
2293 { Xfake_door_3, EL_EM_GATE_3_GRAY },
2294 { Xfake_door_4, EL_EM_GATE_4_GRAY },
2295 { Xfake_door_5, EL_EMC_GATE_5_GRAY },
2296 { Xfake_door_6, EL_EMC_GATE_6_GRAY },
2297 { Xfake_door_7, EL_EMC_GATE_7_GRAY },
2298 { Xfake_door_8, EL_EMC_GATE_8_GRAY },
2299 { Xsteel_1, EL_STEELWALL },
2300 { Xsteel_2, EL_UNKNOWN },
2301 { Xsteel_3, EL_EMC_STEELWALL_1 },
2302 { Xsteel_4, EL_UNKNOWN },
2303 { Xwall_1, EL_WALL },
2304 { Xwall_2, EL_UNKNOWN },
2305 { Xwall_3, EL_UNKNOWN },
2306 { Xwall_4, EL_UNKNOWN },
2307 { Xround_wall_1, EL_WALL_SLIPPERY },
2308 { Xround_wall_2, EL_UNKNOWN },
2309 { Xround_wall_3, EL_UNKNOWN },
2310 { Xround_wall_4, EL_UNKNOWN },
2311 { Xdecor_1, EL_UNKNOWN },
2312 { Xdecor_2, EL_EMC_WALL_6 },
2313 { Xdecor_3, EL_EMC_WALL_4 },
2314 { Xdecor_4, EL_EMC_WALL_5 },
2315 { Xdecor_5, EL_EMC_WALL_7 },
2316 { Xdecor_6, EL_EMC_WALL_8 },
2317 { Xdecor_7, EL_UNKNOWN },
2318 { Xdecor_8, EL_EMC_WALL_1 },
2319 { Xdecor_9, EL_EMC_WALL_2 },
2320 { Xdecor_10, EL_EMC_WALL_3 },
2321 { Xdecor_11, EL_UNKNOWN },
2322 { Xdecor_12, EL_UNKNOWN },
2323 { Xalpha_0, EL_CHAR('0') },
2324 { Xalpha_1, EL_CHAR('1') },
2325 { Xalpha_2, EL_CHAR('2') },
2326 { Xalpha_3, EL_CHAR('3') },
2327 { Xalpha_4, EL_CHAR('4') },
2328 { Xalpha_5, EL_CHAR('5') },
2329 { Xalpha_6, EL_CHAR('6') },
2330 { Xalpha_7, EL_CHAR('7') },
2331 { Xalpha_8, EL_CHAR('8') },
2332 { Xalpha_9, EL_CHAR('9') },
2333 { Xalpha_excla, EL_CHAR('!') },
2334 { Xalpha_quote, EL_CHAR('"') },
2335 { Xalpha_comma, EL_CHAR(',') },
2336 { Xalpha_minus, EL_CHAR('-') },
2337 { Xalpha_perio, EL_CHAR('.') },
2338 { Xalpha_colon, EL_CHAR(':') },
2339 { Xalpha_quest, EL_CHAR('?') },
2340 { Xalpha_a, EL_CHAR('A') },
2341 { Xalpha_b, EL_CHAR('B') },
2342 { Xalpha_c, EL_CHAR('C') },
2343 { Xalpha_d, EL_CHAR('D') },
2344 { Xalpha_e, EL_CHAR('E') },
2345 { Xalpha_f, EL_CHAR('F') },
2346 { Xalpha_g, EL_CHAR('G') },
2347 { Xalpha_h, EL_CHAR('H') },
2348 { Xalpha_i, EL_CHAR('I') },
2349 { Xalpha_j, EL_CHAR('J') },
2350 { Xalpha_k, EL_CHAR('K') },
2351 { Xalpha_l, EL_CHAR('L') },
2352 { Xalpha_m, EL_CHAR('M') },
2353 { Xalpha_n, EL_CHAR('N') },
2354 { Xalpha_o, EL_CHAR('O') },
2355 { Xalpha_p, EL_CHAR('P') },
2356 { Xalpha_q, EL_CHAR('Q') },
2357 { Xalpha_r, EL_CHAR('R') },
2358 { Xalpha_s, EL_CHAR('S') },
2359 { Xalpha_t, EL_CHAR('T') },
2360 { Xalpha_u, EL_CHAR('U') },
2361 { Xalpha_v, EL_CHAR('V') },
2362 { Xalpha_w, EL_CHAR('W') },
2363 { Xalpha_x, EL_CHAR('X') },
2364 { Xalpha_y, EL_CHAR('Y') },
2365 { Xalpha_z, EL_CHAR('Z') },
2366 { Xalpha_arrow_e, EL_CHAR('>') },
2367 { Xalpha_arrow_w, EL_CHAR('<') },
2368 { Xalpha_copyr, EL_CHAR('©') },
2370 { Zplayer, EL_PLAYER_1 },
2372 { ZBORDER, EL_EMC_LEVEL_BORDER },
2377 if (!mapping_initialized)
2381 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
2382 for (i = 0; i < TILE_MAX; i++)
2383 mapping_EM_to_RND[i] = EL_UNKNOWN;
2385 for (i = 0; mapping_EM_to_RND_list[i].element_em != -1; i++)
2386 mapping_EM_to_RND[mapping_EM_to_RND_list[i].element_em] =
2387 mapping_EM_to_RND_list[i].element_rnd;
2389 mapping_initialized = TRUE;
2392 if (element_em >= 0 && element_em < TILE_MAX)
2393 return mapping_EM_to_RND[element_em];
2395 Error(ERR_WARN, "invalid EM level element %d", element_em);
2400 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
2402 struct LevelInfo_EM *level_em = level->native_em_level;
2403 struct LEVEL *lev = level_em->lev;
2404 struct PLAYER *ply1 = level_em->ply1;
2405 struct PLAYER *ply2 = level_em->ply2;
2408 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
2409 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
2411 lev->time_initial = level->time;
2412 lev->required_initial = level->gems_needed;
2414 lev->emerald_score = level->score[SC_EMERALD];
2415 lev->diamond_score = level->score[SC_DIAMOND];
2416 lev->alien_score = level->score[SC_ROBOT];
2417 lev->tank_score = level->score[SC_SPACESHIP];
2418 lev->bug_score = level->score[SC_BUG];
2419 lev->eater_score = level->score[SC_YAMYAM];
2420 lev->nut_score = level->score[SC_NUT];
2421 lev->dynamite_score = level->score[SC_DYNAMITE];
2422 lev->key_score = level->score[SC_TIME_BONUS]; /* ??? CHECK THIS */
2424 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2425 for (y = 0; y < 3; y++)
2426 for (x = 0; x < 3; x++)
2427 lev->eater_array[i][y * 3 + x] =
2428 map_element_RND_to_EM(level->yamyam_content[i][x][y]);
2430 lev->ameuba_time = level->amoeba_speed;
2431 lev->wonderwall_time_initial = level->time_magic_wall;
2432 lev->wheel_time = level->time_wheel;
2434 /* first fill the complete playfield with the default border element */
2435 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
2436 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
2437 level_em->cave[x][y] = ZBORDER;
2439 /* then copy the real level contents from level file into the playfield */
2440 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2442 int new_element = map_element_RND_to_EM(level->field[x][y]);
2444 level_em->cave[x + 1][y + 1] = new_element;
2447 ply1->x_initial = 0;
2448 ply1->y_initial = 0;
2450 ply2->x_initial = 0;
2451 ply2->y_initial = 0;
2453 /* at last, set the two players at their positions in the playfield */
2454 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2456 if (level->field[x][y] == EL_PLAYER_1)
2458 ply1->x_initial = x + 1;
2459 ply1->y_initial = y + 1;
2461 else if (level->field[x][y] == EL_PLAYER_2)
2463 ply2->x_initial = x + 1;
2464 ply2->y_initial = y + 1;
2469 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
2471 struct LevelInfo_EM *level_em = level->native_em_level;
2472 struct LEVEL *lev = level_em->lev;
2473 struct PLAYER *ply1 = level_em->ply1;
2474 struct PLAYER *ply2 = level_em->ply2;
2477 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
2478 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
2480 level->time = lev->time_initial;
2481 level->gems_needed = lev->required_initial;
2483 sprintf(level->name, "Level %d", level->file_info.nr);
2485 level->score[SC_EMERALD] = lev->emerald_score;
2486 level->score[SC_DIAMOND] = lev->diamond_score;
2487 level->score[SC_ROBOT] = lev->alien_score;
2488 level->score[SC_SPACESHIP] = lev->tank_score;
2489 level->score[SC_BUG] = lev->bug_score;
2490 level->score[SC_YAMYAM] = lev->eater_score;
2491 level->score[SC_NUT] = lev->nut_score;
2492 level->score[SC_DYNAMITE] = lev->dynamite_score;
2493 level->score[SC_TIME_BONUS] = lev->key_score; /* ??? CHECK THIS */
2495 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
2497 for (i = 0; i < level->num_yamyam_contents; i++)
2498 for (y = 0; y < 3; y++)
2499 for (x = 0; x < 3; x++)
2500 level->yamyam_content[i][x][y] =
2501 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
2503 level->amoeba_speed = lev->ameuba_time;
2504 level->time_magic_wall = lev->wonderwall_time_initial;
2505 level->time_wheel = lev->wheel_time;
2507 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2509 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
2511 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
2512 new_element = EL_AMOEBA_DEAD;
2514 level->field[x][y] = new_element;
2517 level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
2518 level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
2521 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2522 struct LevelFileInfo *level_file_info)
2524 if (!LoadNativeLevel_EM(level_file_info->filename))
2525 level->no_valid_file = TRUE;
2530 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
2532 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2533 CopyNativeLevel_RND_to_EM(level);
2536 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
2538 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2539 CopyNativeLevel_EM_to_RND(level);
2543 /* ------------------------------------------------------------------------- */
2544 /* functions for loading SP level */
2545 /* ------------------------------------------------------------------------- */
2547 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
2548 #define SP_LEVEL_SIZE 1536
2549 #define SP_LEVEL_XSIZE 60
2550 #define SP_LEVEL_YSIZE 24
2551 #define SP_LEVEL_NAME_LEN 23
2553 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
2556 int num_special_ports;
2559 /* for details of the Supaplex level format, see Herman Perk's Supaplex
2560 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
2562 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
2563 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2565 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2567 int element_old = fgetc(file);
2570 if (element_old <= 0x27)
2571 element_new = getMappedElement(EL_SP_START + element_old);
2572 else if (element_old == 0x28)
2573 element_new = EL_INVISIBLE_WALL;
2576 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
2577 Error(ERR_WARN, "invalid level element %d", element_old);
2579 element_new = EL_UNKNOWN;
2582 level->field[x][y] = element_new;
2586 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2588 /* initial gravity: 1 == "on", anything else (0) == "off" */
2589 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
2591 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2593 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
2594 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
2595 level->name[i] = fgetc(file);
2596 level->name[SP_LEVEL_NAME_LEN] = '\0';
2598 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
2599 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2601 /* number of infotrons needed; 0 means that Supaplex will count the total
2602 amount of infotrons in the level and use the low byte of that number
2603 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
2604 level->gems_needed = fgetc(file);
2606 /* number of special ("gravity") port entries below (maximum 10 allowed) */
2607 num_special_ports = fgetc(file);
2609 /* database of properties of up to 10 special ports (6 bytes per port) */
2610 for (i = 0; i < 10; i++)
2612 int port_location, port_x, port_y, port_element;
2615 /* high and low byte of the location of a special port; if (x, y) are the
2616 coordinates of a port in the field and (0, 0) is the top-left corner,
2617 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
2618 of what may be expected: Supaplex works with a game field in memory
2619 which is 2 bytes per tile) */
2620 port_location = getFile16BitBE(file);
2622 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
2623 gravity = fgetc(file);
2625 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
2626 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2628 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
2629 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2631 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2633 if (i >= num_special_ports)
2636 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2637 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2639 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2640 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2642 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2648 port_element = level->field[port_x][port_y];
2650 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2651 port_element > EL_SP_GRAVITY_PORT_UP)
2653 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2658 /* change previous (wrong) gravity inverting special port to either
2659 gravity enabling special port or gravity disabling special port */
2660 level->field[port_x][port_y] +=
2661 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2662 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2665 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2667 /* change special gravity ports without database entries to normal ports */
2668 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2669 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2670 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2671 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2672 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2674 /* auto-determine number of infotrons if it was stored as "0" -- see above */
2675 if (level->gems_needed == 0)
2677 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2678 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2679 if (level->field[x][y] == EL_SP_INFOTRON)
2680 level->gems_needed++;
2682 level->gems_needed &= 0xff; /* only use low byte -- see above */
2685 level->fieldx = SP_LEVEL_XSIZE;
2686 level->fieldy = SP_LEVEL_YSIZE;
2688 level->time = 0; /* no time limit */
2689 level->amoeba_speed = 0;
2690 level->time_magic_wall = 0;
2691 level->time_wheel = 0;
2692 level->amoeba_content = EL_EMPTY;
2694 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2695 level->score[i] = 0; /* !!! CORRECT THIS !!! */
2697 /* there are no yamyams in supaplex levels */
2698 for (i = 0; i < level->num_yamyam_contents; i++)
2699 for (y = 0; y < 3; y++)
2700 for (x = 0; x < 3; x++)
2701 level->yamyam_content[i][x][y] = EL_EMPTY;
2704 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2705 struct LevelFileInfo *level_file_info)
2707 char *filename = level_file_info->filename;
2709 int nr = level_file_info->nr - leveldir_current->first_level;
2711 char name_first, name_last;
2712 struct LevelInfo multipart_level;
2713 int multipart_xpos, multipart_ypos;
2714 boolean is_multipart_level;
2715 boolean is_first_part;
2716 boolean reading_multipart_level = FALSE;
2717 boolean use_empty_level = FALSE;
2719 if (!(file = fopen(filename, MODE_READ)))
2721 level->no_valid_file = TRUE;
2723 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2728 /* position file stream to the requested level inside the level package */
2729 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2731 level->no_valid_file = TRUE;
2733 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2738 /* there exist Supaplex level package files with multi-part levels which
2739 can be detected as follows: instead of leading and trailing dashes ('-')
2740 to pad the level name, they have leading and trailing numbers which are
2741 the x and y coordinations of the current part of the multi-part level;
2742 if there are '?' characters instead of numbers on the left or right side
2743 of the level name, the multi-part level consists of only horizontal or
2746 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2748 LoadLevelFromFileStream_SP(file, level, l);
2750 /* check if this level is a part of a bigger multi-part level */
2752 name_first = level->name[0];
2753 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
2755 is_multipart_level =
2756 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2757 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
2760 ((name_first == '?' || name_first == '1') &&
2761 (name_last == '?' || name_last == '1'));
2763 /* correct leading multipart level meta information in level name */
2764 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2765 level->name[i] = '-';
2767 /* correct trailing multipart level meta information in level name */
2768 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2769 level->name[i] = '-';
2771 /* ---------- check for normal single level ---------- */
2773 if (!reading_multipart_level && !is_multipart_level)
2775 /* the current level is simply a normal single-part level, and we are
2776 not reading a multi-part level yet, so return the level as it is */
2781 /* ---------- check for empty level (unused multi-part) ---------- */
2783 if (!reading_multipart_level && is_multipart_level && !is_first_part)
2785 /* this is a part of a multi-part level, but not the first part
2786 (and we are not already reading parts of a multi-part level);
2787 in this case, use an empty level instead of the single part */
2789 use_empty_level = TRUE;
2794 /* ---------- check for finished multi-part level ---------- */
2796 if (reading_multipart_level &&
2797 (!is_multipart_level ||
2798 strcmp(level->name, multipart_level.name) != 0))
2800 /* we are already reading parts of a multi-part level, but this level is
2801 either not a multi-part level, or a part of a different multi-part
2802 level; in both cases, the multi-part level seems to be complete */
2807 /* ---------- here we have one part of a multi-part level ---------- */
2809 reading_multipart_level = TRUE;
2811 if (is_first_part) /* start with first part of new multi-part level */
2813 /* copy level info structure from first part */
2814 multipart_level = *level;
2816 /* clear playfield of new multi-part level */
2817 for (y = 0; y < MAX_LEV_FIELDY; y++)
2818 for (x = 0; x < MAX_LEV_FIELDX; x++)
2819 multipart_level.field[x][y] = EL_EMPTY;
2822 if (name_first == '?')
2824 if (name_last == '?')
2827 multipart_xpos = (int)(name_first - '0');
2828 multipart_ypos = (int)(name_last - '0');
2831 printf("----------> part (%d/%d) of multi-part level '%s'\n",
2832 multipart_xpos, multipart_ypos, multipart_level.name);
2835 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2836 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2838 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2843 multipart_level.fieldx = MAX(multipart_level.fieldx,
2844 multipart_xpos * SP_LEVEL_XSIZE);
2845 multipart_level.fieldy = MAX(multipart_level.fieldy,
2846 multipart_ypos * SP_LEVEL_YSIZE);
2848 /* copy level part at the right position of multi-part level */
2849 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2851 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2853 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2854 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2856 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2863 if (use_empty_level)
2865 setLevelInfoToDefaults(level);
2867 level->fieldx = SP_LEVEL_XSIZE;
2868 level->fieldy = SP_LEVEL_YSIZE;
2870 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2871 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2872 level->field[x][y] = EL_EMPTY;
2874 strcpy(level->name, "-------- EMPTY --------");
2876 Error(ERR_WARN, "single part of multi-part level -- using empty level");
2879 if (reading_multipart_level)
2880 *level = multipart_level;
2883 /* ------------------------------------------------------------------------- */
2884 /* functions for loading generic level */
2885 /* ------------------------------------------------------------------------- */
2887 void LoadLevelFromFileInfo(struct LevelInfo *level,
2888 struct LevelFileInfo *level_file_info)
2890 /* always start with reliable default values */
2891 setLevelInfoToDefaults(level);
2893 switch (level_file_info->type)
2895 case LEVEL_FILE_TYPE_RND:
2896 LoadLevelFromFileInfo_RND(level, level_file_info);
2899 case LEVEL_FILE_TYPE_EM:
2900 LoadLevelFromFileInfo_EM(level, level_file_info);
2901 level->game_engine_type = GAME_ENGINE_TYPE_EM;
2904 case LEVEL_FILE_TYPE_SP:
2905 LoadLevelFromFileInfo_SP(level, level_file_info);
2909 LoadLevelFromFileInfo_RND(level, level_file_info);
2913 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2914 level->game_engine_type = GAME_ENGINE_TYPE_RND;
2916 CopyNativeLevel_Native_to_RND(level);
2919 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2921 static struct LevelFileInfo level_file_info;
2923 /* always start with reliable default values */
2924 setFileInfoToDefaults(&level_file_info);
2926 level_file_info.nr = 0; /* unknown level number */
2927 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2928 level_file_info.filename = filename;
2930 LoadLevelFromFileInfo(level, &level_file_info);
2933 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2935 if (leveldir_current == NULL) /* only when dumping level */
2939 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2942 /* determine correct game engine version of current level */
2944 if (!leveldir_current->latest_engine)
2946 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2947 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2948 IS_LEVELCLASS_UNDEFINED(leveldir_current))
2952 printf("\n::: This level is private or contributed: '%s'\n", filename);
2956 printf("\n::: Use the stored game engine version for this level\n");
2959 /* For all levels which are not forced to use the latest game engine
2960 version (normally user contributed, private and undefined levels),
2961 use the version of the game engine the levels were created for.
2963 Since 2.0.1, the game engine version is now directly stored
2964 in the level file (chunk "VERS"), so there is no need anymore
2965 to set the game version from the file version (except for old,
2966 pre-2.0 levels, where the game version is still taken from the
2967 file format version used to store the level -- see above). */
2970 /* player was faster than enemies in 1.0.0 and before */
2971 if (level->file_version == FILE_VERSION_1_0)
2972 level->double_speed = TRUE;
2974 /* do some special adjustments to support older level versions */
2975 if (level->file_version == FILE_VERSION_1_0)
2977 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2978 Error(ERR_WARN, "using high speed movement for player");
2980 /* player was faster than monsters in (pre-)1.0 levels */
2981 level->double_speed = TRUE;
2985 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2986 if (level->game_version == VERSION_IDENT(2,0,1,0))
2987 level->em_slippery_gems = TRUE;
2989 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2990 if (level->game_version < VERSION_IDENT(2,2,0,0))
2991 level->use_spring_bug = TRUE;
2993 /* only few elements were able to actively move into acid before 3.1.0 */
2994 if (level->game_version < VERSION_IDENT(3,1,0,0))
2998 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2999 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
3001 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
3002 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
3003 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
3004 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
3006 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3007 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 struct ElementChangeInfo *change = &ei->change_page[j];
3018 change->trigger_player = CH_PLAYER_ANY;
3019 change->trigger_page = CH_PAGE_ANY;
3024 #if 1 /* USE_NEW_BLOCK_STYLE */
3025 /* blocking the last field when moving was corrected in version 3.1.1 */
3026 if (level->game_version < VERSION_IDENT(3,1,1,0))
3029 printf("::: %d\n", level->block_last_field);
3032 /* even "not blocking" was blocking the last field for one frame */
3033 level->block_delay = (level->block_last_field ? 7 : 1);
3034 level->sp_block_delay = (level->sp_block_last_field ? 7 : 1);
3036 level->block_last_field = TRUE;
3037 level->sp_block_last_field = TRUE;
3041 else /* always use the latest game engine version */
3044 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
3045 leveldir_current->sort_priority, filename);
3049 printf("\n::: Use latest game engine version for this level.\n");
3052 /* For all levels which are forced to use the latest game engine version
3053 (normally all but user contributed, private and undefined levels), set
3054 the game engine version to the actual version; this allows for actual
3055 corrections in the game engine to take effect for existing, converted
3056 levels (from "classic" or other existing games) to make the emulation
3057 of the corresponding game more accurate, while (hopefully) not breaking
3058 existing levels created from other players. */
3061 printf("::: changing engine from %d to %d\n",
3062 level->game_version, GAME_VERSION_ACTUAL);
3065 level->game_version = GAME_VERSION_ACTUAL;
3067 /* Set special EM style gems behaviour: EM style gems slip down from
3068 normal, steel and growing wall. As this is a more fundamental change,
3069 it seems better to set the default behaviour to "off" (as it is more
3070 natural) and make it configurable in the level editor (as a property
3071 of gem style elements). Already existing converted levels (neither
3072 private nor contributed levels) are changed to the new behaviour. */
3074 if (level->file_version < FILE_VERSION_2_0)
3075 level->em_slippery_gems = TRUE;
3079 printf("::: => %d\n", level->game_version);
3083 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
3087 /* map custom element change events that have changed in newer versions
3088 (these following values were accidentally changed in version 3.0.1) */
3089 if (level->game_version <= VERSION_IDENT(3,0,0,0))
3091 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3093 int element = EL_CUSTOM_START + i;
3095 /* order of checking and copying events to be mapped is important */
3096 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
3098 if (HAS_CHANGE_EVENT(element, j - 2))
3100 SET_CHANGE_EVENT(element, j - 2, FALSE);
3101 SET_CHANGE_EVENT(element, j, TRUE);
3105 /* order of checking and copying events to be mapped is important */
3106 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
3108 if (HAS_CHANGE_EVENT(element, j - 1))
3110 SET_CHANGE_EVENT(element, j - 1, FALSE);
3111 SET_CHANGE_EVENT(element, j, TRUE);
3117 /* some custom element change events get mapped since version 3.0.3 */
3118 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3120 int element = EL_CUSTOM_START + i;
3122 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
3123 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
3125 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
3126 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
3128 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
3132 /* initialize "can_change" field for old levels with only one change page */
3133 if (level->game_version <= VERSION_IDENT(3,0,2,0))
3135 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3137 int element = EL_CUSTOM_START + i;
3139 if (CAN_CHANGE(element))
3140 element_info[element].change->can_change = TRUE;
3144 /* correct custom element values (for old levels without these options) */
3145 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3147 int element = EL_CUSTOM_START + i;
3148 struct ElementInfo *ei = &element_info[element];
3150 if (ei->access_direction == MV_NO_MOVING)
3151 ei->access_direction = MV_ALL_DIRECTIONS;
3153 for (j = 0; j < ei->num_change_pages; j++)
3155 struct ElementChangeInfo *change = &ei->change_page[j];
3157 if (change->trigger_side == CH_SIDE_NONE)
3158 change->trigger_side = CH_SIDE_ANY;
3162 /* initialize "can_explode" field for old levels which did not store this */
3163 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
3164 if (level->game_version <= VERSION_IDENT(3,1,0,0))
3166 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168 int element = EL_CUSTOM_START + i;
3170 if (EXPLODES_1X1_OLD(element))
3171 element_info[element].explosion_type = EXPLODES_1X1;
3173 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
3174 EXPLODES_SMASHED(element) ||
3175 EXPLODES_IMPACT(element)));
3179 /* correct previously hard-coded move delay values for maze runner style */
3180 if (level->game_version < VERSION_IDENT(3,1,1,0))
3182 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3184 int element = EL_CUSTOM_START + i;
3186 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
3188 /* previously hard-coded and therefore ignored */
3189 element_info[element].move_delay_fixed = 9;
3190 element_info[element].move_delay_random = 0;
3196 /* set default push delay values (corrected since version 3.0.7-1) */
3197 if (level->game_version < VERSION_IDENT(3,0,7,1))
3199 game.default_push_delay_fixed = 2;
3200 game.default_push_delay_random = 8;
3204 game.default_push_delay_fixed = 8;
3205 game.default_push_delay_random = 8;
3208 /* set uninitialized push delay values of custom elements in older levels */
3209 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3211 int element = EL_CUSTOM_START + i;
3213 if (element_info[element].push_delay_fixed == -1)
3214 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
3215 if (element_info[element].push_delay_random == -1)
3216 element_info[element].push_delay_random = game.default_push_delay_random;
3220 /* map elements that have changed in newer versions */
3221 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
3222 level->game_version);
3223 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3224 for (x = 0; x < 3; x++)
3225 for (y = 0; y < 3; y++)
3226 level->yamyam_content[i][x][y] =
3227 getMappedElementByVersion(level->yamyam_content[i][x][y],
3228 level->game_version);
3230 /* initialize element properties for level editor etc. */
3231 InitElementPropertiesEngine(level->game_version);
3234 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
3238 /* map elements that have changed in newer versions */
3239 for (y = 0; y < level->fieldy; y++)
3241 for (x = 0; x < level->fieldx; x++)
3243 int element = level->field[x][y];
3246 element = getMappedElementByVersion(element, level->game_version);
3248 if (level->game_version <= VERSION_IDENT(2,2,0,0))
3250 /* map game font elements */
3251 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3252 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3253 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3254 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3257 if (level->game_version < VERSION_IDENT(3,0,0,0))
3259 /* map Supaplex gravity tube elements */
3260 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3261 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3262 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3263 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3268 level->field[x][y] = element;
3272 /* copy elements to runtime playfield array */
3273 for (x = 0; x < MAX_LEV_FIELDX; x++)
3274 for (y = 0; y < MAX_LEV_FIELDY; y++)
3275 Feld[x][y] = level->field[x][y];
3277 /* initialize level size variables for faster access */
3278 lev_fieldx = level->fieldx;
3279 lev_fieldy = level->fieldy;
3281 /* determine border element for this level */
3285 void LoadLevelTemplate(int nr)
3290 setLevelFileInfo(&level_template.file_info, nr);
3291 filename = level_template.file_info.filename;
3293 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
3298 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
3299 char *filename = level_file_info->filename;
3301 LoadLevelFromFileInfo(&level_template, level_file_info);
3303 char *filename = getDefaultLevelFilename(nr);
3305 LoadLevelFromFilename_RND(&level_template, filename);
3310 LoadLevel_InitVersion(&level_template, filename);
3311 LoadLevel_InitElements(&level_template, filename);
3313 LoadLevel_InitVersion(&level, filename);
3314 LoadLevel_InitElements(&level, filename);
3317 ActivateLevelTemplate();
3320 void LoadLevel(int nr)
3325 setLevelFileInfo(&level.file_info, nr);
3326 filename = level.file_info.filename;
3328 LoadLevelFromFileInfo(&level, &level.file_info);
3333 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
3334 char *filename = level_file_info->filename;
3336 LoadLevelFromFileInfo(&level, level_file_info);
3338 char *filename = getLevelFilename(nr);
3340 LoadLevelFromFilename_RND(&level, filename);
3344 if (level.use_custom_template)
3345 LoadLevelTemplate(-1);
3348 LoadLevel_InitVersion(&level, filename);
3349 LoadLevel_InitElements(&level, filename);
3350 LoadLevel_InitPlayfield(&level, filename);
3352 LoadLevel_InitLevel(&level, filename);
3356 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
3358 putFileVersion(file, level->file_version);
3359 putFileVersion(file, level->game_version);
3362 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
3366 putFile8Bit(file, level->fieldx);
3367 putFile8Bit(file, level->fieldy);
3369 putFile16BitBE(file, level->time);
3370 putFile16BitBE(file, level->gems_needed);
3372 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3373 putFile8Bit(file, level->name[i]);
3375 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3376 putFile8Bit(file, level->score[i]);
3378 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3379 for (y = 0; y < 3; y++)
3380 for (x = 0; x < 3; x++)
3381 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3382 level->yamyam_content[i][x][y]));
3383 putFile8Bit(file, level->amoeba_speed);
3384 putFile8Bit(file, level->time_magic_wall);
3385 putFile8Bit(file, level->time_wheel);
3386 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3387 level->amoeba_content));
3388 putFile8Bit(file, (level->double_speed ? 1 : 0));
3389 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3390 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3391 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3393 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3395 putFile8Bit(file, (level->block_last_field ? 1 : 0));
3396 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3397 putFile32BitBE(file, level->can_move_into_acid_bits);
3398 putFile8Bit(file, level->dont_collide_with_bits);
3400 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3401 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3403 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3404 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3405 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3407 putFile8Bit(file, level->game_engine_type);
3409 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3412 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3416 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3417 putFile8Bit(file, level->author[i]);
3420 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3424 for (y = 0; y < level->fieldy; y++)
3425 for (x = 0; x < level->fieldx; x++)
3426 if (level->encoding_16bit_field)
3427 putFile16BitBE(file, level->field[x][y]);
3429 putFile8Bit(file, level->field[x][y]);
3433 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3437 putFile8Bit(file, EL_YAMYAM);
3438 putFile8Bit(file, level->num_yamyam_contents);
3439 putFile8Bit(file, 0);
3440 putFile8Bit(file, 0);
3442 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3443 for (y = 0; y < 3; y++)
3444 for (x = 0; x < 3; x++)
3445 if (level->encoding_16bit_field)
3446 putFile16BitBE(file, level->yamyam_content[i][x][y]);
3448 putFile8Bit(file, level->yamyam_content[i][x][y]);
3452 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3455 int num_contents, content_xsize, content_ysize;
3456 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3458 if (element == EL_YAMYAM)
3460 num_contents = level->num_yamyam_contents;
3464 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3465 for (y = 0; y < 3; y++)
3466 for (x = 0; x < 3; x++)
3467 content_array[i][x][y] = level->yamyam_content[i][x][y];
3469 else if (element == EL_BD_AMOEBA)
3475 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3476 for (y = 0; y < 3; y++)
3477 for (x = 0; x < 3; x++)
3478 content_array[i][x][y] = EL_EMPTY;
3479 content_array[0][0][0] = level->amoeba_content;
3483 /* chunk header already written -- write empty chunk data */
3484 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3486 Error(ERR_WARN, "cannot save content for element '%d'", element);
3490 putFile16BitBE(file, element);
3491 putFile8Bit(file, num_contents);
3492 putFile8Bit(file, content_xsize);
3493 putFile8Bit(file, content_ysize);
3495 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3497 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3498 for (y = 0; y < 3; y++)
3499 for (x = 0; x < 3; x++)
3500 putFile16BitBE(file, content_array[i][x][y]);
3503 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3506 int envelope_nr = element - EL_ENVELOPE_1;
3507 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3509 putFile16BitBE(file, element);
3510 putFile16BitBE(file, envelope_len);
3511 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3512 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3514 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3516 for (i = 0; i < envelope_len; i++)
3517 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3521 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3522 int num_changed_custom_elements)
3526 putFile16BitBE(file, num_changed_custom_elements);
3528 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3530 int element = EL_CUSTOM_START + i;
3532 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3534 if (check < num_changed_custom_elements)
3536 putFile16BitBE(file, element);
3537 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3544 if (check != num_changed_custom_elements) /* should not happen */
3545 Error(ERR_WARN, "inconsistent number of custom element properties");
3550 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3551 int num_changed_custom_elements)
3555 putFile16BitBE(file, num_changed_custom_elements);
3557 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3559 int element = EL_CUSTOM_START + i;
3561 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3563 if (check < num_changed_custom_elements)
3565 putFile16BitBE(file, element);
3566 putFile16BitBE(file, element_info[element].change->target_element);
3573 if (check != num_changed_custom_elements) /* should not happen */
3574 Error(ERR_WARN, "inconsistent number of custom target elements");
3579 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3580 int num_changed_custom_elements)
3582 int i, j, x, y, check = 0;
3584 putFile16BitBE(file, num_changed_custom_elements);
3586 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3588 int element = EL_CUSTOM_START + i;
3590 if (element_info[element].modified_settings)
3592 if (check < num_changed_custom_elements)
3594 putFile16BitBE(file, element);
3596 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3597 putFile8Bit(file, element_info[element].description[j]);
3599 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3601 /* some free bytes for future properties and padding */
3602 WriteUnusedBytesToFile(file, 7);
3604 putFile8Bit(file, element_info[element].use_gfx_element);
3605 putFile16BitBE(file, element_info[element].gfx_element);
3607 putFile8Bit(file, element_info[element].collect_score);
3608 putFile8Bit(file, element_info[element].collect_count);
3610 putFile16BitBE(file, element_info[element].push_delay_fixed);
3611 putFile16BitBE(file, element_info[element].push_delay_random);
3612 putFile16BitBE(file, element_info[element].move_delay_fixed);
3613 putFile16BitBE(file, element_info[element].move_delay_random);
3615 putFile16BitBE(file, element_info[element].move_pattern);
3616 putFile8Bit(file, element_info[element].move_direction_initial);
3617 putFile8Bit(file, element_info[element].move_stepsize);
3619 for (y = 0; y < 3; y++)
3620 for (x = 0; x < 3; x++)
3621 putFile16BitBE(file, element_info[element].content[x][y]);
3623 putFile32BitBE(file, element_info[element].change->events);
3625 putFile16BitBE(file, element_info[element].change->target_element);
3627 putFile16BitBE(file, element_info[element].change->delay_fixed);
3628 putFile16BitBE(file, element_info[element].change->delay_random);
3629 putFile16BitBE(file, element_info[element].change->delay_frames);
3631 putFile16BitBE(file, element_info[element].change->trigger_element);
3633 putFile8Bit(file, element_info[element].change->explode);
3634 putFile8Bit(file, element_info[element].change->use_target_content);
3635 putFile8Bit(file, element_info[element].change->only_if_complete);
3636 putFile8Bit(file, element_info[element].change->use_random_replace);
3638 putFile8Bit(file, element_info[element].change->random_percentage);
3639 putFile8Bit(file, element_info[element].change->replace_when);
3641 for (y = 0; y < 3; y++)
3642 for (x = 0; x < 3; x++)
3643 putFile16BitBE(file, element_info[element].change->content[x][y]);
3645 putFile8Bit(file, element_info[element].slippery_type);
3647 /* some free bytes for future properties and padding */
3648 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3655 if (check != num_changed_custom_elements) /* should not happen */
3656 Error(ERR_WARN, "inconsistent number of custom element properties");
3660 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3662 struct ElementInfo *ei = &element_info[element];
3665 putFile16BitBE(file, element);
3667 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3668 putFile8Bit(file, ei->description[i]);
3670 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3671 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
3673 putFile8Bit(file, ei->num_change_pages);
3675 /* some free bytes for future base property values and padding */
3676 WriteUnusedBytesToFile(file, 5);
3678 /* write custom property values */
3680 putFile8Bit(file, ei->use_gfx_element);
3681 putFile16BitBE(file, ei->gfx_element);
3683 putFile8Bit(file, ei->collect_score);
3684 putFile8Bit(file, ei->collect_count);
3686 putFile8Bit(file, ei->drop_delay_fixed);
3687 putFile8Bit(file, ei->push_delay_fixed);
3688 putFile8Bit(file, ei->drop_delay_random);
3689 putFile8Bit(file, ei->push_delay_random);
3690 putFile16BitBE(file, ei->move_delay_fixed);
3691 putFile16BitBE(file, ei->move_delay_random);
3693 /* bits 0 - 15 of "move_pattern" ... */
3694 putFile16BitBE(file, ei->move_pattern & 0xffff);
3695 putFile8Bit(file, ei->move_direction_initial);
3696 putFile8Bit(file, ei->move_stepsize);
3698 putFile8Bit(file, ei->slippery_type);
3700 for (y = 0; y < 3; y++)
3701 for (x = 0; x < 3; x++)
3702 putFile16BitBE(file, ei->content[x][y]);
3704 putFile16BitBE(file, ei->move_enter_element);
3705 putFile16BitBE(file, ei->move_leave_element);
3706 putFile8Bit(file, ei->move_leave_type);
3708 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3709 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3711 putFile8Bit(file, ei->access_direction);
3713 putFile8Bit(file, ei->explosion_delay);
3714 putFile8Bit(file, ei->ignition_delay);
3715 putFile8Bit(file, ei->explosion_type);
3717 /* some free bytes for future custom property values and padding */
3718 WriteUnusedBytesToFile(file, 1);
3720 /* write change property values */
3722 for (i = 0; i < ei->num_change_pages; i++)
3724 struct ElementChangeInfo *change = &ei->change_page[i];
3726 putFile32BitBE(file, change->events);
3728 putFile16BitBE(file, change->target_element);
3730 putFile16BitBE(file, change->delay_fixed);
3731 putFile16BitBE(file, change->delay_random);
3732 putFile16BitBE(file, change->delay_frames);
3734 putFile16BitBE(file, change->trigger_element);
3736 putFile8Bit(file, change->explode);
3737 putFile8Bit(file, change->use_target_content);
3738 putFile8Bit(file, change->only_if_complete);
3739 putFile8Bit(file, change->use_random_replace);
3741 putFile8Bit(file, change->random_percentage);
3742 putFile8Bit(file, change->replace_when);
3744 for (y = 0; y < 3; y++)
3745 for (x = 0; x < 3; x++)
3746 putFile16BitBE(file, change->target_content[x][y]);
3748 putFile8Bit(file, change->can_change);
3750 putFile8Bit(file, change->trigger_side);
3753 putFile8Bit(file, change->trigger_player);
3754 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3755 log_2(change->trigger_page)));
3757 /* some free bytes for future change property values and padding */
3758 WriteUnusedBytesToFile(file, 6);
3762 /* some free bytes for future change property values and padding */
3763 WriteUnusedBytesToFile(file, 8);
3768 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3770 struct ElementInfo *ei = &element_info[element];
3771 struct ElementGroupInfo *group = ei->group;
3774 putFile16BitBE(file, element);
3776 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3777 putFile8Bit(file, ei->description[i]);
3779 putFile8Bit(file, group->num_elements);
3781 putFile8Bit(file, ei->use_gfx_element);
3782 putFile16BitBE(file, ei->gfx_element);
3784 putFile8Bit(file, group->choice_mode);
3786 /* some free bytes for future values and padding */
3787 WriteUnusedBytesToFile(file, 3);
3789 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3790 putFile16BitBE(file, group->element[i]);
3793 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3795 int body_chunk_size;
3799 if (!(file = fopen(filename, MODE_WRITE)))
3801 Error(ERR_WARN, "cannot save level file '%s'", filename);
3805 level->file_version = FILE_VERSION_ACTUAL;
3806 level->game_version = GAME_VERSION_ACTUAL;
3808 /* check level field for 16-bit elements */
3809 level->encoding_16bit_field = FALSE;
3810 for (y = 0; y < level->fieldy; y++)
3811 for (x = 0; x < level->fieldx; x++)
3812 if (level->field[x][y] > 255)
3813 level->encoding_16bit_field = TRUE;
3815 /* check yamyam content for 16-bit elements */
3816 level->encoding_16bit_yamyam = FALSE;
3817 for (i = 0; i < level->num_yamyam_contents; i++)
3818 for (y = 0; y < 3; y++)
3819 for (x = 0; x < 3; x++)
3820 if (level->yamyam_content[i][x][y] > 255)
3821 level->encoding_16bit_yamyam = TRUE;
3823 /* check amoeba content for 16-bit elements */
3824 level->encoding_16bit_amoeba = FALSE;
3825 if (level->amoeba_content > 255)
3826 level->encoding_16bit_amoeba = TRUE;
3828 /* calculate size of "BODY" chunk */
3830 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3832 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3833 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3835 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3836 SaveLevel_VERS(file, level);
3838 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3839 SaveLevel_HEAD(file, level);
3841 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3842 SaveLevel_AUTH(file, level);
3844 putFileChunkBE(file, "BODY", body_chunk_size);
3845 SaveLevel_BODY(file, level);
3847 if (level->encoding_16bit_yamyam ||
3848 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3850 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3851 SaveLevel_CNT2(file, level, EL_YAMYAM);
3854 if (level->encoding_16bit_amoeba)
3856 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3857 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3860 /* check for envelope content */
3861 for (i = 0; i < 4; i++)
3863 if (strlen(level->envelope_text[i]) > 0)
3865 int envelope_len = strlen(level->envelope_text[i]) + 1;
3867 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3868 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3872 /* check for non-default custom elements (unless using template level) */
3873 if (!level->use_custom_template)
3875 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3877 int element = EL_CUSTOM_START + i;
3879 if (element_info[element].modified_settings)
3881 int num_change_pages = element_info[element].num_change_pages;
3883 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3884 SaveLevel_CUS4(file, level, element);
3889 /* check for non-default group elements (unless using template level) */
3890 if (!level->use_custom_template)
3892 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3894 int element = EL_GROUP_START + i;
3896 if (element_info[element].modified_settings)
3898 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3899 SaveLevel_GRP1(file, level, element);
3906 SetFilePermissions(filename, PERMS_PRIVATE);
3909 void SaveLevel(int nr)
3911 char *filename = getDefaultLevelFilename(nr);
3913 SaveLevelFromFilename(&level, filename);
3916 void SaveLevelTemplate()
3918 char *filename = getDefaultLevelFilename(-1);
3920 SaveLevelFromFilename(&level, filename);
3923 void DumpLevel(struct LevelInfo *level)
3925 if (level->no_valid_file)
3927 Error(ERR_WARN, "cannot dump -- no valid level file found");
3932 printf_line("-", 79);
3933 printf("Level xxx (file version %08d, game version %08d)\n",
3934 level->file_version, level->game_version);
3935 printf_line("-", 79);
3937 printf("Level author: '%s'\n", level->author);
3938 printf("Level title: '%s'\n", level->name);
3940 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3942 printf("Level time: %d seconds\n", level->time);
3943 printf("Gems needed: %d\n", level->gems_needed);
3945 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3946 printf("Time for wheel: %d seconds\n", level->time_wheel);
3947 printf("Time for light: %d seconds\n", level->time_light);
3948 printf("Time for timegate: %d seconds\n", level->time_timegate);
3950 printf("Amoeba speed: %d\n", level->amoeba_speed);
3952 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
3953 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
3954 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
3955 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
3956 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3957 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3958 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3960 printf_line("-", 79);
3964 /* ========================================================================= */
3965 /* tape file functions */
3966 /* ========================================================================= */
3968 static void setTapeInfoToDefaults()
3972 /* always start with reliable default values (empty tape) */
3975 /* default values (also for pre-1.2 tapes) with only the first player */
3976 tape.player_participates[0] = TRUE;
3977 for (i = 1; i < MAX_PLAYERS; i++)
3978 tape.player_participates[i] = FALSE;
3980 /* at least one (default: the first) player participates in every tape */
3981 tape.num_participating_players = 1;
3983 tape.level_nr = level_nr;
3985 tape.changed = FALSE;
3987 tape.recording = FALSE;
3988 tape.playing = FALSE;
3989 tape.pausing = FALSE;
3991 tape.no_valid_file = FALSE;
3994 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3996 tape->file_version = getFileVersion(file);
3997 tape->game_version = getFileVersion(file);
4002 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
4006 tape->random_seed = getFile32BitBE(file);
4007 tape->date = getFile32BitBE(file);
4008 tape->length = getFile32BitBE(file);
4010 /* read header fields that are new since version 1.2 */
4011 if (tape->file_version >= FILE_VERSION_1_2)
4013 byte store_participating_players = getFile8Bit(file);
4016 /* since version 1.2, tapes store which players participate in the tape */
4017 tape->num_participating_players = 0;
4018 for (i = 0; i < MAX_PLAYERS; i++)
4020 tape->player_participates[i] = FALSE;
4022 if (store_participating_players & (1 << i))
4024 tape->player_participates[i] = TRUE;
4025 tape->num_participating_players++;
4029 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
4031 engine_version = getFileVersion(file);
4032 if (engine_version > 0)
4033 tape->engine_version = engine_version;
4035 tape->engine_version = tape->game_version;
4041 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
4043 int level_identifier_size;
4046 level_identifier_size = getFile16BitBE(file);
4048 tape->level_identifier =
4049 checked_realloc(tape->level_identifier, level_identifier_size);
4051 for (i = 0; i < level_identifier_size; i++)
4052 tape->level_identifier[i] = getFile8Bit(file);
4054 tape->level_nr = getFile16BitBE(file);
4056 chunk_size = 2 + level_identifier_size + 2;
4061 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
4064 int chunk_size_expected =
4065 (tape->num_participating_players + 1) * tape->length;
4067 if (chunk_size_expected != chunk_size)
4069 ReadUnusedBytesFromFile(file, chunk_size);
4070 return chunk_size_expected;
4073 for (i = 0; i < tape->length; i++)
4075 if (i >= MAX_TAPELEN)
4078 for (j = 0; j < MAX_PLAYERS; j++)
4080 tape->pos[i].action[j] = MV_NO_MOVING;
4082 if (tape->player_participates[j])
4083 tape->pos[i].action[j] = getFile8Bit(file);
4086 tape->pos[i].delay = getFile8Bit(file);
4088 if (tape->file_version == FILE_VERSION_1_0)
4090 /* eliminate possible diagonal moves in old tapes */
4091 /* this is only for backward compatibility */
4093 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
4094 byte action = tape->pos[i].action[0];
4095 int k, num_moves = 0;
4097 for (k = 0; k<4; k++)
4099 if (action & joy_dir[k])
4101 tape->pos[i + num_moves].action[0] = joy_dir[k];
4103 tape->pos[i + num_moves].delay = 0;
4112 tape->length += num_moves;
4115 else if (tape->file_version < FILE_VERSION_2_0)
4117 /* convert pre-2.0 tapes to new tape format */
4119 if (tape->pos[i].delay > 1)
4122 tape->pos[i + 1] = tape->pos[i];
4123 tape->pos[i + 1].delay = 1;
4126 for (j = 0; j < MAX_PLAYERS; j++)
4127 tape->pos[i].action[j] = MV_NO_MOVING;
4128 tape->pos[i].delay--;
4139 if (i != tape->length)
4140 chunk_size = (tape->num_participating_players + 1) * i;
4145 void LoadTapeFromFilename(char *filename)
4147 char cookie[MAX_LINE_LEN];
4148 char chunk_name[CHUNK_ID_LEN + 1];
4152 /* always start with reliable default values */
4153 setTapeInfoToDefaults();
4155 if (!(file = fopen(filename, MODE_READ)))
4157 tape.no_valid_file = TRUE;
4160 Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
4166 getFileChunkBE(file, chunk_name, NULL);
4167 if (strcmp(chunk_name, "RND1") == 0)
4169 getFile32BitBE(file); /* not used */
4171 getFileChunkBE(file, chunk_name, NULL);
4172 if (strcmp(chunk_name, "TAPE") != 0)
4174 tape.no_valid_file = TRUE;
4176 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4181 else /* check for pre-2.0 file format with cookie string */
4183 strcpy(cookie, chunk_name);
4184 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
4185 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4186 cookie[strlen(cookie) - 1] = '\0';
4188 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
4190 tape.no_valid_file = TRUE;
4192 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4197 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
4199 tape.no_valid_file = TRUE;
4201 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
4206 /* pre-2.0 tape files have no game version, so use file version here */
4207 tape.game_version = tape.file_version;
4210 if (tape.file_version < FILE_VERSION_1_2)
4212 /* tape files from versions before 1.2.0 without chunk structure */
4213 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
4214 LoadTape_BODY(file, 2 * tape.length, &tape);
4222 int (*loader)(FILE *, int, struct TapeInfo *);
4226 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
4227 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
4228 { "INFO", -1, LoadTape_INFO },
4229 { "BODY", -1, LoadTape_BODY },
4233 while (getFileChunkBE(file, chunk_name, &chunk_size))
4237 while (chunk_info[i].name != NULL &&
4238 strcmp(chunk_name, chunk_info[i].name) != 0)
4241 if (chunk_info[i].name == NULL)
4243 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
4244 chunk_name, filename);
4245 ReadUnusedBytesFromFile(file, chunk_size);
4247 else if (chunk_info[i].size != -1 &&
4248 chunk_info[i].size != chunk_size)
4250 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4251 chunk_size, chunk_name, filename);
4252 ReadUnusedBytesFromFile(file, chunk_size);
4256 /* call function to load this tape chunk */
4257 int chunk_size_expected =
4258 (chunk_info[i].loader)(file, chunk_size, &tape);
4260 /* the size of some chunks cannot be checked before reading other
4261 chunks first (like "HEAD" and "BODY") that contain some header
4262 information, so check them here */
4263 if (chunk_size_expected != chunk_size)
4265 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4266 chunk_size, chunk_name, filename);
4274 tape.length_seconds = GetTapeLength();
4277 printf("::: tape game version: %d\n", tape.game_version);
4278 printf("::: tape engine version: %d\n", tape.engine_version);
4282 void LoadTape(int nr)
4284 char *filename = getTapeFilename(nr);
4286 LoadTapeFromFilename(filename);
4289 void LoadSolutionTape(int nr)
4291 char *filename = getSolutionTapeFilename(nr);
4293 LoadTapeFromFilename(filename);
4296 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
4298 putFileVersion(file, tape->file_version);
4299 putFileVersion(file, tape->game_version);
4302 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
4305 byte store_participating_players = 0;
4307 /* set bits for participating players for compact storage */
4308 for (i = 0; i < MAX_PLAYERS; i++)
4309 if (tape->player_participates[i])
4310 store_participating_players |= (1 << i);
4312 putFile32BitBE(file, tape->random_seed);
4313 putFile32BitBE(file, tape->date);
4314 putFile32BitBE(file, tape->length);
4316 putFile8Bit(file, store_participating_players);
4318 /* unused bytes not at the end here for 4-byte alignment of engine_version */
4319 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
4321 putFileVersion(file, tape->engine_version);
4324 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
4326 int level_identifier_size = strlen(tape->level_identifier) + 1;
4329 putFile16BitBE(file, level_identifier_size);
4331 for (i = 0; i < level_identifier_size; i++)
4332 putFile8Bit(file, tape->level_identifier[i]);
4334 putFile16BitBE(file, tape->level_nr);
4337 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
4341 for (i = 0; i < tape->length; i++)
4343 for (j = 0; j < MAX_PLAYERS; j++)
4344 if (tape->player_participates[j])
4345 putFile8Bit(file, tape->pos[i].action[j]);
4347 putFile8Bit(file, tape->pos[i].delay);
4351 void SaveTape(int nr)
4353 char *filename = getTapeFilename(nr);
4355 boolean new_tape = TRUE;
4356 int num_participating_players = 0;
4357 int info_chunk_size;
4358 int body_chunk_size;
4361 InitTapeDirectory(leveldir_current->subdir);
4363 /* if a tape still exists, ask to overwrite it */
4364 if (access(filename, F_OK) == 0)
4367 if (!Request("Replace old tape ?", REQ_ASK))
4371 if (!(file = fopen(filename, MODE_WRITE)))
4373 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4377 tape.file_version = FILE_VERSION_ACTUAL;
4378 tape.game_version = GAME_VERSION_ACTUAL;
4380 /* count number of participating players */
4381 for (i = 0; i < MAX_PLAYERS; i++)
4382 if (tape.player_participates[i])
4383 num_participating_players++;
4385 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4386 body_chunk_size = (num_participating_players + 1) * tape.length;
4388 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4389 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4391 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4392 SaveTape_VERS(file, &tape);
4394 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4395 SaveTape_HEAD(file, &tape);
4397 putFileChunkBE(file, "INFO", info_chunk_size);
4398 SaveTape_INFO(file, &tape);
4400 putFileChunkBE(file, "BODY", body_chunk_size);
4401 SaveTape_BODY(file, &tape);
4405 SetFilePermissions(filename, PERMS_PRIVATE);
4407 tape.changed = FALSE;
4410 Request("tape saved !", REQ_CONFIRM);
4413 void DumpTape(struct TapeInfo *tape)
4418 if (tape->no_valid_file)
4420 Error(ERR_WARN, "cannot dump -- no valid tape file found");
4425 if (TAPE_IS_EMPTY(*tape))
4427 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
4433 printf_line("-", 79);
4434 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4435 tape->level_nr, tape->file_version, tape->game_version);
4436 printf(" (effective engine version %08d)\n",
4437 tape->engine_version);
4438 printf("Level series identifier: '%s'\n", tape->level_identifier);
4439 printf_line("-", 79);
4441 for (i = 0; i < tape->length; i++)
4443 if (i >= MAX_TAPELEN)
4446 printf("%03d: ", i);
4448 for (j = 0; j < MAX_PLAYERS; j++)
4450 if (tape->player_participates[j])
4452 int action = tape->pos[i].action[j];
4454 printf("%d:%02x ", j, action);
4455 printf("[%c%c%c%c|%c%c] - ",
4456 (action & JOY_LEFT ? '<' : ' '),
4457 (action & JOY_RIGHT ? '>' : ' '),
4458 (action & JOY_UP ? '^' : ' '),
4459 (action & JOY_DOWN ? 'v' : ' '),
4460 (action & JOY_BUTTON_1 ? '1' : ' '),
4461 (action & JOY_BUTTON_2 ? '2' : ' '));
4465 printf("(%03d)\n", tape->pos[i].delay);
4468 printf_line("-", 79);
4472 /* ========================================================================= */
4473 /* score file functions */
4474 /* ========================================================================= */
4476 void LoadScore(int nr)
4479 char *filename = getScoreFilename(nr);
4480 char cookie[MAX_LINE_LEN];
4481 char line[MAX_LINE_LEN];
4485 /* always start with reliable default values */
4486 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4488 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4489 highscore[i].Score = 0;
4492 if (!(file = fopen(filename, MODE_READ)))
4495 /* check file identifier */
4496 fgets(cookie, MAX_LINE_LEN, file);
4497 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4498 cookie[strlen(cookie) - 1] = '\0';
4500 if (!checkCookieString(cookie, SCORE_COOKIE))
4502 Error(ERR_WARN, "unknown format of score file '%s'", filename);
4507 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4509 fscanf(file, "%d", &highscore[i].Score);
4510 fgets(line, MAX_LINE_LEN, file);
4512 if (line[strlen(line) - 1] == '\n')
4513 line[strlen(line) - 1] = '\0';
4515 for (line_ptr = line; *line_ptr; line_ptr++)
4517 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4519 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4520 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4529 void SaveScore(int nr)
4532 char *filename = getScoreFilename(nr);
4535 InitScoreDirectory(leveldir_current->subdir);
4537 if (!(file = fopen(filename, MODE_WRITE)))
4539 Error(ERR_WARN, "cannot save score for level %d", nr);
4543 fprintf(file, "%s\n\n", SCORE_COOKIE);
4545 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4546 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4550 SetFilePermissions(filename, PERMS_PUBLIC);
4554 /* ========================================================================= */
4555 /* setup file functions */
4556 /* ========================================================================= */
4558 #define TOKEN_STR_PLAYER_PREFIX "player_"
4561 #define SETUP_TOKEN_PLAYER_NAME 0
4562 #define SETUP_TOKEN_SOUND 1
4563 #define SETUP_TOKEN_SOUND_LOOPS 2
4564 #define SETUP_TOKEN_SOUND_MUSIC 3
4565 #define SETUP_TOKEN_SOUND_SIMPLE 4
4566 #define SETUP_TOKEN_TOONS 5
4567 #define SETUP_TOKEN_SCROLL_DELAY 6
4568 #define SETUP_TOKEN_SOFT_SCROLLING 7
4569 #define SETUP_TOKEN_FADING 8
4570 #define SETUP_TOKEN_AUTORECORD 9
4571 #define SETUP_TOKEN_QUICK_DOORS 10
4572 #define SETUP_TOKEN_TEAM_MODE 11
4573 #define SETUP_TOKEN_HANDICAP 12
4574 #define SETUP_TOKEN_TIME_LIMIT 13
4575 #define SETUP_TOKEN_FULLSCREEN 14
4576 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
4577 #define SETUP_TOKEN_GRAPHICS_SET 16
4578 #define SETUP_TOKEN_SOUNDS_SET 17
4579 #define SETUP_TOKEN_MUSIC_SET 18
4580 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
4581 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
4582 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
4584 #define NUM_GLOBAL_SETUP_TOKENS 22
4587 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
4588 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
4589 #define SETUP_TOKEN_EDITOR_EL_MORE 2
4590 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
4591 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
4592 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
4593 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
4594 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
4595 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
4596 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
4597 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
4598 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
4600 #define NUM_EDITOR_SETUP_TOKENS 12
4602 /* shortcut setup */
4603 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
4604 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
4605 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
4607 #define NUM_SHORTCUT_SETUP_TOKENS 3
4610 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
4611 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
4612 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
4613 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
4614 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
4615 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
4616 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
4617 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
4618 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
4619 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
4620 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
4621 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
4622 #define SETUP_TOKEN_PLAYER_KEY_UP 12
4623 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
4624 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
4625 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
4627 #define NUM_PLAYER_SETUP_TOKENS 16
4630 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
4631 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
4633 #define NUM_SYSTEM_SETUP_TOKENS 2
4636 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
4638 #define NUM_OPTIONS_SETUP_TOKENS 1
4641 static struct SetupInfo si;
4642 static struct SetupEditorInfo sei;
4643 static struct SetupShortcutInfo ssi;
4644 static struct SetupInputInfo sii;
4645 static struct SetupSystemInfo syi;
4646 static struct OptionInfo soi;
4648 static struct TokenInfo global_setup_tokens[] =
4650 { TYPE_STRING, &si.player_name, "player_name" },
4651 { TYPE_SWITCH, &si.sound, "sound" },
4652 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
4653 { TYPE_SWITCH, &si.sound_music, "background_music" },
4654 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
4655 { TYPE_SWITCH, &si.toons, "toons" },
4656 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
4657 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
4658 { TYPE_SWITCH, &si.fading, "screen_fading" },
4659 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
4660 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
4661 { TYPE_SWITCH, &si.team_mode, "team_mode" },
4662 { TYPE_SWITCH, &si.handicap, "handicap" },
4663 { TYPE_SWITCH, &si.time_limit, "time_limit" },
4664 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
4665 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
4666 { TYPE_STRING, &si.graphics_set, "graphics_set" },
4667 { TYPE_STRING, &si.sounds_set, "sounds_set" },
4668 { TYPE_STRING, &si.music_set, "music_set" },
4669 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4670 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
4671 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
4674 static struct TokenInfo editor_setup_tokens[] =
4676 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
4677 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
4678 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
4679 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
4680 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
4681 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
4682 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
4683 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
4684 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
4685 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
4686 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
4687 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
4690 static struct TokenInfo shortcut_setup_tokens[] =
4692 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
4693 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
4694 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
4697 static struct TokenInfo player_setup_tokens[] =
4699 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
4700 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
4701 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
4702 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
4703 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
4704 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
4705 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
4706 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
4707 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
4708 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
4709 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
4710 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
4711 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
4712 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
4713 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
4714 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
4717 static struct TokenInfo system_setup_tokens[] =
4719 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
4720 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4723 static struct TokenInfo options_setup_tokens[] =
4725 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
4728 static char *get_corrected_login_name(char *login_name)
4730 /* needed because player name must be a fixed length string */
4731 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4733 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4734 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4736 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
4737 if (strchr(login_name_new, ' '))
4738 *strchr(login_name_new, ' ') = '\0';
4740 return login_name_new;
4743 static void setSetupInfoToDefaults(struct SetupInfo *si)
4747 si->player_name = get_corrected_login_name(getLoginName());
4750 si->sound_loops = TRUE;
4751 si->sound_music = TRUE;
4752 si->sound_simple = TRUE;
4754 si->double_buffering = TRUE;
4755 si->direct_draw = !si->double_buffering;
4756 si->scroll_delay = TRUE;
4757 si->soft_scrolling = TRUE;
4759 si->autorecord = TRUE;
4760 si->quick_doors = FALSE;
4761 si->team_mode = FALSE;
4762 si->handicap = TRUE;
4763 si->time_limit = TRUE;
4764 si->fullscreen = FALSE;
4765 si->ask_on_escape = TRUE;
4767 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4768 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4769 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4770 si->override_level_graphics = FALSE;
4771 si->override_level_sounds = FALSE;
4772 si->override_level_music = FALSE;
4774 si->editor.el_boulderdash = TRUE;
4775 si->editor.el_emerald_mine = TRUE;
4776 si->editor.el_more = TRUE;
4777 si->editor.el_sokoban = TRUE;
4778 si->editor.el_supaplex = TRUE;
4779 si->editor.el_diamond_caves = TRUE;
4780 si->editor.el_dx_boulderdash = TRUE;
4781 si->editor.el_chars = TRUE;
4782 si->editor.el_custom = TRUE;
4783 si->editor.el_custom_more = FALSE;
4785 si->editor.el_headlines = TRUE;
4786 si->editor.el_user_defined = FALSE;
4788 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4789 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4790 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4792 for (i = 0; i < MAX_PLAYERS; i++)
4794 si->input[i].use_joystick = FALSE;
4795 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4796 si->input[i].joy.xleft = JOYSTICK_XLEFT;
4797 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4798 si->input[i].joy.xright = JOYSTICK_XRIGHT;
4799 si->input[i].joy.yupper = JOYSTICK_YUPPER;
4800 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4801 si->input[i].joy.ylower = JOYSTICK_YLOWER;
4802 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
4803 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
4804 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
4805 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4806 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
4807 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
4808 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
4809 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
4812 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4813 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4815 si->options.verbose = FALSE;
4818 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4822 if (!setup_file_hash)
4827 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4828 setSetupInfo(global_setup_tokens, i,
4829 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4834 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4835 setSetupInfo(editor_setup_tokens, i,
4836 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4839 /* shortcut setup */
4840 ssi = setup.shortcut;
4841 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4842 setSetupInfo(shortcut_setup_tokens, i,
4843 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4844 setup.shortcut = ssi;
4847 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4851 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4853 sii = setup.input[pnr];
4854 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4856 char full_token[100];
4858 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4859 setSetupInfo(player_setup_tokens, i,
4860 getHashEntry(setup_file_hash, full_token));
4862 setup.input[pnr] = sii;
4867 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4868 setSetupInfo(system_setup_tokens, i,
4869 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4873 soi = setup.options;
4874 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4875 setSetupInfo(options_setup_tokens, i,
4876 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4877 setup.options = soi;
4882 char *filename = getSetupFilename();
4883 SetupFileHash *setup_file_hash = NULL;
4885 /* always start with reliable default values */
4886 setSetupInfoToDefaults(&setup);
4888 setup_file_hash = loadSetupFileHash(filename);
4890 if (setup_file_hash)
4892 char *player_name_new;
4894 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4895 decodeSetupFileHash(setup_file_hash);
4897 setup.direct_draw = !setup.double_buffering;
4899 freeSetupFileHash(setup_file_hash);
4901 /* needed to work around problems with fixed length strings */
4902 player_name_new = get_corrected_login_name(setup.player_name);
4903 free(setup.player_name);
4904 setup.player_name = player_name_new;
4907 Error(ERR_WARN, "using default setup values");
4912 char *filename = getSetupFilename();
4916 InitUserDataDirectory();
4918 if (!(file = fopen(filename, MODE_WRITE)))
4920 Error(ERR_WARN, "cannot write setup file '%s'", filename);
4924 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4925 getCookie("SETUP")));
4926 fprintf(file, "\n");
4930 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4932 /* just to make things nicer :) */
4933 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4934 i == SETUP_TOKEN_GRAPHICS_SET)
4935 fprintf(file, "\n");
4937 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4942 fprintf(file, "\n");
4943 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4944 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4946 /* shortcut setup */
4947 ssi = setup.shortcut;
4948 fprintf(file, "\n");
4949 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4950 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4953 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4957 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4958 fprintf(file, "\n");
4960 sii = setup.input[pnr];
4961 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4962 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4967 fprintf(file, "\n");
4968 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4969 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4972 soi = setup.options;
4973 fprintf(file, "\n");
4974 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4975 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4979 SetFilePermissions(filename, PERMS_PRIVATE);
4982 void LoadCustomElementDescriptions()
4984 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4985 SetupFileHash *setup_file_hash;
4988 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4990 if (element_info[i].custom_description != NULL)
4992 free(element_info[i].custom_description);
4993 element_info[i].custom_description = NULL;
4997 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
5000 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5002 char *token = getStringCat2(element_info[i].token_name, ".name");
5003 char *value = getHashEntry(setup_file_hash, token);
5006 element_info[i].custom_description = getStringCopy(value);
5011 freeSetupFileHash(setup_file_hash);
5014 void LoadSpecialMenuDesignSettings()
5016 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
5017 SetupFileHash *setup_file_hash;
5020 /* always start with reliable default values from default config */
5021 for (i = 0; image_config_vars[i].token != NULL; i++)
5022 for (j = 0; image_config[j].token != NULL; j++)
5023 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
5024 *image_config_vars[i].value =
5025 get_auto_parameter_value(image_config_vars[i].token,
5026 image_config[j].value);
5028 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
5031 /* special case: initialize with default values that may be overwritten */
5032 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5034 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
5035 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
5036 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
5038 if (value_x != NULL)
5039 menu.draw_xoffset[i] = get_integer_from_string(value_x);
5040 if (value_y != NULL)
5041 menu.draw_yoffset[i] = get_integer_from_string(value_y);
5042 if (list_size != NULL)
5043 menu.list_size[i] = get_integer_from_string(list_size);
5046 /* read (and overwrite with) values that may be specified in config file */
5047 for (i = 0; image_config_vars[i].token != NULL; i++)
5049 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
5052 *image_config_vars[i].value =
5053 get_auto_parameter_value(image_config_vars[i].token, value);
5056 freeSetupFileHash(setup_file_hash);
5059 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
5061 char *filename = getEditorSetupFilename();
5062 SetupFileList *setup_file_list, *list;
5063 SetupFileHash *element_hash;
5064 int num_unknown_tokens = 0;
5067 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
5070 element_hash = newSetupFileHash();
5072 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5073 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5075 /* determined size may be larger than needed (due to unknown elements) */
5077 for (list = setup_file_list; list != NULL; list = list->next)
5080 /* add space for up to 3 more elements for padding that may be needed */
5083 *elements = checked_malloc(*num_elements * sizeof(int));
5086 for (list = setup_file_list; list != NULL; list = list->next)
5088 char *value = getHashEntry(element_hash, list->token);
5092 (*elements)[(*num_elements)++] = atoi(value);
5096 if (num_unknown_tokens == 0)
5098 Error(ERR_RETURN_LINE, "-");
5099 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5100 Error(ERR_RETURN, "- config file: '%s'", filename);
5102 num_unknown_tokens++;
5105 Error(ERR_RETURN, "- token: '%s'", list->token);
5109 if (num_unknown_tokens > 0)
5110 Error(ERR_RETURN_LINE, "-");
5112 while (*num_elements % 4) /* pad with empty elements, if needed */
5113 (*elements)[(*num_elements)++] = EL_EMPTY;
5115 freeSetupFileList(setup_file_list);
5116 freeSetupFileHash(element_hash);
5120 for (i = 0; i < *num_elements; i++)
5121 printf("editor: element '%s' [%d]\n",
5122 element_info[(*elements)[i]].token_name, (*elements)[i]);
5126 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
5129 SetupFileHash *setup_file_hash = NULL;
5130 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
5131 char *filename_music, *filename_prefix, *filename_info;
5137 token_to_value_ptr[] =
5139 { "title_header", &tmp_music_file_info.title_header },
5140 { "artist_header", &tmp_music_file_info.artist_header },
5141 { "album_header", &tmp_music_file_info.album_header },
5142 { "year_header", &tmp_music_file_info.year_header },
5144 { "title", &tmp_music_file_info.title },
5145 { "artist", &tmp_music_file_info.artist },
5146 { "album", &tmp_music_file_info.album },
5147 { "year", &tmp_music_file_info.year },
5153 filename_music = (is_sound ? getCustomSoundFilename(basename) :
5154 getCustomMusicFilename(basename));
5156 if (filename_music == NULL)
5159 /* ---------- try to replace file extension ---------- */
5161 filename_prefix = getStringCopy(filename_music);
5162 if (strrchr(filename_prefix, '.') != NULL)
5163 *strrchr(filename_prefix, '.') = '\0';
5164 filename_info = getStringCat2(filename_prefix, ".txt");
5167 printf("trying to load file '%s'...\n", filename_info);
5170 if (fileExists(filename_info))
5171 setup_file_hash = loadSetupFileHash(filename_info);
5173 free(filename_prefix);
5174 free(filename_info);
5176 if (setup_file_hash == NULL)
5178 /* ---------- try to add file extension ---------- */
5180 filename_prefix = getStringCopy(filename_music);
5181 filename_info = getStringCat2(filename_prefix, ".txt");
5184 printf("trying to load file '%s'...\n", filename_info);
5187 if (fileExists(filename_info))
5188 setup_file_hash = loadSetupFileHash(filename_info);
5190 free(filename_prefix);
5191 free(filename_info);
5194 if (setup_file_hash == NULL)
5197 /* ---------- music file info found ---------- */
5199 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
5201 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
5203 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
5205 *token_to_value_ptr[i].value_ptr =
5206 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
5209 tmp_music_file_info.basename = getStringCopy(basename);
5210 tmp_music_file_info.music = music;
5211 tmp_music_file_info.is_sound = is_sound;
5213 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
5214 *new_music_file_info = tmp_music_file_info;
5216 return new_music_file_info;
5219 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
5221 return get_music_file_info_ext(basename, music, FALSE);
5224 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
5226 return get_music_file_info_ext(basename, sound, TRUE);
5229 static boolean music_info_listed_ext(struct MusicFileInfo *list,
5230 char *basename, boolean is_sound)
5232 for (; list != NULL; list = list->next)
5233 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
5239 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
5241 return music_info_listed_ext(list, basename, FALSE);
5244 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
5246 return music_info_listed_ext(list, basename, TRUE);
5249 void LoadMusicInfo()
5251 char *music_directory = getCustomMusicDirectory();
5252 int num_music = getMusicListSize();
5253 int num_music_noconf = 0;
5254 int num_sounds = getSoundListSize();
5256 struct dirent *dir_entry;
5257 struct FileInfo *music, *sound;
5258 struct MusicFileInfo *next, **new;
5261 while (music_file_info != NULL)
5263 next = music_file_info->next;
5265 checked_free(music_file_info->basename);
5267 checked_free(music_file_info->title_header);
5268 checked_free(music_file_info->artist_header);
5269 checked_free(music_file_info->album_header);
5270 checked_free(music_file_info->year_header);
5272 checked_free(music_file_info->title);
5273 checked_free(music_file_info->artist);
5274 checked_free(music_file_info->album);
5275 checked_free(music_file_info->year);
5277 free(music_file_info);
5279 music_file_info = next;
5282 new = &music_file_info;
5285 printf("::: num_music == %d\n", num_music);
5288 for (i = 0; i < num_music; i++)
5290 music = getMusicListEntry(i);
5293 printf("::: %d [%08x]\n", i, music->filename);
5296 if (music->filename == NULL)
5299 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
5302 /* a configured file may be not recognized as music */
5303 if (!FileIsMusic(music->filename))
5307 printf("::: -> '%s' (configured)\n", music->filename);
5310 if (!music_info_listed(music_file_info, music->filename))
5312 *new = get_music_file_info(music->filename, i);
5314 new = &(*new)->next;
5318 if ((dir = opendir(music_directory)) == NULL)
5320 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
5324 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
5326 char *basename = dir_entry->d_name;
5327 boolean music_already_used = FALSE;
5330 /* skip all music files that are configured in music config file */
5331 for (i = 0; i < num_music; i++)
5333 music = getMusicListEntry(i);
5335 if (music->filename == NULL)
5338 if (strcmp(basename, music->filename) == 0)
5340 music_already_used = TRUE;
5345 if (music_already_used)
5348 if (!FileIsMusic(basename))
5352 printf("::: -> '%s' (found in directory)\n", basename);
5355 if (!music_info_listed(music_file_info, basename))
5357 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5359 new = &(*new)->next;
5367 for (i = 0; i < num_sounds; i++)
5369 sound = getSoundListEntry(i);
5371 if (sound->filename == NULL)
5374 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5377 /* a configured file may be not recognized as sound */
5378 if (!FileIsSound(sound->filename))
5382 printf("::: -> '%s' (configured)\n", sound->filename);
5385 if (!sound_info_listed(music_file_info, sound->filename))
5387 *new = get_sound_file_info(sound->filename, i);
5389 new = &(*new)->next;
5395 for (next = music_file_info; next != NULL; next = next->next)
5396 printf("::: title == '%s'\n", next->title);
5400 void add_helpanim_entry(int element, int action, int direction, int delay,
5401 int *num_list_entries)
5403 struct HelpAnimInfo *new_list_entry;
5404 (*num_list_entries)++;
5407 checked_realloc(helpanim_info,
5408 *num_list_entries * sizeof(struct HelpAnimInfo));
5409 new_list_entry = &helpanim_info[*num_list_entries - 1];
5411 new_list_entry->element = element;
5412 new_list_entry->action = action;
5413 new_list_entry->direction = direction;
5414 new_list_entry->delay = delay;
5417 void print_unknown_token(char *filename, char *token, int token_nr)
5421 Error(ERR_RETURN_LINE, "-");
5422 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5423 Error(ERR_RETURN, "- config file: '%s'", filename);
5426 Error(ERR_RETURN, "- token: '%s'", token);
5429 void print_unknown_token_end(int token_nr)
5432 Error(ERR_RETURN_LINE, "-");
5435 void LoadHelpAnimInfo()
5437 char *filename = getHelpAnimFilename();
5438 SetupFileList *setup_file_list = NULL, *list;
5439 SetupFileHash *element_hash, *action_hash, *direction_hash;
5440 int num_list_entries = 0;
5441 int num_unknown_tokens = 0;
5444 if (fileExists(filename))
5445 setup_file_list = loadSetupFileList(filename);
5447 if (setup_file_list == NULL)
5449 /* use reliable default values from static configuration */
5450 SetupFileList *insert_ptr;
5452 insert_ptr = setup_file_list =
5453 newSetupFileList(helpanim_config[0].token,
5454 helpanim_config[0].value);
5456 for (i = 1; helpanim_config[i].token; i++)
5457 insert_ptr = addListEntry(insert_ptr,
5458 helpanim_config[i].token,
5459 helpanim_config[i].value);
5462 element_hash = newSetupFileHash();
5463 action_hash = newSetupFileHash();
5464 direction_hash = newSetupFileHash();
5466 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5467 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5469 for (i = 0; i < NUM_ACTIONS; i++)
5470 setHashEntry(action_hash, element_action_info[i].suffix,
5471 i_to_a(element_action_info[i].value));
5473 /* do not store direction index (bit) here, but direction value! */
5474 for (i = 0; i < NUM_DIRECTIONS; i++)
5475 setHashEntry(direction_hash, element_direction_info[i].suffix,
5476 i_to_a(1 << element_direction_info[i].value));
5478 for (list = setup_file_list; list != NULL; list = list->next)
5480 char *element_token, *action_token, *direction_token;
5481 char *element_value, *action_value, *direction_value;
5482 int delay = atoi(list->value);
5484 if (strcmp(list->token, "end") == 0)
5486 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5491 /* first try to break element into element/action/direction parts;
5492 if this does not work, also accept combined "element[.act][.dir]"
5493 elements (like "dynamite.active"), which are unique elements */
5495 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
5497 element_value = getHashEntry(element_hash, list->token);
5498 if (element_value != NULL) /* element found */
5499 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5503 /* no further suffixes found -- this is not an element */
5504 print_unknown_token(filename, list->token, num_unknown_tokens++);
5510 /* token has format "<prefix>.<something>" */
5512 action_token = strchr(list->token, '.'); /* suffix may be action ... */
5513 direction_token = action_token; /* ... or direction */
5515 element_token = getStringCopy(list->token);
5516 *strchr(element_token, '.') = '\0';
5518 element_value = getHashEntry(element_hash, element_token);
5520 if (element_value == NULL) /* this is no element */
5522 element_value = getHashEntry(element_hash, list->token);
5523 if (element_value != NULL) /* combined element found */
5524 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5527 print_unknown_token(filename, list->token, num_unknown_tokens++);
5529 free(element_token);
5534 action_value = getHashEntry(action_hash, action_token);
5536 if (action_value != NULL) /* action found */
5538 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5541 free(element_token);
5546 direction_value = getHashEntry(direction_hash, direction_token);
5548 if (direction_value != NULL) /* direction found */
5550 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5553 free(element_token);
5558 if (strchr(action_token + 1, '.') == NULL)
5560 /* no further suffixes found -- this is not an action nor direction */
5562 element_value = getHashEntry(element_hash, list->token);
5563 if (element_value != NULL) /* combined element found */
5564 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5567 print_unknown_token(filename, list->token, num_unknown_tokens++);
5569 free(element_token);
5574 /* token has format "<prefix>.<suffix>.<something>" */
5576 direction_token = strchr(action_token + 1, '.');
5578 action_token = getStringCopy(action_token);
5579 *strchr(action_token + 1, '.') = '\0';
5581 action_value = getHashEntry(action_hash, action_token);
5583 if (action_value == NULL) /* this is no action */
5585 element_value = getHashEntry(element_hash, list->token);
5586 if (element_value != NULL) /* combined element found */
5587 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5590 print_unknown_token(filename, list->token, num_unknown_tokens++);
5592 free(element_token);
5598 direction_value = getHashEntry(direction_hash, direction_token);
5600 if (direction_value != NULL) /* direction found */
5602 add_helpanim_entry(atoi(element_value), atoi(action_value),
5603 atoi(direction_value), delay, &num_list_entries);
5605 free(element_token);
5611 /* this is no direction */
5613 element_value = getHashEntry(element_hash, list->token);
5614 if (element_value != NULL) /* combined element found */
5615 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5618 print_unknown_token(filename, list->token, num_unknown_tokens++);
5620 free(element_token);
5624 print_unknown_token_end(num_unknown_tokens);
5626 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5627 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
5629 freeSetupFileList(setup_file_list);
5630 freeSetupFileHash(element_hash);
5631 freeSetupFileHash(action_hash);
5632 freeSetupFileHash(direction_hash);
5636 for (i = 0; i < num_list_entries; i++)
5637 printf("::: %d, %d, %d => %d\n",
5638 helpanim_info[i].element,
5639 helpanim_info[i].action,
5640 helpanim_info[i].direction,
5641 helpanim_info[i].delay);
5645 void LoadHelpTextInfo()
5647 char *filename = getHelpTextFilename();
5650 if (helptext_info != NULL)
5652 freeSetupFileHash(helptext_info);
5653 helptext_info = NULL;
5656 if (fileExists(filename))
5657 helptext_info = loadSetupFileHash(filename);
5659 if (helptext_info == NULL)
5661 /* use reliable default values from static configuration */
5662 helptext_info = newSetupFileHash();
5664 for (i = 0; helptext_config[i].token; i++)
5665 setHashEntry(helptext_info,
5666 helptext_config[i].token,
5667 helptext_config[i].value);
5672 BEGIN_HASH_ITERATION(helptext_info, itr)
5674 printf("::: '%s' => '%s'\n",
5675 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5677 END_HASH_ITERATION(hash, itr)
5682 /* ------------------------------------------------------------------------- *
5684 * ------------------------------------------------------------------------- */
5686 #define MAX_NUM_CONVERT_LEVELS 1000
5688 void ConvertLevels()
5690 static LevelDirTree *convert_leveldir = NULL;
5691 static int convert_level_nr = -1;
5692 static int num_levels_handled = 0;
5693 static int num_levels_converted = 0;
5694 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5697 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5698 global.convert_leveldir);
5700 if (convert_leveldir == NULL)
5701 Error(ERR_EXIT, "no such level identifier: '%s'",
5702 global.convert_leveldir);
5704 leveldir_current = convert_leveldir;
5706 if (global.convert_level_nr != -1)
5708 convert_leveldir->first_level = global.convert_level_nr;
5709 convert_leveldir->last_level = global.convert_level_nr;
5712 convert_level_nr = convert_leveldir->first_level;
5714 printf_line("=", 79);
5715 printf("Converting levels\n");
5716 printf_line("-", 79);
5717 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5718 printf("Level series name: '%s'\n", convert_leveldir->name);
5719 printf("Level series author: '%s'\n", convert_leveldir->author);
5720 printf("Number of levels: %d\n", convert_leveldir->levels);
5721 printf_line("=", 79);
5724 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5725 levels_failed[i] = FALSE;
5727 while (convert_level_nr <= convert_leveldir->last_level)
5729 char *level_filename;
5732 level_nr = convert_level_nr++;
5734 printf("Level %03d: ", level_nr);
5736 LoadLevel(level_nr);
5737 if (level.no_valid_file)
5739 printf("(no level)\n");
5743 printf("converting level ... ");
5745 level_filename = getDefaultLevelFilename(level_nr);
5746 new_level = !fileExists(level_filename);
5750 SaveLevel(level_nr);
5752 num_levels_converted++;
5754 printf("converted.\n");
5758 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5759 levels_failed[level_nr] = TRUE;
5761 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5764 num_levels_handled++;
5768 printf_line("=", 79);
5769 printf("Number of levels handled: %d\n", num_levels_handled);
5770 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5771 (num_levels_handled ?
5772 num_levels_converted * 100 / num_levels_handled : 0));
5773 printf_line("-", 79);
5774 printf("Summary (for automatic parsing by scripts):\n");
5775 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5776 convert_leveldir->identifier, num_levels_converted,
5778 (num_levels_handled ?
5779 num_levels_converted * 100 / num_levels_handled : 0));
5781 if (num_levels_handled != num_levels_converted)
5783 printf(", FAILED:");
5784 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5785 if (levels_failed[i])
5790 printf_line("=", 79);