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) (96 + (x) * 48)
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
52 /* values for "CONF" chunk */
53 #define CONF_MASK_1_BYTE 0x00
54 #define CONF_MASK_2_BYTE 0x40
55 #define CONF_MASK_4_BYTE 0x80
56 #define CONF_MASK_MULTI_BYTES 0xc0
58 #define CONF_MASK_BYTES 0xc0
59 #define CONF_MASK_TOKEN 0x3f
61 #define CONF_LAST_ENTRY (CONF_MASK_1_BYTE | 0)
63 #define CONF_VALUE_INTEGER_1 (CONF_MASK_1_BYTE | 1)
64 #define CONF_VALUE_INTEGER_2 (CONF_MASK_1_BYTE | 2)
65 #define CONF_VALUE_INTEGER_3 (CONF_MASK_1_BYTE | 3)
66 #define CONF_VALUE_INTEGER_4 (CONF_MASK_1_BYTE | 4)
67 #define CONF_VALUE_BOOLEAN_1 (CONF_MASK_1_BYTE | 5)
68 #define CONF_VALUE_BOOLEAN_2 (CONF_MASK_1_BYTE | 6)
69 #define CONF_VALUE_BOOLEAN_3 (CONF_MASK_1_BYTE | 7)
70 #define CONF_VALUE_BOOLEAN_4 (CONF_MASK_1_BYTE | 8)
72 #define CONF_VALUE_ELEMENT_1 (CONF_MASK_2_BYTE | 1)
73 #define CONF_VALUE_ELEMENT_2 (CONF_MASK_2_BYTE | 2)
74 #define CONF_VALUE_ELEMENT_3 (CONF_MASK_2_BYTE | 3)
75 #define CONF_VALUE_ELEMENT_4 (CONF_MASK_2_BYTE | 4)
77 #define CONF_VALUE_CONTENT_1 (CONF_MASK_MULTI_BYTES | 1)
78 #define CONF_VALUE_CONTENT_8 (CONF_MASK_MULTI_BYTES | 2)
80 #define CONF_VALUE_INTEGER(x) ((x) >= CONF_VALUE_INTEGER_1 && \
81 (x) <= CONF_VALUE_INTEGER_4)
83 #define CONF_VALUE_BOOLEAN(x) ((x) >= CONF_VALUE_BOOLEAN_1 && \
84 (x) <= CONF_VALUE_BOOLEAN_4)
86 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
87 (x) == CONF_MASK_2_BYTE ? 2 : \
88 (x) == CONF_MASK_4_BYTE ? 4 : 0)
90 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
91 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
93 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
95 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * 2)
96 #define CONF_CONTENT_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)] << 8)|\
97 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
99 static struct LevelInfo li;
109 /* ---------- 1-byte values ---------------------------------------------- */
111 EL_EMC_ANDROID, CONF_VALUE_INTEGER_1,
112 &li.android_move_time, 10
115 EL_EMC_ANDROID, CONF_VALUE_INTEGER_2,
116 &li.android_clone_time, 10
119 EL_EMC_MAGIC_BALL, CONF_VALUE_INTEGER_1,
123 EL_EMC_LENSES, CONF_VALUE_INTEGER_1,
127 EL_EMC_LENSES, CONF_VALUE_INTEGER_2,
131 EL_EMC_MAGNIFIER, CONF_VALUE_INTEGER_1,
132 &li.magnify_score, 10
135 EL_EMC_MAGNIFIER, CONF_VALUE_INTEGER_2,
139 EL_ROBOT, CONF_VALUE_INTEGER_1,
143 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_1,
144 &li.game_of_life[0], 2
147 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_2,
148 &li.game_of_life[1], 3
151 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_3,
152 &li.game_of_life[2], 3
155 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_4,
156 &li.game_of_life[3], 3
159 EL_BIOMAZE, CONF_VALUE_INTEGER_1,
163 EL_BIOMAZE, CONF_VALUE_INTEGER_2,
167 EL_BIOMAZE, CONF_VALUE_INTEGER_3,
171 EL_BIOMAZE, CONF_VALUE_INTEGER_4,
175 EL_BALLOON, CONF_VALUE_INTEGER_1,
176 &li.wind_direction_initial, MV_NONE
179 EL_TIMEGATE_SWITCH, CONF_VALUE_INTEGER_1,
180 &li.time_timegate, 10
183 EL_LIGHT_SWITCH_ACTIVE, CONF_VALUE_INTEGER_1,
187 EL_SHIELD_NORMAL, CONF_VALUE_INTEGER_1,
188 &li.shield_normal_time, 10
191 EL_SHIELD_DEADLY, CONF_VALUE_INTEGER_1,
192 &li.shield_deadly_time, 10
195 EL_EXTRA_TIME, CONF_VALUE_INTEGER_1,
199 EL_TIME_ORB_FULL, CONF_VALUE_INTEGER_1,
200 &li.time_orb_time, 10
203 EL_TIME_ORB_FULL, CONF_VALUE_BOOLEAN_1,
204 &li.use_time_orb_bug, FALSE
207 /* ---------- multi-byte values ------------------------------------------ */
209 EL_EMC_MAGIC_BALL, CONF_VALUE_CONTENT_8,
210 &li.ball_content, EL_EMPTY
226 { LEVEL_FILE_TYPE_RND, "RND" },
227 { LEVEL_FILE_TYPE_BD, "BD" },
228 { LEVEL_FILE_TYPE_EM, "EM" },
229 { LEVEL_FILE_TYPE_SP, "SP" },
230 { LEVEL_FILE_TYPE_DX, "DX" },
231 { LEVEL_FILE_TYPE_SB, "SB" },
232 { LEVEL_FILE_TYPE_DC, "DC" },
237 /* ========================================================================= */
238 /* level file functions */
239 /* ========================================================================= */
241 static void setLevelInfoToDefaultsFromConfigList(struct LevelInfo *level)
245 li = *level; /* copy level information into temporary buffer */
247 for (i = 0; element_conf[i].element != -1; i++)
249 int default_value = element_conf[i].default_value;
250 int type = element_conf[i].type;
251 int bytes = type & CONF_MASK_BYTES;
253 if (bytes != CONF_MASK_MULTI_BYTES)
255 if (CONF_VALUE_BOOLEAN(type))
256 *(boolean *)(element_conf[i].value) = default_value;
258 *(int *) (element_conf[i].value) = default_value;
260 else if (type == CONF_VALUE_CONTENT_8)
262 struct Content *content = (struct Content *)(element_conf[i].value);
265 for (c = 0; c < MAX_ELEMENT_CONTENTS; c++)
266 for (y = 0; y < 3; y++)
267 for (x = 0; x < 3; x++)
268 content[c].e[x][y] = default_value;
272 *level = li; /* copy temporary buffer back to level information */
275 void setElementChangePages(struct ElementInfo *ei, int change_pages)
277 int change_page_size = sizeof(struct ElementChangeInfo);
279 ei->num_change_pages = MAX(1, change_pages);
282 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
284 if (ei->current_change_page >= ei->num_change_pages)
285 ei->current_change_page = ei->num_change_pages - 1;
287 ei->change = &ei->change_page[ei->current_change_page];
290 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
294 change->can_change = FALSE;
296 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
297 change->has_event[i] = FALSE;
299 change->trigger_player = CH_PLAYER_ANY;
300 change->trigger_side = CH_SIDE_ANY;
301 change->trigger_page = CH_PAGE_ANY;
303 change->target_element = EL_EMPTY_SPACE;
305 change->delay_fixed = 0;
306 change->delay_random = 0;
307 change->delay_frames = 1;
309 change->trigger_element = EL_EMPTY_SPACE;
311 change->explode = FALSE;
312 change->use_target_content = FALSE;
313 change->only_if_complete = FALSE;
314 change->use_random_replace = FALSE;
315 change->random_percentage = 100;
316 change->replace_when = CP_WHEN_EMPTY;
318 change->has_action = FALSE;
319 change->action_type = CA_NO_ACTION;
320 change->action_mode = CA_MODE_UNDEFINED;
321 change->action_arg = CA_ARG_UNDEFINED;
323 for (x = 0; x < 3; x++)
324 for (y = 0; y < 3; y++)
325 change->target_content.e[x][y] = EL_EMPTY_SPACE;
327 change->direct_action = 0;
328 change->other_action = 0;
330 change->pre_change_function = NULL;
331 change->change_function = NULL;
332 change->post_change_function = NULL;
335 static void setLevelInfoToDefaults(struct LevelInfo *level)
337 static boolean clipboard_elements_initialized = FALSE;
340 setLevelInfoToDefaultsFromConfigList(level);
341 setLevelInfoToDefaults_EM();
343 level->native_em_level = &native_em_level;
345 level->game_engine_type = GAME_ENGINE_TYPE_RND;
347 level->file_version = FILE_VERSION_ACTUAL;
348 level->game_version = GAME_VERSION_ACTUAL;
350 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
351 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
352 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
354 level->fieldx = STD_LEV_FIELDX;
355 level->fieldy = STD_LEV_FIELDY;
357 for (x = 0; x < MAX_LEV_FIELDX; x++)
358 for (y = 0; y < MAX_LEV_FIELDY; y++)
359 level->field[x][y] = EL_SAND;
362 level->gems_needed = 0;
364 level->amoeba_speed = 10;
366 level->time_magic_wall = 10;
367 level->time_wheel = 10;
369 level->time_light = 10;
370 level->time_timegate = 10;
373 level->amoeba_content = EL_DIAMOND;
375 level->game_of_life[0] = 2;
376 level->game_of_life[1] = 3;
377 level->game_of_life[2] = 3;
378 level->game_of_life[3] = 3;
380 level->biomaze[0] = 2;
381 level->biomaze[1] = 3;
382 level->biomaze[2] = 3;
383 level->biomaze[3] = 3;
385 level->double_speed = FALSE;
386 level->initial_gravity = FALSE;
387 level->em_slippery_gems = FALSE;
388 level->instant_relocation = FALSE;
389 level->can_pass_to_walkable = FALSE;
390 level->grow_into_diggable = TRUE;
392 level->block_snap_field = TRUE;
394 level->block_last_field = FALSE; /* EM does not block by default */
395 level->sp_block_last_field = TRUE; /* SP blocks the last field */
397 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
398 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
400 level->use_spring_bug = FALSE;
401 level->use_time_orb_bug = FALSE;
403 level->use_step_counter = FALSE;
405 /* values for the new EMC elements */
407 level->android_move_time = 10;
408 level->android_clone_time = 10;
409 level->ball_time = 10;
410 level->lenses_score = 10;
411 level->lenses_time = 10;
412 level->magnify_score = 10;
413 level->magnify_time = 10;
414 level->slurp_score = 10;
415 level->wind_direction_initial = MV_NONE;
417 level->ball_random = FALSE;
418 level->ball_state_initial = FALSE;
420 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
421 for (x = 0; x < 3; x++)
422 for (y = 0; y < 3; y++)
423 level->ball_content[i].e[x][y] = EL_EMPTY;
425 for (i = 0; i < 16; i++)
426 level->android_array[i] = FALSE;
428 level->use_custom_template = FALSE;
430 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
431 level->name[i] = '\0';
432 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
433 level->author[i] = '\0';
435 strcpy(level->name, NAMELESS_LEVEL_NAME);
436 strcpy(level->author, ANONYMOUS_NAME);
438 for (i = 0; i < 4; i++)
440 level->envelope_text[i][0] = '\0';
441 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
442 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
445 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
446 level->score[i] = 10;
448 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
449 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
450 for (x = 0; x < 3; x++)
451 for (y = 0; y < 3; y++)
452 level->yamyam_content[i].e[x][y] =
453 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
455 level->field[0][0] = EL_PLAYER_1;
456 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
458 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
461 struct ElementInfo *ei = &element_info[element];
463 /* never initialize clipboard elements after the very first time */
464 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
467 setElementChangePages(ei, 1);
468 setElementChangeInfoToDefaults(ei->change);
470 if (IS_CUSTOM_ELEMENT(element) ||
471 IS_GROUP_ELEMENT(element) ||
472 IS_INTERNAL_ELEMENT(element))
474 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
475 ei->description[j] = '\0';
477 if (ei->custom_description != NULL)
478 strncpy(ei->description, ei->custom_description,MAX_ELEMENT_NAME_LEN);
480 strcpy(ei->description, ei->editor_description);
482 ei->use_gfx_element = FALSE;
483 ei->gfx_element = EL_EMPTY_SPACE;
485 ei->modified_settings = FALSE;
488 if (IS_CUSTOM_ELEMENT(element) ||
489 IS_INTERNAL_ELEMENT(element))
491 ei->access_direction = MV_ALL_DIRECTIONS;
493 ei->collect_score_initial = 10; /* special default */
494 ei->collect_count_initial = 1; /* special default */
496 ei->ce_value_fixed_initial = 0;
497 ei->ce_value_random_initial = 0;
498 ei->use_last_ce_value = FALSE;
500 ei->push_delay_fixed = -1; /* initialize later */
501 ei->push_delay_random = -1; /* initialize later */
502 ei->drop_delay_fixed = 0;
503 ei->drop_delay_random = 0;
504 ei->move_delay_fixed = 0;
505 ei->move_delay_random = 0;
507 ei->move_pattern = MV_ALL_DIRECTIONS;
508 ei->move_direction_initial = MV_START_AUTOMATIC;
509 ei->move_stepsize = TILEX / 8;
511 ei->move_enter_element = EL_EMPTY_SPACE;
512 ei->move_leave_element = EL_EMPTY_SPACE;
513 ei->move_leave_type = LEAVE_TYPE_UNLIMITED;
515 ei->slippery_type = SLIPPERY_ANY_RANDOM;
517 ei->explosion_type = EXPLODES_3X3;
518 ei->explosion_delay = 16;
519 ei->ignition_delay = 8;
521 for (x = 0; x < 3; x++)
522 for (y = 0; y < 3; y++)
523 ei->content.e[x][y] = EL_EMPTY_SPACE;
526 ei->access_layer = 0;
527 ei->access_protected = 0;
528 ei->walk_to_action = 0;
529 ei->smash_targets = 0;
532 ei->can_explode_by_fire = FALSE;
533 ei->can_explode_smashed = FALSE;
534 ei->can_explode_impact = FALSE;
536 ei->current_change_page = 0;
538 /* start with no properties at all */
539 for (j = 0; j < NUM_EP_BITFIELDS; j++)
540 Properties[element][j] = EP_BITMASK_DEFAULT;
542 /* now set default properties */
543 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
546 if (IS_GROUP_ELEMENT(element) ||
547 IS_INTERNAL_ELEMENT(element))
549 struct ElementGroupInfo *group;
551 /* initialize memory for list of elements in group */
552 if (ei->group == NULL)
553 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
557 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
558 group->element[j] = EL_EMPTY_SPACE;
560 /* default: only one element in group */
561 group->num_elements = 1;
563 group->choice_mode = ANIM_RANDOM;
567 clipboard_elements_initialized = TRUE;
569 BorderElement = EL_STEELWALL;
571 level->no_valid_file = FALSE;
573 level->changed = FALSE;
575 if (leveldir_current == NULL) /* only when dumping level */
578 /* try to determine better author name than 'anonymous' */
579 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
581 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
582 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
586 switch (LEVELCLASS(leveldir_current))
588 case LEVELCLASS_TUTORIAL:
589 strcpy(level->author, PROGRAM_AUTHOR_STRING);
592 case LEVELCLASS_CONTRIB:
593 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
594 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
597 case LEVELCLASS_PRIVATE:
598 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
599 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
603 /* keep default value */
609 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
611 level_file_info->nr = 0;
612 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
613 level_file_info->packed = FALSE;
614 level_file_info->basename = NULL;
615 level_file_info->filename = NULL;
618 static void ActivateLevelTemplate()
620 /* Currently there is no special action needed to activate the template
621 data, because 'element_info' and 'Properties' overwrite the original
622 level data, while all other variables do not change. */
625 static char *getLevelFilenameFromBasename(char *basename)
627 static char *filename = NULL;
629 checked_free(filename);
631 filename = getPath2(getCurrentLevelDir(), basename);
636 static int getFileTypeFromBasename(char *basename)
638 static char *filename = NULL;
639 struct stat file_status;
641 /* ---------- try to determine file type from filename ---------- */
643 /* check for typical filename of a Supaplex level package file */
644 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
645 strncmp(basename, "LEVELS.D", 8) == 0))
646 return LEVEL_FILE_TYPE_SP;
648 /* ---------- try to determine file type from filesize ---------- */
650 checked_free(filename);
651 filename = getPath2(getCurrentLevelDir(), basename);
653 if (stat(filename, &file_status) == 0)
655 /* check for typical filesize of a Supaplex level package file */
656 if (file_status.st_size == 170496)
657 return LEVEL_FILE_TYPE_SP;
660 return LEVEL_FILE_TYPE_UNKNOWN;
663 static char *getSingleLevelBasename(int nr)
665 static char basename[MAX_FILENAME_LEN];
668 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
670 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
675 static char *getPackedLevelBasename(int type)
677 static char basename[MAX_FILENAME_LEN];
678 char *directory = getCurrentLevelDir();
680 struct dirent *dir_entry;
682 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
684 if ((dir = opendir(directory)) == NULL)
686 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
691 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
693 char *entry_basename = dir_entry->d_name;
694 int entry_type = getFileTypeFromBasename(entry_basename);
696 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
698 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
701 strcpy(basename, entry_basename);
713 static char *getSingleLevelFilename(int nr)
715 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
719 static char *getPackedLevelFilename(int type)
721 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
725 char *getDefaultLevelFilename(int nr)
727 return getSingleLevelFilename(nr);
731 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
736 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
737 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
741 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
742 int type, char *format, ...)
744 static char basename[MAX_FILENAME_LEN];
747 va_start(ap, format);
748 vsprintf(basename, format, ap);
753 lfi->basename = basename;
754 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
757 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
762 lfi->basename = getPackedLevelBasename(lfi->type);
763 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
766 static int getFiletypeFromID(char *filetype_id)
768 char *filetype_id_lower;
769 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
772 if (filetype_id == NULL)
773 return LEVEL_FILE_TYPE_UNKNOWN;
775 filetype_id_lower = getStringToLower(filetype_id);
777 for (i = 0; filetype_id_list[i].id != NULL; i++)
779 char *id_lower = getStringToLower(filetype_id_list[i].id);
781 if (strcmp(filetype_id_lower, id_lower) == 0)
782 filetype = filetype_id_list[i].filetype;
786 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
790 free(filetype_id_lower);
795 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
799 /* special case: level number is negative => check for level template file */
802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
803 "template.%s", LEVELFILE_EXTENSION);
805 /* no fallback if template file not existing */
809 /* special case: check for file name/pattern specified in "levelinfo.conf" */
810 if (leveldir_current->level_filename != NULL)
812 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
814 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
815 leveldir_current->level_filename, nr);
816 if (fileExists(lfi->filename))
820 /* check for native Rocks'n'Diamonds level file */
821 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
822 "%03d.%s", nr, LEVELFILE_EXTENSION);
823 if (fileExists(lfi->filename))
826 /* check for Emerald Mine level file (V1) */
827 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
828 'a' + (nr / 10) % 26, '0' + nr % 10);
829 if (fileExists(lfi->filename))
831 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
832 'A' + (nr / 10) % 26, '0' + nr % 10);
833 if (fileExists(lfi->filename))
836 /* check for Emerald Mine level file (V2 to V5) */
837 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
838 if (fileExists(lfi->filename))
841 /* check for Emerald Mine level file (V6 / single mode) */
842 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
843 if (fileExists(lfi->filename))
845 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
846 if (fileExists(lfi->filename))
849 /* check for Emerald Mine level file (V6 / teamwork mode) */
850 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
851 if (fileExists(lfi->filename))
853 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
854 if (fileExists(lfi->filename))
857 /* check for various packed level file formats */
858 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
859 if (fileExists(lfi->filename))
862 /* no known level file found -- use default values (and fail later) */
863 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
864 "%03d.%s", nr, LEVELFILE_EXTENSION);
867 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
869 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
870 lfi->type = getFileTypeFromBasename(lfi->basename);
873 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
875 /* always start with reliable default values */
876 setFileInfoToDefaults(level_file_info);
878 level_file_info->nr = nr; /* set requested level number */
880 determineLevelFileInfo_Filename(level_file_info);
881 determineLevelFileInfo_Filetype(level_file_info);
884 /* ------------------------------------------------------------------------- */
885 /* functions for loading R'n'D level */
886 /* ------------------------------------------------------------------------- */
888 int getMappedElement(int element)
890 /* remap some (historic, now obsolete) elements */
894 case EL_PLAYER_OBSOLETE:
895 element = EL_PLAYER_1;
898 case EL_KEY_OBSOLETE:
901 case EL_EM_KEY_1_FILE_OBSOLETE:
902 element = EL_EM_KEY_1;
905 case EL_EM_KEY_2_FILE_OBSOLETE:
906 element = EL_EM_KEY_2;
909 case EL_EM_KEY_3_FILE_OBSOLETE:
910 element = EL_EM_KEY_3;
913 case EL_EM_KEY_4_FILE_OBSOLETE:
914 element = EL_EM_KEY_4;
917 case EL_ENVELOPE_OBSOLETE:
918 element = EL_ENVELOPE_1;
926 if (element >= NUM_FILE_ELEMENTS)
928 Error(ERR_WARN, "invalid level element %d", element);
930 element = EL_UNKNOWN;
938 int getMappedElementByVersion(int element, int game_version)
940 /* remap some elements due to certain game version */
942 if (game_version <= VERSION_IDENT(2,2,0,0))
944 /* map game font elements */
945 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
946 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
947 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
948 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
951 if (game_version < VERSION_IDENT(3,0,0,0))
953 /* map Supaplex gravity tube elements */
954 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
955 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
956 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
957 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
964 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
966 level->file_version = getFileVersion(file);
967 level->game_version = getFileVersion(file);
972 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
976 level->fieldx = getFile8Bit(file);
977 level->fieldy = getFile8Bit(file);
979 level->time = getFile16BitBE(file);
980 level->gems_needed = getFile16BitBE(file);
982 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
983 level->name[i] = getFile8Bit(file);
984 level->name[MAX_LEVEL_NAME_LEN] = 0;
986 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
987 level->score[i] = getFile8Bit(file);
989 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
990 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
991 for (y = 0; y < 3; y++)
992 for (x = 0; x < 3; x++)
993 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
995 level->amoeba_speed = getFile8Bit(file);
996 level->time_magic_wall = getFile8Bit(file);
997 level->time_wheel = getFile8Bit(file);
998 level->amoeba_content = getMappedElement(getFile8Bit(file));
999 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1000 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1001 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1002 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1004 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1006 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1007 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1008 level->can_move_into_acid_bits = getFile32BitBE(file);
1009 level->dont_collide_with_bits = getFile8Bit(file);
1011 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1012 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1014 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1015 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1016 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1018 level->game_engine_type = getFile8Bit(file);
1020 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
1025 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
1029 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1030 level->author[i] = getFile8Bit(file);
1031 level->author[MAX_LEVEL_NAME_LEN] = 0;
1036 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
1039 int chunk_size_expected = level->fieldx * level->fieldy;
1041 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1042 stored with 16-bit encoding (and should be twice as big then).
1043 Even worse, playfield data was stored 16-bit when only yamyam content
1044 contained 16-bit elements and vice versa. */
1046 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1047 chunk_size_expected *= 2;
1049 if (chunk_size_expected != chunk_size)
1051 ReadUnusedBytesFromFile(file, chunk_size);
1052 return chunk_size_expected;
1055 for (y = 0; y < level->fieldy; y++)
1056 for (x = 0; x < level->fieldx; x++)
1057 level->field[x][y] =
1058 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
1063 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
1066 int header_size = 4;
1067 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
1068 int chunk_size_expected = header_size + content_size;
1070 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1071 stored with 16-bit encoding (and should be twice as big then).
1072 Even worse, playfield data was stored 16-bit when only yamyam content
1073 contained 16-bit elements and vice versa. */
1075 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1076 chunk_size_expected += content_size;
1078 if (chunk_size_expected != chunk_size)
1080 ReadUnusedBytesFromFile(file, chunk_size);
1081 return chunk_size_expected;
1085 level->num_yamyam_contents = getFile8Bit(file);
1089 /* correct invalid number of content fields -- should never happen */
1090 if (level->num_yamyam_contents < 1 ||
1091 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
1092 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1094 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1095 for (y = 0; y < 3; y++)
1096 for (x = 0; x < 3; x++)
1097 level->yamyam_content[i].e[x][y] =
1098 getMappedElement(level->encoding_16bit_field ?
1099 getFile16BitBE(file) : getFile8Bit(file));
1103 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
1107 int num_contents, content_xsize, content_ysize;
1108 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1110 element = getMappedElement(getFile16BitBE(file));
1111 num_contents = getFile8Bit(file);
1112 content_xsize = getFile8Bit(file);
1113 content_ysize = getFile8Bit(file);
1115 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1117 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1118 for (y = 0; y < 3; y++)
1119 for (x = 0; x < 3; x++)
1120 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
1122 /* correct invalid number of content fields -- should never happen */
1123 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
1124 num_contents = STD_ELEMENT_CONTENTS;
1126 if (element == EL_YAMYAM)
1128 level->num_yamyam_contents = num_contents;
1130 for (i = 0; i < num_contents; i++)
1131 for (y = 0; y < 3; y++)
1132 for (x = 0; x < 3; x++)
1133 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
1135 else if (element == EL_BD_AMOEBA)
1137 level->amoeba_content = content_array[0][0][0];
1141 Error(ERR_WARN, "cannot load content for element '%d'", element);
1147 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
1153 int chunk_size_expected;
1155 element = getMappedElement(getFile16BitBE(file));
1156 if (!IS_ENVELOPE(element))
1157 element = EL_ENVELOPE_1;
1159 envelope_nr = element - EL_ENVELOPE_1;
1161 envelope_len = getFile16BitBE(file);
1163 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
1164 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
1166 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1168 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
1169 if (chunk_size_expected != chunk_size)
1171 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
1172 return chunk_size_expected;
1175 for (i = 0; i < envelope_len; i++)
1176 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
1181 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
1183 int num_changed_custom_elements = getFile16BitBE(file);
1184 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
1187 if (chunk_size_expected != chunk_size)
1189 ReadUnusedBytesFromFile(file, chunk_size - 2);
1190 return chunk_size_expected;
1193 for (i = 0; i < num_changed_custom_elements; i++)
1195 int element = getFile16BitBE(file);
1196 int properties = getFile32BitBE(file);
1198 if (IS_CUSTOM_ELEMENT(element))
1199 Properties[element][EP_BITFIELD_BASE] = properties;
1201 Error(ERR_WARN, "invalid custom element number %d", element);
1207 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
1209 int num_changed_custom_elements = getFile16BitBE(file);
1210 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
1213 if (chunk_size_expected != chunk_size)
1215 ReadUnusedBytesFromFile(file, chunk_size - 2);
1216 return chunk_size_expected;
1219 for (i = 0; i < num_changed_custom_elements; i++)
1221 int element = getFile16BitBE(file);
1222 int custom_target_element = getFile16BitBE(file);
1224 if (IS_CUSTOM_ELEMENT(element))
1225 element_info[element].change->target_element = custom_target_element;
1227 Error(ERR_WARN, "invalid custom element number %d", element);
1233 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
1235 int num_changed_custom_elements = getFile16BitBE(file);
1236 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1239 if (chunk_size_expected != chunk_size)
1241 ReadUnusedBytesFromFile(file, chunk_size - 2);
1242 return chunk_size_expected;
1245 for (i = 0; i < num_changed_custom_elements; i++)
1247 int element = getFile16BitBE(file);
1248 struct ElementInfo *ei = &element_info[element];
1249 unsigned long event_bits;
1251 if (!IS_CUSTOM_ELEMENT(element))
1253 Error(ERR_WARN, "invalid custom element number %d", element);
1255 element = EL_INTERNAL_DUMMY;
1258 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1259 ei->description[j] = getFile8Bit(file);
1260 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1262 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1264 /* some free bytes for future properties and padding */
1265 ReadUnusedBytesFromFile(file, 7);
1267 ei->use_gfx_element = getFile8Bit(file);
1268 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1270 ei->collect_score_initial = getFile8Bit(file);
1271 ei->collect_count_initial = getFile8Bit(file);
1273 ei->push_delay_fixed = getFile16BitBE(file);
1274 ei->push_delay_random = getFile16BitBE(file);
1275 ei->move_delay_fixed = getFile16BitBE(file);
1276 ei->move_delay_random = getFile16BitBE(file);
1278 ei->move_pattern = getFile16BitBE(file);
1279 ei->move_direction_initial = getFile8Bit(file);
1280 ei->move_stepsize = getFile8Bit(file);
1282 for (y = 0; y < 3; y++)
1283 for (x = 0; x < 3; x++)
1284 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1286 event_bits = getFile32BitBE(file);
1287 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1288 if (event_bits & (1 << j))
1289 ei->change->has_event[j] = TRUE;
1291 ei->change->target_element = getMappedElement(getFile16BitBE(file));
1293 ei->change->delay_fixed = getFile16BitBE(file);
1294 ei->change->delay_random = getFile16BitBE(file);
1295 ei->change->delay_frames = getFile16BitBE(file);
1297 ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
1299 ei->change->explode = getFile8Bit(file);
1300 ei->change->use_target_content = getFile8Bit(file);
1301 ei->change->only_if_complete = getFile8Bit(file);
1302 ei->change->use_random_replace = getFile8Bit(file);
1304 ei->change->random_percentage = getFile8Bit(file);
1305 ei->change->replace_when = getFile8Bit(file);
1307 for (y = 0; y < 3; y++)
1308 for (x = 0; x < 3; x++)
1309 ei->change->target_content.e[x][y] =
1310 getMappedElement(getFile16BitBE(file));
1312 ei->slippery_type = getFile8Bit(file);
1314 /* some free bytes for future properties and padding */
1315 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1317 /* mark that this custom element has been modified */
1318 ei->modified_settings = TRUE;
1324 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1326 struct ElementInfo *ei;
1327 int chunk_size_expected;
1331 /* ---------- custom element base property values (96 bytes) ------------- */
1333 element = getFile16BitBE(file);
1335 if (!IS_CUSTOM_ELEMENT(element))
1337 Error(ERR_WARN, "invalid custom element number %d", element);
1339 ReadUnusedBytesFromFile(file, chunk_size - 2);
1343 ei = &element_info[element];
1345 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1346 ei->description[i] = getFile8Bit(file);
1347 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1349 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1350 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1352 ei->num_change_pages = getFile8Bit(file);
1354 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1355 if (chunk_size_expected != chunk_size)
1357 ReadUnusedBytesFromFile(file, chunk_size - 43);
1358 return chunk_size_expected;
1361 ei->ce_value_fixed_initial = getFile16BitBE(file);
1362 ei->ce_value_random_initial = getFile16BitBE(file);
1363 ei->use_last_ce_value = getFile8Bit(file);
1365 ei->use_gfx_element = getFile8Bit(file);
1366 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1368 ei->collect_score_initial = getFile8Bit(file);
1369 ei->collect_count_initial = getFile8Bit(file);
1371 ei->drop_delay_fixed = getFile8Bit(file);
1372 ei->push_delay_fixed = getFile8Bit(file);
1373 ei->drop_delay_random = getFile8Bit(file);
1374 ei->push_delay_random = getFile8Bit(file);
1375 ei->move_delay_fixed = getFile16BitBE(file);
1376 ei->move_delay_random = getFile16BitBE(file);
1378 /* bits 0 - 15 of "move_pattern" ... */
1379 ei->move_pattern = getFile16BitBE(file);
1380 ei->move_direction_initial = getFile8Bit(file);
1381 ei->move_stepsize = getFile8Bit(file);
1383 ei->slippery_type = getFile8Bit(file);
1385 for (y = 0; y < 3; y++)
1386 for (x = 0; x < 3; x++)
1387 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1389 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1390 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1391 ei->move_leave_type = getFile8Bit(file);
1393 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1394 ei->move_pattern |= (getFile16BitBE(file) << 16);
1396 ei->access_direction = getFile8Bit(file);
1398 ei->explosion_delay = getFile8Bit(file);
1399 ei->ignition_delay = getFile8Bit(file);
1400 ei->explosion_type = getFile8Bit(file);
1402 /* some free bytes for future custom property values and padding */
1403 ReadUnusedBytesFromFile(file, 1);
1405 /* ---------- change page property values (48 bytes) --------------------- */
1407 setElementChangePages(ei, ei->num_change_pages);
1409 for (i = 0; i < ei->num_change_pages; i++)
1411 struct ElementChangeInfo *change = &ei->change_page[i];
1412 unsigned long event_bits;
1414 /* always start with reliable default values */
1415 setElementChangeInfoToDefaults(change);
1417 event_bits = getFile32BitBE(file);
1418 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1419 if (event_bits & (1 << j))
1420 change->has_event[j] = TRUE;
1422 change->target_element = getMappedElement(getFile16BitBE(file));
1424 change->delay_fixed = getFile16BitBE(file);
1425 change->delay_random = getFile16BitBE(file);
1426 change->delay_frames = getFile16BitBE(file);
1428 change->trigger_element = getMappedElement(getFile16BitBE(file));
1430 change->explode = getFile8Bit(file);
1431 change->use_target_content = getFile8Bit(file);
1432 change->only_if_complete = getFile8Bit(file);
1433 change->use_random_replace = getFile8Bit(file);
1435 change->random_percentage = getFile8Bit(file);
1436 change->replace_when = getFile8Bit(file);
1438 for (y = 0; y < 3; y++)
1439 for (x = 0; x < 3; x++)
1440 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
1442 change->can_change = getFile8Bit(file);
1444 change->trigger_side = getFile8Bit(file);
1446 change->trigger_player = getFile8Bit(file);
1447 change->trigger_page = getFile8Bit(file);
1449 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1450 CH_PAGE_ANY : (1 << change->trigger_page));
1452 change->has_action = getFile8Bit(file);
1453 change->action_type = getFile8Bit(file);
1454 change->action_mode = getFile8Bit(file);
1455 change->action_arg = getFile16BitBE(file);
1457 /* some free bytes for future change property values and padding */
1458 ReadUnusedBytesFromFile(file, 1);
1461 /* mark this custom element as modified */
1462 ei->modified_settings = TRUE;
1467 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1469 struct ElementInfo *ei;
1470 struct ElementGroupInfo *group;
1474 element = getFile16BitBE(file);
1476 if (!IS_GROUP_ELEMENT(element))
1478 Error(ERR_WARN, "invalid group element number %d", element);
1480 ReadUnusedBytesFromFile(file, chunk_size - 2);
1484 ei = &element_info[element];
1486 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1487 ei->description[i] = getFile8Bit(file);
1488 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1490 group = element_info[element].group;
1492 group->num_elements = getFile8Bit(file);
1494 ei->use_gfx_element = getFile8Bit(file);
1495 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1497 group->choice_mode = getFile8Bit(file);
1499 /* some free bytes for future values and padding */
1500 ReadUnusedBytesFromFile(file, 3);
1502 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1503 group->element[i] = getMappedElement(getFile16BitBE(file));
1505 /* mark this group element as modified */
1506 element_info[element].modified_settings = TRUE;
1511 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
1513 int real_chunk_size = 0;
1518 int element = getFile16BitBE(file);
1519 int type = getFile8Bit(file);
1520 int bytes = type & CONF_MASK_BYTES;
1521 boolean element_found = FALSE;
1523 real_chunk_size += 3;
1525 li = *level; /* copy level information into temporary buffer */
1527 if (bytes == CONF_MASK_MULTI_BYTES)
1529 int num_bytes = getFile16BitBE(file);
1530 byte *buffer = checked_malloc(num_bytes);
1532 ReadBytesFromFile(file, buffer, num_bytes);
1534 for (i = 0; element_conf[i].element != -1; i++)
1536 if (element_conf[i].element == element &&
1537 element_conf[i].type == type)
1539 element_found = TRUE;
1541 if (type == CONF_VALUE_CONTENT_8)
1543 struct Content *content= (struct Content *)(element_conf[i].value);
1544 int num_contents = num_bytes / CONF_CONTENT_NUM_BYTES;
1547 for (c = 0; c < num_contents; c++)
1548 for (y = 0; y < 3; y++)
1549 for (x = 0; x < 3; x++)
1550 content[c].e[x][y] =
1551 getMappedElement(CONF_CONTENT_ELEMENT(buffer, c, x, y));
1554 element_found = FALSE;
1560 checked_free(buffer);
1562 real_chunk_size += 2 + num_bytes;
1566 int value = (bytes == CONF_MASK_1_BYTE ? getFile8Bit (file) :
1567 bytes == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
1568 bytes == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
1570 for (i = 0; element_conf[i].element != -1; i++)
1572 if (element_conf[i].element == element &&
1573 element_conf[i].type == type)
1575 if (CONF_VALUE_BOOLEAN(type))
1576 *(boolean *)(element_conf[i].value) = value;
1578 *(int *) (element_conf[i].value) = value;
1580 element_found = TRUE;
1586 real_chunk_size += CONF_VALUE_NUM_BYTES(bytes);
1589 *level = li; /* copy temporary buffer back to level information */
1592 Error(ERR_WARN, "cannot load CONF value for element %d", element);
1594 if (type == CONF_LAST_ENTRY || real_chunk_size >= chunk_size)
1598 return real_chunk_size;
1601 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1602 struct LevelFileInfo *level_file_info)
1604 char *filename = level_file_info->filename;
1605 char cookie[MAX_LINE_LEN];
1606 char chunk_name[CHUNK_ID_LEN + 1];
1610 if (!(file = fopen(filename, MODE_READ)))
1612 level->no_valid_file = TRUE;
1614 if (level != &level_template)
1615 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1620 getFileChunkBE(file, chunk_name, NULL);
1621 if (strcmp(chunk_name, "RND1") == 0)
1623 getFile32BitBE(file); /* not used */
1625 getFileChunkBE(file, chunk_name, NULL);
1626 if (strcmp(chunk_name, "CAVE") != 0)
1628 level->no_valid_file = TRUE;
1630 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1635 else /* check for pre-2.0 file format with cookie string */
1637 strcpy(cookie, chunk_name);
1638 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1639 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1640 cookie[strlen(cookie) - 1] = '\0';
1642 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1644 level->no_valid_file = TRUE;
1646 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1651 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1653 level->no_valid_file = TRUE;
1655 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1660 /* pre-2.0 level files have no game version, so use file version here */
1661 level->game_version = level->file_version;
1664 if (level->file_version < FILE_VERSION_1_2)
1666 /* level files from versions before 1.2.0 without chunk structure */
1667 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1668 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1676 int (*loader)(FILE *, int, struct LevelInfo *);
1680 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1681 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1682 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1683 { "BODY", -1, LoadLevel_BODY },
1684 { "CONT", -1, LoadLevel_CONT },
1685 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1686 { "CNT3", -1, LoadLevel_CNT3 },
1687 { "CUS1", -1, LoadLevel_CUS1 },
1688 { "CUS2", -1, LoadLevel_CUS2 },
1689 { "CUS3", -1, LoadLevel_CUS3 },
1690 { "CUS4", -1, LoadLevel_CUS4 },
1691 { "GRP1", -1, LoadLevel_GRP1 },
1692 { "CONF", -1, LoadLevel_CONF },
1697 while (getFileChunkBE(file, chunk_name, &chunk_size))
1701 while (chunk_info[i].name != NULL &&
1702 strcmp(chunk_name, chunk_info[i].name) != 0)
1705 if (chunk_info[i].name == NULL)
1707 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1708 chunk_name, filename);
1709 ReadUnusedBytesFromFile(file, chunk_size);
1711 else if (chunk_info[i].size != -1 &&
1712 chunk_info[i].size != chunk_size)
1714 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1715 chunk_size, chunk_name, filename);
1716 ReadUnusedBytesFromFile(file, chunk_size);
1720 /* call function to load this level chunk */
1721 int chunk_size_expected =
1722 (chunk_info[i].loader)(file, chunk_size, level);
1724 /* the size of some chunks cannot be checked before reading other
1725 chunks first (like "HEAD" and "BODY") that contain some header
1726 information, so check them here */
1727 if (chunk_size_expected != chunk_size)
1729 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1730 chunk_size, chunk_name, filename);
1739 /* ------------------------------------------------------------------------- */
1740 /* functions for loading EM level */
1741 /* ------------------------------------------------------------------------- */
1745 static int map_em_element_yam(int element)
1749 case 0x00: return EL_EMPTY;
1750 case 0x01: return EL_EMERALD;
1751 case 0x02: return EL_DIAMOND;
1752 case 0x03: return EL_ROCK;
1753 case 0x04: return EL_ROBOT;
1754 case 0x05: return EL_SPACESHIP_UP;
1755 case 0x06: return EL_BOMB;
1756 case 0x07: return EL_BUG_UP;
1757 case 0x08: return EL_AMOEBA_DROP;
1758 case 0x09: return EL_NUT;
1759 case 0x0a: return EL_YAMYAM;
1760 case 0x0b: return EL_QUICKSAND_FULL;
1761 case 0x0c: return EL_SAND;
1762 case 0x0d: return EL_WALL_SLIPPERY;
1763 case 0x0e: return EL_STEELWALL;
1764 case 0x0f: return EL_WALL;
1765 case 0x10: return EL_EM_KEY_1;
1766 case 0x11: return EL_EM_KEY_2;
1767 case 0x12: return EL_EM_KEY_4;
1768 case 0x13: return EL_EM_KEY_3;
1769 case 0x14: return EL_MAGIC_WALL;
1770 case 0x15: return EL_ROBOT_WHEEL;
1771 case 0x16: return EL_DYNAMITE;
1773 case 0x17: return EL_EM_KEY_1; /* EMC */
1774 case 0x18: return EL_BUG_UP; /* EMC */
1775 case 0x1a: return EL_DIAMOND; /* EMC */
1776 case 0x1b: return EL_EMERALD; /* EMC */
1777 case 0x25: return EL_NUT; /* EMC */
1778 case 0x80: return EL_EMPTY; /* EMC */
1779 case 0x85: return EL_EM_KEY_1; /* EMC */
1780 case 0x86: return EL_EM_KEY_2; /* EMC */
1781 case 0x87: return EL_EM_KEY_4; /* EMC */
1782 case 0x88: return EL_EM_KEY_3; /* EMC */
1783 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1784 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1785 case 0xaf: return EL_DYNAMITE; /* EMC */
1786 case 0xbd: return EL_SAND; /* EMC */
1789 Error(ERR_WARN, "invalid level element %d", element);
1794 static int map_em_element_field(int element)
1796 if (element >= 0xc8 && element <= 0xe1)
1797 return EL_CHAR_A + (element - 0xc8);
1798 else if (element >= 0xe2 && element <= 0xeb)
1799 return EL_CHAR_0 + (element - 0xe2);
1803 case 0x00: return EL_ROCK;
1804 case 0x01: return EL_ROCK; /* EMC */
1805 case 0x02: return EL_DIAMOND;
1806 case 0x03: return EL_DIAMOND;
1807 case 0x04: return EL_ROBOT;
1808 case 0x05: return EL_ROBOT; /* EMC */
1809 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1810 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1811 case 0x08: return EL_SPACESHIP_UP;
1812 case 0x09: return EL_SPACESHIP_RIGHT;
1813 case 0x0a: return EL_SPACESHIP_DOWN;
1814 case 0x0b: return EL_SPACESHIP_LEFT;
1815 case 0x0c: return EL_SPACESHIP_UP;
1816 case 0x0d: return EL_SPACESHIP_RIGHT;
1817 case 0x0e: return EL_SPACESHIP_DOWN;
1818 case 0x0f: return EL_SPACESHIP_LEFT;
1820 case 0x10: return EL_BOMB;
1821 case 0x11: return EL_BOMB; /* EMC */
1822 case 0x12: return EL_EMERALD;
1823 case 0x13: return EL_EMERALD;
1824 case 0x14: return EL_BUG_UP;
1825 case 0x15: return EL_BUG_RIGHT;
1826 case 0x16: return EL_BUG_DOWN;
1827 case 0x17: return EL_BUG_LEFT;
1828 case 0x18: return EL_BUG_UP;
1829 case 0x19: return EL_BUG_RIGHT;
1830 case 0x1a: return EL_BUG_DOWN;
1831 case 0x1b: return EL_BUG_LEFT;
1832 case 0x1c: return EL_AMOEBA_DROP;
1833 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1834 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1835 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1837 case 0x20: return EL_ROCK;
1838 case 0x21: return EL_BOMB; /* EMC */
1839 case 0x22: return EL_DIAMOND; /* EMC */
1840 case 0x23: return EL_EMERALD; /* EMC */
1841 case 0x24: return EL_MAGIC_WALL;
1842 case 0x25: return EL_NUT;
1843 case 0x26: return EL_NUT; /* EMC */
1844 case 0x27: return EL_NUT; /* EMC */
1846 /* looks like magic wheel, but is _always_ activated */
1847 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1849 case 0x29: return EL_YAMYAM; /* up */
1850 case 0x2a: return EL_YAMYAM; /* down */
1851 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1852 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1853 case 0x2d: return EL_QUICKSAND_FULL;
1854 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1855 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1857 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1858 case 0x31: return EL_SAND; /* EMC */
1859 case 0x32: return EL_SAND; /* EMC */
1860 case 0x33: return EL_SAND; /* EMC */
1861 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1862 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1863 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1864 case 0x37: return EL_SAND; /* EMC */
1865 case 0x38: return EL_ROCK; /* EMC */
1866 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1867 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1868 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1869 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1870 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1871 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1872 case 0x3f: return EL_ACID_POOL_BOTTOM;
1874 case 0x40: return EL_EXIT_OPEN; /* 1 */
1875 case 0x41: return EL_EXIT_OPEN; /* 2 */
1876 case 0x42: return EL_EXIT_OPEN; /* 3 */
1877 case 0x43: return EL_BALLOON; /* EMC */
1878 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1879 case 0x45: return EL_SPRING; /* EMC */
1880 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1881 case 0x47: return EL_SPRING; /* left */ /* EMC */
1882 case 0x48: return EL_SPRING; /* right */ /* EMC */
1883 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1884 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1885 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1886 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1887 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1888 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1889 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1891 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1892 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1893 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1894 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1895 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1896 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1897 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1898 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1899 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1900 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1901 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1902 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1903 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1904 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1905 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1906 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1908 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1909 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1910 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1911 case 0x63: return EL_SPRING; /* left */ /* EMC */
1912 case 0x64: return EL_SPRING; /* right */ /* EMC */
1913 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1914 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1915 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1916 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1917 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1918 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1919 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1920 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1921 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1922 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1923 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1925 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1926 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1927 case 0x72: return EL_NUT; /* left */ /* EMC */
1928 case 0x73: return EL_SAND; /* EMC (? "nut") */
1929 case 0x74: return EL_STEELWALL;
1930 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1931 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1932 case 0x77: return EL_BOMB; /* left */ /* EMC */
1933 case 0x78: return EL_BOMB; /* right */ /* EMC */
1934 case 0x79: return EL_ROCK; /* left */ /* EMC */
1935 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1936 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1937 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1938 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1939 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1940 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1942 case 0x80: return EL_EMPTY;
1943 case 0x81: return EL_WALL_SLIPPERY;
1944 case 0x82: return EL_SAND;
1945 case 0x83: return EL_STEELWALL;
1946 case 0x84: return EL_WALL;
1947 case 0x85: return EL_EM_KEY_1;
1948 case 0x86: return EL_EM_KEY_2;
1949 case 0x87: return EL_EM_KEY_4;
1950 case 0x88: return EL_EM_KEY_3;
1951 case 0x89: return EL_EM_GATE_1;
1952 case 0x8a: return EL_EM_GATE_2;
1953 case 0x8b: return EL_EM_GATE_4;
1954 case 0x8c: return EL_EM_GATE_3;
1955 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1956 case 0x8e: return EL_EM_GATE_1_GRAY;
1957 case 0x8f: return EL_EM_GATE_2_GRAY;
1959 case 0x90: return EL_EM_GATE_4_GRAY;
1960 case 0x91: return EL_EM_GATE_3_GRAY;
1961 case 0x92: return EL_MAGIC_WALL;
1962 case 0x93: return EL_ROBOT_WHEEL;
1963 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1964 case 0x95: return EL_ACID_POOL_TOPLEFT;
1965 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1966 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1967 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1968 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1969 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1970 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1971 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1972 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1973 case 0x9e: return EL_EXIT_CLOSED;
1974 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1976 /* looks like normal sand, but behaves like wall */
1977 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1978 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1979 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1980 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1981 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1982 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1983 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1984 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1985 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1986 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1987 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1988 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1989 case 0xac: return EL_CHAR_COMMA; /* EMC */
1990 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1991 case 0xae: return EL_CHAR_MINUS; /* EMC */
1992 case 0xaf: return EL_DYNAMITE;
1994 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1995 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1996 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1997 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1998 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1999 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
2000 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
2001 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
2002 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
2003 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
2004 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
2005 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
2006 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
2007 case 0xbd: return EL_SAND; /* EMC ("dirt") */
2008 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
2009 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
2011 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
2012 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
2013 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
2014 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
2015 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
2016 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
2017 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
2018 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
2020 /* characters: see above */
2022 case 0xec: return EL_CHAR_PERIOD;
2023 case 0xed: return EL_CHAR_EXCLAM;
2024 case 0xee: return EL_CHAR_COLON;
2025 case 0xef: return EL_CHAR_QUESTION;
2027 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
2028 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
2029 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
2030 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
2031 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
2032 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
2033 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
2034 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
2036 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
2037 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
2038 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
2039 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
2040 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
2041 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
2043 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
2044 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
2047 /* should never happen (all 8-bit value cases should be handled) */
2048 Error(ERR_WARN, "invalid level element %d", element);
2053 #define EM_LEVEL_SIZE 2106
2054 #define EM_LEVEL_XSIZE 64
2055 #define EM_LEVEL_YSIZE 32
2057 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2058 struct LevelFileInfo *level_file_info)
2060 char *filename = level_file_info->filename;
2062 unsigned char leveldata[EM_LEVEL_SIZE];
2063 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
2064 int nr = level_file_info->nr;
2067 if (!(file = fopen(filename, MODE_READ)))
2069 level->no_valid_file = TRUE;
2071 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2076 for (i = 0; i < EM_LEVEL_SIZE; i++)
2077 leveldata[i] = fgetc(file);
2081 /* check if level data is crypted by testing against known starting bytes
2082 of the few existing crypted level files (from Emerald Mine 1 + 2) */
2084 if ((leveldata[0] == 0xf1 ||
2085 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
2087 unsigned char code0 = 0x65;
2088 unsigned char code1 = 0x11;
2090 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
2091 leveldata[0] = 0xf1;
2093 /* decode crypted level data */
2095 for (i = 0; i < EM_LEVEL_SIZE; i++)
2097 leveldata[i] ^= code0;
2098 leveldata[i] -= code1;
2100 code0 = (code0 + 7) & 0xff;
2104 level->fieldx = EM_LEVEL_XSIZE;
2105 level->fieldy = EM_LEVEL_YSIZE;
2107 level->time = header[46] * 10;
2108 level->gems_needed = header[47];
2110 /* The original Emerald Mine levels have their level number stored
2111 at the second byte of the level file...
2112 Do not trust this information at other level files, e.g. EMC,
2113 but correct it anyway (normally the first row is completely
2114 steel wall, so the correction does not hurt anyway). */
2116 if (leveldata[1] == nr)
2117 leveldata[1] = leveldata[2]; /* correct level number field */
2119 sprintf(level->name, "Level %d", nr); /* set level name */
2121 level->score[SC_EMERALD] = header[36];
2122 level->score[SC_DIAMOND] = header[37];
2123 level->score[SC_ROBOT] = header[38];
2124 level->score[SC_SPACESHIP] = header[39];
2125 level->score[SC_BUG] = header[40];
2126 level->score[SC_YAMYAM] = header[41];
2127 level->score[SC_NUT] = header[42];
2128 level->score[SC_DYNAMITE] = header[43];
2129 level->score[SC_TIME_BONUS] = header[44];
2131 level->num_yamyam_contents = 4;
2133 for (i = 0; i < level->num_yamyam_contents; i++)
2134 for (y = 0; y < 3; y++)
2135 for (x = 0; x < 3; x++)
2136 level->yamyam_content[i].e[x][y] =
2137 map_em_element_yam(header[i * 9 + y * 3 + x]);
2139 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
2140 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
2141 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
2142 level->amoeba_content = EL_DIAMOND;
2144 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2146 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
2148 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
2149 new_element = EL_AMOEBA_WET;
2151 level->field[x][y] = new_element;
2154 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
2155 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
2156 level->field[x][y] = EL_PLAYER_1;
2158 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
2159 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
2160 level->field[x][y] = EL_PLAYER_2;
2165 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
2167 static int ball_xy[8][2] =
2178 struct LevelInfo_EM *level_em = level->native_em_level;
2179 struct LEVEL *lev = level_em->lev;
2180 struct PLAYER *ply1 = level_em->ply1;
2181 struct PLAYER *ply2 = level_em->ply2;
2184 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
2185 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
2187 lev->time_seconds = level->time;
2188 lev->required_initial = level->gems_needed;
2190 lev->emerald_score = level->score[SC_EMERALD];
2191 lev->diamond_score = level->score[SC_DIAMOND];
2192 lev->alien_score = level->score[SC_ROBOT];
2193 lev->tank_score = level->score[SC_SPACESHIP];
2194 lev->bug_score = level->score[SC_BUG];
2195 lev->eater_score = level->score[SC_YAMYAM];
2196 lev->nut_score = level->score[SC_NUT];
2197 lev->dynamite_score = level->score[SC_DYNAMITE];
2198 lev->key_score = level->score[SC_KEY];
2199 lev->exit_score = level->score[SC_TIME_BONUS];
2201 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2202 for (y = 0; y < 3; y++)
2203 for (x = 0; x < 3; x++)
2204 lev->eater_array[i][y * 3 + x] =
2205 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
2207 lev->amoeba_time = level->amoeba_speed;
2208 lev->wonderwall_time_initial = level->time_magic_wall;
2209 lev->wheel_time = level->time_wheel;
2211 lev->android_move_time = level->android_move_time;
2212 lev->android_clone_time = level->android_clone_time;
2213 lev->ball_random = level->ball_random;
2214 lev->ball_state_initial = level->ball_state_initial;
2215 lev->ball_time = level->ball_time;
2217 lev->lenses_score = level->lenses_score;
2218 lev->magnify_score = level->magnify_score;
2219 lev->slurp_score = level->slurp_score;
2221 lev->lenses_time = level->lenses_time;
2222 lev->magnify_time = level->magnify_time;
2223 lev->wind_direction_initial = level->wind_direction_initial;
2225 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2226 for (j = 0; j < 8; j++)
2227 lev->ball_array[i][j] =
2228 map_element_RND_to_EM(level->
2229 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
2231 for (i = 0; i < 16; i++)
2232 lev->android_array[i] = FALSE; /* !!! YET TO COME !!! */
2234 /* first fill the complete playfield with the default border element */
2235 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
2236 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
2237 level_em->cave[x][y] = ZBORDER;
2239 /* then copy the real level contents from level file into the playfield */
2240 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2242 int new_element = map_element_RND_to_EM(level->field[x][y]);
2244 if (level->field[x][y] == EL_AMOEBA_DEAD)
2245 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
2247 level_em->cave[x + 1][y + 1] = new_element;
2250 ply1->x_initial = 0;
2251 ply1->y_initial = 0;
2253 ply2->x_initial = 0;
2254 ply2->y_initial = 0;
2256 /* initialize player positions and delete players from the playfield */
2257 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2259 if (level->field[x][y] == EL_PLAYER_1)
2261 ply1->x_initial = x + 1;
2262 ply1->y_initial = y + 1;
2263 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2265 else if (level->field[x][y] == EL_PLAYER_2)
2267 ply2->x_initial = x + 1;
2268 ply2->y_initial = y + 1;
2269 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2274 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
2276 static int ball_xy[8][2] =
2287 struct LevelInfo_EM *level_em = level->native_em_level;
2288 struct LEVEL *lev = level_em->lev;
2289 struct PLAYER *ply1 = level_em->ply1;
2290 struct PLAYER *ply2 = level_em->ply2;
2293 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
2294 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
2296 level->time = lev->time_seconds;
2297 level->gems_needed = lev->required_initial;
2299 sprintf(level->name, "Level %d", level->file_info.nr);
2301 level->score[SC_EMERALD] = lev->emerald_score;
2302 level->score[SC_DIAMOND] = lev->diamond_score;
2303 level->score[SC_ROBOT] = lev->alien_score;
2304 level->score[SC_SPACESHIP] = lev->tank_score;
2305 level->score[SC_BUG] = lev->bug_score;
2306 level->score[SC_YAMYAM] = lev->eater_score;
2307 level->score[SC_NUT] = lev->nut_score;
2308 level->score[SC_DYNAMITE] = lev->dynamite_score;
2309 level->score[SC_KEY] = lev->key_score;
2310 level->score[SC_TIME_BONUS] = lev->exit_score;
2312 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
2314 for (i = 0; i < level->num_yamyam_contents; i++)
2315 for (y = 0; y < 3; y++)
2316 for (x = 0; x < 3; x++)
2317 level->yamyam_content[i].e[x][y] =
2318 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
2320 level->amoeba_speed = lev->amoeba_time;
2321 level->time_magic_wall = lev->wonderwall_time_initial;
2322 level->time_wheel = lev->wheel_time;
2324 level->android_move_time = lev->android_move_time;
2325 level->android_clone_time = lev->android_clone_time;
2326 level->ball_random = lev->ball_random;
2327 level->ball_state_initial = lev->ball_state_initial;
2328 level->ball_time = lev->ball_time;
2330 level->lenses_score = lev->lenses_score;
2331 level->magnify_score = lev->magnify_score;
2332 level->slurp_score = lev->slurp_score;
2334 level->lenses_time = lev->lenses_time;
2335 level->magnify_time = lev->magnify_time;
2336 level->wind_direction_initial = lev->wind_direction_initial;
2338 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2339 for (j = 0; j < 8; j++)
2340 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
2341 map_element_EM_to_RND(lev->ball_array[i][j]);
2343 for (i = 0; i < 16; i++)
2344 level->android_array[i] = FALSE; /* !!! YET TO COME !!! */
2346 /* convert the playfield (some elements need special treatment) */
2347 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2349 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
2351 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
2352 new_element = EL_AMOEBA_DEAD;
2354 level->field[x][y] = new_element;
2357 /* in case of both players set to the same field, use the first player */
2358 level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
2359 level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
2362 printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
2366 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2367 struct LevelFileInfo *level_file_info)
2369 if (!LoadNativeLevel_EM(level_file_info->filename))
2370 level->no_valid_file = TRUE;
2373 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
2375 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2376 CopyNativeLevel_RND_to_EM(level);
2379 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
2381 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2382 CopyNativeLevel_EM_to_RND(level);
2386 /* ------------------------------------------------------------------------- */
2387 /* functions for loading SP level */
2388 /* ------------------------------------------------------------------------- */
2390 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
2391 #define SP_LEVEL_SIZE 1536
2392 #define SP_LEVEL_XSIZE 60
2393 #define SP_LEVEL_YSIZE 24
2394 #define SP_LEVEL_NAME_LEN 23
2396 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
2399 int num_special_ports;
2402 /* for details of the Supaplex level format, see Herman Perk's Supaplex
2403 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
2405 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
2406 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2408 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2410 int element_old = fgetc(file);
2413 if (element_old <= 0x27)
2414 element_new = getMappedElement(EL_SP_START + element_old);
2415 else if (element_old == 0x28)
2416 element_new = EL_INVISIBLE_WALL;
2419 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
2420 Error(ERR_WARN, "invalid level element %d", element_old);
2422 element_new = EL_UNKNOWN;
2425 level->field[x][y] = element_new;
2429 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2431 /* initial gravity: 1 == "on", anything else (0) == "off" */
2432 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
2434 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2436 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
2437 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
2438 level->name[i] = fgetc(file);
2439 level->name[SP_LEVEL_NAME_LEN] = '\0';
2441 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
2442 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2444 /* number of infotrons needed; 0 means that Supaplex will count the total
2445 amount of infotrons in the level and use the low byte of that number
2446 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
2447 level->gems_needed = fgetc(file);
2449 /* number of special ("gravity") port entries below (maximum 10 allowed) */
2450 num_special_ports = fgetc(file);
2452 /* database of properties of up to 10 special ports (6 bytes per port) */
2453 for (i = 0; i < 10; i++)
2455 int port_location, port_x, port_y, port_element;
2458 /* high and low byte of the location of a special port; if (x, y) are the
2459 coordinates of a port in the field and (0, 0) is the top-left corner,
2460 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
2461 of what may be expected: Supaplex works with a game field in memory
2462 which is 2 bytes per tile) */
2463 port_location = getFile16BitBE(file);
2465 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
2466 gravity = fgetc(file);
2468 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
2469 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2471 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
2472 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2474 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2476 if (i >= num_special_ports)
2479 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2480 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2482 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2483 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2485 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2491 port_element = level->field[port_x][port_y];
2493 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2494 port_element > EL_SP_GRAVITY_PORT_UP)
2496 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2501 /* change previous (wrong) gravity inverting special port to either
2502 gravity enabling special port or gravity disabling special port */
2503 level->field[port_x][port_y] +=
2504 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2505 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2508 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2510 /* change special gravity ports without database entries to normal ports */
2511 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2512 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2513 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2514 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2515 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2517 /* auto-determine number of infotrons if it was stored as "0" -- see above */
2518 if (level->gems_needed == 0)
2520 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2521 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2522 if (level->field[x][y] == EL_SP_INFOTRON)
2523 level->gems_needed++;
2525 level->gems_needed &= 0xff; /* only use low byte -- see above */
2528 level->fieldx = SP_LEVEL_XSIZE;
2529 level->fieldy = SP_LEVEL_YSIZE;
2531 level->time = 0; /* no time limit */
2532 level->amoeba_speed = 0;
2533 level->time_magic_wall = 0;
2534 level->time_wheel = 0;
2535 level->amoeba_content = EL_EMPTY;
2537 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2538 level->score[i] = 0; /* !!! CORRECT THIS !!! */
2540 /* there are no yamyams in supaplex levels */
2541 for (i = 0; i < level->num_yamyam_contents; i++)
2542 for (y = 0; y < 3; y++)
2543 for (x = 0; x < 3; x++)
2544 level->yamyam_content[i].e[x][y] = EL_EMPTY;
2547 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2548 struct LevelFileInfo *level_file_info)
2550 char *filename = level_file_info->filename;
2552 int nr = level_file_info->nr - leveldir_current->first_level;
2554 char name_first, name_last;
2555 struct LevelInfo multipart_level;
2556 int multipart_xpos, multipart_ypos;
2557 boolean is_multipart_level;
2558 boolean is_first_part;
2559 boolean reading_multipart_level = FALSE;
2560 boolean use_empty_level = FALSE;
2562 if (!(file = fopen(filename, MODE_READ)))
2564 level->no_valid_file = TRUE;
2566 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2571 /* position file stream to the requested level inside the level package */
2572 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2574 level->no_valid_file = TRUE;
2576 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2581 /* there exist Supaplex level package files with multi-part levels which
2582 can be detected as follows: instead of leading and trailing dashes ('-')
2583 to pad the level name, they have leading and trailing numbers which are
2584 the x and y coordinations of the current part of the multi-part level;
2585 if there are '?' characters instead of numbers on the left or right side
2586 of the level name, the multi-part level consists of only horizontal or
2589 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2591 LoadLevelFromFileStream_SP(file, level, l);
2593 /* check if this level is a part of a bigger multi-part level */
2595 name_first = level->name[0];
2596 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
2598 is_multipart_level =
2599 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2600 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
2603 ((name_first == '?' || name_first == '1') &&
2604 (name_last == '?' || name_last == '1'));
2606 /* correct leading multipart level meta information in level name */
2607 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2608 level->name[i] = '-';
2610 /* correct trailing multipart level meta information in level name */
2611 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2612 level->name[i] = '-';
2614 /* ---------- check for normal single level ---------- */
2616 if (!reading_multipart_level && !is_multipart_level)
2618 /* the current level is simply a normal single-part level, and we are
2619 not reading a multi-part level yet, so return the level as it is */
2624 /* ---------- check for empty level (unused multi-part) ---------- */
2626 if (!reading_multipart_level && is_multipart_level && !is_first_part)
2628 /* this is a part of a multi-part level, but not the first part
2629 (and we are not already reading parts of a multi-part level);
2630 in this case, use an empty level instead of the single part */
2632 use_empty_level = TRUE;
2637 /* ---------- check for finished multi-part level ---------- */
2639 if (reading_multipart_level &&
2640 (!is_multipart_level ||
2641 strcmp(level->name, multipart_level.name) != 0))
2643 /* we are already reading parts of a multi-part level, but this level is
2644 either not a multi-part level, or a part of a different multi-part
2645 level; in both cases, the multi-part level seems to be complete */
2650 /* ---------- here we have one part of a multi-part level ---------- */
2652 reading_multipart_level = TRUE;
2654 if (is_first_part) /* start with first part of new multi-part level */
2656 /* copy level info structure from first part */
2657 multipart_level = *level;
2659 /* clear playfield of new multi-part level */
2660 for (y = 0; y < MAX_LEV_FIELDY; y++)
2661 for (x = 0; x < MAX_LEV_FIELDX; x++)
2662 multipart_level.field[x][y] = EL_EMPTY;
2665 if (name_first == '?')
2667 if (name_last == '?')
2670 multipart_xpos = (int)(name_first - '0');
2671 multipart_ypos = (int)(name_last - '0');
2674 printf("----------> part (%d/%d) of multi-part level '%s'\n",
2675 multipart_xpos, multipart_ypos, multipart_level.name);
2678 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2679 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2681 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2686 multipart_level.fieldx = MAX(multipart_level.fieldx,
2687 multipart_xpos * SP_LEVEL_XSIZE);
2688 multipart_level.fieldy = MAX(multipart_level.fieldy,
2689 multipart_ypos * SP_LEVEL_YSIZE);
2691 /* copy level part at the right position of multi-part level */
2692 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2694 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2696 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2697 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2699 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2706 if (use_empty_level)
2708 setLevelInfoToDefaults(level);
2710 level->fieldx = SP_LEVEL_XSIZE;
2711 level->fieldy = SP_LEVEL_YSIZE;
2713 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2714 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2715 level->field[x][y] = EL_EMPTY;
2717 strcpy(level->name, "-------- EMPTY --------");
2719 Error(ERR_WARN, "single part of multi-part level -- using empty level");
2722 if (reading_multipart_level)
2723 *level = multipart_level;
2726 /* ------------------------------------------------------------------------- */
2727 /* functions for loading generic level */
2728 /* ------------------------------------------------------------------------- */
2730 void LoadLevelFromFileInfo(struct LevelInfo *level,
2731 struct LevelFileInfo *level_file_info)
2733 /* always start with reliable default values */
2734 setLevelInfoToDefaults(level);
2736 switch (level_file_info->type)
2738 case LEVEL_FILE_TYPE_RND:
2739 LoadLevelFromFileInfo_RND(level, level_file_info);
2742 case LEVEL_FILE_TYPE_EM:
2743 LoadLevelFromFileInfo_EM(level, level_file_info);
2744 level->game_engine_type = GAME_ENGINE_TYPE_EM;
2747 case LEVEL_FILE_TYPE_SP:
2748 LoadLevelFromFileInfo_SP(level, level_file_info);
2752 LoadLevelFromFileInfo_RND(level, level_file_info);
2756 /* if level file is invalid, restore level structure to default values */
2757 if (level->no_valid_file)
2758 setLevelInfoToDefaults(level);
2760 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2761 level->game_engine_type = GAME_ENGINE_TYPE_RND;
2763 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
2764 CopyNativeLevel_RND_to_Native(level);
2766 CopyNativeLevel_Native_to_RND(level);
2769 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2771 static struct LevelFileInfo level_file_info;
2773 /* always start with reliable default values */
2774 setFileInfoToDefaults(&level_file_info);
2776 level_file_info.nr = 0; /* unknown level number */
2777 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2778 level_file_info.filename = filename;
2780 LoadLevelFromFileInfo(level, &level_file_info);
2783 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2785 if (leveldir_current == NULL) /* only when dumping level */
2788 if (leveldir_current->latest_engine)
2790 /* ---------- use latest game engine ----------------------------------- */
2792 /* For all levels which are forced to use the latest game engine version
2793 (normally all but user contributed, private and undefined levels), set
2794 the game engine version to the actual version; this allows for actual
2795 corrections in the game engine to take effect for existing, converted
2796 levels (from "classic" or other existing games) to make the emulation
2797 of the corresponding game more accurate, while (hopefully) not breaking
2798 existing levels created from other players. */
2800 level->game_version = GAME_VERSION_ACTUAL;
2802 /* Set special EM style gems behaviour: EM style gems slip down from
2803 normal, steel and growing wall. As this is a more fundamental change,
2804 it seems better to set the default behaviour to "off" (as it is more
2805 natural) and make it configurable in the level editor (as a property
2806 of gem style elements). Already existing converted levels (neither
2807 private nor contributed levels) are changed to the new behaviour. */
2809 if (level->file_version < FILE_VERSION_2_0)
2810 level->em_slippery_gems = TRUE;
2815 /* ---------- use game engine the level was created with ----------------- */
2817 /* For all levels which are not forced to use the latest game engine
2818 version (normally user contributed, private and undefined levels),
2819 use the version of the game engine the levels were created for.
2821 Since 2.0.1, the game engine version is now directly stored
2822 in the level file (chunk "VERS"), so there is no need anymore
2823 to set the game version from the file version (except for old,
2824 pre-2.0 levels, where the game version is still taken from the
2825 file format version used to store the level -- see above). */
2827 /* player was faster than enemies in 1.0.0 and before */
2828 if (level->file_version == FILE_VERSION_1_0)
2829 level->double_speed = TRUE;
2831 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2832 if (level->game_version == VERSION_IDENT(2,0,1,0))
2833 level->em_slippery_gems = TRUE;
2835 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2836 if (level->game_version < VERSION_IDENT(2,2,0,0))
2837 level->use_spring_bug = TRUE;
2839 /* time orb caused limited time in endless time levels before 3.1.2 */
2840 if (level->game_version < VERSION_IDENT(3,1,2,0))
2841 level->use_time_orb_bug = TRUE;
2843 /* default behaviour for snapping was "no snap delay" before 3.1.2 */
2844 if (level->game_version < VERSION_IDENT(3,1,2,0))
2845 level->block_snap_field = FALSE;
2847 /* only few elements were able to actively move into acid before 3.1.0 */
2848 /* trigger settings did not exist before 3.1.0; set to default "any" */
2849 if (level->game_version < VERSION_IDENT(3,1,0,0))
2853 /* correct "can move into acid" settings (all zero in old levels) */
2855 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2856 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
2858 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
2859 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2860 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
2861 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
2863 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2864 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2866 /* correct trigger settings (stored as zero == "none" in old levels) */
2868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2870 int element = EL_CUSTOM_START + i;
2871 struct ElementInfo *ei = &element_info[element];
2873 for (j = 0; j < ei->num_change_pages; j++)
2875 struct ElementChangeInfo *change = &ei->change_page[j];
2877 change->trigger_player = CH_PLAYER_ANY;
2878 change->trigger_page = CH_PAGE_ANY;
2884 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2888 /* map custom element change events that have changed in newer versions
2889 (these following values were accidentally changed in version 3.0.1)
2890 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
2891 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2893 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2895 int element = EL_CUSTOM_START + i;
2897 /* order of checking and copying events to be mapped is important */
2898 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
2900 if (HAS_CHANGE_EVENT(element, j - 2))
2902 SET_CHANGE_EVENT(element, j - 2, FALSE);
2903 SET_CHANGE_EVENT(element, j, TRUE);
2907 /* order of checking and copying events to be mapped is important */
2908 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
2910 if (HAS_CHANGE_EVENT(element, j - 1))
2912 SET_CHANGE_EVENT(element, j - 1, FALSE);
2913 SET_CHANGE_EVENT(element, j, TRUE);
2919 /* initialize "can_change" field for old levels with only one change page */
2920 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2922 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2924 int element = EL_CUSTOM_START + i;
2926 if (CAN_CHANGE(element))
2927 element_info[element].change->can_change = TRUE;
2931 /* correct custom element values (for old levels without these options) */
2932 if (level->game_version < VERSION_IDENT(3,1,1,0))
2934 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2936 int element = EL_CUSTOM_START + i;
2937 struct ElementInfo *ei = &element_info[element];
2939 if (ei->access_direction == MV_NO_DIRECTION)
2940 ei->access_direction = MV_ALL_DIRECTIONS;
2943 for (j = 0; j < ei->num_change_pages; j++)
2945 struct ElementChangeInfo *change = &ei->change_page[j];
2947 if (change->trigger_side == CH_SIDE_NONE)
2948 change->trigger_side = CH_SIDE_ANY;
2955 /* correct custom element values (fix invalid values for all versions) */
2958 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2960 int element = EL_CUSTOM_START + i;
2961 struct ElementInfo *ei = &element_info[element];
2963 for (j = 0; j < ei->num_change_pages; j++)
2965 struct ElementChangeInfo *change = &ei->change_page[j];
2967 if (change->trigger_player == CH_PLAYER_NONE)
2968 change->trigger_player = CH_PLAYER_ANY;
2970 if (change->trigger_side == CH_SIDE_NONE)
2971 change->trigger_side = CH_SIDE_ANY;
2977 /* initialize "can_explode" field for old levels which did not store this */
2978 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
2979 if (level->game_version <= VERSION_IDENT(3,1,0,0))
2981 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2983 int element = EL_CUSTOM_START + i;
2985 if (EXPLODES_1X1_OLD(element))
2986 element_info[element].explosion_type = EXPLODES_1X1;
2988 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
2989 EXPLODES_SMASHED(element) ||
2990 EXPLODES_IMPACT(element)));
2994 /* correct previously hard-coded move delay values for maze runner style */
2995 if (level->game_version < VERSION_IDENT(3,1,1,0))
2997 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2999 int element = EL_CUSTOM_START + i;
3001 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
3003 /* previously hard-coded and therefore ignored */
3004 element_info[element].move_delay_fixed = 9;
3005 element_info[element].move_delay_random = 0;
3010 /* map elements that have changed in newer versions */
3011 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
3012 level->game_version);
3013 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3014 for (x = 0; x < 3; x++)
3015 for (y = 0; y < 3; y++)
3016 level->yamyam_content[i].e[x][y] =
3017 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
3018 level->game_version);
3020 /* initialize element properties for level editor etc. */
3021 InitElementPropertiesEngine(level->game_version);
3024 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
3028 /* map elements that have changed in newer versions */
3029 for (y = 0; y < level->fieldy; y++)
3030 for (x = 0; x < level->fieldx; x++)
3031 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
3032 level->game_version);
3034 /* copy elements to runtime playfield array */
3035 for (x = 0; x < MAX_LEV_FIELDX; x++)
3036 for (y = 0; y < MAX_LEV_FIELDY; y++)
3037 Feld[x][y] = level->field[x][y];
3039 /* initialize level size variables for faster access */
3040 lev_fieldx = level->fieldx;
3041 lev_fieldy = level->fieldy;
3043 /* determine border element for this level */
3047 void LoadLevelTemplate(int nr)
3051 setLevelFileInfo(&level_template.file_info, nr);
3052 filename = level_template.file_info.filename;
3054 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
3056 LoadLevel_InitVersion(&level_template, filename);
3057 LoadLevel_InitElements(&level_template, filename);
3059 ActivateLevelTemplate();
3062 void LoadLevel(int nr)
3066 setLevelFileInfo(&level.file_info, nr);
3067 filename = level.file_info.filename;
3069 LoadLevelFromFileInfo(&level, &level.file_info);
3071 if (level.use_custom_template)
3072 LoadLevelTemplate(-1);
3074 LoadLevel_InitVersion(&level, filename);
3075 LoadLevel_InitElements(&level, filename);
3076 LoadLevel_InitPlayfield(&level, filename);
3079 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
3081 putFileVersion(file, level->file_version);
3082 putFileVersion(file, level->game_version);
3085 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
3089 putFile8Bit(file, level->fieldx);
3090 putFile8Bit(file, level->fieldy);
3092 putFile16BitBE(file, level->time);
3093 putFile16BitBE(file, level->gems_needed);
3095 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3096 putFile8Bit(file, level->name[i]);
3098 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3099 putFile8Bit(file, level->score[i]);
3101 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3102 for (y = 0; y < 3; y++)
3103 for (x = 0; x < 3; x++)
3104 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3105 level->yamyam_content[i].e[x][y]));
3106 putFile8Bit(file, level->amoeba_speed);
3107 putFile8Bit(file, level->time_magic_wall);
3108 putFile8Bit(file, level->time_wheel);
3109 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3110 level->amoeba_content));
3111 putFile8Bit(file, (level->double_speed ? 1 : 0));
3112 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3113 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3114 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3116 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3118 putFile8Bit(file, (level->block_last_field ? 1 : 0));
3119 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3120 putFile32BitBE(file, level->can_move_into_acid_bits);
3121 putFile8Bit(file, level->dont_collide_with_bits);
3123 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3124 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3126 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3127 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3128 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3130 putFile8Bit(file, level->game_engine_type);
3132 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3135 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3139 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3140 putFile8Bit(file, level->author[i]);
3143 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3147 for (y = 0; y < level->fieldy; y++)
3148 for (x = 0; x < level->fieldx; x++)
3149 if (level->encoding_16bit_field)
3150 putFile16BitBE(file, level->field[x][y]);
3152 putFile8Bit(file, level->field[x][y]);
3156 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3160 putFile8Bit(file, EL_YAMYAM);
3161 putFile8Bit(file, level->num_yamyam_contents);
3162 putFile8Bit(file, 0);
3163 putFile8Bit(file, 0);
3165 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3166 for (y = 0; y < 3; y++)
3167 for (x = 0; x < 3; x++)
3168 if (level->encoding_16bit_field)
3169 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
3171 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
3175 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3178 int num_contents, content_xsize, content_ysize;
3179 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3181 if (element == EL_YAMYAM)
3183 num_contents = level->num_yamyam_contents;
3187 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3188 for (y = 0; y < 3; y++)
3189 for (x = 0; x < 3; x++)
3190 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
3192 else if (element == EL_BD_AMOEBA)
3198 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3199 for (y = 0; y < 3; y++)
3200 for (x = 0; x < 3; x++)
3201 content_array[i][x][y] = EL_EMPTY;
3202 content_array[0][0][0] = level->amoeba_content;
3206 /* chunk header already written -- write empty chunk data */
3207 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3209 Error(ERR_WARN, "cannot save content for element '%d'", element);
3213 putFile16BitBE(file, element);
3214 putFile8Bit(file, num_contents);
3215 putFile8Bit(file, content_xsize);
3216 putFile8Bit(file, content_ysize);
3218 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3220 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3221 for (y = 0; y < 3; y++)
3222 for (x = 0; x < 3; x++)
3223 putFile16BitBE(file, content_array[i][x][y]);
3226 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3229 int envelope_nr = element - EL_ENVELOPE_1;
3230 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3232 putFile16BitBE(file, element);
3233 putFile16BitBE(file, envelope_len);
3234 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3235 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3237 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3239 for (i = 0; i < envelope_len; i++)
3240 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3244 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3245 int num_changed_custom_elements)
3249 putFile16BitBE(file, num_changed_custom_elements);
3251 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3253 int element = EL_CUSTOM_START + i;
3255 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3257 if (check < num_changed_custom_elements)
3259 putFile16BitBE(file, element);
3260 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3267 if (check != num_changed_custom_elements) /* should not happen */
3268 Error(ERR_WARN, "inconsistent number of custom element properties");
3273 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3274 int num_changed_custom_elements)
3278 putFile16BitBE(file, num_changed_custom_elements);
3280 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3282 int element = EL_CUSTOM_START + i;
3284 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3286 if (check < num_changed_custom_elements)
3288 putFile16BitBE(file, element);
3289 putFile16BitBE(file, element_info[element].change->target_element);
3296 if (check != num_changed_custom_elements) /* should not happen */
3297 Error(ERR_WARN, "inconsistent number of custom target elements");
3302 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3303 int num_changed_custom_elements)
3305 int i, j, x, y, check = 0;
3307 putFile16BitBE(file, num_changed_custom_elements);
3309 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3311 int element = EL_CUSTOM_START + i;
3312 struct ElementInfo *ei = &element_info[element];
3314 if (ei->modified_settings)
3316 if (check < num_changed_custom_elements)
3318 putFile16BitBE(file, element);
3320 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3321 putFile8Bit(file, ei->description[j]);
3323 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3325 /* some free bytes for future properties and padding */
3326 WriteUnusedBytesToFile(file, 7);
3328 putFile8Bit(file, ei->use_gfx_element);
3329 putFile16BitBE(file, ei->gfx_element);
3331 putFile8Bit(file, ei->collect_score_initial);
3332 putFile8Bit(file, ei->collect_count_initial);
3334 putFile16BitBE(file, ei->push_delay_fixed);
3335 putFile16BitBE(file, ei->push_delay_random);
3336 putFile16BitBE(file, ei->move_delay_fixed);
3337 putFile16BitBE(file, ei->move_delay_random);
3339 putFile16BitBE(file, ei->move_pattern);
3340 putFile8Bit(file, ei->move_direction_initial);
3341 putFile8Bit(file, ei->move_stepsize);
3343 for (y = 0; y < 3; y++)
3344 for (x = 0; x < 3; x++)
3345 putFile16BitBE(file, ei->content.e[x][y]);
3347 putFile32BitBE(file, ei->change->events);
3349 putFile16BitBE(file, ei->change->target_element);
3351 putFile16BitBE(file, ei->change->delay_fixed);
3352 putFile16BitBE(file, ei->change->delay_random);
3353 putFile16BitBE(file, ei->change->delay_frames);
3355 putFile16BitBE(file, ei->change->trigger_element);
3357 putFile8Bit(file, ei->change->explode);
3358 putFile8Bit(file, ei->change->use_target_content);
3359 putFile8Bit(file, ei->change->only_if_complete);
3360 putFile8Bit(file, ei->change->use_random_replace);
3362 putFile8Bit(file, ei->change->random_percentage);
3363 putFile8Bit(file, ei->change->replace_when);
3365 for (y = 0; y < 3; y++)
3366 for (x = 0; x < 3; x++)
3367 putFile16BitBE(file, ei->change->content.e[x][y]);
3369 putFile8Bit(file, ei->slippery_type);
3371 /* some free bytes for future properties and padding */
3372 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3379 if (check != num_changed_custom_elements) /* should not happen */
3380 Error(ERR_WARN, "inconsistent number of custom element properties");
3384 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3386 struct ElementInfo *ei = &element_info[element];
3389 /* ---------- custom element base property values (96 bytes) ------------- */
3391 putFile16BitBE(file, element);
3393 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3394 putFile8Bit(file, ei->description[i]);
3396 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3397 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
3399 putFile8Bit(file, ei->num_change_pages);
3401 putFile16BitBE(file, ei->ce_value_fixed_initial);
3402 putFile16BitBE(file, ei->ce_value_random_initial);
3403 putFile8Bit(file, ei->use_last_ce_value);
3405 putFile8Bit(file, ei->use_gfx_element);
3406 putFile16BitBE(file, ei->gfx_element);
3408 putFile8Bit(file, ei->collect_score_initial);
3409 putFile8Bit(file, ei->collect_count_initial);
3411 putFile8Bit(file, ei->drop_delay_fixed);
3412 putFile8Bit(file, ei->push_delay_fixed);
3413 putFile8Bit(file, ei->drop_delay_random);
3414 putFile8Bit(file, ei->push_delay_random);
3415 putFile16BitBE(file, ei->move_delay_fixed);
3416 putFile16BitBE(file, ei->move_delay_random);
3418 /* bits 0 - 15 of "move_pattern" ... */
3419 putFile16BitBE(file, ei->move_pattern & 0xffff);
3420 putFile8Bit(file, ei->move_direction_initial);
3421 putFile8Bit(file, ei->move_stepsize);
3423 putFile8Bit(file, ei->slippery_type);
3425 for (y = 0; y < 3; y++)
3426 for (x = 0; x < 3; x++)
3427 putFile16BitBE(file, ei->content.e[x][y]);
3429 putFile16BitBE(file, ei->move_enter_element);
3430 putFile16BitBE(file, ei->move_leave_element);
3431 putFile8Bit(file, ei->move_leave_type);
3433 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3434 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3436 putFile8Bit(file, ei->access_direction);
3438 putFile8Bit(file, ei->explosion_delay);
3439 putFile8Bit(file, ei->ignition_delay);
3440 putFile8Bit(file, ei->explosion_type);
3442 /* some free bytes for future custom property values and padding */
3443 WriteUnusedBytesToFile(file, 1);
3445 /* ---------- change page property values (48 bytes) --------------------- */
3447 for (i = 0; i < ei->num_change_pages; i++)
3449 struct ElementChangeInfo *change = &ei->change_page[i];
3450 unsigned long event_bits = 0;
3452 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3453 if (change->has_event[j])
3454 event_bits |= (1 << j);
3456 putFile32BitBE(file, event_bits);
3458 putFile16BitBE(file, change->target_element);
3460 putFile16BitBE(file, change->delay_fixed);
3461 putFile16BitBE(file, change->delay_random);
3462 putFile16BitBE(file, change->delay_frames);
3464 putFile16BitBE(file, change->trigger_element);
3466 putFile8Bit(file, change->explode);
3467 putFile8Bit(file, change->use_target_content);
3468 putFile8Bit(file, change->only_if_complete);
3469 putFile8Bit(file, change->use_random_replace);
3471 putFile8Bit(file, change->random_percentage);
3472 putFile8Bit(file, change->replace_when);
3474 for (y = 0; y < 3; y++)
3475 for (x = 0; x < 3; x++)
3476 putFile16BitBE(file, change->target_content.e[x][y]);
3478 putFile8Bit(file, change->can_change);
3480 putFile8Bit(file, change->trigger_side);
3482 putFile8Bit(file, change->trigger_player);
3483 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3484 log_2(change->trigger_page)));
3486 putFile8Bit(file, change->has_action);
3487 putFile8Bit(file, change->action_type);
3488 putFile8Bit(file, change->action_mode);
3489 putFile16BitBE(file, change->action_arg);
3491 /* some free bytes for future change property values and padding */
3492 WriteUnusedBytesToFile(file, 1);
3496 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3498 struct ElementInfo *ei = &element_info[element];
3499 struct ElementGroupInfo *group = ei->group;
3502 putFile16BitBE(file, element);
3504 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3505 putFile8Bit(file, ei->description[i]);
3507 putFile8Bit(file, group->num_elements);
3509 putFile8Bit(file, ei->use_gfx_element);
3510 putFile16BitBE(file, ei->gfx_element);
3512 putFile8Bit(file, group->choice_mode);
3514 /* some free bytes for future values and padding */
3515 WriteUnusedBytesToFile(file, 3);
3517 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3518 putFile16BitBE(file, group->element[i]);
3521 static int SaveLevel_CONF_Value(FILE *file, int pos)
3523 int default_value = element_conf[pos].default_value;
3524 int element = element_conf[pos].element;
3525 int type = element_conf[pos].type;
3526 int bytes = type & CONF_MASK_BYTES;
3527 void *value_ptr = element_conf[pos].value;
3528 int value = (CONF_VALUE_BOOLEAN(type) ? *(boolean *)value_ptr :
3531 boolean modified = FALSE;
3533 /* check if any settings have been modified before saving them */
3534 if (value != default_value)
3537 if (!modified) /* do not save unmodified default settings */
3540 if (bytes == CONF_MASK_MULTI_BYTES)
3541 Error(ERR_EXIT, "SaveLevel_CONF_Value: cannot save multi-byte values");
3543 num_bytes += putFile16BitBE(file, element);
3544 num_bytes += putFile8Bit(file, type);
3545 num_bytes += (bytes == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
3546 bytes == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
3547 bytes == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) : 0);
3552 static int SaveLevel_CONF_Content(FILE *file, int pos, int num_contents)
3554 struct Content *content = (struct Content *)(element_conf[pos].value);
3555 int default_value = element_conf[pos].default_value;
3556 int element = element_conf[pos].element;
3557 int type = element_conf[pos].type;
3559 boolean modified = FALSE;
3562 /* check if any settings have been modified before saving them */
3563 for (i = 0; i < num_contents; i++)
3564 for (y = 0; y < 3; y++)
3565 for (x = 0; x < 3; x++)
3566 if (content[i].e[x][y] != default_value)
3569 if (!modified) /* do not save unmodified default settings */
3572 num_bytes += putFile16BitBE(file, element);
3573 num_bytes += putFile8Bit(file, type);
3574 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
3576 for (i = 0; i < num_contents; i++)
3577 for (y = 0; y < 3; y++)
3578 for (x = 0; x < 3; x++)
3579 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
3584 static int SaveLevel_CONF(FILE *file, struct LevelInfo *level)
3589 li = *level; /* copy level information into temporary buffer */
3591 for (i = 0; element_conf[i].element != -1; i++)
3593 int type = element_conf[i].type;
3594 int bytes = type & CONF_MASK_BYTES;
3596 if (bytes != CONF_MASK_MULTI_BYTES)
3597 chunk_size += SaveLevel_CONF_Value(file, i);
3598 else if (type == CONF_VALUE_CONTENT_8)
3599 chunk_size += SaveLevel_CONF_Content(file, i, MAX_ELEMENT_CONTENTS);
3605 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3607 int body_chunk_size, conf_chunk_size;
3611 if (!(file = fopen(filename, MODE_WRITE)))
3613 Error(ERR_WARN, "cannot save level file '%s'", filename);
3617 level->file_version = FILE_VERSION_ACTUAL;
3618 level->game_version = GAME_VERSION_ACTUAL;
3620 /* check level field for 16-bit elements */
3621 level->encoding_16bit_field = FALSE;
3622 for (y = 0; y < level->fieldy; y++)
3623 for (x = 0; x < level->fieldx; x++)
3624 if (level->field[x][y] > 255)
3625 level->encoding_16bit_field = TRUE;
3627 /* check yamyam content for 16-bit elements */
3628 level->encoding_16bit_yamyam = FALSE;
3629 for (i = 0; i < level->num_yamyam_contents; i++)
3630 for (y = 0; y < 3; y++)
3631 for (x = 0; x < 3; x++)
3632 if (level->yamyam_content[i].e[x][y] > 255)
3633 level->encoding_16bit_yamyam = TRUE;
3635 /* check amoeba content for 16-bit elements */
3636 level->encoding_16bit_amoeba = FALSE;
3637 if (level->amoeba_content > 255)
3638 level->encoding_16bit_amoeba = TRUE;
3640 /* calculate size of "BODY" chunk */
3642 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3644 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3645 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3647 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3648 SaveLevel_VERS(file, level);
3650 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3651 SaveLevel_HEAD(file, level);
3653 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3654 SaveLevel_AUTH(file, level);
3656 putFileChunkBE(file, "BODY", body_chunk_size);
3657 SaveLevel_BODY(file, level);
3659 if (level->encoding_16bit_yamyam ||
3660 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3662 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3663 SaveLevel_CNT2(file, level, EL_YAMYAM);
3666 if (level->encoding_16bit_amoeba)
3668 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3669 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3672 /* check for envelope content */
3673 for (i = 0; i < 4; i++)
3675 if (strlen(level->envelope_text[i]) > 0)
3677 int envelope_len = strlen(level->envelope_text[i]) + 1;
3679 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3680 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3684 /* check for non-default custom elements (unless using template level) */
3685 if (!level->use_custom_template)
3687 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3689 int element = EL_CUSTOM_START + i;
3691 if (element_info[element].modified_settings)
3693 int num_change_pages = element_info[element].num_change_pages;
3695 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3696 SaveLevel_CUS4(file, level, element);
3701 /* check for non-default group elements (unless using template level) */
3702 if (!level->use_custom_template)
3704 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3706 int element = EL_GROUP_START + i;
3708 if (element_info[element].modified_settings)
3710 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3711 SaveLevel_GRP1(file, level, element);
3716 conf_chunk_size = SaveLevel_CONF(NULL, level); /* get chunk size */
3718 /* check for non-default configuration settings to be saved in CONF chunk */
3719 if (conf_chunk_size > 0)
3721 putFileChunkBE(file, "CONF", conf_chunk_size);
3722 SaveLevel_CONF(file, level);
3727 SetFilePermissions(filename, PERMS_PRIVATE);
3730 void SaveLevel(int nr)
3732 char *filename = getDefaultLevelFilename(nr);
3734 SaveLevelFromFilename(&level, filename);
3737 void SaveLevelTemplate()
3739 char *filename = getDefaultLevelFilename(-1);
3741 SaveLevelFromFilename(&level, filename);
3744 void DumpLevel(struct LevelInfo *level)
3746 if (level->no_valid_file)
3748 Error(ERR_WARN, "cannot dump -- no valid level file found");
3753 printf_line("-", 79);
3754 printf("Level xxx (file version %08d, game version %08d)\n",
3755 level->file_version, level->game_version);
3756 printf_line("-", 79);
3758 printf("Level author: '%s'\n", level->author);
3759 printf("Level title: '%s'\n", level->name);
3761 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3763 printf("Level time: %d seconds\n", level->time);
3764 printf("Gems needed: %d\n", level->gems_needed);
3766 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3767 printf("Time for wheel: %d seconds\n", level->time_wheel);
3768 printf("Time for light: %d seconds\n", level->time_light);
3769 printf("Time for timegate: %d seconds\n", level->time_timegate);
3771 printf("Amoeba speed: %d\n", level->amoeba_speed);
3773 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
3774 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
3775 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
3776 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
3777 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3778 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3779 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3781 printf_line("-", 79);
3785 /* ========================================================================= */
3786 /* tape file functions */
3787 /* ========================================================================= */
3789 static void setTapeInfoToDefaults()
3793 /* always start with reliable default values (empty tape) */
3796 /* default values (also for pre-1.2 tapes) with only the first player */
3797 tape.player_participates[0] = TRUE;
3798 for (i = 1; i < MAX_PLAYERS; i++)
3799 tape.player_participates[i] = FALSE;
3801 /* at least one (default: the first) player participates in every tape */
3802 tape.num_participating_players = 1;
3804 tape.level_nr = level_nr;
3806 tape.changed = FALSE;
3808 tape.recording = FALSE;
3809 tape.playing = FALSE;
3810 tape.pausing = FALSE;
3812 tape.no_valid_file = FALSE;
3815 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3817 tape->file_version = getFileVersion(file);
3818 tape->game_version = getFileVersion(file);
3823 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3827 tape->random_seed = getFile32BitBE(file);
3828 tape->date = getFile32BitBE(file);
3829 tape->length = getFile32BitBE(file);
3831 /* read header fields that are new since version 1.2 */
3832 if (tape->file_version >= FILE_VERSION_1_2)
3834 byte store_participating_players = getFile8Bit(file);
3837 /* since version 1.2, tapes store which players participate in the tape */
3838 tape->num_participating_players = 0;
3839 for (i = 0; i < MAX_PLAYERS; i++)
3841 tape->player_participates[i] = FALSE;
3843 if (store_participating_players & (1 << i))
3845 tape->player_participates[i] = TRUE;
3846 tape->num_participating_players++;
3850 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3852 engine_version = getFileVersion(file);
3853 if (engine_version > 0)
3854 tape->engine_version = engine_version;
3856 tape->engine_version = tape->game_version;
3862 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3864 int level_identifier_size;
3867 level_identifier_size = getFile16BitBE(file);
3869 tape->level_identifier =
3870 checked_realloc(tape->level_identifier, level_identifier_size);
3872 for (i = 0; i < level_identifier_size; i++)
3873 tape->level_identifier[i] = getFile8Bit(file);
3875 tape->level_nr = getFile16BitBE(file);
3877 chunk_size = 2 + level_identifier_size + 2;
3882 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3885 int chunk_size_expected =
3886 (tape->num_participating_players + 1) * tape->length;
3888 if (chunk_size_expected != chunk_size)
3890 ReadUnusedBytesFromFile(file, chunk_size);
3891 return chunk_size_expected;
3894 for (i = 0; i < tape->length; i++)
3896 if (i >= MAX_TAPE_LEN)
3899 for (j = 0; j < MAX_PLAYERS; j++)
3901 tape->pos[i].action[j] = MV_NONE;
3903 if (tape->player_participates[j])
3904 tape->pos[i].action[j] = getFile8Bit(file);
3907 tape->pos[i].delay = getFile8Bit(file);
3909 if (tape->file_version == FILE_VERSION_1_0)
3911 /* eliminate possible diagonal moves in old tapes */
3912 /* this is only for backward compatibility */
3914 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3915 byte action = tape->pos[i].action[0];
3916 int k, num_moves = 0;
3918 for (k = 0; k<4; k++)
3920 if (action & joy_dir[k])
3922 tape->pos[i + num_moves].action[0] = joy_dir[k];
3924 tape->pos[i + num_moves].delay = 0;
3933 tape->length += num_moves;
3936 else if (tape->file_version < FILE_VERSION_2_0)
3938 /* convert pre-2.0 tapes to new tape format */
3940 if (tape->pos[i].delay > 1)
3943 tape->pos[i + 1] = tape->pos[i];
3944 tape->pos[i + 1].delay = 1;
3947 for (j = 0; j < MAX_PLAYERS; j++)
3948 tape->pos[i].action[j] = MV_NONE;
3949 tape->pos[i].delay--;
3960 if (i != tape->length)
3961 chunk_size = (tape->num_participating_players + 1) * i;
3966 void LoadTapeFromFilename(char *filename)
3968 char cookie[MAX_LINE_LEN];
3969 char chunk_name[CHUNK_ID_LEN + 1];
3973 /* always start with reliable default values */
3974 setTapeInfoToDefaults();
3976 if (!(file = fopen(filename, MODE_READ)))
3978 tape.no_valid_file = TRUE;
3983 getFileChunkBE(file, chunk_name, NULL);
3984 if (strcmp(chunk_name, "RND1") == 0)
3986 getFile32BitBE(file); /* not used */
3988 getFileChunkBE(file, chunk_name, NULL);
3989 if (strcmp(chunk_name, "TAPE") != 0)
3991 tape.no_valid_file = TRUE;
3993 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3998 else /* check for pre-2.0 file format with cookie string */
4000 strcpy(cookie, chunk_name);
4001 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
4002 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4003 cookie[strlen(cookie) - 1] = '\0';
4005 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
4007 tape.no_valid_file = TRUE;
4009 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4014 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
4016 tape.no_valid_file = TRUE;
4018 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
4023 /* pre-2.0 tape files have no game version, so use file version here */
4024 tape.game_version = tape.file_version;
4027 if (tape.file_version < FILE_VERSION_1_2)
4029 /* tape files from versions before 1.2.0 without chunk structure */
4030 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
4031 LoadTape_BODY(file, 2 * tape.length, &tape);
4039 int (*loader)(FILE *, int, struct TapeInfo *);
4043 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
4044 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
4045 { "INFO", -1, LoadTape_INFO },
4046 { "BODY", -1, LoadTape_BODY },
4050 while (getFileChunkBE(file, chunk_name, &chunk_size))
4054 while (chunk_info[i].name != NULL &&
4055 strcmp(chunk_name, chunk_info[i].name) != 0)
4058 if (chunk_info[i].name == NULL)
4060 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
4061 chunk_name, filename);
4062 ReadUnusedBytesFromFile(file, chunk_size);
4064 else if (chunk_info[i].size != -1 &&
4065 chunk_info[i].size != chunk_size)
4067 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4068 chunk_size, chunk_name, filename);
4069 ReadUnusedBytesFromFile(file, chunk_size);
4073 /* call function to load this tape chunk */
4074 int chunk_size_expected =
4075 (chunk_info[i].loader)(file, chunk_size, &tape);
4077 /* the size of some chunks cannot be checked before reading other
4078 chunks first (like "HEAD" and "BODY") that contain some header
4079 information, so check them here */
4080 if (chunk_size_expected != chunk_size)
4082 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4083 chunk_size, chunk_name, filename);
4091 tape.length_seconds = GetTapeLength();
4094 printf("::: tape game version: %d\n", tape.game_version);
4095 printf("::: tape engine version: %d\n", tape.engine_version);
4099 void LoadTape(int nr)
4101 char *filename = getTapeFilename(nr);
4103 LoadTapeFromFilename(filename);
4106 void LoadSolutionTape(int nr)
4108 char *filename = getSolutionTapeFilename(nr);
4110 LoadTapeFromFilename(filename);
4113 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
4115 putFileVersion(file, tape->file_version);
4116 putFileVersion(file, tape->game_version);
4119 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
4122 byte store_participating_players = 0;
4124 /* set bits for participating players for compact storage */
4125 for (i = 0; i < MAX_PLAYERS; i++)
4126 if (tape->player_participates[i])
4127 store_participating_players |= (1 << i);
4129 putFile32BitBE(file, tape->random_seed);
4130 putFile32BitBE(file, tape->date);
4131 putFile32BitBE(file, tape->length);
4133 putFile8Bit(file, store_participating_players);
4135 /* unused bytes not at the end here for 4-byte alignment of engine_version */
4136 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
4138 putFileVersion(file, tape->engine_version);
4141 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
4143 int level_identifier_size = strlen(tape->level_identifier) + 1;
4146 putFile16BitBE(file, level_identifier_size);
4148 for (i = 0; i < level_identifier_size; i++)
4149 putFile8Bit(file, tape->level_identifier[i]);
4151 putFile16BitBE(file, tape->level_nr);
4154 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
4158 for (i = 0; i < tape->length; i++)
4160 for (j = 0; j < MAX_PLAYERS; j++)
4161 if (tape->player_participates[j])
4162 putFile8Bit(file, tape->pos[i].action[j]);
4164 putFile8Bit(file, tape->pos[i].delay);
4168 void SaveTape(int nr)
4170 char *filename = getTapeFilename(nr);
4172 boolean new_tape = TRUE;
4173 int num_participating_players = 0;
4174 int info_chunk_size;
4175 int body_chunk_size;
4178 InitTapeDirectory(leveldir_current->subdir);
4180 /* if a tape still exists, ask to overwrite it */
4181 if (fileExists(filename))
4184 if (!Request("Replace old tape ?", REQ_ASK))
4188 if (!(file = fopen(filename, MODE_WRITE)))
4190 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4194 tape.file_version = FILE_VERSION_ACTUAL;
4195 tape.game_version = GAME_VERSION_ACTUAL;
4197 /* count number of participating players */
4198 for (i = 0; i < MAX_PLAYERS; i++)
4199 if (tape.player_participates[i])
4200 num_participating_players++;
4202 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4203 body_chunk_size = (num_participating_players + 1) * tape.length;
4205 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4206 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4208 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4209 SaveTape_VERS(file, &tape);
4211 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4212 SaveTape_HEAD(file, &tape);
4214 putFileChunkBE(file, "INFO", info_chunk_size);
4215 SaveTape_INFO(file, &tape);
4217 putFileChunkBE(file, "BODY", body_chunk_size);
4218 SaveTape_BODY(file, &tape);
4222 SetFilePermissions(filename, PERMS_PRIVATE);
4224 tape.changed = FALSE;
4227 Request("Tape saved !", REQ_CONFIRM);
4230 void DumpTape(struct TapeInfo *tape)
4234 if (tape->no_valid_file)
4236 Error(ERR_WARN, "cannot dump -- no valid tape file found");
4241 printf_line("-", 79);
4242 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4243 tape->level_nr, tape->file_version, tape->game_version);
4244 printf(" (effective engine version %08d)\n",
4245 tape->engine_version);
4246 printf("Level series identifier: '%s'\n", tape->level_identifier);
4247 printf_line("-", 79);
4249 for (i = 0; i < tape->length; i++)
4251 if (i >= MAX_TAPE_LEN)
4254 printf("%03d: ", i);
4256 for (j = 0; j < MAX_PLAYERS; j++)
4258 if (tape->player_participates[j])
4260 int action = tape->pos[i].action[j];
4262 printf("%d:%02x ", j, action);
4263 printf("[%c%c%c%c|%c%c] - ",
4264 (action & JOY_LEFT ? '<' : ' '),
4265 (action & JOY_RIGHT ? '>' : ' '),
4266 (action & JOY_UP ? '^' : ' '),
4267 (action & JOY_DOWN ? 'v' : ' '),
4268 (action & JOY_BUTTON_1 ? '1' : ' '),
4269 (action & JOY_BUTTON_2 ? '2' : ' '));
4273 printf("(%03d)\n", tape->pos[i].delay);
4276 printf_line("-", 79);
4280 /* ========================================================================= */
4281 /* score file functions */
4282 /* ========================================================================= */
4284 void LoadScore(int nr)
4287 char *filename = getScoreFilename(nr);
4288 char cookie[MAX_LINE_LEN];
4289 char line[MAX_LINE_LEN];
4293 /* always start with reliable default values */
4294 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4296 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4297 highscore[i].Score = 0;
4300 if (!(file = fopen(filename, MODE_READ)))
4303 /* check file identifier */
4304 fgets(cookie, MAX_LINE_LEN, file);
4305 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4306 cookie[strlen(cookie) - 1] = '\0';
4308 if (!checkCookieString(cookie, SCORE_COOKIE))
4310 Error(ERR_WARN, "unknown format of score file '%s'", filename);
4315 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4317 fscanf(file, "%d", &highscore[i].Score);
4318 fgets(line, MAX_LINE_LEN, file);
4320 if (line[strlen(line) - 1] == '\n')
4321 line[strlen(line) - 1] = '\0';
4323 for (line_ptr = line; *line_ptr; line_ptr++)
4325 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4327 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4328 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4337 void SaveScore(int nr)
4340 char *filename = getScoreFilename(nr);
4343 InitScoreDirectory(leveldir_current->subdir);
4345 if (!(file = fopen(filename, MODE_WRITE)))
4347 Error(ERR_WARN, "cannot save score for level %d", nr);
4351 fprintf(file, "%s\n\n", SCORE_COOKIE);
4353 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4354 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4358 SetFilePermissions(filename, PERMS_PUBLIC);
4362 /* ========================================================================= */
4363 /* setup file functions */
4364 /* ========================================================================= */
4366 #define TOKEN_STR_PLAYER_PREFIX "player_"
4369 #define SETUP_TOKEN_PLAYER_NAME 0
4370 #define SETUP_TOKEN_SOUND 1
4371 #define SETUP_TOKEN_SOUND_LOOPS 2
4372 #define SETUP_TOKEN_SOUND_MUSIC 3
4373 #define SETUP_TOKEN_SOUND_SIMPLE 4
4374 #define SETUP_TOKEN_TOONS 5
4375 #define SETUP_TOKEN_SCROLL_DELAY 6
4376 #define SETUP_TOKEN_SOFT_SCROLLING 7
4377 #define SETUP_TOKEN_FADING 8
4378 #define SETUP_TOKEN_AUTORECORD 9
4379 #define SETUP_TOKEN_QUICK_DOORS 10
4380 #define SETUP_TOKEN_TEAM_MODE 11
4381 #define SETUP_TOKEN_HANDICAP 12
4382 #define SETUP_TOKEN_SKIP_LEVELS 13
4383 #define SETUP_TOKEN_TIME_LIMIT 14
4384 #define SETUP_TOKEN_FULLSCREEN 15
4385 #define SETUP_TOKEN_ASK_ON_ESCAPE 16
4386 #define SETUP_TOKEN_GRAPHICS_SET 17
4387 #define SETUP_TOKEN_SOUNDS_SET 18
4388 #define SETUP_TOKEN_MUSIC_SET 19
4389 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 20
4390 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 21
4391 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 22
4393 #define NUM_GLOBAL_SETUP_TOKENS 23
4396 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
4397 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
4398 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
4399 #define SETUP_TOKEN_EDITOR_EL_MORE 3
4400 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
4401 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
4402 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
4403 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
4404 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
4405 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 9
4406 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 10
4407 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
4408 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
4410 #define NUM_EDITOR_SETUP_TOKENS 13
4412 /* shortcut setup */
4413 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
4414 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
4415 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
4417 #define NUM_SHORTCUT_SETUP_TOKENS 3
4420 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
4421 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
4422 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
4423 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
4424 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
4425 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
4426 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
4427 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
4428 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
4429 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
4430 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
4431 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
4432 #define SETUP_TOKEN_PLAYER_KEY_UP 12
4433 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
4434 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
4435 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
4437 #define NUM_PLAYER_SETUP_TOKENS 16
4440 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
4441 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
4443 #define NUM_SYSTEM_SETUP_TOKENS 2
4446 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
4448 #define NUM_OPTIONS_SETUP_TOKENS 1
4451 static struct SetupInfo si;
4452 static struct SetupEditorInfo sei;
4453 static struct SetupShortcutInfo ssi;
4454 static struct SetupInputInfo sii;
4455 static struct SetupSystemInfo syi;
4456 static struct OptionInfo soi;
4458 static struct TokenInfo global_setup_tokens[] =
4460 { TYPE_STRING, &si.player_name, "player_name" },
4461 { TYPE_SWITCH, &si.sound, "sound" },
4462 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
4463 { TYPE_SWITCH, &si.sound_music, "background_music" },
4464 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
4465 { TYPE_SWITCH, &si.toons, "toons" },
4466 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
4467 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
4468 { TYPE_SWITCH, &si.fading, "screen_fading" },
4469 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
4470 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
4471 { TYPE_SWITCH, &si.team_mode, "team_mode" },
4472 { TYPE_SWITCH, &si.handicap, "handicap" },
4473 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
4474 { TYPE_SWITCH, &si.time_limit, "time_limit" },
4475 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
4476 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
4477 { TYPE_STRING, &si.graphics_set, "graphics_set" },
4478 { TYPE_STRING, &si.sounds_set, "sounds_set" },
4479 { TYPE_STRING, &si.music_set, "music_set" },
4480 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4481 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
4482 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
4485 static struct TokenInfo editor_setup_tokens[] =
4487 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
4488 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
4489 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
4490 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
4491 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
4492 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
4493 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
4494 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
4495 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
4496 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
4497 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
4498 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
4499 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
4502 static struct TokenInfo shortcut_setup_tokens[] =
4504 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
4505 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
4506 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
4509 static struct TokenInfo player_setup_tokens[] =
4511 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
4512 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
4513 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
4514 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
4515 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
4516 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
4517 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
4518 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
4519 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
4520 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
4521 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
4522 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
4523 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
4524 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
4525 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
4526 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
4529 static struct TokenInfo system_setup_tokens[] =
4531 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
4532 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4535 static struct TokenInfo options_setup_tokens[] =
4537 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
4540 static char *get_corrected_login_name(char *login_name)
4542 /* needed because player name must be a fixed length string */
4543 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4545 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4546 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4548 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
4549 if (strchr(login_name_new, ' '))
4550 *strchr(login_name_new, ' ') = '\0';
4552 return login_name_new;
4555 static void setSetupInfoToDefaults(struct SetupInfo *si)
4559 si->player_name = get_corrected_login_name(getLoginName());
4562 si->sound_loops = TRUE;
4563 si->sound_music = TRUE;
4564 si->sound_simple = TRUE;
4566 si->double_buffering = TRUE;
4567 si->direct_draw = !si->double_buffering;
4568 si->scroll_delay = TRUE;
4569 si->soft_scrolling = TRUE;
4571 si->autorecord = TRUE;
4572 si->quick_doors = FALSE;
4573 si->team_mode = FALSE;
4574 si->handicap = TRUE;
4575 si->skip_levels = TRUE;
4576 si->time_limit = TRUE;
4577 si->fullscreen = FALSE;
4578 si->ask_on_escape = TRUE;
4580 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4581 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4582 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4583 si->override_level_graphics = FALSE;
4584 si->override_level_sounds = FALSE;
4585 si->override_level_music = FALSE;
4587 si->editor.el_boulderdash = TRUE;
4588 si->editor.el_emerald_mine = TRUE;
4589 si->editor.el_emerald_mine_club = TRUE;
4590 si->editor.el_more = TRUE;
4591 si->editor.el_sokoban = TRUE;
4592 si->editor.el_supaplex = TRUE;
4593 si->editor.el_diamond_caves = TRUE;
4594 si->editor.el_dx_boulderdash = TRUE;
4595 si->editor.el_chars = TRUE;
4596 si->editor.el_custom = TRUE;
4597 si->editor.el_custom_more = FALSE;
4599 si->editor.el_headlines = TRUE;
4600 si->editor.el_user_defined = FALSE;
4602 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4603 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4604 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4606 for (i = 0; i < MAX_PLAYERS; i++)
4608 si->input[i].use_joystick = FALSE;
4609 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4610 si->input[i].joy.xleft = JOYSTICK_XLEFT;
4611 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4612 si->input[i].joy.xright = JOYSTICK_XRIGHT;
4613 si->input[i].joy.yupper = JOYSTICK_YUPPER;
4614 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4615 si->input[i].joy.ylower = JOYSTICK_YLOWER;
4616 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
4617 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
4618 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
4619 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4620 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
4621 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
4622 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
4623 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
4626 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4627 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4629 si->options.verbose = FALSE;
4632 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4636 if (!setup_file_hash)
4641 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4642 setSetupInfo(global_setup_tokens, i,
4643 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4648 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4649 setSetupInfo(editor_setup_tokens, i,
4650 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4653 /* shortcut setup */
4654 ssi = setup.shortcut;
4655 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4656 setSetupInfo(shortcut_setup_tokens, i,
4657 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4658 setup.shortcut = ssi;
4661 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4665 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4667 sii = setup.input[pnr];
4668 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4670 char full_token[100];
4672 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4673 setSetupInfo(player_setup_tokens, i,
4674 getHashEntry(setup_file_hash, full_token));
4676 setup.input[pnr] = sii;
4681 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4682 setSetupInfo(system_setup_tokens, i,
4683 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4687 soi = setup.options;
4688 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4689 setSetupInfo(options_setup_tokens, i,
4690 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4691 setup.options = soi;
4696 char *filename = getSetupFilename();
4697 SetupFileHash *setup_file_hash = NULL;
4699 /* always start with reliable default values */
4700 setSetupInfoToDefaults(&setup);
4702 setup_file_hash = loadSetupFileHash(filename);
4704 if (setup_file_hash)
4706 char *player_name_new;
4708 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4709 decodeSetupFileHash(setup_file_hash);
4711 setup.direct_draw = !setup.double_buffering;
4713 freeSetupFileHash(setup_file_hash);
4715 /* needed to work around problems with fixed length strings */
4716 player_name_new = get_corrected_login_name(setup.player_name);
4717 free(setup.player_name);
4718 setup.player_name = player_name_new;
4721 Error(ERR_WARN, "using default setup values");
4726 char *filename = getSetupFilename();
4730 InitUserDataDirectory();
4732 if (!(file = fopen(filename, MODE_WRITE)))
4734 Error(ERR_WARN, "cannot write setup file '%s'", filename);
4738 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4739 getCookie("SETUP")));
4740 fprintf(file, "\n");
4744 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4746 /* just to make things nicer :) */
4747 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4748 i == SETUP_TOKEN_GRAPHICS_SET)
4749 fprintf(file, "\n");
4751 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4756 fprintf(file, "\n");
4757 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4758 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4760 /* shortcut setup */
4761 ssi = setup.shortcut;
4762 fprintf(file, "\n");
4763 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4764 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4767 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4771 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4772 fprintf(file, "\n");
4774 sii = setup.input[pnr];
4775 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4776 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4781 fprintf(file, "\n");
4782 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4783 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4786 soi = setup.options;
4787 fprintf(file, "\n");
4788 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4789 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4793 SetFilePermissions(filename, PERMS_PRIVATE);
4796 void LoadCustomElementDescriptions()
4798 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4799 SetupFileHash *setup_file_hash;
4802 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4804 if (element_info[i].custom_description != NULL)
4806 free(element_info[i].custom_description);
4807 element_info[i].custom_description = NULL;
4811 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4814 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4816 char *token = getStringCat2(element_info[i].token_name, ".name");
4817 char *value = getHashEntry(setup_file_hash, token);
4820 element_info[i].custom_description = getStringCopy(value);
4825 freeSetupFileHash(setup_file_hash);
4828 void LoadSpecialMenuDesignSettings()
4830 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4831 SetupFileHash *setup_file_hash;
4834 /* always start with reliable default values from default config */
4835 for (i = 0; image_config_vars[i].token != NULL; i++)
4836 for (j = 0; image_config[j].token != NULL; j++)
4837 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4838 *image_config_vars[i].value =
4839 get_auto_parameter_value(image_config_vars[i].token,
4840 image_config[j].value);
4842 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4845 /* special case: initialize with default values that may be overwritten */
4846 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4848 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4849 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4850 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4852 if (value_x != NULL)
4853 menu.draw_xoffset[i] = get_integer_from_string(value_x);
4854 if (value_y != NULL)
4855 menu.draw_yoffset[i] = get_integer_from_string(value_y);
4856 if (list_size != NULL)
4857 menu.list_size[i] = get_integer_from_string(list_size);
4860 /* read (and overwrite with) values that may be specified in config file */
4861 for (i = 0; image_config_vars[i].token != NULL; i++)
4863 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4866 *image_config_vars[i].value =
4867 get_auto_parameter_value(image_config_vars[i].token, value);
4870 freeSetupFileHash(setup_file_hash);
4873 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4875 char *filename = getEditorSetupFilename();
4876 SetupFileList *setup_file_list, *list;
4877 SetupFileHash *element_hash;
4878 int num_unknown_tokens = 0;
4881 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4884 element_hash = newSetupFileHash();
4886 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4887 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4889 /* determined size may be larger than needed (due to unknown elements) */
4891 for (list = setup_file_list; list != NULL; list = list->next)
4894 /* add space for up to 3 more elements for padding that may be needed */
4897 *elements = checked_malloc(*num_elements * sizeof(int));
4900 for (list = setup_file_list; list != NULL; list = list->next)
4902 char *value = getHashEntry(element_hash, list->token);
4904 if (value == NULL) /* try to find obsolete token mapping */
4906 char *mapped_token = get_mapped_token(list->token);
4908 if (mapped_token != NULL)
4910 value = getHashEntry(element_hash, mapped_token);
4918 (*elements)[(*num_elements)++] = atoi(value);
4922 if (num_unknown_tokens == 0)
4924 Error(ERR_RETURN_LINE, "-");
4925 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4926 Error(ERR_RETURN, "- config file: '%s'", filename);
4928 num_unknown_tokens++;
4931 Error(ERR_RETURN, "- token: '%s'", list->token);
4935 if (num_unknown_tokens > 0)
4936 Error(ERR_RETURN_LINE, "-");
4938 while (*num_elements % 4) /* pad with empty elements, if needed */
4939 (*elements)[(*num_elements)++] = EL_EMPTY;
4941 freeSetupFileList(setup_file_list);
4942 freeSetupFileHash(element_hash);
4945 for (i = 0; i < *num_elements; i++)
4946 printf("editor: element '%s' [%d]\n",
4947 element_info[(*elements)[i]].token_name, (*elements)[i]);
4951 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4954 SetupFileHash *setup_file_hash = NULL;
4955 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4956 char *filename_music, *filename_prefix, *filename_info;
4962 token_to_value_ptr[] =
4964 { "title_header", &tmp_music_file_info.title_header },
4965 { "artist_header", &tmp_music_file_info.artist_header },
4966 { "album_header", &tmp_music_file_info.album_header },
4967 { "year_header", &tmp_music_file_info.year_header },
4969 { "title", &tmp_music_file_info.title },
4970 { "artist", &tmp_music_file_info.artist },
4971 { "album", &tmp_music_file_info.album },
4972 { "year", &tmp_music_file_info.year },
4978 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4979 getCustomMusicFilename(basename));
4981 if (filename_music == NULL)
4984 /* ---------- try to replace file extension ---------- */
4986 filename_prefix = getStringCopy(filename_music);
4987 if (strrchr(filename_prefix, '.') != NULL)
4988 *strrchr(filename_prefix, '.') = '\0';
4989 filename_info = getStringCat2(filename_prefix, ".txt");
4992 printf("trying to load file '%s'...\n", filename_info);
4995 if (fileExists(filename_info))
4996 setup_file_hash = loadSetupFileHash(filename_info);
4998 free(filename_prefix);
4999 free(filename_info);
5001 if (setup_file_hash == NULL)
5003 /* ---------- try to add file extension ---------- */
5005 filename_prefix = getStringCopy(filename_music);
5006 filename_info = getStringCat2(filename_prefix, ".txt");
5009 printf("trying to load file '%s'...\n", filename_info);
5012 if (fileExists(filename_info))
5013 setup_file_hash = loadSetupFileHash(filename_info);
5015 free(filename_prefix);
5016 free(filename_info);
5019 if (setup_file_hash == NULL)
5022 /* ---------- music file info found ---------- */
5024 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
5026 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
5028 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
5030 *token_to_value_ptr[i].value_ptr =
5031 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
5034 tmp_music_file_info.basename = getStringCopy(basename);
5035 tmp_music_file_info.music = music;
5036 tmp_music_file_info.is_sound = is_sound;
5038 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
5039 *new_music_file_info = tmp_music_file_info;
5041 return new_music_file_info;
5044 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
5046 return get_music_file_info_ext(basename, music, FALSE);
5049 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
5051 return get_music_file_info_ext(basename, sound, TRUE);
5054 static boolean music_info_listed_ext(struct MusicFileInfo *list,
5055 char *basename, boolean is_sound)
5057 for (; list != NULL; list = list->next)
5058 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
5064 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
5066 return music_info_listed_ext(list, basename, FALSE);
5069 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
5071 return music_info_listed_ext(list, basename, TRUE);
5074 void LoadMusicInfo()
5076 char *music_directory = getCustomMusicDirectory();
5077 int num_music = getMusicListSize();
5078 int num_music_noconf = 0;
5079 int num_sounds = getSoundListSize();
5081 struct dirent *dir_entry;
5082 struct FileInfo *music, *sound;
5083 struct MusicFileInfo *next, **new;
5086 while (music_file_info != NULL)
5088 next = music_file_info->next;
5090 checked_free(music_file_info->basename);
5092 checked_free(music_file_info->title_header);
5093 checked_free(music_file_info->artist_header);
5094 checked_free(music_file_info->album_header);
5095 checked_free(music_file_info->year_header);
5097 checked_free(music_file_info->title);
5098 checked_free(music_file_info->artist);
5099 checked_free(music_file_info->album);
5100 checked_free(music_file_info->year);
5102 free(music_file_info);
5104 music_file_info = next;
5107 new = &music_file_info;
5109 for (i = 0; i < num_music; i++)
5111 music = getMusicListEntry(i);
5113 if (music->filename == NULL)
5116 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
5119 /* a configured file may be not recognized as music */
5120 if (!FileIsMusic(music->filename))
5124 printf("::: -> '%s' (configured)\n", music->filename);
5127 if (!music_info_listed(music_file_info, music->filename))
5129 *new = get_music_file_info(music->filename, i);
5131 new = &(*new)->next;
5135 if ((dir = opendir(music_directory)) == NULL)
5137 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
5141 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
5143 char *basename = dir_entry->d_name;
5144 boolean music_already_used = FALSE;
5147 /* skip all music files that are configured in music config file */
5148 for (i = 0; i < num_music; i++)
5150 music = getMusicListEntry(i);
5152 if (music->filename == NULL)
5155 if (strcmp(basename, music->filename) == 0)
5157 music_already_used = TRUE;
5162 if (music_already_used)
5165 if (!FileIsMusic(basename))
5169 printf("::: -> '%s' (found in directory)\n", basename);
5172 if (!music_info_listed(music_file_info, basename))
5174 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5176 new = &(*new)->next;
5184 for (i = 0; i < num_sounds; i++)
5186 sound = getSoundListEntry(i);
5188 if (sound->filename == NULL)
5191 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5194 /* a configured file may be not recognized as sound */
5195 if (!FileIsSound(sound->filename))
5199 printf("::: -> '%s' (configured)\n", sound->filename);
5202 if (!sound_info_listed(music_file_info, sound->filename))
5204 *new = get_sound_file_info(sound->filename, i);
5206 new = &(*new)->next;
5211 for (next = music_file_info; next != NULL; next = next->next)
5212 printf("::: title == '%s'\n", next->title);
5216 void add_helpanim_entry(int element, int action, int direction, int delay,
5217 int *num_list_entries)
5219 struct HelpAnimInfo *new_list_entry;
5220 (*num_list_entries)++;
5223 checked_realloc(helpanim_info,
5224 *num_list_entries * sizeof(struct HelpAnimInfo));
5225 new_list_entry = &helpanim_info[*num_list_entries - 1];
5227 new_list_entry->element = element;
5228 new_list_entry->action = action;
5229 new_list_entry->direction = direction;
5230 new_list_entry->delay = delay;
5233 void print_unknown_token(char *filename, char *token, int token_nr)
5237 Error(ERR_RETURN_LINE, "-");
5238 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5239 Error(ERR_RETURN, "- config file: '%s'", filename);
5242 Error(ERR_RETURN, "- token: '%s'", token);
5245 void print_unknown_token_end(int token_nr)
5248 Error(ERR_RETURN_LINE, "-");
5251 void LoadHelpAnimInfo()
5253 char *filename = getHelpAnimFilename();
5254 SetupFileList *setup_file_list = NULL, *list;
5255 SetupFileHash *element_hash, *action_hash, *direction_hash;
5256 int num_list_entries = 0;
5257 int num_unknown_tokens = 0;
5260 if (fileExists(filename))
5261 setup_file_list = loadSetupFileList(filename);
5263 if (setup_file_list == NULL)
5265 /* use reliable default values from static configuration */
5266 SetupFileList *insert_ptr;
5268 insert_ptr = setup_file_list =
5269 newSetupFileList(helpanim_config[0].token,
5270 helpanim_config[0].value);
5272 for (i = 1; helpanim_config[i].token; i++)
5273 insert_ptr = addListEntry(insert_ptr,
5274 helpanim_config[i].token,
5275 helpanim_config[i].value);
5278 element_hash = newSetupFileHash();
5279 action_hash = newSetupFileHash();
5280 direction_hash = newSetupFileHash();
5282 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5283 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5285 for (i = 0; i < NUM_ACTIONS; i++)
5286 setHashEntry(action_hash, element_action_info[i].suffix,
5287 i_to_a(element_action_info[i].value));
5289 /* do not store direction index (bit) here, but direction value! */
5290 for (i = 0; i < NUM_DIRECTIONS; i++)
5291 setHashEntry(direction_hash, element_direction_info[i].suffix,
5292 i_to_a(1 << element_direction_info[i].value));
5294 for (list = setup_file_list; list != NULL; list = list->next)
5296 char *element_token, *action_token, *direction_token;
5297 char *element_value, *action_value, *direction_value;
5298 int delay = atoi(list->value);
5300 if (strcmp(list->token, "end") == 0)
5302 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5307 /* first try to break element into element/action/direction parts;
5308 if this does not work, also accept combined "element[.act][.dir]"
5309 elements (like "dynamite.active"), which are unique elements */
5311 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
5313 element_value = getHashEntry(element_hash, list->token);
5314 if (element_value != NULL) /* element found */
5315 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5319 /* no further suffixes found -- this is not an element */
5320 print_unknown_token(filename, list->token, num_unknown_tokens++);
5326 /* token has format "<prefix>.<something>" */
5328 action_token = strchr(list->token, '.'); /* suffix may be action ... */
5329 direction_token = action_token; /* ... or direction */
5331 element_token = getStringCopy(list->token);
5332 *strchr(element_token, '.') = '\0';
5334 element_value = getHashEntry(element_hash, element_token);
5336 if (element_value == NULL) /* this is no element */
5338 element_value = getHashEntry(element_hash, list->token);
5339 if (element_value != NULL) /* combined element found */
5340 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5343 print_unknown_token(filename, list->token, num_unknown_tokens++);
5345 free(element_token);
5350 action_value = getHashEntry(action_hash, action_token);
5352 if (action_value != NULL) /* action found */
5354 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5357 free(element_token);
5362 direction_value = getHashEntry(direction_hash, direction_token);
5364 if (direction_value != NULL) /* direction found */
5366 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5369 free(element_token);
5374 if (strchr(action_token + 1, '.') == NULL)
5376 /* no further suffixes found -- this is not an action nor direction */
5378 element_value = getHashEntry(element_hash, list->token);
5379 if (element_value != NULL) /* combined element found */
5380 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5383 print_unknown_token(filename, list->token, num_unknown_tokens++);
5385 free(element_token);
5390 /* token has format "<prefix>.<suffix>.<something>" */
5392 direction_token = strchr(action_token + 1, '.');
5394 action_token = getStringCopy(action_token);
5395 *strchr(action_token + 1, '.') = '\0';
5397 action_value = getHashEntry(action_hash, action_token);
5399 if (action_value == NULL) /* this is no action */
5401 element_value = getHashEntry(element_hash, list->token);
5402 if (element_value != NULL) /* combined element found */
5403 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5406 print_unknown_token(filename, list->token, num_unknown_tokens++);
5408 free(element_token);
5414 direction_value = getHashEntry(direction_hash, direction_token);
5416 if (direction_value != NULL) /* direction found */
5418 add_helpanim_entry(atoi(element_value), atoi(action_value),
5419 atoi(direction_value), delay, &num_list_entries);
5421 free(element_token);
5427 /* this is no direction */
5429 element_value = getHashEntry(element_hash, list->token);
5430 if (element_value != NULL) /* combined element found */
5431 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5434 print_unknown_token(filename, list->token, num_unknown_tokens++);
5436 free(element_token);
5440 print_unknown_token_end(num_unknown_tokens);
5442 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5443 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
5445 freeSetupFileList(setup_file_list);
5446 freeSetupFileHash(element_hash);
5447 freeSetupFileHash(action_hash);
5448 freeSetupFileHash(direction_hash);
5451 for (i = 0; i < num_list_entries; i++)
5452 printf("::: %d, %d, %d => %d\n",
5453 helpanim_info[i].element,
5454 helpanim_info[i].action,
5455 helpanim_info[i].direction,
5456 helpanim_info[i].delay);
5460 void LoadHelpTextInfo()
5462 char *filename = getHelpTextFilename();
5465 if (helptext_info != NULL)
5467 freeSetupFileHash(helptext_info);
5468 helptext_info = NULL;
5471 if (fileExists(filename))
5472 helptext_info = loadSetupFileHash(filename);
5474 if (helptext_info == NULL)
5476 /* use reliable default values from static configuration */
5477 helptext_info = newSetupFileHash();
5479 for (i = 0; helptext_config[i].token; i++)
5480 setHashEntry(helptext_info,
5481 helptext_config[i].token,
5482 helptext_config[i].value);
5486 BEGIN_HASH_ITERATION(helptext_info, itr)
5488 printf("::: '%s' => '%s'\n",
5489 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5491 END_HASH_ITERATION(hash, itr)
5496 /* ------------------------------------------------------------------------- *
5498 * ------------------------------------------------------------------------- */
5500 #define MAX_NUM_CONVERT_LEVELS 1000
5502 void ConvertLevels()
5504 static LevelDirTree *convert_leveldir = NULL;
5505 static int convert_level_nr = -1;
5506 static int num_levels_handled = 0;
5507 static int num_levels_converted = 0;
5508 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5511 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5512 global.convert_leveldir);
5514 if (convert_leveldir == NULL)
5515 Error(ERR_EXIT, "no such level identifier: '%s'",
5516 global.convert_leveldir);
5518 leveldir_current = convert_leveldir;
5520 if (global.convert_level_nr != -1)
5522 convert_leveldir->first_level = global.convert_level_nr;
5523 convert_leveldir->last_level = global.convert_level_nr;
5526 convert_level_nr = convert_leveldir->first_level;
5528 printf_line("=", 79);
5529 printf("Converting levels\n");
5530 printf_line("-", 79);
5531 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5532 printf("Level series name: '%s'\n", convert_leveldir->name);
5533 printf("Level series author: '%s'\n", convert_leveldir->author);
5534 printf("Number of levels: %d\n", convert_leveldir->levels);
5535 printf_line("=", 79);
5538 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5539 levels_failed[i] = FALSE;
5541 while (convert_level_nr <= convert_leveldir->last_level)
5543 char *level_filename;
5546 level_nr = convert_level_nr++;
5548 printf("Level %03d: ", level_nr);
5550 LoadLevel(level_nr);
5551 if (level.no_valid_file)
5553 printf("(no level)\n");
5557 printf("converting level ... ");
5559 level_filename = getDefaultLevelFilename(level_nr);
5560 new_level = !fileExists(level_filename);
5564 SaveLevel(level_nr);
5566 num_levels_converted++;
5568 printf("converted.\n");
5572 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5573 levels_failed[level_nr] = TRUE;
5575 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5578 num_levels_handled++;
5582 printf_line("=", 79);
5583 printf("Number of levels handled: %d\n", num_levels_handled);
5584 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5585 (num_levels_handled ?
5586 num_levels_converted * 100 / num_levels_handled : 0));
5587 printf_line("-", 79);
5588 printf("Summary (for automatic parsing by scripts):\n");
5589 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5590 convert_leveldir->identifier, num_levels_converted,
5592 (num_levels_handled ?
5593 num_levels_converted * 100 / num_levels_handled : 0));
5595 if (num_levels_handled != num_levels_converted)
5597 printf(", FAILED:");
5598 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5599 if (levels_failed[i])
5604 printf_line("=", 79);