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_INTEGER_5 (CONF_MASK_1_BYTE | 5)
68 #define CONF_VALUE_INTEGER_6 (CONF_MASK_1_BYTE | 6)
69 #define CONF_VALUE_INTEGER_7 (CONF_MASK_1_BYTE | 7)
70 #define CONF_VALUE_INTEGER_8 (CONF_MASK_1_BYTE | 8)
71 #define CONF_VALUE_BOOLEAN_1 (CONF_MASK_1_BYTE | 9)
72 #define CONF_VALUE_BOOLEAN_2 (CONF_MASK_1_BYTE | 10)
73 #define CONF_VALUE_BOOLEAN_3 (CONF_MASK_1_BYTE | 11)
74 #define CONF_VALUE_BOOLEAN_4 (CONF_MASK_1_BYTE | 12)
75 #define CONF_VALUE_BOOLEAN_5 (CONF_MASK_1_BYTE | 13)
76 #define CONF_VALUE_BOOLEAN_6 (CONF_MASK_1_BYTE | 14)
77 #define CONF_VALUE_BOOLEAN_7 (CONF_MASK_1_BYTE | 15)
78 #define CONF_VALUE_BOOLEAN_8 (CONF_MASK_1_BYTE | 16)
80 #define CONF_VALUE_ELEMENT_1 (CONF_MASK_2_BYTE | 1)
81 #define CONF_VALUE_ELEMENT_2 (CONF_MASK_2_BYTE | 2)
82 #define CONF_VALUE_ELEMENT_3 (CONF_MASK_2_BYTE | 3)
83 #define CONF_VALUE_ELEMENT_4 (CONF_MASK_2_BYTE | 4)
84 #define CONF_VALUE_ELEMENT_5 (CONF_MASK_2_BYTE | 5)
85 #define CONF_VALUE_ELEMENT_6 (CONF_MASK_2_BYTE | 6)
86 #define CONF_VALUE_ELEMENT_7 (CONF_MASK_2_BYTE | 7)
87 #define CONF_VALUE_ELEMENT_8 (CONF_MASK_2_BYTE | 8)
89 #define CONF_VALUE_CONTENT_1 (CONF_MASK_MULTI_BYTES | 1)
90 #define CONF_VALUE_CONTENT_8 (CONF_MASK_MULTI_BYTES | 2)
92 #define CONF_VALUE_INTEGER(x) ((x) >= CONF_VALUE_INTEGER_1 && \
93 (x) <= CONF_VALUE_INTEGER_8)
95 #define CONF_VALUE_BOOLEAN(x) ((x) >= CONF_VALUE_BOOLEAN_1 && \
96 (x) <= CONF_VALUE_BOOLEAN_8)
98 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
99 (x) == CONF_MASK_2_BYTE ? 2 : \
100 (x) == CONF_MASK_4_BYTE ? 4 : 0)
102 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
103 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
105 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
107 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * 2)
108 #define CONF_CONTENT_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)] << 8)|\
109 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
111 static struct LevelInfo li;
121 /* ---------- 1-byte values ---------------------------------------------- */
123 EL_EMC_ANDROID, CONF_VALUE_INTEGER_1,
124 &li.android_move_time, 10
127 EL_EMC_ANDROID, CONF_VALUE_INTEGER_2,
128 &li.android_clone_time, 10
131 EL_EMC_LENSES, CONF_VALUE_INTEGER_1,
135 EL_EMC_LENSES, CONF_VALUE_INTEGER_2,
139 EL_EMC_MAGNIFIER, CONF_VALUE_INTEGER_1,
140 &li.magnify_score, 10
143 EL_EMC_MAGNIFIER, CONF_VALUE_INTEGER_2,
147 EL_ROBOT, CONF_VALUE_INTEGER_1,
151 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_1,
152 &li.game_of_life[0], 2
155 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_2,
156 &li.game_of_life[1], 3
159 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_3,
160 &li.game_of_life[2], 3
163 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_4,
164 &li.game_of_life[3], 3
167 EL_BIOMAZE, CONF_VALUE_INTEGER_1,
171 EL_BIOMAZE, CONF_VALUE_INTEGER_2,
175 EL_BIOMAZE, CONF_VALUE_INTEGER_3,
179 EL_BIOMAZE, CONF_VALUE_INTEGER_4,
183 EL_BALLOON, CONF_VALUE_INTEGER_1,
184 &li.wind_direction_initial, MV_NONE
187 EL_TIMEGATE_SWITCH, CONF_VALUE_INTEGER_1,
188 &li.time_timegate, 10
191 EL_LIGHT_SWITCH_ACTIVE, CONF_VALUE_INTEGER_1,
195 EL_SHIELD_NORMAL, CONF_VALUE_INTEGER_1,
196 &li.shield_normal_time, 10
199 EL_SHIELD_DEADLY, CONF_VALUE_INTEGER_1,
200 &li.shield_deadly_time, 10
203 EL_EXTRA_TIME, CONF_VALUE_INTEGER_1,
207 EL_EXTRA_TIME, CONF_VALUE_INTEGER_2,
208 &li.extra_time_score, 10
211 EL_TIME_ORB_FULL, CONF_VALUE_INTEGER_1,
212 &li.time_orb_time, 10
215 EL_TIME_ORB_FULL, CONF_VALUE_BOOLEAN_1,
216 &li.use_time_orb_bug, FALSE
219 EL_PLAYER_1, CONF_VALUE_BOOLEAN_1,
220 &li.block_snap_field, TRUE
223 EL_PLAYER_1, CONF_VALUE_BOOLEAN_2,
224 &li.use_start_element[0], FALSE
227 EL_PLAYER_2, CONF_VALUE_BOOLEAN_2,
228 &li.use_start_element[1], FALSE
231 EL_PLAYER_3, CONF_VALUE_BOOLEAN_2,
232 &li.use_start_element[2], FALSE
235 EL_PLAYER_4, CONF_VALUE_BOOLEAN_2,
236 &li.use_start_element[3], FALSE
239 EL_PLAYER_1, CONF_VALUE_BOOLEAN_3,
240 &li.use_artwork_element[0], FALSE
243 EL_PLAYER_2, CONF_VALUE_BOOLEAN_3,
244 &li.use_artwork_element[1], FALSE
247 EL_PLAYER_3, CONF_VALUE_BOOLEAN_3,
248 &li.use_artwork_element[2], FALSE
251 EL_PLAYER_4, CONF_VALUE_BOOLEAN_3,
252 &li.use_artwork_element[3], FALSE
255 EL_PLAYER_1, CONF_VALUE_BOOLEAN_4,
256 &li.use_explosion_element[0], FALSE
259 EL_PLAYER_2, CONF_VALUE_BOOLEAN_4,
260 &li.use_explosion_element[1], FALSE
263 EL_PLAYER_3, CONF_VALUE_BOOLEAN_4,
264 &li.use_explosion_element[2], FALSE
267 EL_PLAYER_4, CONF_VALUE_BOOLEAN_4,
268 &li.use_explosion_element[3], FALSE
271 EL_EMC_MAGIC_BALL, CONF_VALUE_INTEGER_1,
275 EL_EMC_MAGIC_BALL, CONF_VALUE_INTEGER_2,
276 &li.num_ball_contents, 8
279 EL_EMC_MAGIC_BALL, CONF_VALUE_BOOLEAN_1,
280 &li.ball_random, FALSE
283 EL_EMC_MAGIC_BALL, CONF_VALUE_BOOLEAN_2,
284 &li.ball_state_initial, FALSE
287 /* ---------- 2-byte values ---------------------------------------------- */
289 EL_PLAYER_1, CONF_VALUE_ELEMENT_1,
290 &li.start_element[0], EL_PLAYER_1
293 EL_PLAYER_2, CONF_VALUE_ELEMENT_1,
294 &li.start_element[1], EL_PLAYER_2
297 EL_PLAYER_3, CONF_VALUE_ELEMENT_1,
298 &li.start_element[2], EL_PLAYER_3
301 EL_PLAYER_4, CONF_VALUE_ELEMENT_1,
302 &li.start_element[3], EL_PLAYER_4
305 EL_PLAYER_1, CONF_VALUE_ELEMENT_2,
306 &li.artwork_element[0], EL_PLAYER_1
309 EL_PLAYER_2, CONF_VALUE_ELEMENT_2,
310 &li.artwork_element[1], EL_PLAYER_2
313 EL_PLAYER_3, CONF_VALUE_ELEMENT_2,
314 &li.artwork_element[2], EL_PLAYER_3
317 EL_PLAYER_4, CONF_VALUE_ELEMENT_2,
318 &li.artwork_element[3], EL_PLAYER_4
321 EL_PLAYER_1, CONF_VALUE_ELEMENT_3,
322 &li.explosion_element[0], EL_PLAYER_1
325 EL_PLAYER_2, CONF_VALUE_ELEMENT_3,
326 &li.explosion_element[1], EL_PLAYER_2
329 EL_PLAYER_3, CONF_VALUE_ELEMENT_3,
330 &li.explosion_element[2], EL_PLAYER_3
333 EL_PLAYER_4, CONF_VALUE_ELEMENT_3,
334 &li.explosion_element[3], EL_PLAYER_4
337 /* ---------- multi-byte values ------------------------------------------ */
339 EL_EMC_MAGIC_BALL, CONF_VALUE_CONTENT_8,
340 &li.ball_content, EL_EMPTY
356 { LEVEL_FILE_TYPE_RND, "RND" },
357 { LEVEL_FILE_TYPE_BD, "BD" },
358 { LEVEL_FILE_TYPE_EM, "EM" },
359 { LEVEL_FILE_TYPE_SP, "SP" },
360 { LEVEL_FILE_TYPE_DX, "DX" },
361 { LEVEL_FILE_TYPE_SB, "SB" },
362 { LEVEL_FILE_TYPE_DC, "DC" },
367 /* ========================================================================= */
368 /* level file functions */
369 /* ========================================================================= */
371 static void setLevelInfoToDefaultsFromConfigList(struct LevelInfo *level)
375 li = *level; /* copy level information into temporary buffer */
377 for (i = 0; element_conf[i].element != -1; i++)
379 int default_value = element_conf[i].default_value;
380 int type = element_conf[i].type;
381 int bytes = type & CONF_MASK_BYTES;
383 if (bytes != CONF_MASK_MULTI_BYTES)
385 if (CONF_VALUE_BOOLEAN(type))
386 *(boolean *)(element_conf[i].value) = default_value;
388 *(int *) (element_conf[i].value) = default_value;
390 else if (type == CONF_VALUE_CONTENT_8)
392 struct Content *content = (struct Content *)(element_conf[i].value);
395 for (c = 0; c < MAX_ELEMENT_CONTENTS; c++)
396 for (y = 0; y < 3; y++)
397 for (x = 0; x < 3; x++)
398 content[c].e[x][y] = default_value;
402 *level = li; /* copy temporary buffer back to level information */
405 void setElementChangePages(struct ElementInfo *ei, int change_pages)
407 int change_page_size = sizeof(struct ElementChangeInfo);
409 ei->num_change_pages = MAX(1, change_pages);
412 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
414 if (ei->current_change_page >= ei->num_change_pages)
415 ei->current_change_page = ei->num_change_pages - 1;
417 ei->change = &ei->change_page[ei->current_change_page];
420 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
424 change->can_change = FALSE;
426 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
427 change->has_event[i] = FALSE;
429 change->trigger_player = CH_PLAYER_ANY;
430 change->trigger_side = CH_SIDE_ANY;
431 change->trigger_page = CH_PAGE_ANY;
433 change->target_element = EL_EMPTY_SPACE;
435 change->delay_fixed = 0;
436 change->delay_random = 0;
437 change->delay_frames = FRAMES_PER_SECOND;
439 change->trigger_element = EL_EMPTY_SPACE;
441 change->explode = FALSE;
442 change->use_target_content = FALSE;
443 change->only_if_complete = FALSE;
444 change->use_random_replace = FALSE;
445 change->random_percentage = 100;
446 change->replace_when = CP_WHEN_EMPTY;
448 change->has_action = FALSE;
449 change->action_type = CA_NO_ACTION;
450 change->action_mode = CA_MODE_UNDEFINED;
451 change->action_arg = CA_ARG_UNDEFINED;
453 for (x = 0; x < 3; x++)
454 for (y = 0; y < 3; y++)
455 change->target_content.e[x][y] = EL_EMPTY_SPACE;
457 change->direct_action = 0;
458 change->other_action = 0;
460 change->pre_change_function = NULL;
461 change->change_function = NULL;
462 change->post_change_function = NULL;
465 static void setLevelInfoToDefaults(struct LevelInfo *level)
467 static boolean clipboard_elements_initialized = FALSE;
471 InitElementPropertiesStatic();
474 setLevelInfoToDefaultsFromConfigList(level);
475 setLevelInfoToDefaults_EM();
477 level->native_em_level = &native_em_level;
479 level->game_engine_type = GAME_ENGINE_TYPE_RND;
481 level->file_version = FILE_VERSION_ACTUAL;
482 level->game_version = GAME_VERSION_ACTUAL;
484 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
485 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
486 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
488 level->fieldx = STD_LEV_FIELDX;
489 level->fieldy = STD_LEV_FIELDY;
491 for (x = 0; x < MAX_LEV_FIELDX; x++)
492 for (y = 0; y < MAX_LEV_FIELDY; y++)
493 level->field[x][y] = EL_SAND;
496 level->gems_needed = 0;
498 level->amoeba_speed = 10;
500 level->time_magic_wall = 10;
501 level->time_wheel = 10;
503 level->time_light = 10;
504 level->time_timegate = 10;
507 level->amoeba_content = EL_DIAMOND;
509 level->game_of_life[0] = 2;
510 level->game_of_life[1] = 3;
511 level->game_of_life[2] = 3;
512 level->game_of_life[3] = 3;
514 level->biomaze[0] = 2;
515 level->biomaze[1] = 3;
516 level->biomaze[2] = 3;
517 level->biomaze[3] = 3;
519 level->double_speed = FALSE;
520 level->initial_gravity = FALSE;
521 level->em_slippery_gems = FALSE;
522 level->instant_relocation = FALSE;
523 level->can_pass_to_walkable = FALSE;
524 level->grow_into_diggable = TRUE;
526 level->block_snap_field = TRUE;
528 level->block_last_field = FALSE; /* EM does not block by default */
529 level->sp_block_last_field = TRUE; /* SP blocks the last field */
531 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
532 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
534 level->use_spring_bug = FALSE;
535 level->use_time_orb_bug = FALSE;
537 level->use_step_counter = FALSE;
539 /* values for the new EMC elements */
541 level->android_move_time = 10;
542 level->android_clone_time = 10;
543 level->ball_time = 10;
544 level->lenses_score = 10;
545 level->lenses_time = 10;
546 level->magnify_score = 10;
547 level->magnify_time = 10;
548 level->slurp_score = 10;
549 level->wind_direction_initial = MV_NONE;
550 level->ball_random = FALSE;
551 level->ball_state_initial = FALSE;
552 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
553 for (x = 0; x < 3; x++)
554 for (y = 0; y < 3; y++)
555 level->ball_content[i].e[x][y] = EL_EMPTY;
557 for (i = 0; i < 16; i++)
558 level->android_array[i] = FALSE;
560 level->use_custom_template = FALSE;
562 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
563 level->name[i] = '\0';
564 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
565 level->author[i] = '\0';
567 strcpy(level->name, NAMELESS_LEVEL_NAME);
568 strcpy(level->author, ANONYMOUS_NAME);
570 for (i = 0; i < 4; i++)
572 level->envelope_text[i][0] = '\0';
573 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
574 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
577 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
578 level->score[i] = (i == SC_TIME_BONUS ? 1 : 10);
580 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
581 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
582 for (x = 0; x < 3; x++)
583 for (y = 0; y < 3; y++)
584 level->yamyam_content[i].e[x][y] =
585 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
587 level->field[0][0] = EL_PLAYER_1;
588 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
590 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
593 struct ElementInfo *ei = &element_info[element];
595 /* never initialize clipboard elements after the very first time */
596 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
599 setElementChangePages(ei, 1);
600 setElementChangeInfoToDefaults(ei->change);
602 if (IS_CUSTOM_ELEMENT(element) ||
603 IS_GROUP_ELEMENT(element) ||
604 IS_INTERNAL_ELEMENT(element))
606 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
607 ei->description[j] = '\0';
609 if (ei->custom_description != NULL)
610 strncpy(ei->description, ei->custom_description,MAX_ELEMENT_NAME_LEN);
612 strcpy(ei->description, ei->editor_description);
614 ei->use_gfx_element = FALSE;
615 ei->gfx_element = EL_EMPTY_SPACE;
617 ei->modified_settings = FALSE;
620 if (IS_CUSTOM_ELEMENT(element) ||
621 IS_INTERNAL_ELEMENT(element))
623 ei->access_direction = MV_ALL_DIRECTIONS;
625 ei->collect_score_initial = 10; /* special default */
626 ei->collect_count_initial = 1; /* special default */
628 ei->ce_value_fixed_initial = 0;
629 ei->ce_value_random_initial = 0;
630 ei->use_last_ce_value = FALSE;
632 ei->push_delay_fixed = -1; /* initialize later */
633 ei->push_delay_random = -1; /* initialize later */
634 ei->drop_delay_fixed = 0;
635 ei->drop_delay_random = 0;
636 ei->move_delay_fixed = 0;
637 ei->move_delay_random = 0;
639 ei->move_pattern = MV_ALL_DIRECTIONS;
640 ei->move_direction_initial = MV_START_AUTOMATIC;
641 ei->move_stepsize = TILEX / 8;
643 ei->move_enter_element = EL_EMPTY_SPACE;
644 ei->move_leave_element = EL_EMPTY_SPACE;
645 ei->move_leave_type = LEAVE_TYPE_UNLIMITED;
647 ei->slippery_type = SLIPPERY_ANY_RANDOM;
649 ei->explosion_type = EXPLODES_3X3;
650 ei->explosion_delay = 16;
651 ei->ignition_delay = 8;
653 for (x = 0; x < 3; x++)
654 for (y = 0; y < 3; y++)
655 ei->content.e[x][y] = EL_EMPTY_SPACE;
658 ei->access_layer = 0;
659 ei->access_protected = 0;
660 ei->walk_to_action = 0;
661 ei->smash_targets = 0;
664 ei->can_explode_by_fire = FALSE;
665 ei->can_explode_smashed = FALSE;
666 ei->can_explode_impact = FALSE;
668 ei->current_change_page = 0;
671 /* !!! now done in InitElementPropertiesStatic() (see above) !!! */
672 /* !!! (else properties set there will be overwritten here) !!! */
673 /* start with no properties at all */
674 for (j = 0; j < NUM_EP_BITFIELDS; j++)
675 Properties[element][j] = EP_BITMASK_DEFAULT;
678 /* now set default properties */
679 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
682 if (IS_GROUP_ELEMENT(element) ||
683 IS_INTERNAL_ELEMENT(element))
685 struct ElementGroupInfo *group;
687 /* initialize memory for list of elements in group */
688 if (ei->group == NULL)
689 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
693 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
694 group->element[j] = EL_EMPTY_SPACE;
696 /* default: only one element in group */
697 group->num_elements = 1;
699 group->choice_mode = ANIM_RANDOM;
703 clipboard_elements_initialized = TRUE;
705 BorderElement = EL_STEELWALL;
707 level->no_valid_file = FALSE;
709 level->changed = FALSE;
711 if (leveldir_current == NULL) /* only when dumping level */
714 /* try to determine better author name than 'anonymous' */
715 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
717 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
718 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
722 switch (LEVELCLASS(leveldir_current))
724 case LEVELCLASS_TUTORIAL:
725 strcpy(level->author, PROGRAM_AUTHOR_STRING);
728 case LEVELCLASS_CONTRIB:
729 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
730 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
733 case LEVELCLASS_PRIVATE:
734 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
735 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
739 /* keep default value */
745 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
747 level_file_info->nr = 0;
748 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
749 level_file_info->packed = FALSE;
750 level_file_info->basename = NULL;
751 level_file_info->filename = NULL;
754 static void ActivateLevelTemplate()
756 /* Currently there is no special action needed to activate the template
757 data, because 'element_info' and 'Properties' overwrite the original
758 level data, while all other variables do not change. */
761 static char *getLevelFilenameFromBasename(char *basename)
763 static char *filename = NULL;
765 checked_free(filename);
767 filename = getPath2(getCurrentLevelDir(), basename);
772 static int getFileTypeFromBasename(char *basename)
774 static char *filename = NULL;
775 struct stat file_status;
777 /* ---------- try to determine file type from filename ---------- */
779 /* check for typical filename of a Supaplex level package file */
780 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
781 strncmp(basename, "LEVELS.D", 8) == 0))
782 return LEVEL_FILE_TYPE_SP;
784 /* ---------- try to determine file type from filesize ---------- */
786 checked_free(filename);
787 filename = getPath2(getCurrentLevelDir(), basename);
789 if (stat(filename, &file_status) == 0)
791 /* check for typical filesize of a Supaplex level package file */
792 if (file_status.st_size == 170496)
793 return LEVEL_FILE_TYPE_SP;
796 return LEVEL_FILE_TYPE_UNKNOWN;
799 static char *getSingleLevelBasename(int nr)
801 static char basename[MAX_FILENAME_LEN];
804 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
806 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
811 static char *getPackedLevelBasename(int type)
813 static char basename[MAX_FILENAME_LEN];
814 char *directory = getCurrentLevelDir();
816 struct dirent *dir_entry;
818 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
820 if ((dir = opendir(directory)) == NULL)
822 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
827 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
829 char *entry_basename = dir_entry->d_name;
830 int entry_type = getFileTypeFromBasename(entry_basename);
832 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
834 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
837 strcpy(basename, entry_basename);
849 static char *getSingleLevelFilename(int nr)
851 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
855 static char *getPackedLevelFilename(int type)
857 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
861 char *getDefaultLevelFilename(int nr)
863 return getSingleLevelFilename(nr);
867 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
872 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
873 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
877 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
878 int type, char *format, ...)
880 static char basename[MAX_FILENAME_LEN];
883 va_start(ap, format);
884 vsprintf(basename, format, ap);
889 lfi->basename = basename;
890 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
893 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
898 lfi->basename = getPackedLevelBasename(lfi->type);
899 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
902 static int getFiletypeFromID(char *filetype_id)
904 char *filetype_id_lower;
905 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
908 if (filetype_id == NULL)
909 return LEVEL_FILE_TYPE_UNKNOWN;
911 filetype_id_lower = getStringToLower(filetype_id);
913 for (i = 0; filetype_id_list[i].id != NULL; i++)
915 char *id_lower = getStringToLower(filetype_id_list[i].id);
917 if (strcmp(filetype_id_lower, id_lower) == 0)
918 filetype = filetype_id_list[i].filetype;
922 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
926 free(filetype_id_lower);
931 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
935 /* special case: level number is negative => check for level template file */
938 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
939 "template.%s", LEVELFILE_EXTENSION);
941 /* no fallback if template file not existing */
945 /* special case: check for file name/pattern specified in "levelinfo.conf" */
946 if (leveldir_current->level_filename != NULL)
948 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
950 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
951 leveldir_current->level_filename, nr);
952 if (fileExists(lfi->filename))
956 /* check for native Rocks'n'Diamonds level file */
957 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
958 "%03d.%s", nr, LEVELFILE_EXTENSION);
959 if (fileExists(lfi->filename))
962 /* check for Emerald Mine level file (V1) */
963 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
964 'a' + (nr / 10) % 26, '0' + nr % 10);
965 if (fileExists(lfi->filename))
967 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
968 'A' + (nr / 10) % 26, '0' + nr % 10);
969 if (fileExists(lfi->filename))
972 /* check for Emerald Mine level file (V2 to V5) */
973 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
974 if (fileExists(lfi->filename))
977 /* check for Emerald Mine level file (V6 / single mode) */
978 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
979 if (fileExists(lfi->filename))
981 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
982 if (fileExists(lfi->filename))
985 /* check for Emerald Mine level file (V6 / teamwork mode) */
986 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
987 if (fileExists(lfi->filename))
989 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
990 if (fileExists(lfi->filename))
993 /* check for various packed level file formats */
994 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
995 if (fileExists(lfi->filename))
998 /* no known level file found -- use default values (and fail later) */
999 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1000 "%03d.%s", nr, LEVELFILE_EXTENSION);
1003 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
1005 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
1006 lfi->type = getFileTypeFromBasename(lfi->basename);
1009 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
1011 /* always start with reliable default values */
1012 setFileInfoToDefaults(level_file_info);
1014 level_file_info->nr = nr; /* set requested level number */
1016 determineLevelFileInfo_Filename(level_file_info);
1017 determineLevelFileInfo_Filetype(level_file_info);
1020 /* ------------------------------------------------------------------------- */
1021 /* functions for loading R'n'D level */
1022 /* ------------------------------------------------------------------------- */
1024 int getMappedElement(int element)
1026 /* remap some (historic, now obsolete) elements */
1030 case EL_PLAYER_OBSOLETE:
1031 element = EL_PLAYER_1;
1034 case EL_KEY_OBSOLETE:
1037 case EL_EM_KEY_1_FILE_OBSOLETE:
1038 element = EL_EM_KEY_1;
1041 case EL_EM_KEY_2_FILE_OBSOLETE:
1042 element = EL_EM_KEY_2;
1045 case EL_EM_KEY_3_FILE_OBSOLETE:
1046 element = EL_EM_KEY_3;
1049 case EL_EM_KEY_4_FILE_OBSOLETE:
1050 element = EL_EM_KEY_4;
1053 case EL_ENVELOPE_OBSOLETE:
1054 element = EL_ENVELOPE_1;
1062 if (element >= NUM_FILE_ELEMENTS)
1064 Error(ERR_WARN, "invalid level element %d", element);
1066 element = EL_UNKNOWN;
1074 int getMappedElementByVersion(int element, int game_version)
1076 /* remap some elements due to certain game version */
1078 if (game_version <= VERSION_IDENT(2,2,0,0))
1080 /* map game font elements */
1081 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1082 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1083 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1084 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1087 if (game_version < VERSION_IDENT(3,0,0,0))
1089 /* map Supaplex gravity tube elements */
1090 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1091 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1092 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1093 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1100 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
1102 level->file_version = getFileVersion(file);
1103 level->game_version = getFileVersion(file);
1108 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
1112 level->fieldx = getFile8Bit(file);
1113 level->fieldy = getFile8Bit(file);
1115 level->time = getFile16BitBE(file);
1116 level->gems_needed = getFile16BitBE(file);
1118 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1119 level->name[i] = getFile8Bit(file);
1120 level->name[MAX_LEVEL_NAME_LEN] = 0;
1122 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1123 level->score[i] = getFile8Bit(file);
1125 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1126 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1127 for (y = 0; y < 3; y++)
1128 for (x = 0; x < 3; x++)
1129 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
1131 level->amoeba_speed = getFile8Bit(file);
1132 level->time_magic_wall = getFile8Bit(file);
1133 level->time_wheel = getFile8Bit(file);
1134 level->amoeba_content = getMappedElement(getFile8Bit(file));
1135 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1136 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1137 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1138 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1140 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1142 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1143 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1144 level->can_move_into_acid_bits = getFile32BitBE(file);
1145 level->dont_collide_with_bits = getFile8Bit(file);
1147 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1148 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1150 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1151 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1152 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1154 level->game_engine_type = getFile8Bit(file);
1156 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
1161 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
1165 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1166 level->author[i] = getFile8Bit(file);
1167 level->author[MAX_LEVEL_NAME_LEN] = 0;
1172 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
1175 int chunk_size_expected = level->fieldx * level->fieldy;
1177 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1178 stored with 16-bit encoding (and should be twice as big then).
1179 Even worse, playfield data was stored 16-bit when only yamyam content
1180 contained 16-bit elements and vice versa. */
1182 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1183 chunk_size_expected *= 2;
1185 if (chunk_size_expected != chunk_size)
1187 ReadUnusedBytesFromFile(file, chunk_size);
1188 return chunk_size_expected;
1191 for (y = 0; y < level->fieldy; y++)
1192 for (x = 0; x < level->fieldx; x++)
1193 level->field[x][y] =
1194 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
1199 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
1202 int header_size = 4;
1203 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
1204 int chunk_size_expected = header_size + content_size;
1206 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1207 stored with 16-bit encoding (and should be twice as big then).
1208 Even worse, playfield data was stored 16-bit when only yamyam content
1209 contained 16-bit elements and vice versa. */
1211 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1212 chunk_size_expected += content_size;
1214 if (chunk_size_expected != chunk_size)
1216 ReadUnusedBytesFromFile(file, chunk_size);
1217 return chunk_size_expected;
1221 level->num_yamyam_contents = getFile8Bit(file);
1225 /* correct invalid number of content fields -- should never happen */
1226 if (level->num_yamyam_contents < 1 ||
1227 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
1228 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1230 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1231 for (y = 0; y < 3; y++)
1232 for (x = 0; x < 3; x++)
1233 level->yamyam_content[i].e[x][y] =
1234 getMappedElement(level->encoding_16bit_field ?
1235 getFile16BitBE(file) : getFile8Bit(file));
1239 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
1243 int num_contents, content_xsize, content_ysize;
1244 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1246 element = getMappedElement(getFile16BitBE(file));
1247 num_contents = getFile8Bit(file);
1248 content_xsize = getFile8Bit(file);
1249 content_ysize = getFile8Bit(file);
1251 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1253 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1254 for (y = 0; y < 3; y++)
1255 for (x = 0; x < 3; x++)
1256 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
1258 /* correct invalid number of content fields -- should never happen */
1259 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
1260 num_contents = STD_ELEMENT_CONTENTS;
1262 if (element == EL_YAMYAM)
1264 level->num_yamyam_contents = num_contents;
1266 for (i = 0; i < num_contents; i++)
1267 for (y = 0; y < 3; y++)
1268 for (x = 0; x < 3; x++)
1269 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
1271 else if (element == EL_BD_AMOEBA)
1273 level->amoeba_content = content_array[0][0][0];
1277 Error(ERR_WARN, "cannot load content for element '%d'", element);
1283 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
1289 int chunk_size_expected;
1291 element = getMappedElement(getFile16BitBE(file));
1292 if (!IS_ENVELOPE(element))
1293 element = EL_ENVELOPE_1;
1295 envelope_nr = element - EL_ENVELOPE_1;
1297 envelope_len = getFile16BitBE(file);
1299 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
1300 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
1302 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1304 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
1305 if (chunk_size_expected != chunk_size)
1307 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
1308 return chunk_size_expected;
1311 for (i = 0; i < envelope_len; i++)
1312 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
1317 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
1319 int num_changed_custom_elements = getFile16BitBE(file);
1320 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
1323 if (chunk_size_expected != chunk_size)
1325 ReadUnusedBytesFromFile(file, chunk_size - 2);
1326 return chunk_size_expected;
1329 for (i = 0; i < num_changed_custom_elements; i++)
1331 int element = getFile16BitBE(file);
1332 int properties = getFile32BitBE(file);
1334 if (IS_CUSTOM_ELEMENT(element))
1335 Properties[element][EP_BITFIELD_BASE] = properties;
1337 Error(ERR_WARN, "invalid custom element number %d", element);
1343 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
1345 int num_changed_custom_elements = getFile16BitBE(file);
1346 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
1349 if (chunk_size_expected != chunk_size)
1351 ReadUnusedBytesFromFile(file, chunk_size - 2);
1352 return chunk_size_expected;
1355 for (i = 0; i < num_changed_custom_elements; i++)
1357 int element = getFile16BitBE(file);
1358 int custom_target_element = getFile16BitBE(file);
1360 if (IS_CUSTOM_ELEMENT(element))
1361 element_info[element].change->target_element = custom_target_element;
1363 Error(ERR_WARN, "invalid custom element number %d", element);
1369 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
1371 int num_changed_custom_elements = getFile16BitBE(file);
1372 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1375 if (chunk_size_expected != chunk_size)
1377 ReadUnusedBytesFromFile(file, chunk_size - 2);
1378 return chunk_size_expected;
1381 for (i = 0; i < num_changed_custom_elements; i++)
1383 int element = getFile16BitBE(file);
1384 struct ElementInfo *ei = &element_info[element];
1385 unsigned long event_bits;
1387 if (!IS_CUSTOM_ELEMENT(element))
1389 Error(ERR_WARN, "invalid custom element number %d", element);
1391 element = EL_INTERNAL_DUMMY;
1394 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1395 ei->description[j] = getFile8Bit(file);
1396 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1398 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1400 /* some free bytes for future properties and padding */
1401 ReadUnusedBytesFromFile(file, 7);
1403 ei->use_gfx_element = getFile8Bit(file);
1404 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1406 ei->collect_score_initial = getFile8Bit(file);
1407 ei->collect_count_initial = getFile8Bit(file);
1409 ei->push_delay_fixed = getFile16BitBE(file);
1410 ei->push_delay_random = getFile16BitBE(file);
1411 ei->move_delay_fixed = getFile16BitBE(file);
1412 ei->move_delay_random = getFile16BitBE(file);
1414 ei->move_pattern = getFile16BitBE(file);
1415 ei->move_direction_initial = getFile8Bit(file);
1416 ei->move_stepsize = getFile8Bit(file);
1418 for (y = 0; y < 3; y++)
1419 for (x = 0; x < 3; x++)
1420 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1422 event_bits = getFile32BitBE(file);
1423 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1424 if (event_bits & (1 << j))
1425 ei->change->has_event[j] = TRUE;
1427 ei->change->target_element = getMappedElement(getFile16BitBE(file));
1429 ei->change->delay_fixed = getFile16BitBE(file);
1430 ei->change->delay_random = getFile16BitBE(file);
1431 ei->change->delay_frames = getFile16BitBE(file);
1433 ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
1435 ei->change->explode = getFile8Bit(file);
1436 ei->change->use_target_content = getFile8Bit(file);
1437 ei->change->only_if_complete = getFile8Bit(file);
1438 ei->change->use_random_replace = getFile8Bit(file);
1440 ei->change->random_percentage = getFile8Bit(file);
1441 ei->change->replace_when = getFile8Bit(file);
1443 for (y = 0; y < 3; y++)
1444 for (x = 0; x < 3; x++)
1445 ei->change->target_content.e[x][y] =
1446 getMappedElement(getFile16BitBE(file));
1448 ei->slippery_type = getFile8Bit(file);
1450 /* some free bytes for future properties and padding */
1451 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1453 /* mark that this custom element has been modified */
1454 ei->modified_settings = TRUE;
1460 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1462 struct ElementInfo *ei;
1463 int chunk_size_expected;
1467 /* ---------- custom element base property values (96 bytes) ------------- */
1469 element = getFile16BitBE(file);
1471 if (!IS_CUSTOM_ELEMENT(element))
1473 Error(ERR_WARN, "invalid custom element number %d", element);
1475 ReadUnusedBytesFromFile(file, chunk_size - 2);
1479 ei = &element_info[element];
1481 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1482 ei->description[i] = getFile8Bit(file);
1483 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1485 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1486 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1488 ei->num_change_pages = getFile8Bit(file);
1490 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1491 if (chunk_size_expected != chunk_size)
1493 ReadUnusedBytesFromFile(file, chunk_size - 43);
1494 return chunk_size_expected;
1497 ei->ce_value_fixed_initial = getFile16BitBE(file);
1498 ei->ce_value_random_initial = getFile16BitBE(file);
1499 ei->use_last_ce_value = getFile8Bit(file);
1501 ei->use_gfx_element = getFile8Bit(file);
1502 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1504 ei->collect_score_initial = getFile8Bit(file);
1505 ei->collect_count_initial = getFile8Bit(file);
1507 ei->drop_delay_fixed = getFile8Bit(file);
1508 ei->push_delay_fixed = getFile8Bit(file);
1509 ei->drop_delay_random = getFile8Bit(file);
1510 ei->push_delay_random = getFile8Bit(file);
1511 ei->move_delay_fixed = getFile16BitBE(file);
1512 ei->move_delay_random = getFile16BitBE(file);
1514 /* bits 0 - 15 of "move_pattern" ... */
1515 ei->move_pattern = getFile16BitBE(file);
1516 ei->move_direction_initial = getFile8Bit(file);
1517 ei->move_stepsize = getFile8Bit(file);
1519 ei->slippery_type = getFile8Bit(file);
1521 for (y = 0; y < 3; y++)
1522 for (x = 0; x < 3; x++)
1523 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1525 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1526 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1527 ei->move_leave_type = getFile8Bit(file);
1529 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1530 ei->move_pattern |= (getFile16BitBE(file) << 16);
1532 ei->access_direction = getFile8Bit(file);
1534 ei->explosion_delay = getFile8Bit(file);
1535 ei->ignition_delay = getFile8Bit(file);
1536 ei->explosion_type = getFile8Bit(file);
1538 /* some free bytes for future custom property values and padding */
1539 ReadUnusedBytesFromFile(file, 1);
1541 /* ---------- change page property values (48 bytes) --------------------- */
1543 setElementChangePages(ei, ei->num_change_pages);
1545 for (i = 0; i < ei->num_change_pages; i++)
1547 struct ElementChangeInfo *change = &ei->change_page[i];
1548 unsigned long event_bits;
1550 /* always start with reliable default values */
1551 setElementChangeInfoToDefaults(change);
1553 /* bits 0 - 31 of "has_event[]" ... */
1554 event_bits = getFile32BitBE(file);
1555 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
1556 if (event_bits & (1 << j))
1557 change->has_event[j] = TRUE;
1559 change->target_element = getMappedElement(getFile16BitBE(file));
1561 change->delay_fixed = getFile16BitBE(file);
1562 change->delay_random = getFile16BitBE(file);
1563 change->delay_frames = getFile16BitBE(file);
1565 change->trigger_element = getMappedElement(getFile16BitBE(file));
1567 change->explode = getFile8Bit(file);
1568 change->use_target_content = getFile8Bit(file);
1569 change->only_if_complete = getFile8Bit(file);
1570 change->use_random_replace = getFile8Bit(file);
1572 change->random_percentage = getFile8Bit(file);
1573 change->replace_when = getFile8Bit(file);
1575 for (y = 0; y < 3; y++)
1576 for (x = 0; x < 3; x++)
1577 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
1579 change->can_change = getFile8Bit(file);
1581 change->trigger_side = getFile8Bit(file);
1583 change->trigger_player = getFile8Bit(file);
1584 change->trigger_page = getFile8Bit(file);
1586 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1587 CH_PAGE_ANY : (1 << change->trigger_page));
1589 change->has_action = getFile8Bit(file);
1590 change->action_type = getFile8Bit(file);
1591 change->action_mode = getFile8Bit(file);
1592 change->action_arg = getFile16BitBE(file);
1594 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
1595 event_bits = getFile8Bit(file);
1596 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
1597 if (event_bits & (1 << (j - 32)))
1598 change->has_event[j] = TRUE;
1601 /* mark this custom element as modified */
1602 ei->modified_settings = TRUE;
1607 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1609 struct ElementInfo *ei;
1610 struct ElementGroupInfo *group;
1614 element = getFile16BitBE(file);
1616 if (!IS_GROUP_ELEMENT(element))
1618 Error(ERR_WARN, "invalid group element number %d", element);
1620 ReadUnusedBytesFromFile(file, chunk_size - 2);
1624 ei = &element_info[element];
1626 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1627 ei->description[i] = getFile8Bit(file);
1628 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1630 group = element_info[element].group;
1632 group->num_elements = getFile8Bit(file);
1634 ei->use_gfx_element = getFile8Bit(file);
1635 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1637 group->choice_mode = getFile8Bit(file);
1639 /* some free bytes for future values and padding */
1640 ReadUnusedBytesFromFile(file, 3);
1642 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1643 group->element[i] = getMappedElement(getFile16BitBE(file));
1645 /* mark this group element as modified */
1646 element_info[element].modified_settings = TRUE;
1651 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
1653 int real_chunk_size = 0;
1658 int element = getFile16BitBE(file);
1659 int type = getFile8Bit(file);
1660 int bytes = type & CONF_MASK_BYTES;
1661 boolean element_found = FALSE;
1663 real_chunk_size += 3;
1665 li = *level; /* copy level information into temporary buffer */
1667 if (bytes == CONF_MASK_MULTI_BYTES)
1669 int num_bytes = getFile16BitBE(file);
1670 byte *buffer = checked_malloc(num_bytes);
1672 ReadBytesFromFile(file, buffer, num_bytes);
1674 for (i = 0; element_conf[i].element != -1; i++)
1676 if (element_conf[i].element == element &&
1677 element_conf[i].type == type)
1679 element_found = TRUE;
1681 if (type == CONF_VALUE_CONTENT_8)
1683 struct Content *content= (struct Content *)(element_conf[i].value);
1684 int num_contents = num_bytes / CONF_CONTENT_NUM_BYTES;
1687 for (c = 0; c < num_contents; c++)
1688 for (y = 0; y < 3; y++)
1689 for (x = 0; x < 3; x++)
1690 content[c].e[x][y] =
1691 getMappedElement(CONF_CONTENT_ELEMENT(buffer, c, x, y));
1694 element_found = FALSE;
1700 checked_free(buffer);
1702 real_chunk_size += 2 + num_bytes;
1706 int value = (bytes == CONF_MASK_1_BYTE ? getFile8Bit (file) :
1707 bytes == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
1708 bytes == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
1710 for (i = 0; element_conf[i].element != -1; i++)
1712 if (element_conf[i].element == element &&
1713 element_conf[i].type == type)
1715 if (CONF_VALUE_BOOLEAN(type))
1716 *(boolean *)(element_conf[i].value) = value;
1718 *(int *) (element_conf[i].value) = value;
1720 element_found = TRUE;
1726 real_chunk_size += CONF_VALUE_NUM_BYTES(bytes);
1729 *level = li; /* copy temporary buffer back to level information */
1732 Error(ERR_WARN, "cannot load CONF value for element %d", element);
1734 if (type == CONF_LAST_ENTRY || real_chunk_size >= chunk_size)
1738 return real_chunk_size;
1741 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1742 struct LevelFileInfo *level_file_info)
1744 char *filename = level_file_info->filename;
1745 char cookie[MAX_LINE_LEN];
1746 char chunk_name[CHUNK_ID_LEN + 1];
1750 if (!(file = fopen(filename, MODE_READ)))
1752 level->no_valid_file = TRUE;
1754 if (level != &level_template)
1755 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1760 getFileChunkBE(file, chunk_name, NULL);
1761 if (strcmp(chunk_name, "RND1") == 0)
1763 getFile32BitBE(file); /* not used */
1765 getFileChunkBE(file, chunk_name, NULL);
1766 if (strcmp(chunk_name, "CAVE") != 0)
1768 level->no_valid_file = TRUE;
1770 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1775 else /* check for pre-2.0 file format with cookie string */
1777 strcpy(cookie, chunk_name);
1778 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1779 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1780 cookie[strlen(cookie) - 1] = '\0';
1782 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1784 level->no_valid_file = TRUE;
1786 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1791 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1793 level->no_valid_file = TRUE;
1795 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1800 /* pre-2.0 level files have no game version, so use file version here */
1801 level->game_version = level->file_version;
1804 if (level->file_version < FILE_VERSION_1_2)
1806 /* level files from versions before 1.2.0 without chunk structure */
1807 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1808 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1816 int (*loader)(FILE *, int, struct LevelInfo *);
1820 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1821 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1822 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1823 { "BODY", -1, LoadLevel_BODY },
1824 { "CONT", -1, LoadLevel_CONT },
1825 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1826 { "CNT3", -1, LoadLevel_CNT3 },
1827 { "CUS1", -1, LoadLevel_CUS1 },
1828 { "CUS2", -1, LoadLevel_CUS2 },
1829 { "CUS3", -1, LoadLevel_CUS3 },
1830 { "CUS4", -1, LoadLevel_CUS4 },
1831 { "GRP1", -1, LoadLevel_GRP1 },
1832 { "CONF", -1, LoadLevel_CONF },
1837 while (getFileChunkBE(file, chunk_name, &chunk_size))
1841 while (chunk_info[i].name != NULL &&
1842 strcmp(chunk_name, chunk_info[i].name) != 0)
1845 if (chunk_info[i].name == NULL)
1847 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1848 chunk_name, filename);
1849 ReadUnusedBytesFromFile(file, chunk_size);
1851 else if (chunk_info[i].size != -1 &&
1852 chunk_info[i].size != chunk_size)
1854 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1855 chunk_size, chunk_name, filename);
1856 ReadUnusedBytesFromFile(file, chunk_size);
1860 /* call function to load this level chunk */
1861 int chunk_size_expected =
1862 (chunk_info[i].loader)(file, chunk_size, level);
1864 /* the size of some chunks cannot be checked before reading other
1865 chunks first (like "HEAD" and "BODY") that contain some header
1866 information, so check them here */
1867 if (chunk_size_expected != chunk_size)
1869 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1870 chunk_size, chunk_name, filename);
1879 /* ------------------------------------------------------------------------- */
1880 /* functions for loading EM level */
1881 /* ------------------------------------------------------------------------- */
1885 static int map_em_element_yam(int element)
1889 case 0x00: return EL_EMPTY;
1890 case 0x01: return EL_EMERALD;
1891 case 0x02: return EL_DIAMOND;
1892 case 0x03: return EL_ROCK;
1893 case 0x04: return EL_ROBOT;
1894 case 0x05: return EL_SPACESHIP_UP;
1895 case 0x06: return EL_BOMB;
1896 case 0x07: return EL_BUG_UP;
1897 case 0x08: return EL_AMOEBA_DROP;
1898 case 0x09: return EL_NUT;
1899 case 0x0a: return EL_YAMYAM;
1900 case 0x0b: return EL_QUICKSAND_FULL;
1901 case 0x0c: return EL_SAND;
1902 case 0x0d: return EL_WALL_SLIPPERY;
1903 case 0x0e: return EL_STEELWALL;
1904 case 0x0f: return EL_WALL;
1905 case 0x10: return EL_EM_KEY_1;
1906 case 0x11: return EL_EM_KEY_2;
1907 case 0x12: return EL_EM_KEY_4;
1908 case 0x13: return EL_EM_KEY_3;
1909 case 0x14: return EL_MAGIC_WALL;
1910 case 0x15: return EL_ROBOT_WHEEL;
1911 case 0x16: return EL_DYNAMITE;
1913 case 0x17: return EL_EM_KEY_1; /* EMC */
1914 case 0x18: return EL_BUG_UP; /* EMC */
1915 case 0x1a: return EL_DIAMOND; /* EMC */
1916 case 0x1b: return EL_EMERALD; /* EMC */
1917 case 0x25: return EL_NUT; /* EMC */
1918 case 0x80: return EL_EMPTY; /* EMC */
1919 case 0x85: return EL_EM_KEY_1; /* EMC */
1920 case 0x86: return EL_EM_KEY_2; /* EMC */
1921 case 0x87: return EL_EM_KEY_4; /* EMC */
1922 case 0x88: return EL_EM_KEY_3; /* EMC */
1923 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1924 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1925 case 0xaf: return EL_DYNAMITE; /* EMC */
1926 case 0xbd: return EL_SAND; /* EMC */
1929 Error(ERR_WARN, "invalid level element %d", element);
1934 static int map_em_element_field(int element)
1936 if (element >= 0xc8 && element <= 0xe1)
1937 return EL_CHAR_A + (element - 0xc8);
1938 else if (element >= 0xe2 && element <= 0xeb)
1939 return EL_CHAR_0 + (element - 0xe2);
1943 case 0x00: return EL_ROCK;
1944 case 0x01: return EL_ROCK; /* EMC */
1945 case 0x02: return EL_DIAMOND;
1946 case 0x03: return EL_DIAMOND;
1947 case 0x04: return EL_ROBOT;
1948 case 0x05: return EL_ROBOT; /* EMC */
1949 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1950 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1951 case 0x08: return EL_SPACESHIP_UP;
1952 case 0x09: return EL_SPACESHIP_RIGHT;
1953 case 0x0a: return EL_SPACESHIP_DOWN;
1954 case 0x0b: return EL_SPACESHIP_LEFT;
1955 case 0x0c: return EL_SPACESHIP_UP;
1956 case 0x0d: return EL_SPACESHIP_RIGHT;
1957 case 0x0e: return EL_SPACESHIP_DOWN;
1958 case 0x0f: return EL_SPACESHIP_LEFT;
1960 case 0x10: return EL_BOMB;
1961 case 0x11: return EL_BOMB; /* EMC */
1962 case 0x12: return EL_EMERALD;
1963 case 0x13: return EL_EMERALD;
1964 case 0x14: return EL_BUG_UP;
1965 case 0x15: return EL_BUG_RIGHT;
1966 case 0x16: return EL_BUG_DOWN;
1967 case 0x17: return EL_BUG_LEFT;
1968 case 0x18: return EL_BUG_UP;
1969 case 0x19: return EL_BUG_RIGHT;
1970 case 0x1a: return EL_BUG_DOWN;
1971 case 0x1b: return EL_BUG_LEFT;
1972 case 0x1c: return EL_AMOEBA_DROP;
1973 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1974 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1975 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1977 case 0x20: return EL_ROCK;
1978 case 0x21: return EL_BOMB; /* EMC */
1979 case 0x22: return EL_DIAMOND; /* EMC */
1980 case 0x23: return EL_EMERALD; /* EMC */
1981 case 0x24: return EL_MAGIC_WALL;
1982 case 0x25: return EL_NUT;
1983 case 0x26: return EL_NUT; /* EMC */
1984 case 0x27: return EL_NUT; /* EMC */
1986 /* looks like magic wheel, but is _always_ activated */
1987 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1989 case 0x29: return EL_YAMYAM; /* up */
1990 case 0x2a: return EL_YAMYAM; /* down */
1991 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1992 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1993 case 0x2d: return EL_QUICKSAND_FULL;
1994 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1995 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1997 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1998 case 0x31: return EL_SAND; /* EMC */
1999 case 0x32: return EL_SAND; /* EMC */
2000 case 0x33: return EL_SAND; /* EMC */
2001 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
2002 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
2003 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
2004 case 0x37: return EL_SAND; /* EMC */
2005 case 0x38: return EL_ROCK; /* EMC */
2006 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
2007 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
2008 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
2009 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
2010 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
2011 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
2012 case 0x3f: return EL_ACID_POOL_BOTTOM;
2014 case 0x40: return EL_EXIT_OPEN; /* 1 */
2015 case 0x41: return EL_EXIT_OPEN; /* 2 */
2016 case 0x42: return EL_EXIT_OPEN; /* 3 */
2017 case 0x43: return EL_BALLOON; /* EMC */
2018 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
2019 case 0x45: return EL_SPRING; /* EMC */
2020 case 0x46: return EL_SPRING; /* falling */ /* EMC */
2021 case 0x47: return EL_SPRING; /* left */ /* EMC */
2022 case 0x48: return EL_SPRING; /* right */ /* EMC */
2023 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
2024 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
2025 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
2026 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
2027 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
2028 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
2029 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
2031 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
2032 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
2033 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
2034 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
2035 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
2036 case 0x55: return EL_EMPTY_SPACE; /* EMC */
2037 case 0x56: return EL_EMPTY_SPACE; /* EMC */
2038 case 0x57: return EL_EMPTY_SPACE; /* EMC */
2039 case 0x58: return EL_EMPTY_SPACE; /* EMC */
2040 case 0x59: return EL_EMPTY_SPACE; /* EMC */
2041 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
2042 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
2043 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
2044 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
2045 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
2046 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
2048 case 0x60: return EL_EMPTY_SPACE; /* EMC */
2049 case 0x61: return EL_EMPTY_SPACE; /* EMC */
2050 case 0x62: return EL_EMPTY_SPACE; /* EMC */
2051 case 0x63: return EL_SPRING; /* left */ /* EMC */
2052 case 0x64: return EL_SPRING; /* right */ /* EMC */
2053 case 0x65: return EL_ACID; /* 1 */ /* EMC */
2054 case 0x66: return EL_ACID; /* 2 */ /* EMC */
2055 case 0x67: return EL_ACID; /* 3 */ /* EMC */
2056 case 0x68: return EL_ACID; /* 4 */ /* EMC */
2057 case 0x69: return EL_ACID; /* 5 */ /* EMC */
2058 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
2059 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
2060 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
2061 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
2062 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
2063 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
2065 case 0x70: return EL_EMPTY_SPACE; /* EMC */
2066 case 0x71: return EL_EMPTY_SPACE; /* EMC */
2067 case 0x72: return EL_NUT; /* left */ /* EMC */
2068 case 0x73: return EL_SAND; /* EMC (? "nut") */
2069 case 0x74: return EL_STEELWALL;
2070 case 0x75: return EL_EMPTY_SPACE; /* EMC */
2071 case 0x76: return EL_EMPTY_SPACE; /* EMC */
2072 case 0x77: return EL_BOMB; /* left */ /* EMC */
2073 case 0x78: return EL_BOMB; /* right */ /* EMC */
2074 case 0x79: return EL_ROCK; /* left */ /* EMC */
2075 case 0x7a: return EL_ROCK; /* right */ /* EMC */
2076 case 0x7b: return EL_ACID; /* (? EMC "blank") */
2077 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
2078 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
2079 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
2080 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
2082 case 0x80: return EL_EMPTY;
2083 case 0x81: return EL_WALL_SLIPPERY;
2084 case 0x82: return EL_SAND;
2085 case 0x83: return EL_STEELWALL;
2086 case 0x84: return EL_WALL;
2087 case 0x85: return EL_EM_KEY_1;
2088 case 0x86: return EL_EM_KEY_2;
2089 case 0x87: return EL_EM_KEY_4;
2090 case 0x88: return EL_EM_KEY_3;
2091 case 0x89: return EL_EM_GATE_1;
2092 case 0x8a: return EL_EM_GATE_2;
2093 case 0x8b: return EL_EM_GATE_4;
2094 case 0x8c: return EL_EM_GATE_3;
2095 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
2096 case 0x8e: return EL_EM_GATE_1_GRAY;
2097 case 0x8f: return EL_EM_GATE_2_GRAY;
2099 case 0x90: return EL_EM_GATE_4_GRAY;
2100 case 0x91: return EL_EM_GATE_3_GRAY;
2101 case 0x92: return EL_MAGIC_WALL;
2102 case 0x93: return EL_ROBOT_WHEEL;
2103 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
2104 case 0x95: return EL_ACID_POOL_TOPLEFT;
2105 case 0x96: return EL_ACID_POOL_TOPRIGHT;
2106 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
2107 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
2108 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
2109 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
2110 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
2111 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
2112 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
2113 case 0x9e: return EL_EXIT_CLOSED;
2114 case 0x9f: return EL_CHAR_LESS; /* arrow left */
2116 /* looks like normal sand, but behaves like wall */
2117 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
2118 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
2119 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
2120 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
2121 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
2122 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
2123 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
2124 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
2125 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
2126 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
2127 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
2128 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
2129 case 0xac: return EL_CHAR_COMMA; /* EMC */
2130 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
2131 case 0xae: return EL_CHAR_MINUS; /* EMC */
2132 case 0xaf: return EL_DYNAMITE;
2134 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
2135 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
2136 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
2137 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
2138 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
2139 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
2140 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
2141 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
2142 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
2143 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
2144 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
2145 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
2146 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
2147 case 0xbd: return EL_SAND; /* EMC ("dirt") */
2148 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
2149 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
2151 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
2152 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
2153 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
2154 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
2155 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
2156 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
2157 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
2158 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
2160 /* characters: see above */
2162 case 0xec: return EL_CHAR_PERIOD;
2163 case 0xed: return EL_CHAR_EXCLAM;
2164 case 0xee: return EL_CHAR_COLON;
2165 case 0xef: return EL_CHAR_QUESTION;
2167 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
2168 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
2169 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
2170 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
2171 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
2172 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
2173 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
2174 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
2176 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
2177 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
2178 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
2179 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
2180 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
2181 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
2183 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
2184 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
2187 /* should never happen (all 8-bit value cases should be handled) */
2188 Error(ERR_WARN, "invalid level element %d", element);
2193 #define EM_LEVEL_SIZE 2106
2194 #define EM_LEVEL_XSIZE 64
2195 #define EM_LEVEL_YSIZE 32
2197 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2198 struct LevelFileInfo *level_file_info)
2200 char *filename = level_file_info->filename;
2202 unsigned char leveldata[EM_LEVEL_SIZE];
2203 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
2204 int nr = level_file_info->nr;
2207 if (!(file = fopen(filename, MODE_READ)))
2209 level->no_valid_file = TRUE;
2211 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2216 for (i = 0; i < EM_LEVEL_SIZE; i++)
2217 leveldata[i] = fgetc(file);
2221 /* check if level data is crypted by testing against known starting bytes
2222 of the few existing crypted level files (from Emerald Mine 1 + 2) */
2224 if ((leveldata[0] == 0xf1 ||
2225 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
2227 unsigned char code0 = 0x65;
2228 unsigned char code1 = 0x11;
2230 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
2231 leveldata[0] = 0xf1;
2233 /* decode crypted level data */
2235 for (i = 0; i < EM_LEVEL_SIZE; i++)
2237 leveldata[i] ^= code0;
2238 leveldata[i] -= code1;
2240 code0 = (code0 + 7) & 0xff;
2244 level->fieldx = EM_LEVEL_XSIZE;
2245 level->fieldy = EM_LEVEL_YSIZE;
2247 level->time = header[46] * 10;
2248 level->gems_needed = header[47];
2250 /* The original Emerald Mine levels have their level number stored
2251 at the second byte of the level file...
2252 Do not trust this information at other level files, e.g. EMC,
2253 but correct it anyway (normally the first row is completely
2254 steel wall, so the correction does not hurt anyway). */
2256 if (leveldata[1] == nr)
2257 leveldata[1] = leveldata[2]; /* correct level number field */
2259 sprintf(level->name, "Level %d", nr); /* set level name */
2261 level->score[SC_EMERALD] = header[36];
2262 level->score[SC_DIAMOND] = header[37];
2263 level->score[SC_ROBOT] = header[38];
2264 level->score[SC_SPACESHIP] = header[39];
2265 level->score[SC_BUG] = header[40];
2266 level->score[SC_YAMYAM] = header[41];
2267 level->score[SC_NUT] = header[42];
2268 level->score[SC_DYNAMITE] = header[43];
2269 level->score[SC_TIME_BONUS] = header[44];
2271 level->num_yamyam_contents = 4;
2273 for (i = 0; i < level->num_yamyam_contents; i++)
2274 for (y = 0; y < 3; y++)
2275 for (x = 0; x < 3; x++)
2276 level->yamyam_content[i].e[x][y] =
2277 map_em_element_yam(header[i * 9 + y * 3 + x]);
2279 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
2280 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
2281 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
2282 level->amoeba_content = EL_DIAMOND;
2284 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2286 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
2288 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
2289 new_element = EL_AMOEBA_WET;
2291 level->field[x][y] = new_element;
2294 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
2295 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
2296 level->field[x][y] = EL_PLAYER_1;
2298 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
2299 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
2300 level->field[x][y] = EL_PLAYER_2;
2305 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
2307 static int ball_xy[8][2] =
2318 struct LevelInfo_EM *level_em = level->native_em_level;
2319 struct LEVEL *lev = level_em->lev;
2320 struct PLAYER *ply1 = level_em->ply1;
2321 struct PLAYER *ply2 = level_em->ply2;
2324 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
2325 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
2327 lev->time_seconds = level->time;
2328 lev->required_initial = level->gems_needed;
2330 lev->emerald_score = level->score[SC_EMERALD];
2331 lev->diamond_score = level->score[SC_DIAMOND];
2332 lev->alien_score = level->score[SC_ROBOT];
2333 lev->tank_score = level->score[SC_SPACESHIP];
2334 lev->bug_score = level->score[SC_BUG];
2335 lev->eater_score = level->score[SC_YAMYAM];
2336 lev->nut_score = level->score[SC_NUT];
2337 lev->dynamite_score = level->score[SC_DYNAMITE];
2338 lev->key_score = level->score[SC_KEY];
2339 lev->exit_score = level->score[SC_TIME_BONUS];
2341 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2342 for (y = 0; y < 3; y++)
2343 for (x = 0; x < 3; x++)
2344 lev->eater_array[i][y * 3 + x] =
2345 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
2347 lev->amoeba_time = level->amoeba_speed;
2348 lev->wonderwall_time_initial = level->time_magic_wall;
2349 lev->wheel_time = level->time_wheel;
2351 lev->android_move_time = level->android_move_time;
2352 lev->android_clone_time = level->android_clone_time;
2353 lev->ball_random = level->ball_random;
2354 lev->ball_state_initial = level->ball_state_initial;
2355 lev->ball_time = level->ball_time;
2356 lev->num_ball_arrays = level->num_ball_contents;
2358 lev->lenses_score = level->lenses_score;
2359 lev->magnify_score = level->magnify_score;
2360 lev->slurp_score = level->slurp_score;
2362 lev->lenses_time = level->lenses_time;
2363 lev->magnify_time = level->magnify_time;
2365 lev->wind_direction_initial =
2366 map_direction_RND_to_EM(level->wind_direction_initial);
2367 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
2368 lev->wind_time : 0);
2370 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2371 for (j = 0; j < 8; j++)
2372 lev->ball_array[i][j] =
2373 map_element_RND_to_EM(level->
2374 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
2376 for (i = 0; i < 16; i++)
2377 lev->android_array[i] = FALSE; /* !!! YET TO COME !!! */
2379 /* first fill the complete playfield with the default border element */
2380 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
2381 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
2382 level_em->cave[x][y] = ZBORDER;
2384 /* then copy the real level contents from level file into the playfield */
2385 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2387 int new_element = map_element_RND_to_EM(level->field[x][y]);
2389 if (level->field[x][y] == EL_AMOEBA_DEAD)
2390 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
2392 level_em->cave[x + 1][y + 1] = new_element;
2395 ply1->x_initial = 0;
2396 ply1->y_initial = 0;
2398 ply2->x_initial = 0;
2399 ply2->y_initial = 0;
2401 /* initialize player positions and delete players from the playfield */
2402 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2405 /* !!! CURRENTLY ONLY SUPPORT FOR ONE PLAYER !!! */
2406 if (ELEM_IS_PLAYER(level->field[x][y]))
2408 ply1->x_initial = x + 1;
2409 ply1->y_initial = y + 1;
2410 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2413 /* !!! ADD SUPPORT FOR MORE THAN ONE PLAYER !!! */
2414 if (level->field[x][y] == EL_PLAYER_1)
2416 ply1->x_initial = x + 1;
2417 ply1->y_initial = y + 1;
2418 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2420 else if (level->field[x][y] == EL_PLAYER_2)
2422 ply2->x_initial = x + 1;
2423 ply2->y_initial = y + 1;
2424 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2430 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
2432 static int ball_xy[8][2] =
2443 struct LevelInfo_EM *level_em = level->native_em_level;
2444 struct LEVEL *lev = level_em->lev;
2445 struct PLAYER *ply1 = level_em->ply1;
2446 struct PLAYER *ply2 = level_em->ply2;
2449 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
2450 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
2452 level->time = lev->time_seconds;
2453 level->gems_needed = lev->required_initial;
2455 sprintf(level->name, "Level %d", level->file_info.nr);
2457 level->score[SC_EMERALD] = lev->emerald_score;
2458 level->score[SC_DIAMOND] = lev->diamond_score;
2459 level->score[SC_ROBOT] = lev->alien_score;
2460 level->score[SC_SPACESHIP] = lev->tank_score;
2461 level->score[SC_BUG] = lev->bug_score;
2462 level->score[SC_YAMYAM] = lev->eater_score;
2463 level->score[SC_NUT] = lev->nut_score;
2464 level->score[SC_DYNAMITE] = lev->dynamite_score;
2465 level->score[SC_KEY] = lev->key_score;
2466 level->score[SC_TIME_BONUS] = lev->exit_score;
2468 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
2470 for (i = 0; i < level->num_yamyam_contents; i++)
2471 for (y = 0; y < 3; y++)
2472 for (x = 0; x < 3; x++)
2473 level->yamyam_content[i].e[x][y] =
2474 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
2476 level->amoeba_speed = lev->amoeba_time;
2477 level->time_magic_wall = lev->wonderwall_time_initial;
2478 level->time_wheel = lev->wheel_time;
2480 level->android_move_time = lev->android_move_time;
2481 level->android_clone_time = lev->android_clone_time;
2482 level->ball_random = lev->ball_random;
2483 level->ball_state_initial = lev->ball_state_initial;
2484 level->ball_time = lev->ball_time;
2485 level->num_ball_contents = lev->num_ball_arrays;
2487 level->lenses_score = lev->lenses_score;
2488 level->magnify_score = lev->magnify_score;
2489 level->slurp_score = lev->slurp_score;
2491 level->lenses_time = lev->lenses_time;
2492 level->magnify_time = lev->magnify_time;
2494 level->wind_direction_initial =
2495 map_direction_EM_to_RND(lev->wind_direction_initial);
2497 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2498 for (j = 0; j < 8; j++)
2499 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
2500 map_element_EM_to_RND(lev->ball_array[i][j]);
2502 for (i = 0; i < 16; i++)
2503 level->android_array[i] = FALSE; /* !!! YET TO COME !!! */
2505 /* convert the playfield (some elements need special treatment) */
2506 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2508 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
2510 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
2511 new_element = EL_AMOEBA_DEAD;
2513 level->field[x][y] = new_element;
2516 /* in case of both players set to the same field, use the first player */
2517 level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
2518 level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
2521 printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
2525 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2526 struct LevelFileInfo *level_file_info)
2528 if (!LoadNativeLevel_EM(level_file_info->filename))
2529 level->no_valid_file = TRUE;
2532 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
2534 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2535 CopyNativeLevel_RND_to_EM(level);
2538 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
2540 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2541 CopyNativeLevel_EM_to_RND(level);
2545 /* ------------------------------------------------------------------------- */
2546 /* functions for loading SP level */
2547 /* ------------------------------------------------------------------------- */
2549 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
2550 #define SP_LEVEL_SIZE 1536
2551 #define SP_LEVEL_XSIZE 60
2552 #define SP_LEVEL_YSIZE 24
2553 #define SP_LEVEL_NAME_LEN 23
2555 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
2558 int num_special_ports;
2561 /* for details of the Supaplex level format, see Herman Perk's Supaplex
2562 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
2564 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
2565 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2567 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2569 int element_old = fgetc(file);
2572 if (element_old <= 0x27)
2573 element_new = getMappedElement(EL_SP_START + element_old);
2574 else if (element_old == 0x28)
2575 element_new = EL_INVISIBLE_WALL;
2578 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
2579 Error(ERR_WARN, "invalid level element %d", element_old);
2581 element_new = EL_UNKNOWN;
2584 level->field[x][y] = element_new;
2588 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2590 /* initial gravity: 1 == "on", anything else (0) == "off" */
2591 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
2593 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2595 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
2596 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
2597 level->name[i] = fgetc(file);
2598 level->name[SP_LEVEL_NAME_LEN] = '\0';
2600 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
2601 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2603 /* number of infotrons needed; 0 means that Supaplex will count the total
2604 amount of infotrons in the level and use the low byte of that number
2605 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
2606 level->gems_needed = fgetc(file);
2608 /* number of special ("gravity") port entries below (maximum 10 allowed) */
2609 num_special_ports = fgetc(file);
2611 /* database of properties of up to 10 special ports (6 bytes per port) */
2612 for (i = 0; i < 10; i++)
2614 int port_location, port_x, port_y, port_element;
2617 /* high and low byte of the location of a special port; if (x, y) are the
2618 coordinates of a port in the field and (0, 0) is the top-left corner,
2619 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
2620 of what may be expected: Supaplex works with a game field in memory
2621 which is 2 bytes per tile) */
2622 port_location = getFile16BitBE(file);
2624 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
2625 gravity = fgetc(file);
2627 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
2628 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2630 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
2631 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2633 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2635 if (i >= num_special_ports)
2638 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2639 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2641 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2642 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2644 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2650 port_element = level->field[port_x][port_y];
2652 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2653 port_element > EL_SP_GRAVITY_PORT_UP)
2655 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2660 /* change previous (wrong) gravity inverting special port to either
2661 gravity enabling special port or gravity disabling special port */
2662 level->field[port_x][port_y] +=
2663 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2664 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2667 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2669 /* change special gravity ports without database entries to normal ports */
2670 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2671 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2672 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2673 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2674 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2676 /* auto-determine number of infotrons if it was stored as "0" -- see above */
2677 if (level->gems_needed == 0)
2679 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2680 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2681 if (level->field[x][y] == EL_SP_INFOTRON)
2682 level->gems_needed++;
2684 level->gems_needed &= 0xff; /* only use low byte -- see above */
2687 level->fieldx = SP_LEVEL_XSIZE;
2688 level->fieldy = SP_LEVEL_YSIZE;
2690 level->time = 0; /* no time limit */
2691 level->amoeba_speed = 0;
2692 level->time_magic_wall = 0;
2693 level->time_wheel = 0;
2694 level->amoeba_content = EL_EMPTY;
2696 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2697 level->score[i] = 0; /* !!! CORRECT THIS !!! */
2699 /* there are no yamyams in supaplex levels */
2700 for (i = 0; i < level->num_yamyam_contents; i++)
2701 for (y = 0; y < 3; y++)
2702 for (x = 0; x < 3; x++)
2703 level->yamyam_content[i].e[x][y] = EL_EMPTY;
2706 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2707 struct LevelFileInfo *level_file_info)
2709 char *filename = level_file_info->filename;
2711 int nr = level_file_info->nr - leveldir_current->first_level;
2713 char name_first, name_last;
2714 struct LevelInfo multipart_level;
2715 int multipart_xpos, multipart_ypos;
2716 boolean is_multipart_level;
2717 boolean is_first_part;
2718 boolean reading_multipart_level = FALSE;
2719 boolean use_empty_level = FALSE;
2721 if (!(file = fopen(filename, MODE_READ)))
2723 level->no_valid_file = TRUE;
2725 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2730 /* position file stream to the requested level inside the level package */
2731 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2733 level->no_valid_file = TRUE;
2735 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2740 /* there exist Supaplex level package files with multi-part levels which
2741 can be detected as follows: instead of leading and trailing dashes ('-')
2742 to pad the level name, they have leading and trailing numbers which are
2743 the x and y coordinations of the current part of the multi-part level;
2744 if there are '?' characters instead of numbers on the left or right side
2745 of the level name, the multi-part level consists of only horizontal or
2748 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2750 LoadLevelFromFileStream_SP(file, level, l);
2752 /* check if this level is a part of a bigger multi-part level */
2754 name_first = level->name[0];
2755 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
2757 is_multipart_level =
2758 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2759 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
2762 ((name_first == '?' || name_first == '1') &&
2763 (name_last == '?' || name_last == '1'));
2765 /* correct leading multipart level meta information in level name */
2766 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2767 level->name[i] = '-';
2769 /* correct trailing multipart level meta information in level name */
2770 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2771 level->name[i] = '-';
2773 /* ---------- check for normal single level ---------- */
2775 if (!reading_multipart_level && !is_multipart_level)
2777 /* the current level is simply a normal single-part level, and we are
2778 not reading a multi-part level yet, so return the level as it is */
2783 /* ---------- check for empty level (unused multi-part) ---------- */
2785 if (!reading_multipart_level && is_multipart_level && !is_first_part)
2787 /* this is a part of a multi-part level, but not the first part
2788 (and we are not already reading parts of a multi-part level);
2789 in this case, use an empty level instead of the single part */
2791 use_empty_level = TRUE;
2796 /* ---------- check for finished multi-part level ---------- */
2798 if (reading_multipart_level &&
2799 (!is_multipart_level ||
2800 strcmp(level->name, multipart_level.name) != 0))
2802 /* we are already reading parts of a multi-part level, but this level is
2803 either not a multi-part level, or a part of a different multi-part
2804 level; in both cases, the multi-part level seems to be complete */
2809 /* ---------- here we have one part of a multi-part level ---------- */
2811 reading_multipart_level = TRUE;
2813 if (is_first_part) /* start with first part of new multi-part level */
2815 /* copy level info structure from first part */
2816 multipart_level = *level;
2818 /* clear playfield of new multi-part level */
2819 for (y = 0; y < MAX_LEV_FIELDY; y++)
2820 for (x = 0; x < MAX_LEV_FIELDX; x++)
2821 multipart_level.field[x][y] = EL_EMPTY;
2824 if (name_first == '?')
2826 if (name_last == '?')
2829 multipart_xpos = (int)(name_first - '0');
2830 multipart_ypos = (int)(name_last - '0');
2833 printf("----------> part (%d/%d) of multi-part level '%s'\n",
2834 multipart_xpos, multipart_ypos, multipart_level.name);
2837 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2838 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2840 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2845 multipart_level.fieldx = MAX(multipart_level.fieldx,
2846 multipart_xpos * SP_LEVEL_XSIZE);
2847 multipart_level.fieldy = MAX(multipart_level.fieldy,
2848 multipart_ypos * SP_LEVEL_YSIZE);
2850 /* copy level part at the right position of multi-part level */
2851 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2853 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2855 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2856 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2858 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2865 if (use_empty_level)
2867 setLevelInfoToDefaults(level);
2869 level->fieldx = SP_LEVEL_XSIZE;
2870 level->fieldy = SP_LEVEL_YSIZE;
2872 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2873 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2874 level->field[x][y] = EL_EMPTY;
2876 strcpy(level->name, "-------- EMPTY --------");
2878 Error(ERR_WARN, "single part of multi-part level -- using empty level");
2881 if (reading_multipart_level)
2882 *level = multipart_level;
2885 /* ------------------------------------------------------------------------- */
2886 /* functions for loading generic level */
2887 /* ------------------------------------------------------------------------- */
2889 void LoadLevelFromFileInfo(struct LevelInfo *level,
2890 struct LevelFileInfo *level_file_info)
2892 /* always start with reliable default values */
2893 setLevelInfoToDefaults(level);
2895 switch (level_file_info->type)
2897 case LEVEL_FILE_TYPE_RND:
2898 LoadLevelFromFileInfo_RND(level, level_file_info);
2901 case LEVEL_FILE_TYPE_EM:
2902 LoadLevelFromFileInfo_EM(level, level_file_info);
2903 level->game_engine_type = GAME_ENGINE_TYPE_EM;
2906 case LEVEL_FILE_TYPE_SP:
2907 LoadLevelFromFileInfo_SP(level, level_file_info);
2911 LoadLevelFromFileInfo_RND(level, level_file_info);
2915 /* if level file is invalid, restore level structure to default values */
2916 if (level->no_valid_file)
2917 setLevelInfoToDefaults(level);
2919 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2920 level->game_engine_type = GAME_ENGINE_TYPE_RND;
2922 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
2923 CopyNativeLevel_RND_to_Native(level);
2925 CopyNativeLevel_Native_to_RND(level);
2928 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2930 static struct LevelFileInfo level_file_info;
2932 /* always start with reliable default values */
2933 setFileInfoToDefaults(&level_file_info);
2935 level_file_info.nr = 0; /* unknown level number */
2936 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2937 level_file_info.filename = filename;
2939 LoadLevelFromFileInfo(level, &level_file_info);
2942 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2944 if (leveldir_current == NULL) /* only when dumping level */
2947 if (leveldir_current->latest_engine)
2949 /* ---------- use latest game engine ----------------------------------- */
2951 /* For all levels which are forced to use the latest game engine version
2952 (normally all but user contributed, private and undefined levels), set
2953 the game engine version to the actual version; this allows for actual
2954 corrections in the game engine to take effect for existing, converted
2955 levels (from "classic" or other existing games) to make the emulation
2956 of the corresponding game more accurate, while (hopefully) not breaking
2957 existing levels created from other players. */
2959 level->game_version = GAME_VERSION_ACTUAL;
2961 /* Set special EM style gems behaviour: EM style gems slip down from
2962 normal, steel and growing wall. As this is a more fundamental change,
2963 it seems better to set the default behaviour to "off" (as it is more
2964 natural) and make it configurable in the level editor (as a property
2965 of gem style elements). Already existing converted levels (neither
2966 private nor contributed levels) are changed to the new behaviour. */
2968 if (level->file_version < FILE_VERSION_2_0)
2969 level->em_slippery_gems = TRUE;
2974 /* ---------- use game engine the level was created with ----------------- */
2976 /* For all levels which are not forced to use the latest game engine
2977 version (normally user contributed, private and undefined levels),
2978 use the version of the game engine the levels were created for.
2980 Since 2.0.1, the game engine version is now directly stored
2981 in the level file (chunk "VERS"), so there is no need anymore
2982 to set the game version from the file version (except for old,
2983 pre-2.0 levels, where the game version is still taken from the
2984 file format version used to store the level -- see above). */
2986 /* player was faster than enemies in 1.0.0 and before */
2987 if (level->file_version == FILE_VERSION_1_0)
2988 level->double_speed = TRUE;
2990 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2991 if (level->game_version == VERSION_IDENT(2,0,1,0))
2992 level->em_slippery_gems = TRUE;
2994 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2995 if (level->game_version < VERSION_IDENT(2,2,0,0))
2996 level->use_spring_bug = TRUE;
2998 if (level->game_version < VERSION_IDENT(3,2,0,5))
3000 /* time orb caused limited time in endless time levels before 3.2.0-5 */
3001 level->use_time_orb_bug = TRUE;
3003 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
3004 level->block_snap_field = FALSE;
3006 /* extra time score was same value as time left score before 3.2.0-5 */
3007 level->extra_time_score = level->score[SC_TIME_BONUS];
3009 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
3010 level->score[SC_TIME_BONUS] /= 10;
3013 /* only few elements were able to actively move into acid before 3.1.0 */
3014 /* trigger settings did not exist before 3.1.0; set to default "any" */
3015 if (level->game_version < VERSION_IDENT(3,1,0,0))
3019 /* correct "can move into acid" settings (all zero in old levels) */
3021 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
3022 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
3024 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
3025 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
3026 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
3027 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
3029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
3032 /* correct trigger settings (stored as zero == "none" in old levels) */
3034 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3036 int element = EL_CUSTOM_START + i;
3037 struct ElementInfo *ei = &element_info[element];
3039 for (j = 0; j < ei->num_change_pages; j++)
3041 struct ElementChangeInfo *change = &ei->change_page[j];
3043 change->trigger_player = CH_PLAYER_ANY;
3044 change->trigger_page = CH_PAGE_ANY;
3050 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
3054 /* map custom element change events that have changed in newer versions
3055 (these following values were accidentally changed in version 3.0.1)
3056 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
3057 if (level->game_version <= VERSION_IDENT(3,0,0,0))
3059 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3061 int element = EL_CUSTOM_START + i;
3063 /* order of checking and copying events to be mapped is important */
3064 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
3066 if (HAS_CHANGE_EVENT(element, j - 2))
3068 SET_CHANGE_EVENT(element, j - 2, FALSE);
3069 SET_CHANGE_EVENT(element, j, TRUE);
3073 /* order of checking and copying events to be mapped is important */
3074 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
3076 if (HAS_CHANGE_EVENT(element, j - 1))
3078 SET_CHANGE_EVENT(element, j - 1, FALSE);
3079 SET_CHANGE_EVENT(element, j, TRUE);
3085 /* initialize "can_change" field for old levels with only one change page */
3086 if (level->game_version <= VERSION_IDENT(3,0,2,0))
3088 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3090 int element = EL_CUSTOM_START + i;
3092 if (CAN_CHANGE(element))
3093 element_info[element].change->can_change = TRUE;
3097 /* correct custom element values (for old levels without these options) */
3098 if (level->game_version < VERSION_IDENT(3,1,1,0))
3100 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3102 int element = EL_CUSTOM_START + i;
3103 struct ElementInfo *ei = &element_info[element];
3105 if (ei->access_direction == MV_NO_DIRECTION)
3106 ei->access_direction = MV_ALL_DIRECTIONS;
3109 for (j = 0; j < ei->num_change_pages; j++)
3111 struct ElementChangeInfo *change = &ei->change_page[j];
3113 if (change->trigger_side == CH_SIDE_NONE)
3114 change->trigger_side = CH_SIDE_ANY;
3121 /* correct custom element values (fix invalid values for all versions) */
3124 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3126 int element = EL_CUSTOM_START + i;
3127 struct ElementInfo *ei = &element_info[element];
3129 for (j = 0; j < ei->num_change_pages; j++)
3131 struct ElementChangeInfo *change = &ei->change_page[j];
3133 if (change->trigger_player == CH_PLAYER_NONE)
3134 change->trigger_player = CH_PLAYER_ANY;
3136 if (change->trigger_side == CH_SIDE_NONE)
3137 change->trigger_side = CH_SIDE_ANY;
3143 /* initialize "can_explode" field for old levels which did not store this */
3144 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
3145 if (level->game_version <= VERSION_IDENT(3,1,0,0))
3147 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3149 int element = EL_CUSTOM_START + i;
3151 if (EXPLODES_1X1_OLD(element))
3152 element_info[element].explosion_type = EXPLODES_1X1;
3154 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
3155 EXPLODES_SMASHED(element) ||
3156 EXPLODES_IMPACT(element)));
3160 /* correct previously hard-coded move delay values for maze runner style */
3161 if (level->game_version < VERSION_IDENT(3,1,1,0))
3163 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3165 int element = EL_CUSTOM_START + i;
3167 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
3169 /* previously hard-coded and therefore ignored */
3170 element_info[element].move_delay_fixed = 9;
3171 element_info[element].move_delay_random = 0;
3176 /* map elements that have changed in newer versions */
3177 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
3178 level->game_version);
3179 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3180 for (x = 0; x < 3; x++)
3181 for (y = 0; y < 3; y++)
3182 level->yamyam_content[i].e[x][y] =
3183 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
3184 level->game_version);
3186 /* initialize element properties for level editor etc. */
3187 InitElementPropertiesEngine(level->game_version);
3190 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
3194 /* map elements that have changed in newer versions */
3195 for (y = 0; y < level->fieldy; y++)
3196 for (x = 0; x < level->fieldx; x++)
3197 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
3198 level->game_version);
3200 /* copy elements to runtime playfield array */
3201 for (x = 0; x < MAX_LEV_FIELDX; x++)
3202 for (y = 0; y < MAX_LEV_FIELDY; y++)
3203 Feld[x][y] = level->field[x][y];
3205 /* initialize level size variables for faster access */
3206 lev_fieldx = level->fieldx;
3207 lev_fieldy = level->fieldy;
3209 /* determine border element for this level */
3213 void LoadLevelTemplate(int nr)
3217 setLevelFileInfo(&level_template.file_info, nr);
3218 filename = level_template.file_info.filename;
3220 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
3222 LoadLevel_InitVersion(&level_template, filename);
3223 LoadLevel_InitElements(&level_template, filename);
3225 ActivateLevelTemplate();
3228 void LoadLevel(int nr)
3232 setLevelFileInfo(&level.file_info, nr);
3233 filename = level.file_info.filename;
3235 LoadLevelFromFileInfo(&level, &level.file_info);
3237 if (level.use_custom_template)
3238 LoadLevelTemplate(-1);
3240 LoadLevel_InitVersion(&level, filename);
3241 LoadLevel_InitElements(&level, filename);
3242 LoadLevel_InitPlayfield(&level, filename);
3245 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
3247 putFileVersion(file, level->file_version);
3248 putFileVersion(file, level->game_version);
3251 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
3255 putFile8Bit(file, level->fieldx);
3256 putFile8Bit(file, level->fieldy);
3258 putFile16BitBE(file, level->time);
3259 putFile16BitBE(file, level->gems_needed);
3261 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3262 putFile8Bit(file, level->name[i]);
3264 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3265 putFile8Bit(file, level->score[i]);
3267 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3268 for (y = 0; y < 3; y++)
3269 for (x = 0; x < 3; x++)
3270 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3271 level->yamyam_content[i].e[x][y]));
3272 putFile8Bit(file, level->amoeba_speed);
3273 putFile8Bit(file, level->time_magic_wall);
3274 putFile8Bit(file, level->time_wheel);
3275 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3276 level->amoeba_content));
3277 putFile8Bit(file, (level->double_speed ? 1 : 0));
3278 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3279 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3280 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3282 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3284 putFile8Bit(file, (level->block_last_field ? 1 : 0));
3285 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3286 putFile32BitBE(file, level->can_move_into_acid_bits);
3287 putFile8Bit(file, level->dont_collide_with_bits);
3289 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3290 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3292 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3293 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3294 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3296 putFile8Bit(file, level->game_engine_type);
3298 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3301 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3305 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3306 putFile8Bit(file, level->author[i]);
3309 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3313 for (y = 0; y < level->fieldy; y++)
3314 for (x = 0; x < level->fieldx; x++)
3315 if (level->encoding_16bit_field)
3316 putFile16BitBE(file, level->field[x][y]);
3318 putFile8Bit(file, level->field[x][y]);
3322 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3326 putFile8Bit(file, EL_YAMYAM);
3327 putFile8Bit(file, level->num_yamyam_contents);
3328 putFile8Bit(file, 0);
3329 putFile8Bit(file, 0);
3331 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3332 for (y = 0; y < 3; y++)
3333 for (x = 0; x < 3; x++)
3334 if (level->encoding_16bit_field)
3335 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
3337 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
3341 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3344 int num_contents, content_xsize, content_ysize;
3345 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3347 if (element == EL_YAMYAM)
3349 num_contents = level->num_yamyam_contents;
3353 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3354 for (y = 0; y < 3; y++)
3355 for (x = 0; x < 3; x++)
3356 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
3358 else if (element == EL_BD_AMOEBA)
3364 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3365 for (y = 0; y < 3; y++)
3366 for (x = 0; x < 3; x++)
3367 content_array[i][x][y] = EL_EMPTY;
3368 content_array[0][0][0] = level->amoeba_content;
3372 /* chunk header already written -- write empty chunk data */
3373 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3375 Error(ERR_WARN, "cannot save content for element '%d'", element);
3379 putFile16BitBE(file, element);
3380 putFile8Bit(file, num_contents);
3381 putFile8Bit(file, content_xsize);
3382 putFile8Bit(file, content_ysize);
3384 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3386 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3387 for (y = 0; y < 3; y++)
3388 for (x = 0; x < 3; x++)
3389 putFile16BitBE(file, content_array[i][x][y]);
3392 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3395 int envelope_nr = element - EL_ENVELOPE_1;
3396 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3398 putFile16BitBE(file, element);
3399 putFile16BitBE(file, envelope_len);
3400 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3401 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3403 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3405 for (i = 0; i < envelope_len; i++)
3406 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3410 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3411 int num_changed_custom_elements)
3415 putFile16BitBE(file, num_changed_custom_elements);
3417 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3419 int element = EL_CUSTOM_START + i;
3421 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3423 if (check < num_changed_custom_elements)
3425 putFile16BitBE(file, element);
3426 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3433 if (check != num_changed_custom_elements) /* should not happen */
3434 Error(ERR_WARN, "inconsistent number of custom element properties");
3439 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3440 int num_changed_custom_elements)
3444 putFile16BitBE(file, num_changed_custom_elements);
3446 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3448 int element = EL_CUSTOM_START + i;
3450 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3452 if (check < num_changed_custom_elements)
3454 putFile16BitBE(file, element);
3455 putFile16BitBE(file, element_info[element].change->target_element);
3462 if (check != num_changed_custom_elements) /* should not happen */
3463 Error(ERR_WARN, "inconsistent number of custom target elements");
3468 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3469 int num_changed_custom_elements)
3471 int i, j, x, y, check = 0;
3473 putFile16BitBE(file, num_changed_custom_elements);
3475 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3477 int element = EL_CUSTOM_START + i;
3478 struct ElementInfo *ei = &element_info[element];
3480 if (ei->modified_settings)
3482 if (check < num_changed_custom_elements)
3484 putFile16BitBE(file, element);
3486 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3487 putFile8Bit(file, ei->description[j]);
3489 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3491 /* some free bytes for future properties and padding */
3492 WriteUnusedBytesToFile(file, 7);
3494 putFile8Bit(file, ei->use_gfx_element);
3495 putFile16BitBE(file, ei->gfx_element);
3497 putFile8Bit(file, ei->collect_score_initial);
3498 putFile8Bit(file, ei->collect_count_initial);
3500 putFile16BitBE(file, ei->push_delay_fixed);
3501 putFile16BitBE(file, ei->push_delay_random);
3502 putFile16BitBE(file, ei->move_delay_fixed);
3503 putFile16BitBE(file, ei->move_delay_random);
3505 putFile16BitBE(file, ei->move_pattern);
3506 putFile8Bit(file, ei->move_direction_initial);
3507 putFile8Bit(file, ei->move_stepsize);
3509 for (y = 0; y < 3; y++)
3510 for (x = 0; x < 3; x++)
3511 putFile16BitBE(file, ei->content.e[x][y]);
3513 putFile32BitBE(file, ei->change->events);
3515 putFile16BitBE(file, ei->change->target_element);
3517 putFile16BitBE(file, ei->change->delay_fixed);
3518 putFile16BitBE(file, ei->change->delay_random);
3519 putFile16BitBE(file, ei->change->delay_frames);
3521 putFile16BitBE(file, ei->change->trigger_element);
3523 putFile8Bit(file, ei->change->explode);
3524 putFile8Bit(file, ei->change->use_target_content);
3525 putFile8Bit(file, ei->change->only_if_complete);
3526 putFile8Bit(file, ei->change->use_random_replace);
3528 putFile8Bit(file, ei->change->random_percentage);
3529 putFile8Bit(file, ei->change->replace_when);
3531 for (y = 0; y < 3; y++)
3532 for (x = 0; x < 3; x++)
3533 putFile16BitBE(file, ei->change->content.e[x][y]);
3535 putFile8Bit(file, ei->slippery_type);
3537 /* some free bytes for future properties and padding */
3538 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3545 if (check != num_changed_custom_elements) /* should not happen */
3546 Error(ERR_WARN, "inconsistent number of custom element properties");
3550 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3552 struct ElementInfo *ei = &element_info[element];
3555 /* ---------- custom element base property values (96 bytes) ------------- */
3557 putFile16BitBE(file, element);
3559 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3560 putFile8Bit(file, ei->description[i]);
3562 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3563 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
3565 putFile8Bit(file, ei->num_change_pages);
3567 putFile16BitBE(file, ei->ce_value_fixed_initial);
3568 putFile16BitBE(file, ei->ce_value_random_initial);
3569 putFile8Bit(file, ei->use_last_ce_value);
3571 putFile8Bit(file, ei->use_gfx_element);
3572 putFile16BitBE(file, ei->gfx_element);
3574 putFile8Bit(file, ei->collect_score_initial);
3575 putFile8Bit(file, ei->collect_count_initial);
3577 putFile8Bit(file, ei->drop_delay_fixed);
3578 putFile8Bit(file, ei->push_delay_fixed);
3579 putFile8Bit(file, ei->drop_delay_random);
3580 putFile8Bit(file, ei->push_delay_random);
3581 putFile16BitBE(file, ei->move_delay_fixed);
3582 putFile16BitBE(file, ei->move_delay_random);
3584 /* bits 0 - 15 of "move_pattern" ... */
3585 putFile16BitBE(file, ei->move_pattern & 0xffff);
3586 putFile8Bit(file, ei->move_direction_initial);
3587 putFile8Bit(file, ei->move_stepsize);
3589 putFile8Bit(file, ei->slippery_type);
3591 for (y = 0; y < 3; y++)
3592 for (x = 0; x < 3; x++)
3593 putFile16BitBE(file, ei->content.e[x][y]);
3595 putFile16BitBE(file, ei->move_enter_element);
3596 putFile16BitBE(file, ei->move_leave_element);
3597 putFile8Bit(file, ei->move_leave_type);
3599 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3600 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3602 putFile8Bit(file, ei->access_direction);
3604 putFile8Bit(file, ei->explosion_delay);
3605 putFile8Bit(file, ei->ignition_delay);
3606 putFile8Bit(file, ei->explosion_type);
3608 /* some free bytes for future custom property values and padding */
3609 WriteUnusedBytesToFile(file, 1);
3611 /* ---------- change page property values (48 bytes) --------------------- */
3613 for (i = 0; i < ei->num_change_pages; i++)
3615 struct ElementChangeInfo *change = &ei->change_page[i];
3616 unsigned long event_bits;
3618 /* bits 0 - 31 of "has_event[]" ... */
3620 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3621 if (change->has_event[j])
3622 event_bits |= (1 << j);
3623 putFile32BitBE(file, event_bits);
3625 putFile16BitBE(file, change->target_element);
3627 putFile16BitBE(file, change->delay_fixed);
3628 putFile16BitBE(file, change->delay_random);
3629 putFile16BitBE(file, change->delay_frames);
3631 putFile16BitBE(file, change->trigger_element);
3633 putFile8Bit(file, change->explode);
3634 putFile8Bit(file, change->use_target_content);
3635 putFile8Bit(file, change->only_if_complete);
3636 putFile8Bit(file, change->use_random_replace);
3638 putFile8Bit(file, change->random_percentage);
3639 putFile8Bit(file, change->replace_when);
3641 for (y = 0; y < 3; y++)
3642 for (x = 0; x < 3; x++)
3643 putFile16BitBE(file, change->target_content.e[x][y]);
3645 putFile8Bit(file, change->can_change);
3647 putFile8Bit(file, change->trigger_side);
3649 putFile8Bit(file, change->trigger_player);
3650 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3651 log_2(change->trigger_page)));
3653 putFile8Bit(file, change->has_action);
3654 putFile8Bit(file, change->action_type);
3655 putFile8Bit(file, change->action_mode);
3656 putFile16BitBE(file, change->action_arg);
3658 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
3660 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3661 if (change->has_event[j])
3662 event_bits |= (1 << (j - 32));
3663 putFile8Bit(file, event_bits);
3667 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3669 struct ElementInfo *ei = &element_info[element];
3670 struct ElementGroupInfo *group = ei->group;
3673 putFile16BitBE(file, element);
3675 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3676 putFile8Bit(file, ei->description[i]);
3678 putFile8Bit(file, group->num_elements);
3680 putFile8Bit(file, ei->use_gfx_element);
3681 putFile16BitBE(file, ei->gfx_element);
3683 putFile8Bit(file, group->choice_mode);
3685 /* some free bytes for future values and padding */
3686 WriteUnusedBytesToFile(file, 3);
3688 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3689 putFile16BitBE(file, group->element[i]);
3692 static int SaveLevel_CONF_Value(FILE *file, int pos)
3694 int default_value = element_conf[pos].default_value;
3695 int element = element_conf[pos].element;
3696 int type = element_conf[pos].type;
3697 int bytes = type & CONF_MASK_BYTES;
3698 void *value_ptr = element_conf[pos].value;
3699 int value = (CONF_VALUE_BOOLEAN(type) ? *(boolean *)value_ptr :
3702 boolean modified = FALSE;
3704 /* check if any settings have been modified before saving them */
3705 if (value != default_value)
3708 if (!modified) /* do not save unmodified default settings */
3711 if (bytes == CONF_MASK_MULTI_BYTES)
3712 Error(ERR_EXIT, "SaveLevel_CONF_Value: cannot save multi-byte values");
3714 num_bytes += putFile16BitBE(file, element);
3715 num_bytes += putFile8Bit(file, type);
3716 num_bytes += (bytes == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
3717 bytes == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
3718 bytes == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) : 0);
3723 static int SaveLevel_CONF_Content(FILE *file, int pos, int num_contents)
3725 struct Content *content = (struct Content *)(element_conf[pos].value);
3726 int default_value = element_conf[pos].default_value;
3727 int element = element_conf[pos].element;
3728 int type = element_conf[pos].type;
3730 boolean modified = FALSE;
3733 /* check if any settings have been modified before saving them */
3734 for (i = 0; i < num_contents; i++)
3735 for (y = 0; y < 3; y++)
3736 for (x = 0; x < 3; x++)
3737 if (content[i].e[x][y] != default_value)
3740 if (!modified) /* do not save unmodified default settings */
3743 num_bytes += putFile16BitBE(file, element);
3744 num_bytes += putFile8Bit(file, type);
3745 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
3747 for (i = 0; i < num_contents; i++)
3748 for (y = 0; y < 3; y++)
3749 for (x = 0; x < 3; x++)
3750 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
3755 static int SaveLevel_CONF(FILE *file, struct LevelInfo *level)
3760 li = *level; /* copy level information into temporary buffer */
3762 for (i = 0; element_conf[i].element != -1; i++)
3764 int type = element_conf[i].type;
3765 int bytes = type & CONF_MASK_BYTES;
3767 if (bytes != CONF_MASK_MULTI_BYTES)
3768 chunk_size += SaveLevel_CONF_Value(file, i);
3769 else if (type == CONF_VALUE_CONTENT_8)
3770 chunk_size += SaveLevel_CONF_Content(file, i, MAX_ELEMENT_CONTENTS);
3776 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3778 int body_chunk_size, conf_chunk_size;
3782 if (!(file = fopen(filename, MODE_WRITE)))
3784 Error(ERR_WARN, "cannot save level file '%s'", filename);
3788 level->file_version = FILE_VERSION_ACTUAL;
3789 level->game_version = GAME_VERSION_ACTUAL;
3791 /* check level field for 16-bit elements */
3792 level->encoding_16bit_field = FALSE;
3793 for (y = 0; y < level->fieldy; y++)
3794 for (x = 0; x < level->fieldx; x++)
3795 if (level->field[x][y] > 255)
3796 level->encoding_16bit_field = TRUE;
3798 /* check yamyam content for 16-bit elements */
3799 level->encoding_16bit_yamyam = FALSE;
3800 for (i = 0; i < level->num_yamyam_contents; i++)
3801 for (y = 0; y < 3; y++)
3802 for (x = 0; x < 3; x++)
3803 if (level->yamyam_content[i].e[x][y] > 255)
3804 level->encoding_16bit_yamyam = TRUE;
3806 /* check amoeba content for 16-bit elements */
3807 level->encoding_16bit_amoeba = FALSE;
3808 if (level->amoeba_content > 255)
3809 level->encoding_16bit_amoeba = TRUE;
3811 /* calculate size of "BODY" chunk */
3813 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3815 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3816 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3818 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3819 SaveLevel_VERS(file, level);
3821 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3822 SaveLevel_HEAD(file, level);
3824 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3825 SaveLevel_AUTH(file, level);
3827 putFileChunkBE(file, "BODY", body_chunk_size);
3828 SaveLevel_BODY(file, level);
3830 if (level->encoding_16bit_yamyam ||
3831 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3833 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3834 SaveLevel_CNT2(file, level, EL_YAMYAM);
3837 if (level->encoding_16bit_amoeba)
3839 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3840 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3843 /* check for envelope content */
3844 for (i = 0; i < 4; i++)
3846 if (strlen(level->envelope_text[i]) > 0)
3848 int envelope_len = strlen(level->envelope_text[i]) + 1;
3850 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3851 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3855 /* check for non-default custom elements (unless using template level) */
3856 if (!level->use_custom_template)
3858 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3860 int element = EL_CUSTOM_START + i;
3862 if (element_info[element].modified_settings)
3864 int num_change_pages = element_info[element].num_change_pages;
3866 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3867 SaveLevel_CUS4(file, level, element);
3872 /* check for non-default group elements (unless using template level) */
3873 if (!level->use_custom_template)
3875 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3877 int element = EL_GROUP_START + i;
3879 if (element_info[element].modified_settings)
3881 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3882 SaveLevel_GRP1(file, level, element);
3887 conf_chunk_size = SaveLevel_CONF(NULL, level); /* get chunk size */
3889 /* check for non-default configuration settings to be saved in CONF chunk */
3890 if (conf_chunk_size > 0)
3892 putFileChunkBE(file, "CONF", conf_chunk_size);
3893 SaveLevel_CONF(file, level);
3898 SetFilePermissions(filename, PERMS_PRIVATE);
3901 void SaveLevel(int nr)
3903 char *filename = getDefaultLevelFilename(nr);
3905 SaveLevelFromFilename(&level, filename);
3908 void SaveLevelTemplate()
3910 char *filename = getDefaultLevelFilename(-1);
3912 SaveLevelFromFilename(&level, filename);
3915 void DumpLevel(struct LevelInfo *level)
3917 if (level->no_valid_file)
3919 Error(ERR_WARN, "cannot dump -- no valid level file found");
3924 printf_line("-", 79);
3925 printf("Level xxx (file version %08d, game version %08d)\n",
3926 level->file_version, level->game_version);
3927 printf_line("-", 79);
3929 printf("Level author: '%s'\n", level->author);
3930 printf("Level title: '%s'\n", level->name);
3932 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3934 printf("Level time: %d seconds\n", level->time);
3935 printf("Gems needed: %d\n", level->gems_needed);
3937 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3938 printf("Time for wheel: %d seconds\n", level->time_wheel);
3939 printf("Time for light: %d seconds\n", level->time_light);
3940 printf("Time for timegate: %d seconds\n", level->time_timegate);
3942 printf("Amoeba speed: %d\n", level->amoeba_speed);
3944 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
3945 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
3946 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
3947 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
3948 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3949 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3950 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3952 printf_line("-", 79);
3956 /* ========================================================================= */
3957 /* tape file functions */
3958 /* ========================================================================= */
3960 static void setTapeInfoToDefaults()
3964 /* always start with reliable default values (empty tape) */
3967 /* default values (also for pre-1.2 tapes) with only the first player */
3968 tape.player_participates[0] = TRUE;
3969 for (i = 1; i < MAX_PLAYERS; i++)
3970 tape.player_participates[i] = FALSE;
3972 /* at least one (default: the first) player participates in every tape */
3973 tape.num_participating_players = 1;
3975 tape.level_nr = level_nr;
3977 tape.changed = FALSE;
3979 tape.recording = FALSE;
3980 tape.playing = FALSE;
3981 tape.pausing = FALSE;
3983 tape.no_valid_file = FALSE;
3986 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3988 tape->file_version = getFileVersion(file);
3989 tape->game_version = getFileVersion(file);
3994 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3998 tape->random_seed = getFile32BitBE(file);
3999 tape->date = getFile32BitBE(file);
4000 tape->length = getFile32BitBE(file);
4002 /* read header fields that are new since version 1.2 */
4003 if (tape->file_version >= FILE_VERSION_1_2)
4005 byte store_participating_players = getFile8Bit(file);
4008 /* since version 1.2, tapes store which players participate in the tape */
4009 tape->num_participating_players = 0;
4010 for (i = 0; i < MAX_PLAYERS; i++)
4012 tape->player_participates[i] = FALSE;
4014 if (store_participating_players & (1 << i))
4016 tape->player_participates[i] = TRUE;
4017 tape->num_participating_players++;
4021 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
4023 engine_version = getFileVersion(file);
4024 if (engine_version > 0)
4025 tape->engine_version = engine_version;
4027 tape->engine_version = tape->game_version;
4033 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
4035 int level_identifier_size;
4038 level_identifier_size = getFile16BitBE(file);
4040 tape->level_identifier =
4041 checked_realloc(tape->level_identifier, level_identifier_size);
4043 for (i = 0; i < level_identifier_size; i++)
4044 tape->level_identifier[i] = getFile8Bit(file);
4046 tape->level_nr = getFile16BitBE(file);
4048 chunk_size = 2 + level_identifier_size + 2;
4053 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
4056 int chunk_size_expected =
4057 (tape->num_participating_players + 1) * tape->length;
4059 if (chunk_size_expected != chunk_size)
4061 ReadUnusedBytesFromFile(file, chunk_size);
4062 return chunk_size_expected;
4065 for (i = 0; i < tape->length; i++)
4067 if (i >= MAX_TAPE_LEN)
4070 for (j = 0; j < MAX_PLAYERS; j++)
4072 tape->pos[i].action[j] = MV_NONE;
4074 if (tape->player_participates[j])
4075 tape->pos[i].action[j] = getFile8Bit(file);
4078 tape->pos[i].delay = getFile8Bit(file);
4080 if (tape->file_version == FILE_VERSION_1_0)
4082 /* eliminate possible diagonal moves in old tapes */
4083 /* this is only for backward compatibility */
4085 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
4086 byte action = tape->pos[i].action[0];
4087 int k, num_moves = 0;
4089 for (k = 0; k<4; k++)
4091 if (action & joy_dir[k])
4093 tape->pos[i + num_moves].action[0] = joy_dir[k];
4095 tape->pos[i + num_moves].delay = 0;
4104 tape->length += num_moves;
4107 else if (tape->file_version < FILE_VERSION_2_0)
4109 /* convert pre-2.0 tapes to new tape format */
4111 if (tape->pos[i].delay > 1)
4114 tape->pos[i + 1] = tape->pos[i];
4115 tape->pos[i + 1].delay = 1;
4118 for (j = 0; j < MAX_PLAYERS; j++)
4119 tape->pos[i].action[j] = MV_NONE;
4120 tape->pos[i].delay--;
4131 if (i != tape->length)
4132 chunk_size = (tape->num_participating_players + 1) * i;
4137 void LoadTapeFromFilename(char *filename)
4139 char cookie[MAX_LINE_LEN];
4140 char chunk_name[CHUNK_ID_LEN + 1];
4144 /* always start with reliable default values */
4145 setTapeInfoToDefaults();
4147 if (!(file = fopen(filename, MODE_READ)))
4149 tape.no_valid_file = TRUE;
4154 getFileChunkBE(file, chunk_name, NULL);
4155 if (strcmp(chunk_name, "RND1") == 0)
4157 getFile32BitBE(file); /* not used */
4159 getFileChunkBE(file, chunk_name, NULL);
4160 if (strcmp(chunk_name, "TAPE") != 0)
4162 tape.no_valid_file = TRUE;
4164 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4169 else /* check for pre-2.0 file format with cookie string */
4171 strcpy(cookie, chunk_name);
4172 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
4173 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4174 cookie[strlen(cookie) - 1] = '\0';
4176 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
4178 tape.no_valid_file = TRUE;
4180 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4185 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
4187 tape.no_valid_file = TRUE;
4189 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
4194 /* pre-2.0 tape files have no game version, so use file version here */
4195 tape.game_version = tape.file_version;
4198 if (tape.file_version < FILE_VERSION_1_2)
4200 /* tape files from versions before 1.2.0 without chunk structure */
4201 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
4202 LoadTape_BODY(file, 2 * tape.length, &tape);
4210 int (*loader)(FILE *, int, struct TapeInfo *);
4214 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
4215 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
4216 { "INFO", -1, LoadTape_INFO },
4217 { "BODY", -1, LoadTape_BODY },
4221 while (getFileChunkBE(file, chunk_name, &chunk_size))
4225 while (chunk_info[i].name != NULL &&
4226 strcmp(chunk_name, chunk_info[i].name) != 0)
4229 if (chunk_info[i].name == NULL)
4231 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
4232 chunk_name, filename);
4233 ReadUnusedBytesFromFile(file, chunk_size);
4235 else if (chunk_info[i].size != -1 &&
4236 chunk_info[i].size != chunk_size)
4238 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4239 chunk_size, chunk_name, filename);
4240 ReadUnusedBytesFromFile(file, chunk_size);
4244 /* call function to load this tape chunk */
4245 int chunk_size_expected =
4246 (chunk_info[i].loader)(file, chunk_size, &tape);
4248 /* the size of some chunks cannot be checked before reading other
4249 chunks first (like "HEAD" and "BODY") that contain some header
4250 information, so check them here */
4251 if (chunk_size_expected != chunk_size)
4253 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4254 chunk_size, chunk_name, filename);
4262 tape.length_seconds = GetTapeLength();
4265 printf("::: tape game version: %d\n", tape.game_version);
4266 printf("::: tape engine version: %d\n", tape.engine_version);
4270 void LoadTape(int nr)
4272 char *filename = getTapeFilename(nr);
4274 LoadTapeFromFilename(filename);
4277 void LoadSolutionTape(int nr)
4279 char *filename = getSolutionTapeFilename(nr);
4281 LoadTapeFromFilename(filename);
4284 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
4286 putFileVersion(file, tape->file_version);
4287 putFileVersion(file, tape->game_version);
4290 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
4293 byte store_participating_players = 0;
4295 /* set bits for participating players for compact storage */
4296 for (i = 0; i < MAX_PLAYERS; i++)
4297 if (tape->player_participates[i])
4298 store_participating_players |= (1 << i);
4300 putFile32BitBE(file, tape->random_seed);
4301 putFile32BitBE(file, tape->date);
4302 putFile32BitBE(file, tape->length);
4304 putFile8Bit(file, store_participating_players);
4306 /* unused bytes not at the end here for 4-byte alignment of engine_version */
4307 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
4309 putFileVersion(file, tape->engine_version);
4312 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
4314 int level_identifier_size = strlen(tape->level_identifier) + 1;
4317 putFile16BitBE(file, level_identifier_size);
4319 for (i = 0; i < level_identifier_size; i++)
4320 putFile8Bit(file, tape->level_identifier[i]);
4322 putFile16BitBE(file, tape->level_nr);
4325 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
4329 for (i = 0; i < tape->length; i++)
4331 for (j = 0; j < MAX_PLAYERS; j++)
4332 if (tape->player_participates[j])
4333 putFile8Bit(file, tape->pos[i].action[j]);
4335 putFile8Bit(file, tape->pos[i].delay);
4339 void SaveTape(int nr)
4341 char *filename = getTapeFilename(nr);
4343 boolean new_tape = TRUE;
4344 int num_participating_players = 0;
4345 int info_chunk_size;
4346 int body_chunk_size;
4349 InitTapeDirectory(leveldir_current->subdir);
4351 /* if a tape still exists, ask to overwrite it */
4352 if (fileExists(filename))
4355 if (!Request("Replace old tape ?", REQ_ASK))
4359 if (!(file = fopen(filename, MODE_WRITE)))
4361 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4365 tape.file_version = FILE_VERSION_ACTUAL;
4366 tape.game_version = GAME_VERSION_ACTUAL;
4368 /* count number of participating players */
4369 for (i = 0; i < MAX_PLAYERS; i++)
4370 if (tape.player_participates[i])
4371 num_participating_players++;
4373 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4374 body_chunk_size = (num_participating_players + 1) * tape.length;
4376 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4377 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4379 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4380 SaveTape_VERS(file, &tape);
4382 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4383 SaveTape_HEAD(file, &tape);
4385 putFileChunkBE(file, "INFO", info_chunk_size);
4386 SaveTape_INFO(file, &tape);
4388 putFileChunkBE(file, "BODY", body_chunk_size);
4389 SaveTape_BODY(file, &tape);
4393 SetFilePermissions(filename, PERMS_PRIVATE);
4395 tape.changed = FALSE;
4398 Request("Tape saved !", REQ_CONFIRM);
4401 void DumpTape(struct TapeInfo *tape)
4405 if (tape->no_valid_file)
4407 Error(ERR_WARN, "cannot dump -- no valid tape file found");
4412 printf_line("-", 79);
4413 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4414 tape->level_nr, tape->file_version, tape->game_version);
4415 printf(" (effective engine version %08d)\n",
4416 tape->engine_version);
4417 printf("Level series identifier: '%s'\n", tape->level_identifier);
4418 printf_line("-", 79);
4420 for (i = 0; i < tape->length; i++)
4422 if (i >= MAX_TAPE_LEN)
4425 printf("%03d: ", i);
4427 for (j = 0; j < MAX_PLAYERS; j++)
4429 if (tape->player_participates[j])
4431 int action = tape->pos[i].action[j];
4433 printf("%d:%02x ", j, action);
4434 printf("[%c%c%c%c|%c%c] - ",
4435 (action & JOY_LEFT ? '<' : ' '),
4436 (action & JOY_RIGHT ? '>' : ' '),
4437 (action & JOY_UP ? '^' : ' '),
4438 (action & JOY_DOWN ? 'v' : ' '),
4439 (action & JOY_BUTTON_1 ? '1' : ' '),
4440 (action & JOY_BUTTON_2 ? '2' : ' '));
4444 printf("(%03d)\n", tape->pos[i].delay);
4447 printf_line("-", 79);
4451 /* ========================================================================= */
4452 /* score file functions */
4453 /* ========================================================================= */
4455 void LoadScore(int nr)
4458 char *filename = getScoreFilename(nr);
4459 char cookie[MAX_LINE_LEN];
4460 char line[MAX_LINE_LEN];
4464 /* always start with reliable default values */
4465 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4467 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4468 highscore[i].Score = 0;
4471 if (!(file = fopen(filename, MODE_READ)))
4474 /* check file identifier */
4475 fgets(cookie, MAX_LINE_LEN, file);
4476 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4477 cookie[strlen(cookie) - 1] = '\0';
4479 if (!checkCookieString(cookie, SCORE_COOKIE))
4481 Error(ERR_WARN, "unknown format of score file '%s'", filename);
4486 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4488 fscanf(file, "%d", &highscore[i].Score);
4489 fgets(line, MAX_LINE_LEN, file);
4491 if (line[strlen(line) - 1] == '\n')
4492 line[strlen(line) - 1] = '\0';
4494 for (line_ptr = line; *line_ptr; line_ptr++)
4496 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4498 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4499 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4508 void SaveScore(int nr)
4511 char *filename = getScoreFilename(nr);
4514 InitScoreDirectory(leveldir_current->subdir);
4516 if (!(file = fopen(filename, MODE_WRITE)))
4518 Error(ERR_WARN, "cannot save score for level %d", nr);
4522 fprintf(file, "%s\n\n", SCORE_COOKIE);
4524 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4525 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4529 SetFilePermissions(filename, PERMS_PUBLIC);
4533 /* ========================================================================= */
4534 /* setup file functions */
4535 /* ========================================================================= */
4537 #define TOKEN_STR_PLAYER_PREFIX "player_"
4540 #define SETUP_TOKEN_PLAYER_NAME 0
4541 #define SETUP_TOKEN_SOUND 1
4542 #define SETUP_TOKEN_SOUND_LOOPS 2
4543 #define SETUP_TOKEN_SOUND_MUSIC 3
4544 #define SETUP_TOKEN_SOUND_SIMPLE 4
4545 #define SETUP_TOKEN_TOONS 5
4546 #define SETUP_TOKEN_SCROLL_DELAY 6
4547 #define SETUP_TOKEN_SOFT_SCROLLING 7
4548 #define SETUP_TOKEN_FADING 8
4549 #define SETUP_TOKEN_AUTORECORD 9
4550 #define SETUP_TOKEN_QUICK_DOORS 10
4551 #define SETUP_TOKEN_TEAM_MODE 11
4552 #define SETUP_TOKEN_HANDICAP 12
4553 #define SETUP_TOKEN_SKIP_LEVELS 13
4554 #define SETUP_TOKEN_TIME_LIMIT 14
4555 #define SETUP_TOKEN_FULLSCREEN 15
4556 #define SETUP_TOKEN_ASK_ON_ESCAPE 16
4557 #define SETUP_TOKEN_GRAPHICS_SET 17
4558 #define SETUP_TOKEN_SOUNDS_SET 18
4559 #define SETUP_TOKEN_MUSIC_SET 19
4560 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 20
4561 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 21
4562 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 22
4564 #define NUM_GLOBAL_SETUP_TOKENS 23
4567 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
4568 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
4569 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
4570 #define SETUP_TOKEN_EDITOR_EL_MORE 3
4571 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
4572 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
4573 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
4574 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
4575 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
4576 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 9
4577 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
4578 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
4579 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 12
4581 #define NUM_EDITOR_SETUP_TOKENS 13
4583 /* editor cascade setup */
4584 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
4585 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
4586 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
4587 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
4588 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
4589 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
4590 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
4591 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
4592 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
4593 #define SETUP_TOKEN_EDITOR_CASCADE_CE 9
4594 #define SETUP_TOKEN_EDITOR_CASCADE_GE 10
4595 #define SETUP_TOKEN_EDITOR_CASCADE_USER 11
4596 #define SETUP_TOKEN_EDITOR_CASCADE_GENERIC 12
4597 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 13
4599 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 14
4601 /* shortcut setup */
4602 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
4603 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
4604 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
4606 #define NUM_SHORTCUT_SETUP_TOKENS 3
4609 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
4610 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
4611 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
4612 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
4613 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
4614 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
4615 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
4616 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
4617 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
4618 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
4619 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
4620 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
4621 #define SETUP_TOKEN_PLAYER_KEY_UP 12
4622 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
4623 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
4624 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
4626 #define NUM_PLAYER_SETUP_TOKENS 16
4629 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
4630 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
4632 #define NUM_SYSTEM_SETUP_TOKENS 2
4635 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
4637 #define NUM_OPTIONS_SETUP_TOKENS 1
4640 static struct SetupInfo si;
4641 static struct SetupEditorInfo sei;
4642 static struct SetupEditorCascadeInfo seci;
4643 static struct SetupShortcutInfo ssi;
4644 static struct SetupInputInfo sii;
4645 static struct SetupSystemInfo syi;
4646 static struct OptionInfo soi;
4648 static struct TokenInfo global_setup_tokens[] =
4650 { TYPE_STRING, &si.player_name, "player_name" },
4651 { TYPE_SWITCH, &si.sound, "sound" },
4652 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
4653 { TYPE_SWITCH, &si.sound_music, "background_music" },
4654 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
4655 { TYPE_SWITCH, &si.toons, "toons" },
4656 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
4657 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
4658 { TYPE_SWITCH, &si.fading, "screen_fading" },
4659 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
4660 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
4661 { TYPE_SWITCH, &si.team_mode, "team_mode" },
4662 { TYPE_SWITCH, &si.handicap, "handicap" },
4663 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
4664 { TYPE_SWITCH, &si.time_limit, "time_limit" },
4665 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
4666 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
4667 { TYPE_STRING, &si.graphics_set, "graphics_set" },
4668 { TYPE_STRING, &si.sounds_set, "sounds_set" },
4669 { TYPE_STRING, &si.music_set, "music_set" },
4670 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4671 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
4672 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
4675 static struct TokenInfo editor_setup_tokens[] =
4677 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
4678 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
4679 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
4680 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
4681 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
4682 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
4683 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
4684 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
4685 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
4686 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
4687 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
4688 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
4689 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
4692 static struct TokenInfo editor_cascade_setup_tokens[] =
4694 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
4695 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
4696 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
4697 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
4698 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
4699 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
4700 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
4701 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
4702 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
4703 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
4704 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
4705 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
4706 { TYPE_SWITCH, &seci.el_generic, "editor.cascade.el_generic" },
4707 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
4710 static struct TokenInfo shortcut_setup_tokens[] =
4712 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
4713 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
4714 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
4717 static struct TokenInfo player_setup_tokens[] =
4719 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
4720 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
4721 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
4722 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
4723 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
4724 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
4725 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
4726 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
4727 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
4728 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
4729 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
4730 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
4731 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
4732 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
4733 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
4734 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
4737 static struct TokenInfo system_setup_tokens[] =
4739 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
4740 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4743 static struct TokenInfo options_setup_tokens[] =
4745 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
4748 static char *get_corrected_login_name(char *login_name)
4750 /* needed because player name must be a fixed length string */
4751 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4753 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4754 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4756 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
4757 if (strchr(login_name_new, ' '))
4758 *strchr(login_name_new, ' ') = '\0';
4760 return login_name_new;
4763 static void setSetupInfoToDefaults(struct SetupInfo *si)
4767 si->player_name = get_corrected_login_name(getLoginName());
4770 si->sound_loops = TRUE;
4771 si->sound_music = TRUE;
4772 si->sound_simple = TRUE;
4774 si->double_buffering = TRUE;
4775 si->direct_draw = !si->double_buffering;
4776 si->scroll_delay = TRUE;
4777 si->soft_scrolling = TRUE;
4779 si->autorecord = TRUE;
4780 si->quick_doors = FALSE;
4781 si->team_mode = FALSE;
4782 si->handicap = TRUE;
4783 si->skip_levels = TRUE;
4784 si->time_limit = TRUE;
4785 si->fullscreen = FALSE;
4786 si->ask_on_escape = TRUE;
4788 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4789 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4790 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4791 si->override_level_graphics = FALSE;
4792 si->override_level_sounds = FALSE;
4793 si->override_level_music = FALSE;
4795 si->editor.el_boulderdash = TRUE;
4796 si->editor.el_emerald_mine = TRUE;
4797 si->editor.el_emerald_mine_club = TRUE;
4798 si->editor.el_more = TRUE;
4799 si->editor.el_sokoban = TRUE;
4800 si->editor.el_supaplex = TRUE;
4801 si->editor.el_diamond_caves = TRUE;
4802 si->editor.el_dx_boulderdash = TRUE;
4803 si->editor.el_chars = TRUE;
4804 si->editor.el_custom = TRUE;
4806 si->editor.el_headlines = TRUE;
4807 si->editor.el_user_defined = FALSE;
4808 si->editor.el_dynamic = TRUE;
4810 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4811 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4812 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4814 for (i = 0; i < MAX_PLAYERS; i++)
4816 si->input[i].use_joystick = FALSE;
4817 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4818 si->input[i].joy.xleft = JOYSTICK_XLEFT;
4819 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4820 si->input[i].joy.xright = JOYSTICK_XRIGHT;
4821 si->input[i].joy.yupper = JOYSTICK_YUPPER;
4822 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4823 si->input[i].joy.ylower = JOYSTICK_YLOWER;
4824 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
4825 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
4826 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
4827 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4828 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
4829 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
4830 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
4831 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
4834 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4835 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4837 si->options.verbose = FALSE;
4840 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
4842 si->editor_cascade.el_bd = TRUE;
4843 si->editor_cascade.el_em = TRUE;
4844 si->editor_cascade.el_emc = TRUE;
4845 si->editor_cascade.el_rnd = TRUE;
4846 si->editor_cascade.el_sb = TRUE;
4847 si->editor_cascade.el_sp = TRUE;
4848 si->editor_cascade.el_dc = TRUE;
4849 si->editor_cascade.el_dx = TRUE;
4851 si->editor_cascade.el_chars = FALSE;
4852 si->editor_cascade.el_ce = FALSE;
4853 si->editor_cascade.el_ge = FALSE;
4854 si->editor_cascade.el_user = FALSE;
4855 si->editor_cascade.el_generic = FALSE;
4856 si->editor_cascade.el_dynamic = FALSE;
4859 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4863 if (!setup_file_hash)
4868 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4869 setSetupInfo(global_setup_tokens, i,
4870 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4875 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4876 setSetupInfo(editor_setup_tokens, i,
4877 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4880 /* shortcut setup */
4881 ssi = setup.shortcut;
4882 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4883 setSetupInfo(shortcut_setup_tokens, i,
4884 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4885 setup.shortcut = ssi;
4888 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4892 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4894 sii = setup.input[pnr];
4895 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4897 char full_token[100];
4899 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4900 setSetupInfo(player_setup_tokens, i,
4901 getHashEntry(setup_file_hash, full_token));
4903 setup.input[pnr] = sii;
4908 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4909 setSetupInfo(system_setup_tokens, i,
4910 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4914 soi = setup.options;
4915 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4916 setSetupInfo(options_setup_tokens, i,
4917 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4918 setup.options = soi;
4921 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
4925 if (!setup_file_hash)
4928 /* editor cascade setup */
4929 seci = setup.editor_cascade;
4930 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
4931 setSetupInfo(editor_cascade_setup_tokens, i,
4932 getHashEntry(setup_file_hash,
4933 editor_cascade_setup_tokens[i].text));
4934 setup.editor_cascade = seci;
4939 char *filename = getSetupFilename();
4940 SetupFileHash *setup_file_hash = NULL;
4942 /* always start with reliable default values */
4943 setSetupInfoToDefaults(&setup);
4945 setup_file_hash = loadSetupFileHash(filename);
4947 if (setup_file_hash)
4949 char *player_name_new;
4951 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4952 decodeSetupFileHash(setup_file_hash);
4954 setup.direct_draw = !setup.double_buffering;
4956 freeSetupFileHash(setup_file_hash);
4958 /* needed to work around problems with fixed length strings */
4959 player_name_new = get_corrected_login_name(setup.player_name);
4960 free(setup.player_name);
4961 setup.player_name = player_name_new;
4964 Error(ERR_WARN, "using default setup values");
4967 void LoadSetup_EditorCascade()
4969 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
4970 SetupFileHash *setup_file_hash = NULL;
4972 /* always start with reliable default values */
4973 setSetupInfoToDefaults_EditorCascade(&setup);
4975 setup_file_hash = loadSetupFileHash(filename);
4977 if (setup_file_hash)
4979 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4980 decodeSetupFileHash_EditorCascade(setup_file_hash);
4982 freeSetupFileHash(setup_file_hash);
4990 char *filename = getSetupFilename();
4994 InitUserDataDirectory();
4996 if (!(file = fopen(filename, MODE_WRITE)))
4998 Error(ERR_WARN, "cannot write setup file '%s'", filename);
5002 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
5003 getCookie("SETUP")));
5004 fprintf(file, "\n");
5008 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
5010 /* just to make things nicer :) */
5011 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
5012 i == SETUP_TOKEN_GRAPHICS_SET)
5013 fprintf(file, "\n");
5015 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
5020 fprintf(file, "\n");
5021 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
5022 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
5024 /* shortcut setup */
5025 ssi = setup.shortcut;
5026 fprintf(file, "\n");
5027 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
5028 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
5031 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
5035 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
5036 fprintf(file, "\n");
5038 sii = setup.input[pnr];
5039 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
5040 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
5045 fprintf(file, "\n");
5046 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
5047 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
5050 soi = setup.options;
5051 fprintf(file, "\n");
5052 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
5053 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
5057 SetFilePermissions(filename, PERMS_PRIVATE);
5060 void SaveSetup_EditorCascade()
5062 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
5066 InitUserDataDirectory();
5068 if (!(file = fopen(filename, MODE_WRITE)))
5070 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
5075 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
5076 getCookie("SETUP")));
5077 fprintf(file, "\n");
5079 seci = setup.editor_cascade;
5080 fprintf(file, "\n");
5081 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
5082 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
5086 SetFilePermissions(filename, PERMS_PRIVATE);
5091 void LoadCustomElementDescriptions()
5093 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
5094 SetupFileHash *setup_file_hash;
5097 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5099 if (element_info[i].custom_description != NULL)
5101 free(element_info[i].custom_description);
5102 element_info[i].custom_description = NULL;
5106 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
5109 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5111 char *token = getStringCat2(element_info[i].token_name, ".name");
5112 char *value = getHashEntry(setup_file_hash, token);
5115 element_info[i].custom_description = getStringCopy(value);
5120 freeSetupFileHash(setup_file_hash);
5123 void LoadSpecialMenuDesignSettings()
5125 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
5126 SetupFileHash *setup_file_hash;
5129 /* always start with reliable default values from default config */
5130 for (i = 0; image_config_vars[i].token != NULL; i++)
5131 for (j = 0; image_config[j].token != NULL; j++)
5132 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
5133 *image_config_vars[i].value =
5134 get_auto_parameter_value(image_config_vars[i].token,
5135 image_config[j].value);
5137 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
5140 /* special case: initialize with default values that may be overwritten */
5141 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5143 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
5144 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
5145 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
5147 if (value_x != NULL)
5148 menu.draw_xoffset[i] = get_integer_from_string(value_x);
5149 if (value_y != NULL)
5150 menu.draw_yoffset[i] = get_integer_from_string(value_y);
5151 if (list_size != NULL)
5152 menu.list_size[i] = get_integer_from_string(list_size);
5155 /* read (and overwrite with) values that may be specified in config file */
5156 for (i = 0; image_config_vars[i].token != NULL; i++)
5158 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
5161 *image_config_vars[i].value =
5162 get_auto_parameter_value(image_config_vars[i].token, value);
5165 freeSetupFileHash(setup_file_hash);
5168 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
5170 char *filename = getEditorSetupFilename();
5171 SetupFileList *setup_file_list, *list;
5172 SetupFileHash *element_hash;
5173 int num_unknown_tokens = 0;
5176 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
5179 element_hash = newSetupFileHash();
5181 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5182 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5184 /* determined size may be larger than needed (due to unknown elements) */
5186 for (list = setup_file_list; list != NULL; list = list->next)
5189 /* add space for up to 3 more elements for padding that may be needed */
5192 /* free memory for old list of elements, if needed */
5193 checked_free(*elements);
5195 /* allocate memory for new list of elements */
5196 *elements = checked_malloc(*num_elements * sizeof(int));
5199 for (list = setup_file_list; list != NULL; list = list->next)
5201 char *value = getHashEntry(element_hash, list->token);
5203 if (value == NULL) /* try to find obsolete token mapping */
5205 char *mapped_token = get_mapped_token(list->token);
5207 if (mapped_token != NULL)
5209 value = getHashEntry(element_hash, mapped_token);
5217 (*elements)[(*num_elements)++] = atoi(value);
5221 if (num_unknown_tokens == 0)
5223 Error(ERR_RETURN_LINE, "-");
5224 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5225 Error(ERR_RETURN, "- config file: '%s'", filename);
5227 num_unknown_tokens++;
5230 Error(ERR_RETURN, "- token: '%s'", list->token);
5234 if (num_unknown_tokens > 0)
5235 Error(ERR_RETURN_LINE, "-");
5237 while (*num_elements % 4) /* pad with empty elements, if needed */
5238 (*elements)[(*num_elements)++] = EL_EMPTY;
5240 freeSetupFileList(setup_file_list);
5241 freeSetupFileHash(element_hash);
5244 for (i = 0; i < *num_elements; i++)
5245 printf("editor: element '%s' [%d]\n",
5246 element_info[(*elements)[i]].token_name, (*elements)[i]);
5250 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
5253 SetupFileHash *setup_file_hash = NULL;
5254 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
5255 char *filename_music, *filename_prefix, *filename_info;
5261 token_to_value_ptr[] =
5263 { "title_header", &tmp_music_file_info.title_header },
5264 { "artist_header", &tmp_music_file_info.artist_header },
5265 { "album_header", &tmp_music_file_info.album_header },
5266 { "year_header", &tmp_music_file_info.year_header },
5268 { "title", &tmp_music_file_info.title },
5269 { "artist", &tmp_music_file_info.artist },
5270 { "album", &tmp_music_file_info.album },
5271 { "year", &tmp_music_file_info.year },
5277 filename_music = (is_sound ? getCustomSoundFilename(basename) :
5278 getCustomMusicFilename(basename));
5280 if (filename_music == NULL)
5283 /* ---------- try to replace file extension ---------- */
5285 filename_prefix = getStringCopy(filename_music);
5286 if (strrchr(filename_prefix, '.') != NULL)
5287 *strrchr(filename_prefix, '.') = '\0';
5288 filename_info = getStringCat2(filename_prefix, ".txt");
5291 printf("trying to load file '%s'...\n", filename_info);
5294 if (fileExists(filename_info))
5295 setup_file_hash = loadSetupFileHash(filename_info);
5297 free(filename_prefix);
5298 free(filename_info);
5300 if (setup_file_hash == NULL)
5302 /* ---------- try to add file extension ---------- */
5304 filename_prefix = getStringCopy(filename_music);
5305 filename_info = getStringCat2(filename_prefix, ".txt");
5308 printf("trying to load file '%s'...\n", filename_info);
5311 if (fileExists(filename_info))
5312 setup_file_hash = loadSetupFileHash(filename_info);
5314 free(filename_prefix);
5315 free(filename_info);
5318 if (setup_file_hash == NULL)
5321 /* ---------- music file info found ---------- */
5323 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
5325 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
5327 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
5329 *token_to_value_ptr[i].value_ptr =
5330 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
5333 tmp_music_file_info.basename = getStringCopy(basename);
5334 tmp_music_file_info.music = music;
5335 tmp_music_file_info.is_sound = is_sound;
5337 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
5338 *new_music_file_info = tmp_music_file_info;
5340 return new_music_file_info;
5343 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
5345 return get_music_file_info_ext(basename, music, FALSE);
5348 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
5350 return get_music_file_info_ext(basename, sound, TRUE);
5353 static boolean music_info_listed_ext(struct MusicFileInfo *list,
5354 char *basename, boolean is_sound)
5356 for (; list != NULL; list = list->next)
5357 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
5363 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
5365 return music_info_listed_ext(list, basename, FALSE);
5368 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
5370 return music_info_listed_ext(list, basename, TRUE);
5373 void LoadMusicInfo()
5375 char *music_directory = getCustomMusicDirectory();
5376 int num_music = getMusicListSize();
5377 int num_music_noconf = 0;
5378 int num_sounds = getSoundListSize();
5380 struct dirent *dir_entry;
5381 struct FileInfo *music, *sound;
5382 struct MusicFileInfo *next, **new;
5385 while (music_file_info != NULL)
5387 next = music_file_info->next;
5389 checked_free(music_file_info->basename);
5391 checked_free(music_file_info->title_header);
5392 checked_free(music_file_info->artist_header);
5393 checked_free(music_file_info->album_header);
5394 checked_free(music_file_info->year_header);
5396 checked_free(music_file_info->title);
5397 checked_free(music_file_info->artist);
5398 checked_free(music_file_info->album);
5399 checked_free(music_file_info->year);
5401 free(music_file_info);
5403 music_file_info = next;
5406 new = &music_file_info;
5408 for (i = 0; i < num_music; i++)
5410 music = getMusicListEntry(i);
5412 if (music->filename == NULL)
5415 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
5418 /* a configured file may be not recognized as music */
5419 if (!FileIsMusic(music->filename))
5423 printf("::: -> '%s' (configured)\n", music->filename);
5426 if (!music_info_listed(music_file_info, music->filename))
5428 *new = get_music_file_info(music->filename, i);
5430 new = &(*new)->next;
5434 if ((dir = opendir(music_directory)) == NULL)
5436 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
5440 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
5442 char *basename = dir_entry->d_name;
5443 boolean music_already_used = FALSE;
5446 /* skip all music files that are configured in music config file */
5447 for (i = 0; i < num_music; i++)
5449 music = getMusicListEntry(i);
5451 if (music->filename == NULL)
5454 if (strcmp(basename, music->filename) == 0)
5456 music_already_used = TRUE;
5461 if (music_already_used)
5464 if (!FileIsMusic(basename))
5468 printf("::: -> '%s' (found in directory)\n", basename);
5471 if (!music_info_listed(music_file_info, basename))
5473 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5475 new = &(*new)->next;
5483 for (i = 0; i < num_sounds; i++)
5485 sound = getSoundListEntry(i);
5487 if (sound->filename == NULL)
5490 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5493 /* a configured file may be not recognized as sound */
5494 if (!FileIsSound(sound->filename))
5498 printf("::: -> '%s' (configured)\n", sound->filename);
5501 if (!sound_info_listed(music_file_info, sound->filename))
5503 *new = get_sound_file_info(sound->filename, i);
5505 new = &(*new)->next;
5510 for (next = music_file_info; next != NULL; next = next->next)
5511 printf("::: title == '%s'\n", next->title);
5515 void add_helpanim_entry(int element, int action, int direction, int delay,
5516 int *num_list_entries)
5518 struct HelpAnimInfo *new_list_entry;
5519 (*num_list_entries)++;
5522 checked_realloc(helpanim_info,
5523 *num_list_entries * sizeof(struct HelpAnimInfo));
5524 new_list_entry = &helpanim_info[*num_list_entries - 1];
5526 new_list_entry->element = element;
5527 new_list_entry->action = action;
5528 new_list_entry->direction = direction;
5529 new_list_entry->delay = delay;
5532 void print_unknown_token(char *filename, char *token, int token_nr)
5536 Error(ERR_RETURN_LINE, "-");
5537 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5538 Error(ERR_RETURN, "- config file: '%s'", filename);
5541 Error(ERR_RETURN, "- token: '%s'", token);
5544 void print_unknown_token_end(int token_nr)
5547 Error(ERR_RETURN_LINE, "-");
5550 void LoadHelpAnimInfo()
5552 char *filename = getHelpAnimFilename();
5553 SetupFileList *setup_file_list = NULL, *list;
5554 SetupFileHash *element_hash, *action_hash, *direction_hash;
5555 int num_list_entries = 0;
5556 int num_unknown_tokens = 0;
5559 if (fileExists(filename))
5560 setup_file_list = loadSetupFileList(filename);
5562 if (setup_file_list == NULL)
5564 /* use reliable default values from static configuration */
5565 SetupFileList *insert_ptr;
5567 insert_ptr = setup_file_list =
5568 newSetupFileList(helpanim_config[0].token,
5569 helpanim_config[0].value);
5571 for (i = 1; helpanim_config[i].token; i++)
5572 insert_ptr = addListEntry(insert_ptr,
5573 helpanim_config[i].token,
5574 helpanim_config[i].value);
5577 element_hash = newSetupFileHash();
5578 action_hash = newSetupFileHash();
5579 direction_hash = newSetupFileHash();
5581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5582 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5584 for (i = 0; i < NUM_ACTIONS; i++)
5585 setHashEntry(action_hash, element_action_info[i].suffix,
5586 i_to_a(element_action_info[i].value));
5588 /* do not store direction index (bit) here, but direction value! */
5589 for (i = 0; i < NUM_DIRECTIONS; i++)
5590 setHashEntry(direction_hash, element_direction_info[i].suffix,
5591 i_to_a(1 << element_direction_info[i].value));
5593 for (list = setup_file_list; list != NULL; list = list->next)
5595 char *element_token, *action_token, *direction_token;
5596 char *element_value, *action_value, *direction_value;
5597 int delay = atoi(list->value);
5599 if (strcmp(list->token, "end") == 0)
5601 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5606 /* first try to break element into element/action/direction parts;
5607 if this does not work, also accept combined "element[.act][.dir]"
5608 elements (like "dynamite.active"), which are unique elements */
5610 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
5612 element_value = getHashEntry(element_hash, list->token);
5613 if (element_value != NULL) /* element found */
5614 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5618 /* no further suffixes found -- this is not an element */
5619 print_unknown_token(filename, list->token, num_unknown_tokens++);
5625 /* token has format "<prefix>.<something>" */
5627 action_token = strchr(list->token, '.'); /* suffix may be action ... */
5628 direction_token = action_token; /* ... or direction */
5630 element_token = getStringCopy(list->token);
5631 *strchr(element_token, '.') = '\0';
5633 element_value = getHashEntry(element_hash, element_token);
5635 if (element_value == NULL) /* this is no element */
5637 element_value = getHashEntry(element_hash, list->token);
5638 if (element_value != NULL) /* combined element found */
5639 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5642 print_unknown_token(filename, list->token, num_unknown_tokens++);
5644 free(element_token);
5649 action_value = getHashEntry(action_hash, action_token);
5651 if (action_value != NULL) /* action found */
5653 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5656 free(element_token);
5661 direction_value = getHashEntry(direction_hash, direction_token);
5663 if (direction_value != NULL) /* direction found */
5665 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5668 free(element_token);
5673 if (strchr(action_token + 1, '.') == NULL)
5675 /* no further suffixes found -- this is not an action nor direction */
5677 element_value = getHashEntry(element_hash, list->token);
5678 if (element_value != NULL) /* combined element found */
5679 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5682 print_unknown_token(filename, list->token, num_unknown_tokens++);
5684 free(element_token);
5689 /* token has format "<prefix>.<suffix>.<something>" */
5691 direction_token = strchr(action_token + 1, '.');
5693 action_token = getStringCopy(action_token);
5694 *strchr(action_token + 1, '.') = '\0';
5696 action_value = getHashEntry(action_hash, action_token);
5698 if (action_value == NULL) /* this is no action */
5700 element_value = getHashEntry(element_hash, list->token);
5701 if (element_value != NULL) /* combined element found */
5702 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5705 print_unknown_token(filename, list->token, num_unknown_tokens++);
5707 free(element_token);
5713 direction_value = getHashEntry(direction_hash, direction_token);
5715 if (direction_value != NULL) /* direction found */
5717 add_helpanim_entry(atoi(element_value), atoi(action_value),
5718 atoi(direction_value), delay, &num_list_entries);
5720 free(element_token);
5726 /* this is no direction */
5728 element_value = getHashEntry(element_hash, list->token);
5729 if (element_value != NULL) /* combined element found */
5730 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5733 print_unknown_token(filename, list->token, num_unknown_tokens++);
5735 free(element_token);
5739 print_unknown_token_end(num_unknown_tokens);
5741 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5742 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
5744 freeSetupFileList(setup_file_list);
5745 freeSetupFileHash(element_hash);
5746 freeSetupFileHash(action_hash);
5747 freeSetupFileHash(direction_hash);
5750 for (i = 0; i < num_list_entries; i++)
5751 printf("::: %d, %d, %d => %d\n",
5752 helpanim_info[i].element,
5753 helpanim_info[i].action,
5754 helpanim_info[i].direction,
5755 helpanim_info[i].delay);
5759 void LoadHelpTextInfo()
5761 char *filename = getHelpTextFilename();
5764 if (helptext_info != NULL)
5766 freeSetupFileHash(helptext_info);
5767 helptext_info = NULL;
5770 if (fileExists(filename))
5771 helptext_info = loadSetupFileHash(filename);
5773 if (helptext_info == NULL)
5775 /* use reliable default values from static configuration */
5776 helptext_info = newSetupFileHash();
5778 for (i = 0; helptext_config[i].token; i++)
5779 setHashEntry(helptext_info,
5780 helptext_config[i].token,
5781 helptext_config[i].value);
5785 BEGIN_HASH_ITERATION(helptext_info, itr)
5787 printf("::: '%s' => '%s'\n",
5788 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5790 END_HASH_ITERATION(hash, itr)
5795 /* ------------------------------------------------------------------------- *
5797 * ------------------------------------------------------------------------- */
5799 #define MAX_NUM_CONVERT_LEVELS 1000
5801 void ConvertLevels()
5803 static LevelDirTree *convert_leveldir = NULL;
5804 static int convert_level_nr = -1;
5805 static int num_levels_handled = 0;
5806 static int num_levels_converted = 0;
5807 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5810 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5811 global.convert_leveldir);
5813 if (convert_leveldir == NULL)
5814 Error(ERR_EXIT, "no such level identifier: '%s'",
5815 global.convert_leveldir);
5817 leveldir_current = convert_leveldir;
5819 if (global.convert_level_nr != -1)
5821 convert_leveldir->first_level = global.convert_level_nr;
5822 convert_leveldir->last_level = global.convert_level_nr;
5825 convert_level_nr = convert_leveldir->first_level;
5827 printf_line("=", 79);
5828 printf("Converting levels\n");
5829 printf_line("-", 79);
5830 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5831 printf("Level series name: '%s'\n", convert_leveldir->name);
5832 printf("Level series author: '%s'\n", convert_leveldir->author);
5833 printf("Number of levels: %d\n", convert_leveldir->levels);
5834 printf_line("=", 79);
5837 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5838 levels_failed[i] = FALSE;
5840 while (convert_level_nr <= convert_leveldir->last_level)
5842 char *level_filename;
5845 level_nr = convert_level_nr++;
5847 printf("Level %03d: ", level_nr);
5849 LoadLevel(level_nr);
5850 if (level.no_valid_file)
5852 printf("(no level)\n");
5856 printf("converting level ... ");
5858 level_filename = getDefaultLevelFilename(level_nr);
5859 new_level = !fileExists(level_filename);
5863 SaveLevel(level_nr);
5865 num_levels_converted++;
5867 printf("converted.\n");
5871 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5872 levels_failed[level_nr] = TRUE;
5874 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5877 num_levels_handled++;
5881 printf_line("=", 79);
5882 printf("Number of levels handled: %d\n", num_levels_handled);
5883 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5884 (num_levels_handled ?
5885 num_levels_converted * 100 / num_levels_handled : 0));
5886 printf_line("-", 79);
5887 printf("Summary (for automatic parsing by scripts):\n");
5888 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5889 convert_leveldir->identifier, num_levels_converted,
5891 (num_levels_handled ?
5892 num_levels_converted * 100 / num_levels_handled : 0));
5894 if (num_levels_handled != num_levels_converted)
5896 printf(", FAILED:");
5897 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5898 if (levels_failed[i])
5903 printf_line("=", 79);