1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
25 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
26 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
27 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
29 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
30 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
31 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
33 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
34 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
36 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
37 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
38 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
39 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
40 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
41 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
42 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
43 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
44 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
45 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
46 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
48 /* (element number, number of change pages, change page number) */
49 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
51 /* (element number only) */
52 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
53 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
55 /* (nothing at all if unchanged) */
56 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
58 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
59 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
60 #define TAPE_CHUNK_HEAD_UNUSED 2 /* unused tape header bytes */
62 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
63 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
64 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
66 /* file identifier strings */
67 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
68 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
69 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
71 /* values for deciding when (not) to save configuration data */
72 #define SAVE_CONF_NEVER 0
73 #define SAVE_CONF_ALWAYS 1
74 #define SAVE_CONF_WHEN_CHANGED -1
76 /* values for chunks using micro chunks */
77 #define CONF_MASK_1_BYTE 0x00
78 #define CONF_MASK_2_BYTE 0x40
79 #define CONF_MASK_4_BYTE 0x80
80 #define CONF_MASK_MULTI_BYTES 0xc0
82 #define CONF_MASK_BYTES 0xc0
83 #define CONF_MASK_TOKEN 0x3f
85 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
86 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
87 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
88 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
90 /* these definitions are just for convenience of use and readability */
91 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
92 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
93 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
94 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
96 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
97 (x) == CONF_MASK_2_BYTE ? 2 : \
98 (x) == CONF_MASK_4_BYTE ? 4 : 0)
100 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
101 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
102 #define CONF_ELEMENT_NUM_BYTES (2)
104 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
105 (t) == TYPE_ELEMENT_LIST ? \
106 CONF_ELEMENT_NUM_BYTES : \
107 (t) == TYPE_CONTENT || \
108 (t) == TYPE_CONTENT_LIST ? \
109 CONF_CONTENT_NUM_BYTES : 1)
111 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
112 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
113 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
115 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
117 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
118 CONF_ELEMENT_NUM_BYTES)
119 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
120 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
122 /* temporary variables used to store pointers to structure members */
123 static struct LevelInfo li;
124 static struct ElementInfo xx_ei, yy_ei;
125 static struct ElementChangeInfo xx_change;
126 static struct ElementGroupInfo xx_group;
127 static struct EnvelopeInfo xx_envelope;
128 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
129 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
130 static int xx_num_contents;
131 static int xx_current_change_page;
132 static char xx_default_string_empty[1] = "";
133 static int xx_string_length_unused;
135 struct LevelFileConfigInfo
137 int element; /* element for which data is to be stored */
138 int save_type; /* save data always, never or when changed */
139 int data_type; /* data type (used internally, not stored) */
140 int conf_type; /* micro chunk identifier (stored in file) */
143 void *value; /* variable that holds the data to be stored */
144 int default_value; /* initial default value for this variable */
147 void *value_copy; /* variable that holds the data to be copied */
148 void *num_entities; /* number of entities for multi-byte data */
149 int default_num_entities; /* default number of entities for this data */
150 int max_num_entities; /* maximal number of entities for this data */
151 char *default_string; /* optional default string for string data */
154 static struct LevelFileConfigInfo chunk_config_INFO[] =
156 /* ---------- values not related to single elements ----------------------- */
159 -1, SAVE_CONF_ALWAYS,
160 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
161 &li.game_engine_type, GAME_ENGINE_TYPE_RND
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
167 &li.fieldx, STD_LEV_FIELDX
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
172 &li.fieldy, STD_LEV_FIELDY
176 -1, SAVE_CONF_ALWAYS,
177 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
182 -1, SAVE_CONF_ALWAYS,
183 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
189 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
208 &li.em_slippery_gems, FALSE
213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
214 &li.use_custom_template, FALSE
219 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
220 &li.can_move_into_acid_bits, ~0 /* default: everything can */
225 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
226 &li.dont_collide_with_bits, ~0 /* default: always deadly */
231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
232 &li.em_explodes_by_fire, FALSE
237 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
238 &li.score[SC_TIME_BONUS], 1
243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
244 &li.auto_exit_sokoban, FALSE
249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
250 &li.auto_count_gems, FALSE
260 static struct LevelFileConfigInfo chunk_config_ELEM[] =
262 /* (these values are the same for each player) */
265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
266 &li.block_last_field, FALSE /* default case for EM levels */
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
271 &li.sp_block_last_field, TRUE /* default case for SP levels */
275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
276 &li.instant_relocation, FALSE
280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
281 &li.can_pass_to_walkable, FALSE
285 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
286 &li.block_snap_field, TRUE
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
291 &li.continuous_snapping, TRUE
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
296 &li.shifted_relocation, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
301 &li.lazy_relocation, FALSE
304 /* (these values are different for each player) */
307 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
308 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
313 &li.initial_player_gravity[0], FALSE
317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
318 &li.use_start_element[0], FALSE
322 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
323 &li.start_element[0], EL_PLAYER_1
327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
328 &li.use_artwork_element[0], FALSE
332 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
333 &li.artwork_element[0], EL_PLAYER_1
337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
338 &li.use_explosion_element[0], FALSE
342 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
343 &li.explosion_element[0], EL_PLAYER_1
347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
348 &li.use_initial_inventory[0], FALSE
352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
353 &li.initial_inventory_size[0], 1
357 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
358 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
359 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
364 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
365 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
370 &li.initial_player_gravity[1], FALSE
374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
375 &li.use_start_element[1], FALSE
379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
380 &li.start_element[1], EL_PLAYER_2
384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
385 &li.use_artwork_element[1], FALSE
389 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
390 &li.artwork_element[1], EL_PLAYER_2
394 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
395 &li.use_explosion_element[1], FALSE
399 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
400 &li.explosion_element[1], EL_PLAYER_2
404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
405 &li.use_initial_inventory[1], FALSE
409 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
410 &li.initial_inventory_size[1], 1
414 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
415 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
416 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
421 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
422 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
427 &li.initial_player_gravity[2], FALSE
431 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
432 &li.use_start_element[2], FALSE
436 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
437 &li.start_element[2], EL_PLAYER_3
441 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
442 &li.use_artwork_element[2], FALSE
446 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
447 &li.artwork_element[2], EL_PLAYER_3
451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
452 &li.use_explosion_element[2], FALSE
456 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
457 &li.explosion_element[2], EL_PLAYER_3
461 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
462 &li.use_initial_inventory[2], FALSE
466 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
467 &li.initial_inventory_size[2], 1
471 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
472 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
473 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
478 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
479 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
484 &li.initial_player_gravity[3], FALSE
488 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
489 &li.use_start_element[3], FALSE
493 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
494 &li.start_element[3], EL_PLAYER_4
498 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
499 &li.use_artwork_element[3], FALSE
503 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
504 &li.artwork_element[3], EL_PLAYER_4
508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
509 &li.use_explosion_element[3], FALSE
513 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
514 &li.explosion_element[3], EL_PLAYER_4
518 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
519 &li.use_initial_inventory[3], FALSE
523 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
524 &li.initial_inventory_size[3], 1
528 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
529 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
530 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
535 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
536 &li.score[SC_EMERALD], 10
541 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
542 &li.score[SC_DIAMOND], 10
547 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
548 &li.score[SC_BUG], 10
553 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
554 &li.score[SC_SPACESHIP], 10
559 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
560 &li.score[SC_PACMAN], 10
565 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
566 &li.score[SC_NUT], 10
571 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
572 &li.score[SC_DYNAMITE], 10
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_KEY], 10
583 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
584 &li.score[SC_PEARL], 10
589 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
590 &li.score[SC_CRYSTAL], 10
595 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
596 &li.amoeba_content, EL_DIAMOND
600 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
606 &li.grow_into_diggable, TRUE
611 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
612 &li.yamyam_content, EL_ROCK, NULL,
613 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
617 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
618 &li.score[SC_YAMYAM], 10
623 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
624 &li.score[SC_ROBOT], 10
628 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
640 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
641 &li.time_magic_wall, 10
646 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
647 &li.game_of_life[0], 2
651 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
652 &li.game_of_life[1], 3
656 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
657 &li.game_of_life[2], 3
661 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
662 &li.game_of_life[3], 3
667 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
672 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
677 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
682 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
687 EL_TIMEGATE_SWITCH, -1,
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.time_timegate, 10
693 EL_LIGHT_SWITCH_ACTIVE, -1,
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
699 EL_SHIELD_NORMAL, -1,
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.shield_normal_time, 10
704 EL_SHIELD_NORMAL, -1,
705 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
706 &li.score[SC_SHIELD], 10
710 EL_SHIELD_DEADLY, -1,
711 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
712 &li.shield_deadly_time, 10
715 EL_SHIELD_DEADLY, -1,
716 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
717 &li.score[SC_SHIELD], 10
722 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
728 &li.extra_time_score, 10
732 EL_TIME_ORB_FULL, -1,
733 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
734 &li.time_orb_time, 10
737 EL_TIME_ORB_FULL, -1,
738 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
739 &li.use_time_orb_bug, FALSE
744 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
745 &li.use_spring_bug, FALSE
750 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 &li.android_move_time, 10
755 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
756 &li.android_clone_time, 10
760 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
761 &li.android_clone_element[0], EL_EMPTY, NULL,
762 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
767 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
777 EL_EMC_MAGNIFIER, -1,
778 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
779 &li.magnify_score, 10
782 EL_EMC_MAGNIFIER, -1,
783 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
788 EL_EMC_MAGIC_BALL, -1,
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
793 EL_EMC_MAGIC_BALL, -1,
794 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
795 &li.ball_random, FALSE
798 EL_EMC_MAGIC_BALL, -1,
799 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
800 &li.ball_state_initial, FALSE
803 EL_EMC_MAGIC_BALL, -1,
804 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
805 &li.ball_content, EL_EMPTY, NULL,
806 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
812 &li.mm_laser_red, FALSE
816 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
817 &li.mm_laser_green, FALSE
821 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
822 &li.mm_laser_blue, TRUE
827 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
828 &li.df_laser_red, TRUE
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
833 &li.df_laser_green, TRUE
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
838 &li.df_laser_blue, FALSE
842 EL_MM_FUSE_ACTIVE, -1,
843 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
848 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
853 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
857 EL_MM_STEEL_BLOCK, -1,
858 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
859 &li.mm_time_block, 75
863 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
864 &li.score[SC_ELEM_BONUS], 10
867 /* ---------- unused values ----------------------------------------------- */
870 EL_UNKNOWN, SAVE_CONF_NEVER,
871 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
872 &li.score[SC_UNKNOWN_15], 10
882 static struct LevelFileConfigInfo chunk_config_NOTE[] =
886 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
887 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
891 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
892 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
897 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
898 &xx_envelope.autowrap, FALSE
902 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
903 &xx_envelope.centered, FALSE
908 TYPE_STRING, CONF_VALUE_BYTES(1),
909 &xx_envelope.text, -1, NULL,
910 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
911 &xx_default_string_empty[0]
921 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
925 TYPE_STRING, CONF_VALUE_BYTES(1),
926 &xx_ei.description[0], -1,
927 &yy_ei.description[0],
928 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
929 &xx_default_description[0]
934 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
935 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
936 &yy_ei.properties[EP_BITFIELD_BASE_NR]
938 #if ENABLE_RESERVED_CODE
939 /* (reserved for later use) */
942 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
943 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
944 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
950 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
951 &xx_ei.use_gfx_element, FALSE,
952 &yy_ei.use_gfx_element
956 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
957 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
958 &yy_ei.gfx_element_initial
963 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
964 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
965 &yy_ei.access_direction
970 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
971 &xx_ei.collect_score_initial, 10,
972 &yy_ei.collect_score_initial
976 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
977 &xx_ei.collect_count_initial, 1,
978 &yy_ei.collect_count_initial
983 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
984 &xx_ei.ce_value_fixed_initial, 0,
985 &yy_ei.ce_value_fixed_initial
989 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
990 &xx_ei.ce_value_random_initial, 0,
991 &yy_ei.ce_value_random_initial
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
996 &xx_ei.use_last_ce_value, FALSE,
997 &yy_ei.use_last_ce_value
1002 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1003 &xx_ei.push_delay_fixed, 8,
1004 &yy_ei.push_delay_fixed
1008 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1009 &xx_ei.push_delay_random, 8,
1010 &yy_ei.push_delay_random
1014 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1015 &xx_ei.drop_delay_fixed, 0,
1016 &yy_ei.drop_delay_fixed
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1021 &xx_ei.drop_delay_random, 0,
1022 &yy_ei.drop_delay_random
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1027 &xx_ei.move_delay_fixed, 0,
1028 &yy_ei.move_delay_fixed
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1033 &xx_ei.move_delay_random, 0,
1034 &yy_ei.move_delay_random
1039 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1040 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1045 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1046 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1047 &yy_ei.move_direction_initial
1051 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1052 &xx_ei.move_stepsize, TILEX / 8,
1053 &yy_ei.move_stepsize
1058 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1059 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1060 &yy_ei.move_enter_element
1064 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1065 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1066 &yy_ei.move_leave_element
1070 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1071 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1072 &yy_ei.move_leave_type
1077 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1078 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1079 &yy_ei.slippery_type
1084 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1085 &xx_ei.explosion_type, EXPLODES_3X3,
1086 &yy_ei.explosion_type
1090 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1091 &xx_ei.explosion_delay, 16,
1092 &yy_ei.explosion_delay
1096 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1097 &xx_ei.ignition_delay, 8,
1098 &yy_ei.ignition_delay
1103 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1104 &xx_ei.content, EL_EMPTY_SPACE,
1106 &xx_num_contents, 1, 1
1109 /* ---------- "num_change_pages" must be the last entry ------------------- */
1112 -1, SAVE_CONF_ALWAYS,
1113 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1114 &xx_ei.num_change_pages, 1,
1115 &yy_ei.num_change_pages
1126 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1128 /* ---------- "current_change_page" must be the first entry --------------- */
1131 -1, SAVE_CONF_ALWAYS,
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1133 &xx_current_change_page, -1
1136 /* ---------- (the remaining entries can be in any order) ----------------- */
1140 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1141 &xx_change.can_change, FALSE
1146 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1147 &xx_event_bits[0], 0
1151 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1152 &xx_event_bits[1], 0
1157 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1158 &xx_change.trigger_player, CH_PLAYER_ANY
1162 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1163 &xx_change.trigger_side, CH_SIDE_ANY
1167 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1168 &xx_change.trigger_page, CH_PAGE_ANY
1173 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1174 &xx_change.target_element, EL_EMPTY_SPACE
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1180 &xx_change.delay_fixed, 0
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1185 &xx_change.delay_random, 0
1189 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1190 &xx_change.delay_frames, FRAMES_PER_SECOND
1195 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1196 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1201 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1202 &xx_change.explode, FALSE
1206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1207 &xx_change.use_target_content, FALSE
1211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1212 &xx_change.only_if_complete, FALSE
1216 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1217 &xx_change.use_random_replace, FALSE
1221 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1222 &xx_change.random_percentage, 100
1226 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1227 &xx_change.replace_when, CP_WHEN_EMPTY
1232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1233 &xx_change.has_action, FALSE
1237 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1238 &xx_change.action_type, CA_NO_ACTION
1242 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1243 &xx_change.action_mode, CA_MODE_UNDEFINED
1247 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1248 &xx_change.action_arg, CA_ARG_UNDEFINED
1253 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1254 &xx_change.action_element, EL_EMPTY_SPACE
1259 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1260 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1261 &xx_num_contents, 1, 1
1271 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1275 TYPE_STRING, CONF_VALUE_BYTES(1),
1276 &xx_ei.description[0], -1, NULL,
1277 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1278 &xx_default_description[0]
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1284 &xx_ei.use_gfx_element, FALSE
1288 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1289 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1294 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1295 &xx_group.choice_mode, ANIM_RANDOM
1300 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1301 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1302 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1312 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1317 &li.block_snap_field, TRUE
1321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1322 &li.continuous_snapping, TRUE
1326 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1327 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1332 &li.use_start_element[0], FALSE
1336 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1337 &li.start_element[0], EL_PLAYER_1
1341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1342 &li.use_artwork_element[0], FALSE
1346 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1347 &li.artwork_element[0], EL_PLAYER_1
1351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1352 &li.use_explosion_element[0], FALSE
1356 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1357 &li.explosion_element[0], EL_PLAYER_1
1372 filetype_id_list[] =
1374 { LEVEL_FILE_TYPE_RND, "RND" },
1375 { LEVEL_FILE_TYPE_BD, "BD" },
1376 { LEVEL_FILE_TYPE_EM, "EM" },
1377 { LEVEL_FILE_TYPE_SP, "SP" },
1378 { LEVEL_FILE_TYPE_DX, "DX" },
1379 { LEVEL_FILE_TYPE_SB, "SB" },
1380 { LEVEL_FILE_TYPE_DC, "DC" },
1381 { LEVEL_FILE_TYPE_MM, "MM" },
1382 { LEVEL_FILE_TYPE_MM, "DF" },
1387 /* ========================================================================= */
1388 /* level file functions */
1389 /* ========================================================================= */
1391 static boolean check_special_flags(char *flag)
1393 if (strEqual(options.special_flags, flag) ||
1394 strEqual(leveldir_current->special_flags, flag))
1400 static struct DateInfo getCurrentDate()
1402 time_t epoch_seconds = time(NULL);
1403 struct tm *now = localtime(&epoch_seconds);
1404 struct DateInfo date;
1406 date.year = now->tm_year + 1900;
1407 date.month = now->tm_mon + 1;
1408 date.day = now->tm_mday;
1410 date.src = DATE_SRC_CLOCK;
1415 static void resetEventFlags(struct ElementChangeInfo *change)
1419 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1420 change->has_event[i] = FALSE;
1423 static void resetEventBits()
1427 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1428 xx_event_bits[i] = 0;
1431 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1435 /* important: only change event flag if corresponding event bit is set
1436 (this is because all xx_event_bits[] values are loaded separately,
1437 and all xx_event_bits[] values are set back to zero before loading
1438 another value xx_event_bits[x] (each value representing 32 flags)) */
1440 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1441 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1442 change->has_event[i] = TRUE;
1445 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1449 /* in contrast to the above function setEventFlagsFromEventBits(), it
1450 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1451 depending on the corresponding change->has_event[i] values here, as
1452 all xx_event_bits[] values are reset in resetEventBits() before */
1454 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1455 if (change->has_event[i])
1456 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1459 static char *getDefaultElementDescription(struct ElementInfo *ei)
1461 static char description[MAX_ELEMENT_NAME_LEN + 1];
1462 char *default_description = (ei->custom_description != NULL ?
1463 ei->custom_description :
1464 ei->editor_description);
1467 /* always start with reliable default values */
1468 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1469 description[i] = '\0';
1471 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1472 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1474 return &description[0];
1477 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1479 char *default_description = getDefaultElementDescription(ei);
1482 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1483 ei->description[i] = default_description[i];
1486 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1490 for (i = 0; conf[i].data_type != -1; i++)
1492 int default_value = conf[i].default_value;
1493 int data_type = conf[i].data_type;
1494 int conf_type = conf[i].conf_type;
1495 int byte_mask = conf_type & CONF_MASK_BYTES;
1497 if (byte_mask == CONF_MASK_MULTI_BYTES)
1499 int default_num_entities = conf[i].default_num_entities;
1500 int max_num_entities = conf[i].max_num_entities;
1502 *(int *)(conf[i].num_entities) = default_num_entities;
1504 if (data_type == TYPE_STRING)
1506 char *default_string = conf[i].default_string;
1507 char *string = (char *)(conf[i].value);
1509 strncpy(string, default_string, max_num_entities);
1511 else if (data_type == TYPE_ELEMENT_LIST)
1513 int *element_array = (int *)(conf[i].value);
1516 for (j = 0; j < max_num_entities; j++)
1517 element_array[j] = default_value;
1519 else if (data_type == TYPE_CONTENT_LIST)
1521 struct Content *content = (struct Content *)(conf[i].value);
1524 for (c = 0; c < max_num_entities; c++)
1525 for (y = 0; y < 3; y++)
1526 for (x = 0; x < 3; x++)
1527 content[c].e[x][y] = default_value;
1530 else /* constant size configuration data (1, 2 or 4 bytes) */
1532 if (data_type == TYPE_BOOLEAN)
1533 *(boolean *)(conf[i].value) = default_value;
1535 *(int *) (conf[i].value) = default_value;
1540 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1544 for (i = 0; conf[i].data_type != -1; i++)
1546 int data_type = conf[i].data_type;
1547 int conf_type = conf[i].conf_type;
1548 int byte_mask = conf_type & CONF_MASK_BYTES;
1550 if (byte_mask == CONF_MASK_MULTI_BYTES)
1552 int max_num_entities = conf[i].max_num_entities;
1554 if (data_type == TYPE_STRING)
1556 char *string = (char *)(conf[i].value);
1557 char *string_copy = (char *)(conf[i].value_copy);
1559 strncpy(string_copy, string, max_num_entities);
1561 else if (data_type == TYPE_ELEMENT_LIST)
1563 int *element_array = (int *)(conf[i].value);
1564 int *element_array_copy = (int *)(conf[i].value_copy);
1567 for (j = 0; j < max_num_entities; j++)
1568 element_array_copy[j] = element_array[j];
1570 else if (data_type == TYPE_CONTENT_LIST)
1572 struct Content *content = (struct Content *)(conf[i].value);
1573 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1576 for (c = 0; c < max_num_entities; c++)
1577 for (y = 0; y < 3; y++)
1578 for (x = 0; x < 3; x++)
1579 content_copy[c].e[x][y] = content[c].e[x][y];
1582 else /* constant size configuration data (1, 2 or 4 bytes) */
1584 if (data_type == TYPE_BOOLEAN)
1585 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1587 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1592 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1596 xx_ei = *ei_from; /* copy element data into temporary buffer */
1597 yy_ei = *ei_to; /* copy element data into temporary buffer */
1599 copyConfigFromConfigList(chunk_config_CUSX_base);
1604 /* ---------- reinitialize and copy change pages ---------- */
1606 ei_to->num_change_pages = ei_from->num_change_pages;
1607 ei_to->current_change_page = ei_from->current_change_page;
1609 setElementChangePages(ei_to, ei_to->num_change_pages);
1611 for (i = 0; i < ei_to->num_change_pages; i++)
1612 ei_to->change_page[i] = ei_from->change_page[i];
1614 /* ---------- copy group element info ---------- */
1615 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1616 *ei_to->group = *ei_from->group;
1618 /* mark this custom element as modified */
1619 ei_to->modified_settings = TRUE;
1622 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1624 int change_page_size = sizeof(struct ElementChangeInfo);
1626 ei->num_change_pages = MAX(1, change_pages);
1629 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1631 if (ei->current_change_page >= ei->num_change_pages)
1632 ei->current_change_page = ei->num_change_pages - 1;
1634 ei->change = &ei->change_page[ei->current_change_page];
1637 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1639 xx_change = *change; /* copy change data into temporary buffer */
1641 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1643 *change = xx_change;
1645 resetEventFlags(change);
1647 change->direct_action = 0;
1648 change->other_action = 0;
1650 change->pre_change_function = NULL;
1651 change->change_function = NULL;
1652 change->post_change_function = NULL;
1655 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1659 li = *level; /* copy level data into temporary buffer */
1660 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1661 *level = li; /* copy temporary buffer back to level data */
1663 setLevelInfoToDefaults_EM();
1664 setLevelInfoToDefaults_SP();
1665 setLevelInfoToDefaults_MM();
1667 level->native_em_level = &native_em_level;
1668 level->native_sp_level = &native_sp_level;
1669 level->native_mm_level = &native_mm_level;
1671 level->file_version = FILE_VERSION_ACTUAL;
1672 level->game_version = GAME_VERSION_ACTUAL;
1674 level->creation_date = getCurrentDate();
1676 level->encoding_16bit_field = TRUE;
1677 level->encoding_16bit_yamyam = TRUE;
1678 level->encoding_16bit_amoeba = TRUE;
1680 /* clear level name and level author string buffers */
1681 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1682 level->name[i] = '\0';
1683 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1684 level->author[i] = '\0';
1686 /* set level name and level author to default values */
1687 strcpy(level->name, NAMELESS_LEVEL_NAME);
1688 strcpy(level->author, ANONYMOUS_NAME);
1690 /* set level playfield to playable default level with player and exit */
1691 for (x = 0; x < MAX_LEV_FIELDX; x++)
1692 for (y = 0; y < MAX_LEV_FIELDY; y++)
1693 level->field[x][y] = EL_SAND;
1695 level->field[0][0] = EL_PLAYER_1;
1696 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1698 BorderElement = EL_STEELWALL;
1700 /* detect custom elements when loading them */
1701 level->file_has_custom_elements = FALSE;
1703 /* set all bug compatibility flags to "false" => do not emulate this bug */
1704 level->use_action_after_change_bug = FALSE;
1706 if (leveldir_current)
1708 /* try to determine better author name than 'anonymous' */
1709 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1711 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1712 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1716 switch (LEVELCLASS(leveldir_current))
1718 case LEVELCLASS_TUTORIAL:
1719 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1722 case LEVELCLASS_CONTRIB:
1723 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1724 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1727 case LEVELCLASS_PRIVATE:
1728 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1729 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1733 /* keep default value */
1740 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1742 static boolean clipboard_elements_initialized = FALSE;
1745 InitElementPropertiesStatic();
1747 li = *level; /* copy level data into temporary buffer */
1748 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1749 *level = li; /* copy temporary buffer back to level data */
1751 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1754 struct ElementInfo *ei = &element_info[element];
1756 /* never initialize clipboard elements after the very first time */
1757 /* (to be able to use clipboard elements between several levels) */
1758 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1761 if (IS_ENVELOPE(element))
1763 int envelope_nr = element - EL_ENVELOPE_1;
1765 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1767 level->envelope[envelope_nr] = xx_envelope;
1770 if (IS_CUSTOM_ELEMENT(element) ||
1771 IS_GROUP_ELEMENT(element) ||
1772 IS_INTERNAL_ELEMENT(element))
1774 xx_ei = *ei; /* copy element data into temporary buffer */
1776 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1781 setElementChangePages(ei, 1);
1782 setElementChangeInfoToDefaults(ei->change);
1784 if (IS_CUSTOM_ELEMENT(element) ||
1785 IS_GROUP_ELEMENT(element) ||
1786 IS_INTERNAL_ELEMENT(element))
1788 setElementDescriptionToDefault(ei);
1790 ei->modified_settings = FALSE;
1793 if (IS_CUSTOM_ELEMENT(element) ||
1794 IS_INTERNAL_ELEMENT(element))
1796 /* internal values used in level editor */
1798 ei->access_type = 0;
1799 ei->access_layer = 0;
1800 ei->access_protected = 0;
1801 ei->walk_to_action = 0;
1802 ei->smash_targets = 0;
1805 ei->can_explode_by_fire = FALSE;
1806 ei->can_explode_smashed = FALSE;
1807 ei->can_explode_impact = FALSE;
1809 ei->current_change_page = 0;
1812 if (IS_GROUP_ELEMENT(element) ||
1813 IS_INTERNAL_ELEMENT(element))
1815 struct ElementGroupInfo *group;
1817 /* initialize memory for list of elements in group */
1818 if (ei->group == NULL)
1819 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1823 xx_group = *group; /* copy group data into temporary buffer */
1825 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1831 clipboard_elements_initialized = TRUE;
1834 static void setLevelInfoToDefaults(struct LevelInfo *level,
1835 boolean level_info_only,
1836 boolean reset_file_status)
1838 setLevelInfoToDefaults_Level(level);
1840 if (!level_info_only)
1841 setLevelInfoToDefaults_Elements(level);
1843 if (reset_file_status)
1845 level->no_valid_file = FALSE;
1846 level->no_level_file = FALSE;
1849 level->changed = FALSE;
1852 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1854 level_file_info->nr = 0;
1855 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1856 level_file_info->packed = FALSE;
1857 level_file_info->basename = NULL;
1858 level_file_info->filename = NULL;
1861 int getMappedElement_SB(int, boolean);
1863 static void ActivateLevelTemplate()
1867 if (check_special_flags("load_xsb_to_ces"))
1869 /* fill smaller playfields with padding "beyond border wall" elements */
1870 if (level.fieldx < level_template.fieldx ||
1871 level.fieldy < level_template.fieldy)
1873 short field[level.fieldx][level.fieldy];
1874 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1875 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1876 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1877 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1879 /* copy old playfield (which is smaller than the visible area) */
1880 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1881 field[x][y] = level.field[x][y];
1883 /* fill new, larger playfield with "beyond border wall" elements */
1884 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1885 level.field[x][y] = getMappedElement_SB('_', TRUE);
1887 /* copy the old playfield to the middle of the new playfield */
1888 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1889 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1891 level.fieldx = new_fieldx;
1892 level.fieldy = new_fieldy;
1896 /* Currently there is no special action needed to activate the template
1897 data, because 'element_info' property settings overwrite the original
1898 level data, while all other variables do not change. */
1900 /* Exception: 'from_level_template' elements in the original level playfield
1901 are overwritten with the corresponding elements at the same position in
1902 playfield from the level template. */
1904 for (x = 0; x < level.fieldx; x++)
1905 for (y = 0; y < level.fieldy; y++)
1906 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1907 level.field[x][y] = level_template.field[x][y];
1909 if (check_special_flags("load_xsb_to_ces"))
1911 struct LevelInfo level_backup = level;
1913 /* overwrite all individual level settings from template level settings */
1914 level = level_template;
1916 /* restore playfield size */
1917 level.fieldx = level_backup.fieldx;
1918 level.fieldy = level_backup.fieldy;
1920 /* restore playfield content */
1921 for (x = 0; x < level.fieldx; x++)
1922 for (y = 0; y < level.fieldy; y++)
1923 level.field[x][y] = level_backup.field[x][y];
1925 /* restore name and author from individual level */
1926 strcpy(level.name, level_backup.name);
1927 strcpy(level.author, level_backup.author);
1929 /* restore flag "use_custom_template" */
1930 level.use_custom_template = level_backup.use_custom_template;
1934 static char *getLevelFilenameFromBasename(char *basename)
1936 static char *filename[2] = { NULL, NULL };
1937 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1939 checked_free(filename[pos]);
1941 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1943 return filename[pos];
1946 static int getFileTypeFromBasename(char *basename)
1948 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1950 static char *filename = NULL;
1951 struct stat file_status;
1953 /* ---------- try to determine file type from filename ---------- */
1955 /* check for typical filename of a Supaplex level package file */
1956 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1957 return LEVEL_FILE_TYPE_SP;
1959 /* check for typical filename of a Diamond Caves II level package file */
1960 if (strSuffixLower(basename, ".dc") ||
1961 strSuffixLower(basename, ".dc2"))
1962 return LEVEL_FILE_TYPE_DC;
1964 /* check for typical filename of a Sokoban level package file */
1965 if (strSuffixLower(basename, ".xsb") &&
1966 strchr(basename, '%') == NULL)
1967 return LEVEL_FILE_TYPE_SB;
1969 /* ---------- try to determine file type from filesize ---------- */
1971 checked_free(filename);
1972 filename = getPath2(getCurrentLevelDir(), basename);
1974 if (stat(filename, &file_status) == 0)
1976 /* check for typical filesize of a Supaplex level package file */
1977 if (file_status.st_size == 170496)
1978 return LEVEL_FILE_TYPE_SP;
1981 return LEVEL_FILE_TYPE_UNKNOWN;
1984 static int getFileTypeFromMagicBytes(char *filename, int type)
1988 if ((file = openFile(filename, MODE_READ)))
1990 char chunk_name[CHUNK_ID_LEN + 1];
1992 getFileChunkBE(file, chunk_name, NULL);
1994 if (strEqual(chunk_name, "MMII") ||
1995 strEqual(chunk_name, "MIRR"))
1996 type = LEVEL_FILE_TYPE_MM;
2004 static boolean checkForPackageFromBasename(char *basename)
2006 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2007 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2009 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2012 static char *getSingleLevelBasenameExt(int nr, char *extension)
2014 static char basename[MAX_FILENAME_LEN];
2017 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2019 sprintf(basename, "%03d.%s", nr, extension);
2024 static char *getSingleLevelBasename(int nr)
2026 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2029 static char *getPackedLevelBasename(int type)
2031 static char basename[MAX_FILENAME_LEN];
2032 char *directory = getCurrentLevelDir();
2034 DirectoryEntry *dir_entry;
2036 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2038 if ((dir = openDirectory(directory)) == NULL)
2040 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2045 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2047 char *entry_basename = dir_entry->basename;
2048 int entry_type = getFileTypeFromBasename(entry_basename);
2050 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2052 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2055 strcpy(basename, entry_basename);
2062 closeDirectory(dir);
2067 static char *getSingleLevelFilename(int nr)
2069 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2072 #if ENABLE_UNUSED_CODE
2073 static char *getPackedLevelFilename(int type)
2075 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2079 char *getDefaultLevelFilename(int nr)
2081 return getSingleLevelFilename(nr);
2084 #if ENABLE_UNUSED_CODE
2085 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2089 lfi->packed = FALSE;
2090 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2091 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2095 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2096 int type, char *format, ...)
2098 static char basename[MAX_FILENAME_LEN];
2101 va_start(ap, format);
2102 vsprintf(basename, format, ap);
2106 lfi->packed = FALSE;
2107 lfi->basename = basename;
2108 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2111 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2116 lfi->basename = getPackedLevelBasename(lfi->type);
2117 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2120 static int getFiletypeFromID(char *filetype_id)
2122 char *filetype_id_lower;
2123 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2126 if (filetype_id == NULL)
2127 return LEVEL_FILE_TYPE_UNKNOWN;
2129 filetype_id_lower = getStringToLower(filetype_id);
2131 for (i = 0; filetype_id_list[i].id != NULL; i++)
2133 char *id_lower = getStringToLower(filetype_id_list[i].id);
2135 if (strEqual(filetype_id_lower, id_lower))
2136 filetype = filetype_id_list[i].filetype;
2140 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2144 free(filetype_id_lower);
2149 char *getLocalLevelTemplateFilename()
2151 return getDefaultLevelFilename(-1);
2154 char *getGlobalLevelTemplateFilename()
2156 /* global variable "leveldir_current" must be modified in the loop below */
2157 LevelDirTree *leveldir_current_last = leveldir_current;
2158 char *filename = NULL;
2160 /* check for template level in path from current to topmost tree node */
2162 while (leveldir_current != NULL)
2164 filename = getDefaultLevelFilename(-1);
2166 if (fileExists(filename))
2169 leveldir_current = leveldir_current->node_parent;
2172 /* restore global variable "leveldir_current" modified in above loop */
2173 leveldir_current = leveldir_current_last;
2178 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2182 /* special case: level number is negative => check for level template file */
2185 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2186 getSingleLevelBasename(-1));
2188 /* replace local level template filename with global template filename */
2189 lfi->filename = getGlobalLevelTemplateFilename();
2191 /* no fallback if template file not existing */
2195 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2196 if (leveldir_current->level_filename != NULL)
2198 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2200 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2201 leveldir_current->level_filename, nr);
2203 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2205 if (fileExists(lfi->filename))
2208 else if (leveldir_current->level_filetype != NULL)
2210 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2212 /* check for specified native level file with standard file name */
2213 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2214 "%03d.%s", nr, LEVELFILE_EXTENSION);
2215 if (fileExists(lfi->filename))
2219 /* check for native Rocks'n'Diamonds level file */
2220 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2221 "%03d.%s", nr, LEVELFILE_EXTENSION);
2222 if (fileExists(lfi->filename))
2225 /* check for Emerald Mine level file (V1) */
2226 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2227 'a' + (nr / 10) % 26, '0' + nr % 10);
2228 if (fileExists(lfi->filename))
2230 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2231 'A' + (nr / 10) % 26, '0' + nr % 10);
2232 if (fileExists(lfi->filename))
2235 /* check for Emerald Mine level file (V2 to V5) */
2236 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2237 if (fileExists(lfi->filename))
2240 /* check for Emerald Mine level file (V6 / single mode) */
2241 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2242 if (fileExists(lfi->filename))
2244 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2245 if (fileExists(lfi->filename))
2248 /* check for Emerald Mine level file (V6 / teamwork mode) */
2249 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2250 if (fileExists(lfi->filename))
2252 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2253 if (fileExists(lfi->filename))
2256 /* check for various packed level file formats */
2257 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2258 if (fileExists(lfi->filename))
2261 /* no known level file found -- use default values (and fail later) */
2262 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2263 "%03d.%s", nr, LEVELFILE_EXTENSION);
2266 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2268 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2269 lfi->type = getFileTypeFromBasename(lfi->basename);
2271 if (lfi->type == LEVEL_FILE_TYPE_RND)
2272 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2275 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2277 /* always start with reliable default values */
2278 setFileInfoToDefaults(level_file_info);
2280 level_file_info->nr = nr; /* set requested level number */
2282 determineLevelFileInfo_Filename(level_file_info);
2283 determineLevelFileInfo_Filetype(level_file_info);
2286 /* ------------------------------------------------------------------------- */
2287 /* functions for loading R'n'D level */
2288 /* ------------------------------------------------------------------------- */
2290 int getMappedElement(int element)
2292 /* remap some (historic, now obsolete) elements */
2296 case EL_PLAYER_OBSOLETE:
2297 element = EL_PLAYER_1;
2300 case EL_KEY_OBSOLETE:
2304 case EL_EM_KEY_1_FILE_OBSOLETE:
2305 element = EL_EM_KEY_1;
2308 case EL_EM_KEY_2_FILE_OBSOLETE:
2309 element = EL_EM_KEY_2;
2312 case EL_EM_KEY_3_FILE_OBSOLETE:
2313 element = EL_EM_KEY_3;
2316 case EL_EM_KEY_4_FILE_OBSOLETE:
2317 element = EL_EM_KEY_4;
2320 case EL_ENVELOPE_OBSOLETE:
2321 element = EL_ENVELOPE_1;
2329 if (element >= NUM_FILE_ELEMENTS)
2331 Error(ERR_WARN, "invalid level element %d", element);
2333 element = EL_UNKNOWN;
2341 int getMappedElementByVersion(int element, int game_version)
2343 /* remap some elements due to certain game version */
2345 if (game_version <= VERSION_IDENT(2,2,0,0))
2347 /* map game font elements */
2348 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2349 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2350 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2351 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2354 if (game_version < VERSION_IDENT(3,0,0,0))
2356 /* map Supaplex gravity tube elements */
2357 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2358 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2359 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2360 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2367 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2369 level->file_version = getFileVersion(file);
2370 level->game_version = getFileVersion(file);
2375 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2377 level->creation_date.year = getFile16BitBE(file);
2378 level->creation_date.month = getFile8Bit(file);
2379 level->creation_date.day = getFile8Bit(file);
2381 level->creation_date.src = DATE_SRC_LEVELFILE;
2386 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2388 int initial_player_stepsize;
2389 int initial_player_gravity;
2392 level->fieldx = getFile8Bit(file);
2393 level->fieldy = getFile8Bit(file);
2395 level->time = getFile16BitBE(file);
2396 level->gems_needed = getFile16BitBE(file);
2398 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2399 level->name[i] = getFile8Bit(file);
2400 level->name[MAX_LEVEL_NAME_LEN] = 0;
2402 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2403 level->score[i] = getFile8Bit(file);
2405 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2406 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2407 for (y = 0; y < 3; y++)
2408 for (x = 0; x < 3; x++)
2409 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2411 level->amoeba_speed = getFile8Bit(file);
2412 level->time_magic_wall = getFile8Bit(file);
2413 level->time_wheel = getFile8Bit(file);
2414 level->amoeba_content = getMappedElement(getFile8Bit(file));
2416 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2419 for (i = 0; i < MAX_PLAYERS; i++)
2420 level->initial_player_stepsize[i] = initial_player_stepsize;
2422 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2424 for (i = 0; i < MAX_PLAYERS; i++)
2425 level->initial_player_gravity[i] = initial_player_gravity;
2427 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2428 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2430 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2432 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2433 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2434 level->can_move_into_acid_bits = getFile32BitBE(file);
2435 level->dont_collide_with_bits = getFile8Bit(file);
2437 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2438 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2440 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2441 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2442 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2444 level->game_engine_type = getFile8Bit(file);
2446 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2451 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2455 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2456 level->name[i] = getFile8Bit(file);
2457 level->name[MAX_LEVEL_NAME_LEN] = 0;
2462 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2466 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2467 level->author[i] = getFile8Bit(file);
2468 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2473 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2476 int chunk_size_expected = level->fieldx * level->fieldy;
2478 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2479 stored with 16-bit encoding (and should be twice as big then).
2480 Even worse, playfield data was stored 16-bit when only yamyam content
2481 contained 16-bit elements and vice versa. */
2483 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2484 chunk_size_expected *= 2;
2486 if (chunk_size_expected != chunk_size)
2488 ReadUnusedBytesFromFile(file, chunk_size);
2489 return chunk_size_expected;
2492 for (y = 0; y < level->fieldy; y++)
2493 for (x = 0; x < level->fieldx; x++)
2494 level->field[x][y] =
2495 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2500 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2503 int header_size = 4;
2504 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2505 int chunk_size_expected = header_size + content_size;
2507 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2508 stored with 16-bit encoding (and should be twice as big then).
2509 Even worse, playfield data was stored 16-bit when only yamyam content
2510 contained 16-bit elements and vice versa. */
2512 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2513 chunk_size_expected += content_size;
2515 if (chunk_size_expected != chunk_size)
2517 ReadUnusedBytesFromFile(file, chunk_size);
2518 return chunk_size_expected;
2522 level->num_yamyam_contents = getFile8Bit(file);
2526 /* correct invalid number of content fields -- should never happen */
2527 if (level->num_yamyam_contents < 1 ||
2528 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2529 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2531 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2532 for (y = 0; y < 3; y++)
2533 for (x = 0; x < 3; x++)
2534 level->yamyam_content[i].e[x][y] =
2535 getMappedElement(level->encoding_16bit_field ?
2536 getFile16BitBE(file) : getFile8Bit(file));
2540 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2545 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2547 element = getMappedElement(getFile16BitBE(file));
2548 num_contents = getFile8Bit(file);
2550 getFile8Bit(file); /* content x size (unused) */
2551 getFile8Bit(file); /* content y size (unused) */
2553 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2555 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2560 /* correct invalid number of content fields -- should never happen */
2561 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2562 num_contents = STD_ELEMENT_CONTENTS;
2564 if (element == EL_YAMYAM)
2566 level->num_yamyam_contents = num_contents;
2568 for (i = 0; i < num_contents; i++)
2569 for (y = 0; y < 3; y++)
2570 for (x = 0; x < 3; x++)
2571 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2573 else if (element == EL_BD_AMOEBA)
2575 level->amoeba_content = content_array[0][0][0];
2579 Error(ERR_WARN, "cannot load content for element '%d'", element);
2585 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2591 int chunk_size_expected;
2593 element = getMappedElement(getFile16BitBE(file));
2594 if (!IS_ENVELOPE(element))
2595 element = EL_ENVELOPE_1;
2597 envelope_nr = element - EL_ENVELOPE_1;
2599 envelope_len = getFile16BitBE(file);
2601 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2602 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2604 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2606 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2607 if (chunk_size_expected != chunk_size)
2609 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2610 return chunk_size_expected;
2613 for (i = 0; i < envelope_len; i++)
2614 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2619 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2621 int num_changed_custom_elements = getFile16BitBE(file);
2622 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2625 if (chunk_size_expected != chunk_size)
2627 ReadUnusedBytesFromFile(file, chunk_size - 2);
2628 return chunk_size_expected;
2631 for (i = 0; i < num_changed_custom_elements; i++)
2633 int element = getMappedElement(getFile16BitBE(file));
2634 int properties = getFile32BitBE(file);
2636 if (IS_CUSTOM_ELEMENT(element))
2637 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2639 Error(ERR_WARN, "invalid custom element number %d", element);
2641 /* older game versions that wrote level files with CUS1 chunks used
2642 different default push delay values (not yet stored in level file) */
2643 element_info[element].push_delay_fixed = 2;
2644 element_info[element].push_delay_random = 8;
2647 level->file_has_custom_elements = TRUE;
2652 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2654 int num_changed_custom_elements = getFile16BitBE(file);
2655 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2658 if (chunk_size_expected != chunk_size)
2660 ReadUnusedBytesFromFile(file, chunk_size - 2);
2661 return chunk_size_expected;
2664 for (i = 0; i < num_changed_custom_elements; i++)
2666 int element = getMappedElement(getFile16BitBE(file));
2667 int custom_target_element = getMappedElement(getFile16BitBE(file));
2669 if (IS_CUSTOM_ELEMENT(element))
2670 element_info[element].change->target_element = custom_target_element;
2672 Error(ERR_WARN, "invalid custom element number %d", element);
2675 level->file_has_custom_elements = TRUE;
2680 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2682 int num_changed_custom_elements = getFile16BitBE(file);
2683 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2686 if (chunk_size_expected != chunk_size)
2688 ReadUnusedBytesFromFile(file, chunk_size - 2);
2689 return chunk_size_expected;
2692 for (i = 0; i < num_changed_custom_elements; i++)
2694 int element = getMappedElement(getFile16BitBE(file));
2695 struct ElementInfo *ei = &element_info[element];
2696 unsigned int event_bits;
2698 if (!IS_CUSTOM_ELEMENT(element))
2700 Error(ERR_WARN, "invalid custom element number %d", element);
2702 element = EL_INTERNAL_DUMMY;
2705 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2706 ei->description[j] = getFile8Bit(file);
2707 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2709 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2711 /* some free bytes for future properties and padding */
2712 ReadUnusedBytesFromFile(file, 7);
2714 ei->use_gfx_element = getFile8Bit(file);
2715 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2717 ei->collect_score_initial = getFile8Bit(file);
2718 ei->collect_count_initial = getFile8Bit(file);
2720 ei->push_delay_fixed = getFile16BitBE(file);
2721 ei->push_delay_random = getFile16BitBE(file);
2722 ei->move_delay_fixed = getFile16BitBE(file);
2723 ei->move_delay_random = getFile16BitBE(file);
2725 ei->move_pattern = getFile16BitBE(file);
2726 ei->move_direction_initial = getFile8Bit(file);
2727 ei->move_stepsize = getFile8Bit(file);
2729 for (y = 0; y < 3; y++)
2730 for (x = 0; x < 3; x++)
2731 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2733 event_bits = getFile32BitBE(file);
2734 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2735 if (event_bits & (1 << j))
2736 ei->change->has_event[j] = TRUE;
2738 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2740 ei->change->delay_fixed = getFile16BitBE(file);
2741 ei->change->delay_random = getFile16BitBE(file);
2742 ei->change->delay_frames = getFile16BitBE(file);
2744 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2746 ei->change->explode = getFile8Bit(file);
2747 ei->change->use_target_content = getFile8Bit(file);
2748 ei->change->only_if_complete = getFile8Bit(file);
2749 ei->change->use_random_replace = getFile8Bit(file);
2751 ei->change->random_percentage = getFile8Bit(file);
2752 ei->change->replace_when = getFile8Bit(file);
2754 for (y = 0; y < 3; y++)
2755 for (x = 0; x < 3; x++)
2756 ei->change->target_content.e[x][y] =
2757 getMappedElement(getFile16BitBE(file));
2759 ei->slippery_type = getFile8Bit(file);
2761 /* some free bytes for future properties and padding */
2762 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2764 /* mark that this custom element has been modified */
2765 ei->modified_settings = TRUE;
2768 level->file_has_custom_elements = TRUE;
2773 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2775 struct ElementInfo *ei;
2776 int chunk_size_expected;
2780 /* ---------- custom element base property values (96 bytes) ------------- */
2782 element = getMappedElement(getFile16BitBE(file));
2784 if (!IS_CUSTOM_ELEMENT(element))
2786 Error(ERR_WARN, "invalid custom element number %d", element);
2788 ReadUnusedBytesFromFile(file, chunk_size - 2);
2792 ei = &element_info[element];
2794 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2795 ei->description[i] = getFile8Bit(file);
2796 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2798 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2800 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2802 ei->num_change_pages = getFile8Bit(file);
2804 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2805 if (chunk_size_expected != chunk_size)
2807 ReadUnusedBytesFromFile(file, chunk_size - 43);
2808 return chunk_size_expected;
2811 ei->ce_value_fixed_initial = getFile16BitBE(file);
2812 ei->ce_value_random_initial = getFile16BitBE(file);
2813 ei->use_last_ce_value = getFile8Bit(file);
2815 ei->use_gfx_element = getFile8Bit(file);
2816 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2818 ei->collect_score_initial = getFile8Bit(file);
2819 ei->collect_count_initial = getFile8Bit(file);
2821 ei->drop_delay_fixed = getFile8Bit(file);
2822 ei->push_delay_fixed = getFile8Bit(file);
2823 ei->drop_delay_random = getFile8Bit(file);
2824 ei->push_delay_random = getFile8Bit(file);
2825 ei->move_delay_fixed = getFile16BitBE(file);
2826 ei->move_delay_random = getFile16BitBE(file);
2828 /* bits 0 - 15 of "move_pattern" ... */
2829 ei->move_pattern = getFile16BitBE(file);
2830 ei->move_direction_initial = getFile8Bit(file);
2831 ei->move_stepsize = getFile8Bit(file);
2833 ei->slippery_type = getFile8Bit(file);
2835 for (y = 0; y < 3; y++)
2836 for (x = 0; x < 3; x++)
2837 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2839 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2840 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2841 ei->move_leave_type = getFile8Bit(file);
2843 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2844 ei->move_pattern |= (getFile16BitBE(file) << 16);
2846 ei->access_direction = getFile8Bit(file);
2848 ei->explosion_delay = getFile8Bit(file);
2849 ei->ignition_delay = getFile8Bit(file);
2850 ei->explosion_type = getFile8Bit(file);
2852 /* some free bytes for future custom property values and padding */
2853 ReadUnusedBytesFromFile(file, 1);
2855 /* ---------- change page property values (48 bytes) --------------------- */
2857 setElementChangePages(ei, ei->num_change_pages);
2859 for (i = 0; i < ei->num_change_pages; i++)
2861 struct ElementChangeInfo *change = &ei->change_page[i];
2862 unsigned int event_bits;
2864 /* always start with reliable default values */
2865 setElementChangeInfoToDefaults(change);
2867 /* bits 0 - 31 of "has_event[]" ... */
2868 event_bits = getFile32BitBE(file);
2869 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2870 if (event_bits & (1 << j))
2871 change->has_event[j] = TRUE;
2873 change->target_element = getMappedElement(getFile16BitBE(file));
2875 change->delay_fixed = getFile16BitBE(file);
2876 change->delay_random = getFile16BitBE(file);
2877 change->delay_frames = getFile16BitBE(file);
2879 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2881 change->explode = getFile8Bit(file);
2882 change->use_target_content = getFile8Bit(file);
2883 change->only_if_complete = getFile8Bit(file);
2884 change->use_random_replace = getFile8Bit(file);
2886 change->random_percentage = getFile8Bit(file);
2887 change->replace_when = getFile8Bit(file);
2889 for (y = 0; y < 3; y++)
2890 for (x = 0; x < 3; x++)
2891 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2893 change->can_change = getFile8Bit(file);
2895 change->trigger_side = getFile8Bit(file);
2897 change->trigger_player = getFile8Bit(file);
2898 change->trigger_page = getFile8Bit(file);
2900 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2901 CH_PAGE_ANY : (1 << change->trigger_page));
2903 change->has_action = getFile8Bit(file);
2904 change->action_type = getFile8Bit(file);
2905 change->action_mode = getFile8Bit(file);
2906 change->action_arg = getFile16BitBE(file);
2908 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2909 event_bits = getFile8Bit(file);
2910 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2911 if (event_bits & (1 << (j - 32)))
2912 change->has_event[j] = TRUE;
2915 /* mark this custom element as modified */
2916 ei->modified_settings = TRUE;
2918 level->file_has_custom_elements = TRUE;
2923 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2925 struct ElementInfo *ei;
2926 struct ElementGroupInfo *group;
2930 element = getMappedElement(getFile16BitBE(file));
2932 if (!IS_GROUP_ELEMENT(element))
2934 Error(ERR_WARN, "invalid group element number %d", element);
2936 ReadUnusedBytesFromFile(file, chunk_size - 2);
2940 ei = &element_info[element];
2942 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2943 ei->description[i] = getFile8Bit(file);
2944 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2946 group = element_info[element].group;
2948 group->num_elements = getFile8Bit(file);
2950 ei->use_gfx_element = getFile8Bit(file);
2951 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2953 group->choice_mode = getFile8Bit(file);
2955 /* some free bytes for future values and padding */
2956 ReadUnusedBytesFromFile(file, 3);
2958 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2959 group->element[i] = getMappedElement(getFile16BitBE(file));
2961 /* mark this group element as modified */
2962 element_info[element].modified_settings = TRUE;
2964 level->file_has_custom_elements = TRUE;
2969 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2970 int element, int real_element)
2972 int micro_chunk_size = 0;
2973 int conf_type = getFile8Bit(file);
2974 int byte_mask = conf_type & CONF_MASK_BYTES;
2975 boolean element_found = FALSE;
2978 micro_chunk_size += 1;
2980 if (byte_mask == CONF_MASK_MULTI_BYTES)
2982 int num_bytes = getFile16BitBE(file);
2983 byte *buffer = checked_malloc(num_bytes);
2985 ReadBytesFromFile(file, buffer, num_bytes);
2987 for (i = 0; conf[i].data_type != -1; i++)
2989 if (conf[i].element == element &&
2990 conf[i].conf_type == conf_type)
2992 int data_type = conf[i].data_type;
2993 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2994 int max_num_entities = conf[i].max_num_entities;
2996 if (num_entities > max_num_entities)
2999 "truncating number of entities for element %d from %d to %d",
3000 element, num_entities, max_num_entities);
3002 num_entities = max_num_entities;
3005 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3006 data_type == TYPE_CONTENT_LIST))
3008 /* for element and content lists, zero entities are not allowed */
3009 Error(ERR_WARN, "found empty list of entities for element %d",
3012 /* do not set "num_entities" here to prevent reading behind buffer */
3014 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3018 *(int *)(conf[i].num_entities) = num_entities;
3021 element_found = TRUE;
3023 if (data_type == TYPE_STRING)
3025 char *string = (char *)(conf[i].value);
3028 for (j = 0; j < max_num_entities; j++)
3029 string[j] = (j < num_entities ? buffer[j] : '\0');
3031 else if (data_type == TYPE_ELEMENT_LIST)
3033 int *element_array = (int *)(conf[i].value);
3036 for (j = 0; j < num_entities; j++)
3038 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3040 else if (data_type == TYPE_CONTENT_LIST)
3042 struct Content *content= (struct Content *)(conf[i].value);
3045 for (c = 0; c < num_entities; c++)
3046 for (y = 0; y < 3; y++)
3047 for (x = 0; x < 3; x++)
3048 content[c].e[x][y] =
3049 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3052 element_found = FALSE;
3058 checked_free(buffer);
3060 micro_chunk_size += 2 + num_bytes;
3062 else /* constant size configuration data (1, 2 or 4 bytes) */
3064 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3065 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3066 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3068 for (i = 0; conf[i].data_type != -1; i++)
3070 if (conf[i].element == element &&
3071 conf[i].conf_type == conf_type)
3073 int data_type = conf[i].data_type;
3075 if (data_type == TYPE_ELEMENT)
3076 value = getMappedElement(value);
3078 if (data_type == TYPE_BOOLEAN)
3079 *(boolean *)(conf[i].value) = value;
3081 *(int *) (conf[i].value) = value;
3083 element_found = TRUE;
3089 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3094 char *error_conf_chunk_bytes =
3095 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3096 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3097 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3098 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3099 int error_element = real_element;
3101 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3102 error_conf_chunk_bytes, error_conf_chunk_token,
3103 error_element, EL_NAME(error_element));
3106 return micro_chunk_size;
3109 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3111 int real_chunk_size = 0;
3113 li = *level; /* copy level data into temporary buffer */
3115 while (!checkEndOfFile(file))
3117 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3119 if (real_chunk_size >= chunk_size)
3123 *level = li; /* copy temporary buffer back to level data */
3125 return real_chunk_size;
3128 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3130 int real_chunk_size = 0;
3132 li = *level; /* copy level data into temporary buffer */
3134 while (!checkEndOfFile(file))
3136 int element = getMappedElement(getFile16BitBE(file));
3138 real_chunk_size += 2;
3139 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3141 if (real_chunk_size >= chunk_size)
3145 *level = li; /* copy temporary buffer back to level data */
3147 return real_chunk_size;
3150 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3152 int real_chunk_size = 0;
3154 li = *level; /* copy level data into temporary buffer */
3156 while (!checkEndOfFile(file))
3158 int element = getMappedElement(getFile16BitBE(file));
3160 real_chunk_size += 2;
3161 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3163 if (real_chunk_size >= chunk_size)
3167 *level = li; /* copy temporary buffer back to level data */
3169 return real_chunk_size;
3172 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3174 int element = getMappedElement(getFile16BitBE(file));
3175 int envelope_nr = element - EL_ENVELOPE_1;
3176 int real_chunk_size = 2;
3178 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3180 while (!checkEndOfFile(file))
3182 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3185 if (real_chunk_size >= chunk_size)
3189 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3191 return real_chunk_size;
3194 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3196 int element = getMappedElement(getFile16BitBE(file));
3197 int real_chunk_size = 2;
3198 struct ElementInfo *ei = &element_info[element];
3201 xx_ei = *ei; /* copy element data into temporary buffer */
3203 xx_ei.num_change_pages = -1;
3205 while (!checkEndOfFile(file))
3207 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3209 if (xx_ei.num_change_pages != -1)
3212 if (real_chunk_size >= chunk_size)
3218 if (ei->num_change_pages == -1)
3220 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3223 ei->num_change_pages = 1;
3225 setElementChangePages(ei, 1);
3226 setElementChangeInfoToDefaults(ei->change);
3228 return real_chunk_size;
3231 /* initialize number of change pages stored for this custom element */
3232 setElementChangePages(ei, ei->num_change_pages);
3233 for (i = 0; i < ei->num_change_pages; i++)
3234 setElementChangeInfoToDefaults(&ei->change_page[i]);
3236 /* start with reading properties for the first change page */
3237 xx_current_change_page = 0;
3239 while (!checkEndOfFile(file))
3241 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3243 xx_change = *change; /* copy change data into temporary buffer */
3245 resetEventBits(); /* reset bits; change page might have changed */
3247 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3250 *change = xx_change;
3252 setEventFlagsFromEventBits(change);
3254 if (real_chunk_size >= chunk_size)
3258 level->file_has_custom_elements = TRUE;
3260 return real_chunk_size;
3263 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3265 int element = getMappedElement(getFile16BitBE(file));
3266 int real_chunk_size = 2;
3267 struct ElementInfo *ei = &element_info[element];
3268 struct ElementGroupInfo *group = ei->group;
3270 xx_ei = *ei; /* copy element data into temporary buffer */
3271 xx_group = *group; /* copy group data into temporary buffer */
3273 while (!checkEndOfFile(file))
3275 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3278 if (real_chunk_size >= chunk_size)
3285 level->file_has_custom_elements = TRUE;
3287 return real_chunk_size;
3290 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3291 struct LevelFileInfo *level_file_info,
3292 boolean level_info_only)
3294 char *filename = level_file_info->filename;
3295 char cookie[MAX_LINE_LEN];
3296 char chunk_name[CHUNK_ID_LEN + 1];
3300 if (!(file = openFile(filename, MODE_READ)))
3302 level->no_valid_file = TRUE;
3303 level->no_level_file = TRUE;
3305 if (level_info_only)
3308 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3310 if (!setup.editor.use_template_for_new_levels)
3313 /* if level file not found, try to initialize level data from template */
3314 filename = getGlobalLevelTemplateFilename();
3316 if (!(file = openFile(filename, MODE_READ)))
3319 /* default: for empty levels, use level template for custom elements */
3320 level->use_custom_template = TRUE;
3322 level->no_valid_file = FALSE;
3325 getFileChunkBE(file, chunk_name, NULL);
3326 if (strEqual(chunk_name, "RND1"))
3328 getFile32BitBE(file); /* not used */
3330 getFileChunkBE(file, chunk_name, NULL);
3331 if (!strEqual(chunk_name, "CAVE"))
3333 level->no_valid_file = TRUE;
3335 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3342 else /* check for pre-2.0 file format with cookie string */
3344 strcpy(cookie, chunk_name);
3345 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3347 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3348 cookie[strlen(cookie) - 1] = '\0';
3350 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3352 level->no_valid_file = TRUE;
3354 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3361 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3363 level->no_valid_file = TRUE;
3365 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3372 /* pre-2.0 level files have no game version, so use file version here */
3373 level->game_version = level->file_version;
3376 if (level->file_version < FILE_VERSION_1_2)
3378 /* level files from versions before 1.2.0 without chunk structure */
3379 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3380 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3388 int (*loader)(File *, int, struct LevelInfo *);
3392 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3393 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3394 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3395 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3396 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3397 { "INFO", -1, LoadLevel_INFO },
3398 { "BODY", -1, LoadLevel_BODY },
3399 { "CONT", -1, LoadLevel_CONT },
3400 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3401 { "CNT3", -1, LoadLevel_CNT3 },
3402 { "CUS1", -1, LoadLevel_CUS1 },
3403 { "CUS2", -1, LoadLevel_CUS2 },
3404 { "CUS3", -1, LoadLevel_CUS3 },
3405 { "CUS4", -1, LoadLevel_CUS4 },
3406 { "GRP1", -1, LoadLevel_GRP1 },
3407 { "CONF", -1, LoadLevel_CONF },
3408 { "ELEM", -1, LoadLevel_ELEM },
3409 { "NOTE", -1, LoadLevel_NOTE },
3410 { "CUSX", -1, LoadLevel_CUSX },
3411 { "GRPX", -1, LoadLevel_GRPX },
3416 while (getFileChunkBE(file, chunk_name, &chunk_size))
3420 while (chunk_info[i].name != NULL &&
3421 !strEqual(chunk_name, chunk_info[i].name))
3424 if (chunk_info[i].name == NULL)
3426 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3427 chunk_name, filename);
3428 ReadUnusedBytesFromFile(file, chunk_size);
3430 else if (chunk_info[i].size != -1 &&
3431 chunk_info[i].size != chunk_size)
3433 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3434 chunk_size, chunk_name, filename);
3435 ReadUnusedBytesFromFile(file, chunk_size);
3439 /* call function to load this level chunk */
3440 int chunk_size_expected =
3441 (chunk_info[i].loader)(file, chunk_size, level);
3443 /* the size of some chunks cannot be checked before reading other
3444 chunks first (like "HEAD" and "BODY") that contain some header
3445 information, so check them here */
3446 if (chunk_size_expected != chunk_size)
3448 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3449 chunk_size, chunk_name, filename);
3459 /* ------------------------------------------------------------------------- */
3460 /* functions for loading EM level */
3461 /* ------------------------------------------------------------------------- */
3463 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3465 static int ball_xy[8][2] =
3476 struct LevelInfo_EM *level_em = level->native_em_level;
3477 struct LEVEL *lev = level_em->lev;
3478 struct PLAYER **ply = level_em->ply;
3481 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3482 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3484 lev->time_seconds = level->time;
3485 lev->required_initial = level->gems_needed;
3487 lev->emerald_score = level->score[SC_EMERALD];
3488 lev->diamond_score = level->score[SC_DIAMOND];
3489 lev->alien_score = level->score[SC_ROBOT];
3490 lev->tank_score = level->score[SC_SPACESHIP];
3491 lev->bug_score = level->score[SC_BUG];
3492 lev->eater_score = level->score[SC_YAMYAM];
3493 lev->nut_score = level->score[SC_NUT];
3494 lev->dynamite_score = level->score[SC_DYNAMITE];
3495 lev->key_score = level->score[SC_KEY];
3496 lev->exit_score = level->score[SC_TIME_BONUS];
3498 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3499 for (y = 0; y < 3; y++)
3500 for (x = 0; x < 3; x++)
3501 lev->eater_array[i][y * 3 + x] =
3502 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3504 lev->amoeba_time = level->amoeba_speed;
3505 lev->wonderwall_time_initial = level->time_magic_wall;
3506 lev->wheel_time = level->time_wheel;
3508 lev->android_move_time = level->android_move_time;
3509 lev->android_clone_time = level->android_clone_time;
3510 lev->ball_random = level->ball_random;
3511 lev->ball_state_initial = level->ball_state_initial;
3512 lev->ball_time = level->ball_time;
3513 lev->num_ball_arrays = level->num_ball_contents;
3515 lev->lenses_score = level->lenses_score;
3516 lev->magnify_score = level->magnify_score;
3517 lev->slurp_score = level->slurp_score;
3519 lev->lenses_time = level->lenses_time;
3520 lev->magnify_time = level->magnify_time;
3522 lev->wind_direction_initial =
3523 map_direction_RND_to_EM(level->wind_direction_initial);
3524 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3525 lev->wind_time : 0);
3527 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3528 for (j = 0; j < 8; j++)
3529 lev->ball_array[i][j] =
3530 map_element_RND_to_EM(level->
3531 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3533 map_android_clone_elements_RND_to_EM(level);
3535 /* first fill the complete playfield with the default border element */
3536 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3537 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3538 level_em->cave[x][y] = ZBORDER;
3540 if (BorderElement == EL_STEELWALL)
3542 for (y = 0; y < lev->height + 2; y++)
3543 for (x = 0; x < lev->width + 2; x++)
3544 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3547 /* then copy the real level contents from level file into the playfield */
3548 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3550 int new_element = map_element_RND_to_EM(level->field[x][y]);
3551 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3552 int xx = x + 1 + offset;
3553 int yy = y + 1 + offset;
3555 if (level->field[x][y] == EL_AMOEBA_DEAD)
3556 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3558 level_em->cave[xx][yy] = new_element;
3561 for (i = 0; i < MAX_PLAYERS; i++)
3563 ply[i]->x_initial = 0;
3564 ply[i]->y_initial = 0;
3567 /* initialize player positions and delete players from the playfield */
3568 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3570 if (ELEM_IS_PLAYER(level->field[x][y]))
3572 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3573 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3574 int xx = x + 1 + offset;
3575 int yy = y + 1 + offset;
3577 ply[player_nr]->x_initial = xx;
3578 ply[player_nr]->y_initial = yy;
3580 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3584 if (BorderElement == EL_STEELWALL)
3591 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3593 static int ball_xy[8][2] =
3604 struct LevelInfo_EM *level_em = level->native_em_level;
3605 struct LEVEL *lev = level_em->lev;
3606 struct PLAYER **ply = level_em->ply;
3609 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3610 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3612 level->time = lev->time_seconds;
3613 level->gems_needed = lev->required_initial;
3615 sprintf(level->name, "Level %d", level->file_info.nr);
3617 level->score[SC_EMERALD] = lev->emerald_score;
3618 level->score[SC_DIAMOND] = lev->diamond_score;
3619 level->score[SC_ROBOT] = lev->alien_score;
3620 level->score[SC_SPACESHIP] = lev->tank_score;
3621 level->score[SC_BUG] = lev->bug_score;
3622 level->score[SC_YAMYAM] = lev->eater_score;
3623 level->score[SC_NUT] = lev->nut_score;
3624 level->score[SC_DYNAMITE] = lev->dynamite_score;
3625 level->score[SC_KEY] = lev->key_score;
3626 level->score[SC_TIME_BONUS] = lev->exit_score;
3628 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3630 for (i = 0; i < level->num_yamyam_contents; i++)
3631 for (y = 0; y < 3; y++)
3632 for (x = 0; x < 3; x++)
3633 level->yamyam_content[i].e[x][y] =
3634 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3636 level->amoeba_speed = lev->amoeba_time;
3637 level->time_magic_wall = lev->wonderwall_time_initial;
3638 level->time_wheel = lev->wheel_time;
3640 level->android_move_time = lev->android_move_time;
3641 level->android_clone_time = lev->android_clone_time;
3642 level->ball_random = lev->ball_random;
3643 level->ball_state_initial = lev->ball_state_initial;
3644 level->ball_time = lev->ball_time;
3645 level->num_ball_contents = lev->num_ball_arrays;
3647 level->lenses_score = lev->lenses_score;
3648 level->magnify_score = lev->magnify_score;
3649 level->slurp_score = lev->slurp_score;
3651 level->lenses_time = lev->lenses_time;
3652 level->magnify_time = lev->magnify_time;
3654 level->wind_direction_initial =
3655 map_direction_EM_to_RND(lev->wind_direction_initial);
3657 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3658 for (j = 0; j < 8; j++)
3659 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3660 map_element_EM_to_RND(lev->ball_array[i][j]);
3662 map_android_clone_elements_EM_to_RND(level);
3664 /* convert the playfield (some elements need special treatment) */
3665 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3667 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3669 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3670 new_element = EL_AMOEBA_DEAD;
3672 level->field[x][y] = new_element;
3675 for (i = 0; i < MAX_PLAYERS; i++)
3677 /* in case of all players set to the same field, use the first player */
3678 int nr = MAX_PLAYERS - i - 1;
3679 int jx = ply[nr]->x_initial - 1;
3680 int jy = ply[nr]->y_initial - 1;
3682 if (jx != -1 && jy != -1)
3683 level->field[jx][jy] = EL_PLAYER_1 + nr;
3688 /* ------------------------------------------------------------------------- */
3689 /* functions for loading SP level */
3690 /* ------------------------------------------------------------------------- */
3692 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3694 struct LevelInfo_SP *level_sp = level->native_sp_level;
3695 LevelInfoType *header = &level_sp->header;
3698 level_sp->width = level->fieldx;
3699 level_sp->height = level->fieldy;
3701 for (x = 0; x < level->fieldx; x++)
3702 for (y = 0; y < level->fieldy; y++)
3703 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3705 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3707 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3708 header->LevelTitle[i] = level->name[i];
3709 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3711 header->InfotronsNeeded = level->gems_needed;
3713 header->SpecialPortCount = 0;
3715 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3717 boolean gravity_port_found = FALSE;
3718 boolean gravity_port_valid = FALSE;
3719 int gravity_port_flag;
3720 int gravity_port_base_element;
3721 int element = level->field[x][y];
3723 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3724 element <= EL_SP_GRAVITY_ON_PORT_UP)
3726 gravity_port_found = TRUE;
3727 gravity_port_valid = TRUE;
3728 gravity_port_flag = 1;
3729 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3731 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3732 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3734 gravity_port_found = TRUE;
3735 gravity_port_valid = TRUE;
3736 gravity_port_flag = 0;
3737 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3739 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3740 element <= EL_SP_GRAVITY_PORT_UP)
3742 /* change R'n'D style gravity inverting special port to normal port
3743 (there are no gravity inverting ports in native Supaplex engine) */
3745 gravity_port_found = TRUE;
3746 gravity_port_valid = FALSE;
3747 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3750 if (gravity_port_found)
3752 if (gravity_port_valid &&
3753 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3755 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3757 port->PortLocation = (y * level->fieldx + x) * 2;
3758 port->Gravity = gravity_port_flag;
3760 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3762 header->SpecialPortCount++;
3766 /* change special gravity port to normal port */
3768 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3771 level_sp->playfield[x][y] = element - EL_SP_START;
3776 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3778 struct LevelInfo_SP *level_sp = level->native_sp_level;
3779 LevelInfoType *header = &level_sp->header;
3780 boolean num_invalid_elements = 0;
3783 level->fieldx = level_sp->width;
3784 level->fieldy = level_sp->height;
3786 for (x = 0; x < level->fieldx; x++)
3788 for (y = 0; y < level->fieldy; y++)
3790 int element_old = level_sp->playfield[x][y];
3791 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3793 if (element_new == EL_UNKNOWN)
3795 num_invalid_elements++;
3797 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3801 level->field[x][y] = element_new;
3805 if (num_invalid_elements > 0)
3806 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3807 (!options.debug ? " (use '--debug' for more details)" : ""));
3809 for (i = 0; i < MAX_PLAYERS; i++)
3810 level->initial_player_gravity[i] =
3811 (header->InitialGravity == 1 ? TRUE : FALSE);
3813 /* skip leading spaces */
3814 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3815 if (header->LevelTitle[i] != ' ')
3818 /* copy level title */
3819 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3820 level->name[j] = header->LevelTitle[i];
3821 level->name[j] = '\0';
3823 /* cut trailing spaces */
3825 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3826 level->name[j - 1] = '\0';
3828 level->gems_needed = header->InfotronsNeeded;
3830 for (i = 0; i < header->SpecialPortCount; i++)
3832 SpecialPortType *port = &header->SpecialPort[i];
3833 int port_location = port->PortLocation;
3834 int gravity = port->Gravity;
3835 int port_x, port_y, port_element;
3837 port_x = (port_location / 2) % level->fieldx;
3838 port_y = (port_location / 2) / level->fieldx;
3840 if (port_x < 0 || port_x >= level->fieldx ||
3841 port_y < 0 || port_y >= level->fieldy)
3843 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3849 port_element = level->field[port_x][port_y];
3851 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3852 port_element > EL_SP_GRAVITY_PORT_UP)
3854 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3859 /* change previous (wrong) gravity inverting special port to either
3860 gravity enabling special port or gravity disabling special port */
3861 level->field[port_x][port_y] +=
3862 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3863 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3866 /* change special gravity ports without database entries to normal ports */
3867 for (x = 0; x < level->fieldx; x++)
3868 for (y = 0; y < level->fieldy; y++)
3869 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3870 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3871 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3873 level->time = 0; /* no time limit */
3874 level->amoeba_speed = 0;
3875 level->time_magic_wall = 0;
3876 level->time_wheel = 0;
3877 level->amoeba_content = EL_EMPTY;
3880 /* original Supaplex does not use score values -- use default values */
3882 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3883 level->score[i] = 0;
3886 /* there are no yamyams in supaplex levels */
3887 for (i = 0; i < level->num_yamyam_contents; i++)
3888 for (x = 0; x < 3; x++)
3889 for (y = 0; y < 3; y++)
3890 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3893 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3895 struct LevelInfo_SP *level_sp = level->native_sp_level;
3896 struct DemoInfo_SP *demo = &level_sp->demo;
3899 /* always start with reliable default values */
3900 demo->is_available = FALSE;
3903 if (TAPE_IS_EMPTY(tape))
3906 demo->level_nr = tape.level_nr; /* (currently not used) */
3908 level_sp->header.DemoRandomSeed = tape.random_seed;
3912 for (i = 0; i < tape.length; i++)
3914 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3915 int demo_repeat = tape.pos[i].delay;
3916 int demo_entries = (demo_repeat + 15) / 16;
3918 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3920 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3926 for (j = 0; j < demo_repeat / 16; j++)
3927 demo->data[demo->length++] = 0xf0 | demo_action;
3929 if (demo_repeat % 16)
3930 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3933 demo->is_available = TRUE;
3936 static void setTapeInfoToDefaults();
3938 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3940 struct LevelInfo_SP *level_sp = level->native_sp_level;
3941 struct DemoInfo_SP *demo = &level_sp->demo;
3942 char *filename = level->file_info.filename;
3945 /* always start with reliable default values */
3946 setTapeInfoToDefaults();
3948 if (!demo->is_available)
3951 tape.level_nr = demo->level_nr; /* (currently not used) */
3952 tape.random_seed = level_sp->header.DemoRandomSeed;
3954 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3957 tape.pos[tape.counter].delay = 0;
3959 for (i = 0; i < demo->length; i++)
3961 int demo_action = demo->data[i] & 0x0f;
3962 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3963 int tape_action = map_key_SP_to_RND(demo_action);
3964 int tape_repeat = demo_repeat + 1;
3965 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3966 boolean success = 0;
3969 for (j = 0; j < tape_repeat; j++)
3970 success = TapeAddAction(action);
3974 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3981 TapeHaltRecording();
3985 /* ------------------------------------------------------------------------- */
3986 /* functions for loading MM level */
3987 /* ------------------------------------------------------------------------- */
3989 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3991 struct LevelInfo_MM *level_mm = level->native_mm_level;
3994 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3995 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3997 level_mm->time = level->time;
3998 level_mm->kettles_needed = level->gems_needed;
3999 level_mm->auto_count_kettles = level->auto_count_gems;
4001 level_mm->laser_red = level->mm_laser_red;
4002 level_mm->laser_green = level->mm_laser_green;
4003 level_mm->laser_blue = level->mm_laser_blue;
4005 strcpy(level_mm->name, level->name);
4006 strcpy(level_mm->author, level->author);
4008 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4009 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4010 level_mm->score[SC_KEY] = level->score[SC_KEY];
4011 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4012 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4014 level_mm->amoeba_speed = level->amoeba_speed;
4015 level_mm->time_fuse = level->mm_time_fuse;
4016 level_mm->time_bomb = level->mm_time_bomb;
4017 level_mm->time_ball = level->mm_time_ball;
4018 level_mm->time_block = level->mm_time_block;
4020 for (x = 0; x < level->fieldx; x++)
4021 for (y = 0; y < level->fieldy; y++)
4023 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4026 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4028 struct LevelInfo_MM *level_mm = level->native_mm_level;
4031 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4032 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4034 level->time = level_mm->time;
4035 level->gems_needed = level_mm->kettles_needed;
4036 level->auto_count_gems = level_mm->auto_count_kettles;
4038 level->mm_laser_red = level_mm->laser_red;
4039 level->mm_laser_green = level_mm->laser_green;
4040 level->mm_laser_blue = level_mm->laser_blue;
4042 strcpy(level->name, level_mm->name);
4044 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4045 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4046 strcpy(level->author, level_mm->author);
4048 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4049 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4050 level->score[SC_KEY] = level_mm->score[SC_KEY];
4051 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4052 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4054 level->amoeba_speed = level_mm->amoeba_speed;
4055 level->mm_time_fuse = level_mm->time_fuse;
4056 level->mm_time_bomb = level_mm->time_bomb;
4057 level->mm_time_ball = level_mm->time_ball;
4058 level->mm_time_block = level_mm->time_block;
4060 for (x = 0; x < level->fieldx; x++)
4061 for (y = 0; y < level->fieldy; y++)
4062 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4066 /* ------------------------------------------------------------------------- */
4067 /* functions for loading DC level */
4068 /* ------------------------------------------------------------------------- */
4070 #define DC_LEVEL_HEADER_SIZE 344
4072 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4074 static int last_data_encoded;
4078 int diff_hi, diff_lo;
4079 int data_hi, data_lo;
4080 unsigned short data_decoded;
4084 last_data_encoded = 0;
4091 diff = data_encoded - last_data_encoded;
4092 diff_hi = diff & ~0xff;
4093 diff_lo = diff & 0xff;
4097 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4098 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4099 data_hi = data_hi & 0xff00;
4101 data_decoded = data_hi | data_lo;
4103 last_data_encoded = data_encoded;
4105 offset1 = (offset1 + 1) % 31;
4106 offset2 = offset2 & 0xff;
4108 return data_decoded;
4111 int getMappedElement_DC(int element)
4119 /* 0x0117 - 0x036e: (?) */
4122 /* 0x042d - 0x0684: (?) */
4138 element = EL_CRYSTAL;
4141 case 0x0e77: /* quicksand (boulder) */
4142 element = EL_QUICKSAND_FAST_FULL;
4145 case 0x0e99: /* slow quicksand (boulder) */
4146 element = EL_QUICKSAND_FULL;
4150 element = EL_EM_EXIT_OPEN;
4154 element = EL_EM_EXIT_CLOSED;
4158 element = EL_EM_STEEL_EXIT_OPEN;
4162 element = EL_EM_STEEL_EXIT_CLOSED;
4165 case 0x0f4f: /* dynamite (lit 1) */
4166 element = EL_EM_DYNAMITE_ACTIVE;
4169 case 0x0f57: /* dynamite (lit 2) */
4170 element = EL_EM_DYNAMITE_ACTIVE;
4173 case 0x0f5f: /* dynamite (lit 3) */
4174 element = EL_EM_DYNAMITE_ACTIVE;
4177 case 0x0f67: /* dynamite (lit 4) */
4178 element = EL_EM_DYNAMITE_ACTIVE;
4185 element = EL_AMOEBA_WET;
4189 element = EL_AMOEBA_DROP;
4193 element = EL_DC_MAGIC_WALL;
4197 element = EL_SPACESHIP_UP;
4201 element = EL_SPACESHIP_DOWN;
4205 element = EL_SPACESHIP_LEFT;
4209 element = EL_SPACESHIP_RIGHT;
4213 element = EL_BUG_UP;
4217 element = EL_BUG_DOWN;
4221 element = EL_BUG_LEFT;
4225 element = EL_BUG_RIGHT;
4229 element = EL_MOLE_UP;
4233 element = EL_MOLE_DOWN;
4237 element = EL_MOLE_LEFT;
4241 element = EL_MOLE_RIGHT;
4249 element = EL_YAMYAM;
4253 element = EL_SWITCHGATE_OPEN;
4257 element = EL_SWITCHGATE_CLOSED;
4261 element = EL_DC_SWITCHGATE_SWITCH_UP;
4265 element = EL_TIMEGATE_CLOSED;
4268 case 0x144c: /* conveyor belt switch (green) */
4269 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4272 case 0x144f: /* conveyor belt switch (red) */
4273 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4276 case 0x1452: /* conveyor belt switch (blue) */
4277 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4281 element = EL_CONVEYOR_BELT_3_MIDDLE;
4285 element = EL_CONVEYOR_BELT_3_LEFT;
4289 element = EL_CONVEYOR_BELT_3_RIGHT;
4293 element = EL_CONVEYOR_BELT_1_MIDDLE;
4297 element = EL_CONVEYOR_BELT_1_LEFT;
4301 element = EL_CONVEYOR_BELT_1_RIGHT;
4305 element = EL_CONVEYOR_BELT_4_MIDDLE;
4309 element = EL_CONVEYOR_BELT_4_LEFT;
4313 element = EL_CONVEYOR_BELT_4_RIGHT;
4317 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4321 element = EL_EXPANDABLE_WALL_VERTICAL;
4325 element = EL_EXPANDABLE_WALL_ANY;
4328 case 0x14ce: /* growing steel wall (left/right) */
4329 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4332 case 0x14df: /* growing steel wall (up/down) */
4333 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4336 case 0x14e8: /* growing steel wall (up/down/left/right) */
4337 element = EL_EXPANDABLE_STEELWALL_ANY;
4341 element = EL_SHIELD_DEADLY;
4345 element = EL_EXTRA_TIME;
4353 element = EL_EMPTY_SPACE;
4356 case 0x1578: /* quicksand (empty) */
4357 element = EL_QUICKSAND_FAST_EMPTY;
4360 case 0x1579: /* slow quicksand (empty) */
4361 element = EL_QUICKSAND_EMPTY;
4364 /* 0x157c - 0x158b: */
4367 /* 0x1590 - 0x159f: */
4368 /* EL_DC_LANDMINE */
4371 element = EL_EM_DYNAMITE;
4374 case 0x15a1: /* key (red) */
4375 element = EL_EM_KEY_1;
4378 case 0x15a2: /* key (yellow) */
4379 element = EL_EM_KEY_2;
4382 case 0x15a3: /* key (blue) */
4383 element = EL_EM_KEY_4;
4386 case 0x15a4: /* key (green) */
4387 element = EL_EM_KEY_3;
4390 case 0x15a5: /* key (white) */
4391 element = EL_DC_KEY_WHITE;
4395 element = EL_WALL_SLIPPERY;
4402 case 0x15a8: /* wall (not round) */
4406 case 0x15a9: /* (blue) */
4407 element = EL_CHAR_A;
4410 case 0x15aa: /* (blue) */
4411 element = EL_CHAR_B;
4414 case 0x15ab: /* (blue) */
4415 element = EL_CHAR_C;
4418 case 0x15ac: /* (blue) */
4419 element = EL_CHAR_D;
4422 case 0x15ad: /* (blue) */
4423 element = EL_CHAR_E;
4426 case 0x15ae: /* (blue) */
4427 element = EL_CHAR_F;
4430 case 0x15af: /* (blue) */
4431 element = EL_CHAR_G;
4434 case 0x15b0: /* (blue) */
4435 element = EL_CHAR_H;
4438 case 0x15b1: /* (blue) */
4439 element = EL_CHAR_I;
4442 case 0x15b2: /* (blue) */
4443 element = EL_CHAR_J;
4446 case 0x15b3: /* (blue) */
4447 element = EL_CHAR_K;
4450 case 0x15b4: /* (blue) */
4451 element = EL_CHAR_L;
4454 case 0x15b5: /* (blue) */
4455 element = EL_CHAR_M;
4458 case 0x15b6: /* (blue) */
4459 element = EL_CHAR_N;
4462 case 0x15b7: /* (blue) */
4463 element = EL_CHAR_O;
4466 case 0x15b8: /* (blue) */
4467 element = EL_CHAR_P;
4470 case 0x15b9: /* (blue) */
4471 element = EL_CHAR_Q;
4474 case 0x15ba: /* (blue) */
4475 element = EL_CHAR_R;
4478 case 0x15bb: /* (blue) */
4479 element = EL_CHAR_S;
4482 case 0x15bc: /* (blue) */
4483 element = EL_CHAR_T;
4486 case 0x15bd: /* (blue) */
4487 element = EL_CHAR_U;
4490 case 0x15be: /* (blue) */
4491 element = EL_CHAR_V;
4494 case 0x15bf: /* (blue) */
4495 element = EL_CHAR_W;
4498 case 0x15c0: /* (blue) */
4499 element = EL_CHAR_X;
4502 case 0x15c1: /* (blue) */
4503 element = EL_CHAR_Y;
4506 case 0x15c2: /* (blue) */
4507 element = EL_CHAR_Z;
4510 case 0x15c3: /* (blue) */
4511 element = EL_CHAR_AUMLAUT;
4514 case 0x15c4: /* (blue) */
4515 element = EL_CHAR_OUMLAUT;
4518 case 0x15c5: /* (blue) */
4519 element = EL_CHAR_UUMLAUT;
4522 case 0x15c6: /* (blue) */
4523 element = EL_CHAR_0;
4526 case 0x15c7: /* (blue) */
4527 element = EL_CHAR_1;
4530 case 0x15c8: /* (blue) */
4531 element = EL_CHAR_2;
4534 case 0x15c9: /* (blue) */
4535 element = EL_CHAR_3;
4538 case 0x15ca: /* (blue) */
4539 element = EL_CHAR_4;
4542 case 0x15cb: /* (blue) */
4543 element = EL_CHAR_5;
4546 case 0x15cc: /* (blue) */
4547 element = EL_CHAR_6;
4550 case 0x15cd: /* (blue) */
4551 element = EL_CHAR_7;
4554 case 0x15ce: /* (blue) */
4555 element = EL_CHAR_8;
4558 case 0x15cf: /* (blue) */
4559 element = EL_CHAR_9;
4562 case 0x15d0: /* (blue) */
4563 element = EL_CHAR_PERIOD;
4566 case 0x15d1: /* (blue) */
4567 element = EL_CHAR_EXCLAM;
4570 case 0x15d2: /* (blue) */
4571 element = EL_CHAR_COLON;
4574 case 0x15d3: /* (blue) */
4575 element = EL_CHAR_LESS;
4578 case 0x15d4: /* (blue) */
4579 element = EL_CHAR_GREATER;
4582 case 0x15d5: /* (blue) */
4583 element = EL_CHAR_QUESTION;
4586 case 0x15d6: /* (blue) */
4587 element = EL_CHAR_COPYRIGHT;
4590 case 0x15d7: /* (blue) */
4591 element = EL_CHAR_UP;
4594 case 0x15d8: /* (blue) */
4595 element = EL_CHAR_DOWN;
4598 case 0x15d9: /* (blue) */
4599 element = EL_CHAR_BUTTON;
4602 case 0x15da: /* (blue) */
4603 element = EL_CHAR_PLUS;
4606 case 0x15db: /* (blue) */
4607 element = EL_CHAR_MINUS;
4610 case 0x15dc: /* (blue) */
4611 element = EL_CHAR_APOSTROPHE;
4614 case 0x15dd: /* (blue) */
4615 element = EL_CHAR_PARENLEFT;
4618 case 0x15de: /* (blue) */
4619 element = EL_CHAR_PARENRIGHT;
4622 case 0x15df: /* (green) */
4623 element = EL_CHAR_A;
4626 case 0x15e0: /* (green) */
4627 element = EL_CHAR_B;
4630 case 0x15e1: /* (green) */
4631 element = EL_CHAR_C;
4634 case 0x15e2: /* (green) */
4635 element = EL_CHAR_D;
4638 case 0x15e3: /* (green) */
4639 element = EL_CHAR_E;
4642 case 0x15e4: /* (green) */
4643 element = EL_CHAR_F;
4646 case 0x15e5: /* (green) */
4647 element = EL_CHAR_G;
4650 case 0x15e6: /* (green) */
4651 element = EL_CHAR_H;
4654 case 0x15e7: /* (green) */
4655 element = EL_CHAR_I;
4658 case 0x15e8: /* (green) */
4659 element = EL_CHAR_J;
4662 case 0x15e9: /* (green) */
4663 element = EL_CHAR_K;
4666 case 0x15ea: /* (green) */
4667 element = EL_CHAR_L;
4670 case 0x15eb: /* (green) */
4671 element = EL_CHAR_M;
4674 case 0x15ec: /* (green) */
4675 element = EL_CHAR_N;
4678 case 0x15ed: /* (green) */
4679 element = EL_CHAR_O;
4682 case 0x15ee: /* (green) */
4683 element = EL_CHAR_P;
4686 case 0x15ef: /* (green) */
4687 element = EL_CHAR_Q;
4690 case 0x15f0: /* (green) */
4691 element = EL_CHAR_R;
4694 case 0x15f1: /* (green) */
4695 element = EL_CHAR_S;
4698 case 0x15f2: /* (green) */
4699 element = EL_CHAR_T;
4702 case 0x15f3: /* (green) */
4703 element = EL_CHAR_U;
4706 case 0x15f4: /* (green) */
4707 element = EL_CHAR_V;
4710 case 0x15f5: /* (green) */
4711 element = EL_CHAR_W;
4714 case 0x15f6: /* (green) */
4715 element = EL_CHAR_X;
4718 case 0x15f7: /* (green) */
4719 element = EL_CHAR_Y;
4722 case 0x15f8: /* (green) */
4723 element = EL_CHAR_Z;
4726 case 0x15f9: /* (green) */
4727 element = EL_CHAR_AUMLAUT;
4730 case 0x15fa: /* (green) */
4731 element = EL_CHAR_OUMLAUT;
4734 case 0x15fb: /* (green) */
4735 element = EL_CHAR_UUMLAUT;
4738 case 0x15fc: /* (green) */
4739 element = EL_CHAR_0;
4742 case 0x15fd: /* (green) */
4743 element = EL_CHAR_1;
4746 case 0x15fe: /* (green) */
4747 element = EL_CHAR_2;
4750 case 0x15ff: /* (green) */
4751 element = EL_CHAR_3;
4754 case 0x1600: /* (green) */
4755 element = EL_CHAR_4;
4758 case 0x1601: /* (green) */
4759 element = EL_CHAR_5;
4762 case 0x1602: /* (green) */
4763 element = EL_CHAR_6;
4766 case 0x1603: /* (green) */
4767 element = EL_CHAR_7;
4770 case 0x1604: /* (green) */
4771 element = EL_CHAR_8;
4774 case 0x1605: /* (green) */
4775 element = EL_CHAR_9;
4778 case 0x1606: /* (green) */
4779 element = EL_CHAR_PERIOD;
4782 case 0x1607: /* (green) */
4783 element = EL_CHAR_EXCLAM;
4786 case 0x1608: /* (green) */
4787 element = EL_CHAR_COLON;
4790 case 0x1609: /* (green) */
4791 element = EL_CHAR_LESS;
4794 case 0x160a: /* (green) */
4795 element = EL_CHAR_GREATER;
4798 case 0x160b: /* (green) */
4799 element = EL_CHAR_QUESTION;
4802 case 0x160c: /* (green) */
4803 element = EL_CHAR_COPYRIGHT;
4806 case 0x160d: /* (green) */
4807 element = EL_CHAR_UP;
4810 case 0x160e: /* (green) */
4811 element = EL_CHAR_DOWN;
4814 case 0x160f: /* (green) */
4815 element = EL_CHAR_BUTTON;
4818 case 0x1610: /* (green) */
4819 element = EL_CHAR_PLUS;
4822 case 0x1611: /* (green) */
4823 element = EL_CHAR_MINUS;
4826 case 0x1612: /* (green) */
4827 element = EL_CHAR_APOSTROPHE;
4830 case 0x1613: /* (green) */
4831 element = EL_CHAR_PARENLEFT;
4834 case 0x1614: /* (green) */
4835 element = EL_CHAR_PARENRIGHT;
4838 case 0x1615: /* (blue steel) */
4839 element = EL_STEEL_CHAR_A;
4842 case 0x1616: /* (blue steel) */
4843 element = EL_STEEL_CHAR_B;
4846 case 0x1617: /* (blue steel) */
4847 element = EL_STEEL_CHAR_C;
4850 case 0x1618: /* (blue steel) */
4851 element = EL_STEEL_CHAR_D;
4854 case 0x1619: /* (blue steel) */
4855 element = EL_STEEL_CHAR_E;
4858 case 0x161a: /* (blue steel) */
4859 element = EL_STEEL_CHAR_F;
4862 case 0x161b: /* (blue steel) */
4863 element = EL_STEEL_CHAR_G;
4866 case 0x161c: /* (blue steel) */
4867 element = EL_STEEL_CHAR_H;
4870 case 0x161d: /* (blue steel) */
4871 element = EL_STEEL_CHAR_I;
4874 case 0x161e: /* (blue steel) */
4875 element = EL_STEEL_CHAR_J;
4878 case 0x161f: /* (blue steel) */
4879 element = EL_STEEL_CHAR_K;
4882 case 0x1620: /* (blue steel) */
4883 element = EL_STEEL_CHAR_L;
4886 case 0x1621: /* (blue steel) */
4887 element = EL_STEEL_CHAR_M;
4890 case 0x1622: /* (blue steel) */
4891 element = EL_STEEL_CHAR_N;
4894 case 0x1623: /* (blue steel) */
4895 element = EL_STEEL_CHAR_O;
4898 case 0x1624: /* (blue steel) */
4899 element = EL_STEEL_CHAR_P;
4902 case 0x1625: /* (blue steel) */
4903 element = EL_STEEL_CHAR_Q;
4906 case 0x1626: /* (blue steel) */
4907 element = EL_STEEL_CHAR_R;
4910 case 0x1627: /* (blue steel) */
4911 element = EL_STEEL_CHAR_S;
4914 case 0x1628: /* (blue steel) */
4915 element = EL_STEEL_CHAR_T;
4918 case 0x1629: /* (blue steel) */
4919 element = EL_STEEL_CHAR_U;
4922 case 0x162a: /* (blue steel) */
4923 element = EL_STEEL_CHAR_V;
4926 case 0x162b: /* (blue steel) */
4927 element = EL_STEEL_CHAR_W;
4930 case 0x162c: /* (blue steel) */
4931 element = EL_STEEL_CHAR_X;
4934 case 0x162d: /* (blue steel) */
4935 element = EL_STEEL_CHAR_Y;
4938 case 0x162e: /* (blue steel) */
4939 element = EL_STEEL_CHAR_Z;
4942 case 0x162f: /* (blue steel) */
4943 element = EL_STEEL_CHAR_AUMLAUT;
4946 case 0x1630: /* (blue steel) */
4947 element = EL_STEEL_CHAR_OUMLAUT;
4950 case 0x1631: /* (blue steel) */
4951 element = EL_STEEL_CHAR_UUMLAUT;
4954 case 0x1632: /* (blue steel) */
4955 element = EL_STEEL_CHAR_0;
4958 case 0x1633: /* (blue steel) */
4959 element = EL_STEEL_CHAR_1;
4962 case 0x1634: /* (blue steel) */
4963 element = EL_STEEL_CHAR_2;
4966 case 0x1635: /* (blue steel) */
4967 element = EL_STEEL_CHAR_3;
4970 case 0x1636: /* (blue steel) */
4971 element = EL_STEEL_CHAR_4;
4974 case 0x1637: /* (blue steel) */
4975 element = EL_STEEL_CHAR_5;
4978 case 0x1638: /* (blue steel) */
4979 element = EL_STEEL_CHAR_6;
4982 case 0x1639: /* (blue steel) */
4983 element = EL_STEEL_CHAR_7;
4986 case 0x163a: /* (blue steel) */
4987 element = EL_STEEL_CHAR_8;
4990 case 0x163b: /* (blue steel) */
4991 element = EL_STEEL_CHAR_9;
4994 case 0x163c: /* (blue steel) */
4995 element = EL_STEEL_CHAR_PERIOD;
4998 case 0x163d: /* (blue steel) */
4999 element = EL_STEEL_CHAR_EXCLAM;
5002 case 0x163e: /* (blue steel) */
5003 element = EL_STEEL_CHAR_COLON;
5006 case 0x163f: /* (blue steel) */
5007 element = EL_STEEL_CHAR_LESS;
5010 case 0x1640: /* (blue steel) */
5011 element = EL_STEEL_CHAR_GREATER;
5014 case 0x1641: /* (blue steel) */
5015 element = EL_STEEL_CHAR_QUESTION;
5018 case 0x1642: /* (blue steel) */
5019 element = EL_STEEL_CHAR_COPYRIGHT;
5022 case 0x1643: /* (blue steel) */
5023 element = EL_STEEL_CHAR_UP;
5026 case 0x1644: /* (blue steel) */
5027 element = EL_STEEL_CHAR_DOWN;
5030 case 0x1645: /* (blue steel) */
5031 element = EL_STEEL_CHAR_BUTTON;
5034 case 0x1646: /* (blue steel) */
5035 element = EL_STEEL_CHAR_PLUS;
5038 case 0x1647: /* (blue steel) */
5039 element = EL_STEEL_CHAR_MINUS;
5042 case 0x1648: /* (blue steel) */
5043 element = EL_STEEL_CHAR_APOSTROPHE;
5046 case 0x1649: /* (blue steel) */
5047 element = EL_STEEL_CHAR_PARENLEFT;
5050 case 0x164a: /* (blue steel) */
5051 element = EL_STEEL_CHAR_PARENRIGHT;
5054 case 0x164b: /* (green steel) */
5055 element = EL_STEEL_CHAR_A;
5058 case 0x164c: /* (green steel) */
5059 element = EL_STEEL_CHAR_B;
5062 case 0x164d: /* (green steel) */
5063 element = EL_STEEL_CHAR_C;
5066 case 0x164e: /* (green steel) */
5067 element = EL_STEEL_CHAR_D;
5070 case 0x164f: /* (green steel) */
5071 element = EL_STEEL_CHAR_E;
5074 case 0x1650: /* (green steel) */
5075 element = EL_STEEL_CHAR_F;
5078 case 0x1651: /* (green steel) */
5079 element = EL_STEEL_CHAR_G;
5082 case 0x1652: /* (green steel) */
5083 element = EL_STEEL_CHAR_H;
5086 case 0x1653: /* (green steel) */
5087 element = EL_STEEL_CHAR_I;
5090 case 0x1654: /* (green steel) */
5091 element = EL_STEEL_CHAR_J;
5094 case 0x1655: /* (green steel) */
5095 element = EL_STEEL_CHAR_K;
5098 case 0x1656: /* (green steel) */
5099 element = EL_STEEL_CHAR_L;
5102 case 0x1657: /* (green steel) */
5103 element = EL_STEEL_CHAR_M;
5106 case 0x1658: /* (green steel) */
5107 element = EL_STEEL_CHAR_N;
5110 case 0x1659: /* (green steel) */
5111 element = EL_STEEL_CHAR_O;
5114 case 0x165a: /* (green steel) */
5115 element = EL_STEEL_CHAR_P;
5118 case 0x165b: /* (green steel) */
5119 element = EL_STEEL_CHAR_Q;
5122 case 0x165c: /* (green steel) */
5123 element = EL_STEEL_CHAR_R;
5126 case 0x165d: /* (green steel) */
5127 element = EL_STEEL_CHAR_S;
5130 case 0x165e: /* (green steel) */
5131 element = EL_STEEL_CHAR_T;
5134 case 0x165f: /* (green steel) */
5135 element = EL_STEEL_CHAR_U;
5138 case 0x1660: /* (green steel) */
5139 element = EL_STEEL_CHAR_V;
5142 case 0x1661: /* (green steel) */
5143 element = EL_STEEL_CHAR_W;
5146 case 0x1662: /* (green steel) */
5147 element = EL_STEEL_CHAR_X;
5150 case 0x1663: /* (green steel) */
5151 element = EL_STEEL_CHAR_Y;
5154 case 0x1664: /* (green steel) */
5155 element = EL_STEEL_CHAR_Z;
5158 case 0x1665: /* (green steel) */
5159 element = EL_STEEL_CHAR_AUMLAUT;
5162 case 0x1666: /* (green steel) */
5163 element = EL_STEEL_CHAR_OUMLAUT;
5166 case 0x1667: /* (green steel) */
5167 element = EL_STEEL_CHAR_UUMLAUT;
5170 case 0x1668: /* (green steel) */
5171 element = EL_STEEL_CHAR_0;
5174 case 0x1669: /* (green steel) */
5175 element = EL_STEEL_CHAR_1;
5178 case 0x166a: /* (green steel) */
5179 element = EL_STEEL_CHAR_2;
5182 case 0x166b: /* (green steel) */
5183 element = EL_STEEL_CHAR_3;
5186 case 0x166c: /* (green steel) */
5187 element = EL_STEEL_CHAR_4;
5190 case 0x166d: /* (green steel) */
5191 element = EL_STEEL_CHAR_5;
5194 case 0x166e: /* (green steel) */
5195 element = EL_STEEL_CHAR_6;
5198 case 0x166f: /* (green steel) */
5199 element = EL_STEEL_CHAR_7;
5202 case 0x1670: /* (green steel) */
5203 element = EL_STEEL_CHAR_8;
5206 case 0x1671: /* (green steel) */
5207 element = EL_STEEL_CHAR_9;
5210 case 0x1672: /* (green steel) */
5211 element = EL_STEEL_CHAR_PERIOD;
5214 case 0x1673: /* (green steel) */
5215 element = EL_STEEL_CHAR_EXCLAM;
5218 case 0x1674: /* (green steel) */
5219 element = EL_STEEL_CHAR_COLON;
5222 case 0x1675: /* (green steel) */
5223 element = EL_STEEL_CHAR_LESS;
5226 case 0x1676: /* (green steel) */
5227 element = EL_STEEL_CHAR_GREATER;
5230 case 0x1677: /* (green steel) */
5231 element = EL_STEEL_CHAR_QUESTION;
5234 case 0x1678: /* (green steel) */
5235 element = EL_STEEL_CHAR_COPYRIGHT;
5238 case 0x1679: /* (green steel) */
5239 element = EL_STEEL_CHAR_UP;
5242 case 0x167a: /* (green steel) */
5243 element = EL_STEEL_CHAR_DOWN;
5246 case 0x167b: /* (green steel) */
5247 element = EL_STEEL_CHAR_BUTTON;
5250 case 0x167c: /* (green steel) */
5251 element = EL_STEEL_CHAR_PLUS;
5254 case 0x167d: /* (green steel) */
5255 element = EL_STEEL_CHAR_MINUS;
5258 case 0x167e: /* (green steel) */
5259 element = EL_STEEL_CHAR_APOSTROPHE;
5262 case 0x167f: /* (green steel) */
5263 element = EL_STEEL_CHAR_PARENLEFT;
5266 case 0x1680: /* (green steel) */
5267 element = EL_STEEL_CHAR_PARENRIGHT;
5270 case 0x1681: /* gate (red) */
5271 element = EL_EM_GATE_1;
5274 case 0x1682: /* secret gate (red) */
5275 element = EL_GATE_1_GRAY;
5278 case 0x1683: /* gate (yellow) */
5279 element = EL_EM_GATE_2;
5282 case 0x1684: /* secret gate (yellow) */
5283 element = EL_GATE_2_GRAY;
5286 case 0x1685: /* gate (blue) */
5287 element = EL_EM_GATE_4;
5290 case 0x1686: /* secret gate (blue) */
5291 element = EL_GATE_4_GRAY;
5294 case 0x1687: /* gate (green) */
5295 element = EL_EM_GATE_3;
5298 case 0x1688: /* secret gate (green) */
5299 element = EL_GATE_3_GRAY;
5302 case 0x1689: /* gate (white) */
5303 element = EL_DC_GATE_WHITE;
5306 case 0x168a: /* secret gate (white) */
5307 element = EL_DC_GATE_WHITE_GRAY;
5310 case 0x168b: /* secret gate (no key) */
5311 element = EL_DC_GATE_FAKE_GRAY;
5315 element = EL_ROBOT_WHEEL;
5319 element = EL_DC_TIMEGATE_SWITCH;
5323 element = EL_ACID_POOL_BOTTOM;
5327 element = EL_ACID_POOL_TOPLEFT;
5331 element = EL_ACID_POOL_TOPRIGHT;
5335 element = EL_ACID_POOL_BOTTOMLEFT;
5339 element = EL_ACID_POOL_BOTTOMRIGHT;
5343 element = EL_STEELWALL;
5347 element = EL_STEELWALL_SLIPPERY;
5350 case 0x1695: /* steel wall (not round) */
5351 element = EL_STEELWALL;
5354 case 0x1696: /* steel wall (left) */
5355 element = EL_DC_STEELWALL_1_LEFT;
5358 case 0x1697: /* steel wall (bottom) */
5359 element = EL_DC_STEELWALL_1_BOTTOM;
5362 case 0x1698: /* steel wall (right) */
5363 element = EL_DC_STEELWALL_1_RIGHT;
5366 case 0x1699: /* steel wall (top) */
5367 element = EL_DC_STEELWALL_1_TOP;
5370 case 0x169a: /* steel wall (left/bottom) */
5371 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5374 case 0x169b: /* steel wall (right/bottom) */
5375 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5378 case 0x169c: /* steel wall (right/top) */
5379 element = EL_DC_STEELWALL_1_TOPRIGHT;
5382 case 0x169d: /* steel wall (left/top) */
5383 element = EL_DC_STEELWALL_1_TOPLEFT;
5386 case 0x169e: /* steel wall (right/bottom small) */
5387 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5390 case 0x169f: /* steel wall (left/bottom small) */
5391 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5394 case 0x16a0: /* steel wall (right/top small) */
5395 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5398 case 0x16a1: /* steel wall (left/top small) */
5399 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5402 case 0x16a2: /* steel wall (left/right) */
5403 element = EL_DC_STEELWALL_1_VERTICAL;
5406 case 0x16a3: /* steel wall (top/bottom) */
5407 element = EL_DC_STEELWALL_1_HORIZONTAL;
5410 case 0x16a4: /* steel wall 2 (left end) */
5411 element = EL_DC_STEELWALL_2_LEFT;
5414 case 0x16a5: /* steel wall 2 (right end) */
5415 element = EL_DC_STEELWALL_2_RIGHT;
5418 case 0x16a6: /* steel wall 2 (top end) */
5419 element = EL_DC_STEELWALL_2_TOP;
5422 case 0x16a7: /* steel wall 2 (bottom end) */
5423 element = EL_DC_STEELWALL_2_BOTTOM;
5426 case 0x16a8: /* steel wall 2 (left/right) */
5427 element = EL_DC_STEELWALL_2_HORIZONTAL;
5430 case 0x16a9: /* steel wall 2 (up/down) */
5431 element = EL_DC_STEELWALL_2_VERTICAL;
5434 case 0x16aa: /* steel wall 2 (mid) */
5435 element = EL_DC_STEELWALL_2_MIDDLE;
5439 element = EL_SIGN_EXCLAMATION;
5443 element = EL_SIGN_RADIOACTIVITY;
5447 element = EL_SIGN_STOP;
5451 element = EL_SIGN_WHEELCHAIR;
5455 element = EL_SIGN_PARKING;
5459 element = EL_SIGN_NO_ENTRY;
5463 element = EL_SIGN_HEART;
5467 element = EL_SIGN_GIVE_WAY;
5471 element = EL_SIGN_ENTRY_FORBIDDEN;
5475 element = EL_SIGN_EMERGENCY_EXIT;
5479 element = EL_SIGN_YIN_YANG;
5483 element = EL_WALL_EMERALD;
5487 element = EL_WALL_DIAMOND;
5491 element = EL_WALL_PEARL;
5495 element = EL_WALL_CRYSTAL;
5499 element = EL_INVISIBLE_WALL;
5503 element = EL_INVISIBLE_STEELWALL;
5506 /* 0x16bc - 0x16cb: */
5507 /* EL_INVISIBLE_SAND */
5510 element = EL_LIGHT_SWITCH;
5514 element = EL_ENVELOPE_1;
5518 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5519 element = EL_DIAMOND;
5520 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5521 element = EL_EMERALD;
5522 else if (element >= 0x157c && element <= 0x158b)
5524 else if (element >= 0x1590 && element <= 0x159f)
5525 element = EL_DC_LANDMINE;
5526 else if (element >= 0x16bc && element <= 0x16cb)
5527 element = EL_INVISIBLE_SAND;
5530 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5531 element = EL_UNKNOWN;
5536 return getMappedElement(element);
5539 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5542 byte header[DC_LEVEL_HEADER_SIZE];
5544 int envelope_header_pos = 62;
5545 int envelope_content_pos = 94;
5546 int level_name_pos = 251;
5547 int level_author_pos = 292;
5548 int envelope_header_len;
5549 int envelope_content_len;
5551 int level_author_len;
5553 int num_yamyam_contents;
5556 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5558 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5560 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5562 header[i * 2 + 0] = header_word >> 8;
5563 header[i * 2 + 1] = header_word & 0xff;
5566 /* read some values from level header to check level decoding integrity */
5567 fieldx = header[6] | (header[7] << 8);
5568 fieldy = header[8] | (header[9] << 8);
5569 num_yamyam_contents = header[60] | (header[61] << 8);
5571 /* do some simple sanity checks to ensure that level was correctly decoded */
5572 if (fieldx < 1 || fieldx > 256 ||
5573 fieldy < 1 || fieldy > 256 ||
5574 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5576 level->no_valid_file = TRUE;
5578 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5583 /* maximum envelope header size is 31 bytes */
5584 envelope_header_len = header[envelope_header_pos];
5585 /* maximum envelope content size is 110 (156?) bytes */
5586 envelope_content_len = header[envelope_content_pos];
5588 /* maximum level title size is 40 bytes */
5589 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5590 /* maximum level author size is 30 (51?) bytes */
5591 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5595 for (i = 0; i < envelope_header_len; i++)
5596 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5597 level->envelope[0].text[envelope_size++] =
5598 header[envelope_header_pos + 1 + i];
5600 if (envelope_header_len > 0 && envelope_content_len > 0)
5602 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5603 level->envelope[0].text[envelope_size++] = '\n';
5604 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5605 level->envelope[0].text[envelope_size++] = '\n';
5608 for (i = 0; i < envelope_content_len; i++)
5609 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5610 level->envelope[0].text[envelope_size++] =
5611 header[envelope_content_pos + 1 + i];
5613 level->envelope[0].text[envelope_size] = '\0';
5615 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5616 level->envelope[0].ysize = 10;
5617 level->envelope[0].autowrap = TRUE;
5618 level->envelope[0].centered = TRUE;
5620 for (i = 0; i < level_name_len; i++)
5621 level->name[i] = header[level_name_pos + 1 + i];
5622 level->name[level_name_len] = '\0';
5624 for (i = 0; i < level_author_len; i++)
5625 level->author[i] = header[level_author_pos + 1 + i];
5626 level->author[level_author_len] = '\0';
5628 num_yamyam_contents = header[60] | (header[61] << 8);
5629 level->num_yamyam_contents =
5630 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5632 for (i = 0; i < num_yamyam_contents; i++)
5634 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5636 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5637 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5639 if (i < MAX_ELEMENT_CONTENTS)
5640 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5644 fieldx = header[6] | (header[7] << 8);
5645 fieldy = header[8] | (header[9] << 8);
5646 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5647 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5649 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5651 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5652 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5654 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5655 level->field[x][y] = getMappedElement_DC(element_dc);
5658 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5659 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5660 level->field[x][y] = EL_PLAYER_1;
5662 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5663 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5664 level->field[x][y] = EL_PLAYER_2;
5666 level->gems_needed = header[18] | (header[19] << 8);
5668 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5669 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5670 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5671 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5672 level->score[SC_NUT] = header[28] | (header[29] << 8);
5673 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5674 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5675 level->score[SC_BUG] = header[34] | (header[35] << 8);
5676 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5677 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5678 level->score[SC_KEY] = header[40] | (header[41] << 8);
5679 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5681 level->time = header[44] | (header[45] << 8);
5683 level->amoeba_speed = header[46] | (header[47] << 8);
5684 level->time_light = header[48] | (header[49] << 8);
5685 level->time_timegate = header[50] | (header[51] << 8);
5686 level->time_wheel = header[52] | (header[53] << 8);
5687 level->time_magic_wall = header[54] | (header[55] << 8);
5688 level->extra_time = header[56] | (header[57] << 8);
5689 level->shield_normal_time = header[58] | (header[59] << 8);
5691 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5692 can slip down from flat walls, like normal walls and steel walls */
5693 level->em_slippery_gems = TRUE;
5696 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5697 struct LevelFileInfo *level_file_info,
5698 boolean level_info_only)
5700 char *filename = level_file_info->filename;
5702 int num_magic_bytes = 8;
5703 char magic_bytes[num_magic_bytes + 1];
5704 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5706 if (!(file = openFile(filename, MODE_READ)))
5708 level->no_valid_file = TRUE;
5710 if (!level_info_only)
5711 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5716 // fseek(file, 0x0000, SEEK_SET);
5718 if (level_file_info->packed)
5720 /* read "magic bytes" from start of file */
5721 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5722 magic_bytes[0] = '\0';
5724 /* check "magic bytes" for correct file format */
5725 if (!strPrefix(magic_bytes, "DC2"))
5727 level->no_valid_file = TRUE;
5729 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5735 if (strPrefix(magic_bytes, "DC2Win95") ||
5736 strPrefix(magic_bytes, "DC2Win98"))
5738 int position_first_level = 0x00fa;
5739 int extra_bytes = 4;
5742 /* advance file stream to first level inside the level package */
5743 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5745 /* each block of level data is followed by block of non-level data */
5746 num_levels_to_skip *= 2;
5748 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5749 while (num_levels_to_skip >= 0)
5751 /* advance file stream to next level inside the level package */
5752 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5754 level->no_valid_file = TRUE;
5756 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5762 /* skip apparently unused extra bytes following each level */
5763 ReadUnusedBytesFromFile(file, extra_bytes);
5765 /* read size of next level in level package */
5766 skip_bytes = getFile32BitLE(file);
5768 num_levels_to_skip--;
5773 level->no_valid_file = TRUE;
5775 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5782 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5788 /* ------------------------------------------------------------------------- */
5789 /* functions for loading SB level */
5790 /* ------------------------------------------------------------------------- */
5792 int getMappedElement_SB(int element_ascii, boolean use_ces)
5800 sb_element_mapping[] =
5802 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5803 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5804 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5805 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5806 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5807 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5808 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5809 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5816 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5817 if (element_ascii == sb_element_mapping[i].ascii)
5818 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5820 return EL_UNDEFINED;
5823 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5824 struct LevelFileInfo *level_file_info,
5825 boolean level_info_only)
5827 char *filename = level_file_info->filename;
5828 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5829 char last_comment[MAX_LINE_LEN];
5830 char level_name[MAX_LINE_LEN];
5833 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5834 boolean read_continued_line = FALSE;
5835 boolean reading_playfield = FALSE;
5836 boolean got_valid_playfield_line = FALSE;
5837 boolean invalid_playfield_char = FALSE;
5838 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5839 int file_level_nr = 0;
5841 int x = 0, y = 0; /* initialized to make compilers happy */
5843 last_comment[0] = '\0';
5844 level_name[0] = '\0';
5846 if (!(file = openFile(filename, MODE_READ)))
5848 level->no_valid_file = TRUE;
5850 if (!level_info_only)
5851 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5856 while (!checkEndOfFile(file))
5858 /* level successfully read, but next level may follow here */
5859 if (!got_valid_playfield_line && reading_playfield)
5861 /* read playfield from single level file -- skip remaining file */
5862 if (!level_file_info->packed)
5865 if (file_level_nr >= num_levels_to_skip)
5870 last_comment[0] = '\0';
5871 level_name[0] = '\0';
5873 reading_playfield = FALSE;
5876 got_valid_playfield_line = FALSE;
5878 /* read next line of input file */
5879 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5882 /* check if line was completely read and is terminated by line break */
5883 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5886 /* cut trailing line break (this can be newline and/or carriage return) */
5887 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5888 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5891 /* copy raw input line for later use (mainly debugging output) */
5892 strcpy(line_raw, line);
5894 if (read_continued_line)
5896 /* append new line to existing line, if there is enough space */
5897 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5898 strcat(previous_line, line_ptr);
5900 strcpy(line, previous_line); /* copy storage buffer to line */
5902 read_continued_line = FALSE;
5905 /* if the last character is '\', continue at next line */
5906 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5908 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5909 strcpy(previous_line, line); /* copy line to storage buffer */
5911 read_continued_line = TRUE;
5916 /* skip empty lines */
5917 if (line[0] == '\0')
5920 /* extract comment text from comment line */
5923 for (line_ptr = line; *line_ptr; line_ptr++)
5924 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5927 strcpy(last_comment, line_ptr);
5932 /* extract level title text from line containing level title */
5933 if (line[0] == '\'')
5935 strcpy(level_name, &line[1]);
5937 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5938 level_name[strlen(level_name) - 1] = '\0';
5943 /* skip lines containing only spaces (or empty lines) */
5944 for (line_ptr = line; *line_ptr; line_ptr++)
5945 if (*line_ptr != ' ')
5947 if (*line_ptr == '\0')
5950 /* at this point, we have found a line containing part of a playfield */
5952 got_valid_playfield_line = TRUE;
5954 if (!reading_playfield)
5956 reading_playfield = TRUE;
5957 invalid_playfield_char = FALSE;
5959 for (x = 0; x < MAX_LEV_FIELDX; x++)
5960 for (y = 0; y < MAX_LEV_FIELDY; y++)
5961 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5966 /* start with topmost tile row */
5970 /* skip playfield line if larger row than allowed */
5971 if (y >= MAX_LEV_FIELDY)
5974 /* start with leftmost tile column */
5977 /* read playfield elements from line */
5978 for (line_ptr = line; *line_ptr; line_ptr++)
5980 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5982 /* stop parsing playfield line if larger column than allowed */
5983 if (x >= MAX_LEV_FIELDX)
5986 if (mapped_sb_element == EL_UNDEFINED)
5988 invalid_playfield_char = TRUE;
5993 level->field[x][y] = mapped_sb_element;
5995 /* continue with next tile column */
5998 level->fieldx = MAX(x, level->fieldx);
6001 if (invalid_playfield_char)
6003 /* if first playfield line, treat invalid lines as comment lines */
6005 reading_playfield = FALSE;
6010 /* continue with next tile row */
6018 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6019 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6021 if (!reading_playfield)
6023 level->no_valid_file = TRUE;
6025 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6030 if (*level_name != '\0')
6032 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6033 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6035 else if (*last_comment != '\0')
6037 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6038 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6042 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6045 /* set all empty fields beyond the border walls to invisible steel wall */
6046 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6048 if ((x == 0 || x == level->fieldx - 1 ||
6049 y == 0 || y == level->fieldy - 1) &&
6050 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6051 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6052 level->field, level->fieldx, level->fieldy);
6055 /* set special level settings for Sokoban levels */
6058 level->use_step_counter = TRUE;
6060 if (load_xsb_to_ces)
6062 /* special global settings can now be set in level template */
6064 level->use_custom_template = TRUE;
6069 /* ------------------------------------------------------------------------- */
6070 /* functions for handling native levels */
6071 /* ------------------------------------------------------------------------- */
6073 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6074 struct LevelFileInfo *level_file_info,
6075 boolean level_info_only)
6077 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6078 level->no_valid_file = TRUE;
6081 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6082 struct LevelFileInfo *level_file_info,
6083 boolean level_info_only)
6087 /* determine position of requested level inside level package */
6088 if (level_file_info->packed)
6089 pos = level_file_info->nr - leveldir_current->first_level;
6091 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6092 level->no_valid_file = TRUE;
6095 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6096 struct LevelFileInfo *level_file_info,
6097 boolean level_info_only)
6099 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6100 level->no_valid_file = TRUE;
6103 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6105 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6106 CopyNativeLevel_RND_to_EM(level);
6107 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6108 CopyNativeLevel_RND_to_SP(level);
6109 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6110 CopyNativeLevel_RND_to_MM(level);
6113 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6115 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6116 CopyNativeLevel_EM_to_RND(level);
6117 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6118 CopyNativeLevel_SP_to_RND(level);
6119 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6120 CopyNativeLevel_MM_to_RND(level);
6123 void SaveNativeLevel(struct LevelInfo *level)
6125 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6127 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6128 char *filename = getLevelFilenameFromBasename(basename);
6130 CopyNativeLevel_RND_to_SP(level);
6131 CopyNativeTape_RND_to_SP(level);
6133 SaveNativeLevel_SP(filename);
6138 /* ------------------------------------------------------------------------- */
6139 /* functions for loading generic level */
6140 /* ------------------------------------------------------------------------- */
6142 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6143 struct LevelFileInfo *level_file_info,
6144 boolean level_info_only)
6146 /* always start with reliable default values */
6147 setLevelInfoToDefaults(level, level_info_only, TRUE);
6149 switch (level_file_info->type)
6151 case LEVEL_FILE_TYPE_RND:
6152 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6155 case LEVEL_FILE_TYPE_EM:
6156 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6157 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6160 case LEVEL_FILE_TYPE_SP:
6161 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6162 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6165 case LEVEL_FILE_TYPE_MM:
6166 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6167 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6170 case LEVEL_FILE_TYPE_DC:
6171 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6174 case LEVEL_FILE_TYPE_SB:
6175 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6179 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6183 /* if level file is invalid, restore level structure to default values */
6184 if (level->no_valid_file)
6185 setLevelInfoToDefaults(level, level_info_only, FALSE);
6187 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6188 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6190 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6191 CopyNativeLevel_Native_to_RND(level);
6194 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6196 static struct LevelFileInfo level_file_info;
6198 /* always start with reliable default values */
6199 setFileInfoToDefaults(&level_file_info);
6201 level_file_info.nr = 0; /* unknown level number */
6202 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6203 level_file_info.filename = filename;
6205 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6208 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6212 if (leveldir_current == NULL) /* only when dumping level */
6215 /* all engine modifications also valid for levels which use latest engine */
6216 if (level->game_version < VERSION_IDENT(3,2,0,5))
6218 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6219 level->score[SC_TIME_BONUS] /= 10;
6222 if (leveldir_current->latest_engine)
6224 /* ---------- use latest game engine ----------------------------------- */
6226 /* For all levels which are forced to use the latest game engine version
6227 (normally all but user contributed, private and undefined levels), set
6228 the game engine version to the actual version; this allows for actual
6229 corrections in the game engine to take effect for existing, converted
6230 levels (from "classic" or other existing games) to make the emulation
6231 of the corresponding game more accurate, while (hopefully) not breaking
6232 existing levels created from other players. */
6234 level->game_version = GAME_VERSION_ACTUAL;
6236 /* Set special EM style gems behaviour: EM style gems slip down from
6237 normal, steel and growing wall. As this is a more fundamental change,
6238 it seems better to set the default behaviour to "off" (as it is more
6239 natural) and make it configurable in the level editor (as a property
6240 of gem style elements). Already existing converted levels (neither
6241 private nor contributed levels) are changed to the new behaviour. */
6243 if (level->file_version < FILE_VERSION_2_0)
6244 level->em_slippery_gems = TRUE;
6249 /* ---------- use game engine the level was created with ----------------- */
6251 /* For all levels which are not forced to use the latest game engine
6252 version (normally user contributed, private and undefined levels),
6253 use the version of the game engine the levels were created for.
6255 Since 2.0.1, the game engine version is now directly stored
6256 in the level file (chunk "VERS"), so there is no need anymore
6257 to set the game version from the file version (except for old,
6258 pre-2.0 levels, where the game version is still taken from the
6259 file format version used to store the level -- see above). */
6261 /* player was faster than enemies in 1.0.0 and before */
6262 if (level->file_version == FILE_VERSION_1_0)
6263 for (i = 0; i < MAX_PLAYERS; i++)
6264 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6266 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6267 if (level->game_version == VERSION_IDENT(2,0,1,0))
6268 level->em_slippery_gems = TRUE;
6270 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6271 if (level->game_version < VERSION_IDENT(2,2,0,0))
6272 level->use_spring_bug = TRUE;
6274 if (level->game_version < VERSION_IDENT(3,2,0,5))
6276 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6277 level->use_time_orb_bug = TRUE;
6279 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6280 level->block_snap_field = FALSE;
6282 /* extra time score was same value as time left score before 3.2.0-5 */
6283 level->extra_time_score = level->score[SC_TIME_BONUS];
6286 if (level->game_version < VERSION_IDENT(3,2,0,7))
6288 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6289 level->continuous_snapping = FALSE;
6292 /* only few elements were able to actively move into acid before 3.1.0 */
6293 /* trigger settings did not exist before 3.1.0; set to default "any" */
6294 if (level->game_version < VERSION_IDENT(3,1,0,0))
6296 /* correct "can move into acid" settings (all zero in old levels) */
6298 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6299 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6301 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6302 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6303 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6304 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6306 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6307 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6309 /* correct trigger settings (stored as zero == "none" in old levels) */
6311 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6313 int element = EL_CUSTOM_START + i;
6314 struct ElementInfo *ei = &element_info[element];
6316 for (j = 0; j < ei->num_change_pages; j++)
6318 struct ElementChangeInfo *change = &ei->change_page[j];
6320 change->trigger_player = CH_PLAYER_ANY;
6321 change->trigger_page = CH_PAGE_ANY;
6326 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6328 int element = EL_CUSTOM_256;
6329 struct ElementInfo *ei = &element_info[element];
6330 struct ElementChangeInfo *change = &ei->change_page[0];
6332 /* This is needed to fix a problem that was caused by a bugfix in function
6333 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6334 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6335 not replace walkable elements, but instead just placed the player on it,
6336 without placing the Sokoban field under the player). Unfortunately, this
6337 breaks "Snake Bite" style levels when the snake is halfway through a door
6338 that just closes (the snake head is still alive and can be moved in this
6339 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6340 player (without Sokoban element) which then gets killed as designed). */
6342 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6343 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6344 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6345 change->target_element = EL_PLAYER_1;
6348 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6349 if (level->game_version < VERSION_IDENT(3,2,5,0))
6351 /* This is needed to fix a problem that was caused by a bugfix in function
6352 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6353 corrects the behaviour when a custom element changes to another custom
6354 element with a higher element number that has change actions defined.
6355 Normally, only one change per frame is allowed for custom elements.
6356 Therefore, it is checked if a custom element already changed in the
6357 current frame; if it did, subsequent changes are suppressed.
6358 Unfortunately, this is only checked for element changes, but not for
6359 change actions, which are still executed. As the function above loops
6360 through all custom elements from lower to higher, an element change
6361 resulting in a lower CE number won't be checked again, while a target
6362 element with a higher number will also be checked, and potential change
6363 actions will get executed for this CE, too (which is wrong), while
6364 further changes are ignored (which is correct). As this bugfix breaks
6365 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6366 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6367 behaviour for existing levels and tapes that make use of this bug */
6369 level->use_action_after_change_bug = TRUE;
6372 /* not centering level after relocating player was default only in 3.2.3 */
6373 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6374 level->shifted_relocation = TRUE;
6376 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6377 if (level->game_version < VERSION_IDENT(3,2,6,0))
6378 level->em_explodes_by_fire = TRUE;
6381 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6385 /* map elements that have changed in newer versions */
6386 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6387 level->game_version);
6388 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6389 for (x = 0; x < 3; x++)
6390 for (y = 0; y < 3; y++)
6391 level->yamyam_content[i].e[x][y] =
6392 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6393 level->game_version);
6397 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6401 /* map custom element change events that have changed in newer versions
6402 (these following values were accidentally changed in version 3.0.1)
6403 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6404 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6406 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6408 int element = EL_CUSTOM_START + i;
6410 /* order of checking and copying events to be mapped is important */
6411 /* (do not change the start and end value -- they are constant) */
6412 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6414 if (HAS_CHANGE_EVENT(element, j - 2))
6416 SET_CHANGE_EVENT(element, j - 2, FALSE);
6417 SET_CHANGE_EVENT(element, j, TRUE);
6421 /* order of checking and copying events to be mapped is important */
6422 /* (do not change the start and end value -- they are constant) */
6423 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6425 if (HAS_CHANGE_EVENT(element, j - 1))
6427 SET_CHANGE_EVENT(element, j - 1, FALSE);
6428 SET_CHANGE_EVENT(element, j, TRUE);
6434 /* initialize "can_change" field for old levels with only one change page */
6435 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6437 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6439 int element = EL_CUSTOM_START + i;
6441 if (CAN_CHANGE(element))
6442 element_info[element].change->can_change = TRUE;
6446 /* correct custom element values (for old levels without these options) */
6447 if (level->game_version < VERSION_IDENT(3,1,1,0))
6449 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6451 int element = EL_CUSTOM_START + i;
6452 struct ElementInfo *ei = &element_info[element];
6454 if (ei->access_direction == MV_NO_DIRECTION)
6455 ei->access_direction = MV_ALL_DIRECTIONS;
6459 /* correct custom element values (fix invalid values for all versions) */
6462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6464 int element = EL_CUSTOM_START + i;
6465 struct ElementInfo *ei = &element_info[element];
6467 for (j = 0; j < ei->num_change_pages; j++)
6469 struct ElementChangeInfo *change = &ei->change_page[j];
6471 if (change->trigger_player == CH_PLAYER_NONE)
6472 change->trigger_player = CH_PLAYER_ANY;
6474 if (change->trigger_side == CH_SIDE_NONE)
6475 change->trigger_side = CH_SIDE_ANY;
6480 /* initialize "can_explode" field for old levels which did not store this */
6481 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6482 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6486 int element = EL_CUSTOM_START + i;
6488 if (EXPLODES_1X1_OLD(element))
6489 element_info[element].explosion_type = EXPLODES_1X1;
6491 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6492 EXPLODES_SMASHED(element) ||
6493 EXPLODES_IMPACT(element)));
6497 /* correct previously hard-coded move delay values for maze runner style */
6498 if (level->game_version < VERSION_IDENT(3,1,1,0))
6500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6502 int element = EL_CUSTOM_START + i;
6504 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6506 /* previously hard-coded and therefore ignored */
6507 element_info[element].move_delay_fixed = 9;
6508 element_info[element].move_delay_random = 0;
6513 /* set some other uninitialized values of custom elements in older levels */
6514 if (level->game_version < VERSION_IDENT(3,1,0,0))
6516 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6518 int element = EL_CUSTOM_START + i;
6520 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6522 element_info[element].explosion_delay = 17;
6523 element_info[element].ignition_delay = 8;
6528 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6530 LoadLevel_InitStandardElements(level);
6532 if (level->file_has_custom_elements)
6533 LoadLevel_InitCustomElements(level);
6535 /* initialize element properties for level editor etc. */
6536 InitElementPropertiesEngine(level->game_version);
6537 InitElementPropertiesGfxElement();
6540 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6544 /* map elements that have changed in newer versions */
6545 for (y = 0; y < level->fieldy; y++)
6546 for (x = 0; x < level->fieldx; x++)
6547 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6548 level->game_version);
6550 /* clear unused playfield data (nicer if level gets resized in editor) */
6551 for (x = 0; x < MAX_LEV_FIELDX; x++)
6552 for (y = 0; y < MAX_LEV_FIELDY; y++)
6553 if (x >= level->fieldx || y >= level->fieldy)
6554 level->field[x][y] = EL_EMPTY;
6556 /* copy elements to runtime playfield array */
6557 for (x = 0; x < MAX_LEV_FIELDX; x++)
6558 for (y = 0; y < MAX_LEV_FIELDY; y++)
6559 Feld[x][y] = level->field[x][y];
6561 /* initialize level size variables for faster access */
6562 lev_fieldx = level->fieldx;
6563 lev_fieldy = level->fieldy;
6565 /* determine border element for this level */
6566 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6567 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6572 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6574 struct LevelFileInfo *level_file_info = &level->file_info;
6576 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6577 CopyNativeLevel_RND_to_Native(level);
6580 void LoadLevelTemplate(int nr)
6584 setLevelFileInfo(&level_template.file_info, nr);
6585 filename = level_template.file_info.filename;
6587 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6589 LoadLevel_InitVersion(&level_template, filename);
6590 LoadLevel_InitElements(&level_template, filename);
6592 ActivateLevelTemplate();
6595 void LoadLevel(int nr)
6599 setLevelFileInfo(&level.file_info, nr);
6600 filename = level.file_info.filename;
6602 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6604 if (level.use_custom_template)
6605 LoadLevelTemplate(-1);
6607 LoadLevel_InitVersion(&level, filename);
6608 LoadLevel_InitElements(&level, filename);
6609 LoadLevel_InitPlayfield(&level, filename);
6611 LoadLevel_InitNativeEngines(&level, filename);
6614 void LoadLevelInfoOnly(int nr)
6616 setLevelFileInfo(&level.file_info, nr);
6618 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6621 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6625 chunk_size += putFileVersion(file, level->file_version);
6626 chunk_size += putFileVersion(file, level->game_version);
6631 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6635 chunk_size += putFile16BitBE(file, level->creation_date.year);
6636 chunk_size += putFile8Bit(file, level->creation_date.month);
6637 chunk_size += putFile8Bit(file, level->creation_date.day);
6642 #if ENABLE_HISTORIC_CHUNKS
6643 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6647 putFile8Bit(file, level->fieldx);
6648 putFile8Bit(file, level->fieldy);
6650 putFile16BitBE(file, level->time);
6651 putFile16BitBE(file, level->gems_needed);
6653 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6654 putFile8Bit(file, level->name[i]);
6656 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6657 putFile8Bit(file, level->score[i]);
6659 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6660 for (y = 0; y < 3; y++)
6661 for (x = 0; x < 3; x++)
6662 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6663 level->yamyam_content[i].e[x][y]));
6664 putFile8Bit(file, level->amoeba_speed);
6665 putFile8Bit(file, level->time_magic_wall);
6666 putFile8Bit(file, level->time_wheel);
6667 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6668 level->amoeba_content));
6669 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6670 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6671 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6672 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6674 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6676 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6677 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6678 putFile32BitBE(file, level->can_move_into_acid_bits);
6679 putFile8Bit(file, level->dont_collide_with_bits);
6681 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6682 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6684 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6685 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6686 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6688 putFile8Bit(file, level->game_engine_type);
6690 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6694 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6699 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6700 chunk_size += putFile8Bit(file, level->name[i]);
6705 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6710 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6711 chunk_size += putFile8Bit(file, level->author[i]);
6716 #if ENABLE_HISTORIC_CHUNKS
6717 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6722 for (y = 0; y < level->fieldy; y++)
6723 for (x = 0; x < level->fieldx; x++)
6724 if (level->encoding_16bit_field)
6725 chunk_size += putFile16BitBE(file, level->field[x][y]);
6727 chunk_size += putFile8Bit(file, level->field[x][y]);
6733 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6738 for (y = 0; y < level->fieldy; y++)
6739 for (x = 0; x < level->fieldx; x++)
6740 chunk_size += putFile16BitBE(file, level->field[x][y]);
6745 #if ENABLE_HISTORIC_CHUNKS
6746 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6750 putFile8Bit(file, EL_YAMYAM);
6751 putFile8Bit(file, level->num_yamyam_contents);
6752 putFile8Bit(file, 0);
6753 putFile8Bit(file, 0);
6755 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6756 for (y = 0; y < 3; y++)
6757 for (x = 0; x < 3; x++)
6758 if (level->encoding_16bit_field)
6759 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6761 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6765 #if ENABLE_HISTORIC_CHUNKS
6766 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6769 int num_contents, content_xsize, content_ysize;
6770 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6772 if (element == EL_YAMYAM)
6774 num_contents = level->num_yamyam_contents;
6778 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6779 for (y = 0; y < 3; y++)
6780 for (x = 0; x < 3; x++)
6781 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6783 else if (element == EL_BD_AMOEBA)
6789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6790 for (y = 0; y < 3; y++)
6791 for (x = 0; x < 3; x++)
6792 content_array[i][x][y] = EL_EMPTY;
6793 content_array[0][0][0] = level->amoeba_content;
6797 /* chunk header already written -- write empty chunk data */
6798 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6800 Error(ERR_WARN, "cannot save content for element '%d'", element);
6804 putFile16BitBE(file, element);
6805 putFile8Bit(file, num_contents);
6806 putFile8Bit(file, content_xsize);
6807 putFile8Bit(file, content_ysize);
6809 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6811 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6812 for (y = 0; y < 3; y++)
6813 for (x = 0; x < 3; x++)
6814 putFile16BitBE(file, content_array[i][x][y]);
6818 #if ENABLE_HISTORIC_CHUNKS
6819 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6821 int envelope_nr = element - EL_ENVELOPE_1;
6822 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6826 chunk_size += putFile16BitBE(file, element);
6827 chunk_size += putFile16BitBE(file, envelope_len);
6828 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6829 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6831 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6832 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6834 for (i = 0; i < envelope_len; i++)
6835 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6841 #if ENABLE_HISTORIC_CHUNKS
6842 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6843 int num_changed_custom_elements)
6847 putFile16BitBE(file, num_changed_custom_elements);
6849 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6851 int element = EL_CUSTOM_START + i;
6853 struct ElementInfo *ei = &element_info[element];
6855 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6857 if (check < num_changed_custom_elements)
6859 putFile16BitBE(file, element);
6860 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6867 if (check != num_changed_custom_elements) /* should not happen */
6868 Error(ERR_WARN, "inconsistent number of custom element properties");
6872 #if ENABLE_HISTORIC_CHUNKS
6873 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6874 int num_changed_custom_elements)
6878 putFile16BitBE(file, num_changed_custom_elements);
6880 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6882 int element = EL_CUSTOM_START + i;
6884 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6886 if (check < num_changed_custom_elements)
6888 putFile16BitBE(file, element);
6889 putFile16BitBE(file, element_info[element].change->target_element);
6896 if (check != num_changed_custom_elements) /* should not happen */
6897 Error(ERR_WARN, "inconsistent number of custom target elements");
6901 #if ENABLE_HISTORIC_CHUNKS
6902 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6903 int num_changed_custom_elements)
6905 int i, j, x, y, check = 0;
6907 putFile16BitBE(file, num_changed_custom_elements);
6909 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6911 int element = EL_CUSTOM_START + i;
6912 struct ElementInfo *ei = &element_info[element];
6914 if (ei->modified_settings)
6916 if (check < num_changed_custom_elements)
6918 putFile16BitBE(file, element);
6920 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6921 putFile8Bit(file, ei->description[j]);
6923 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6925 /* some free bytes for future properties and padding */
6926 WriteUnusedBytesToFile(file, 7);
6928 putFile8Bit(file, ei->use_gfx_element);
6929 putFile16BitBE(file, ei->gfx_element_initial);
6931 putFile8Bit(file, ei->collect_score_initial);
6932 putFile8Bit(file, ei->collect_count_initial);
6934 putFile16BitBE(file, ei->push_delay_fixed);
6935 putFile16BitBE(file, ei->push_delay_random);
6936 putFile16BitBE(file, ei->move_delay_fixed);
6937 putFile16BitBE(file, ei->move_delay_random);
6939 putFile16BitBE(file, ei->move_pattern);
6940 putFile8Bit(file, ei->move_direction_initial);
6941 putFile8Bit(file, ei->move_stepsize);
6943 for (y = 0; y < 3; y++)
6944 for (x = 0; x < 3; x++)
6945 putFile16BitBE(file, ei->content.e[x][y]);
6947 putFile32BitBE(file, ei->change->events);
6949 putFile16BitBE(file, ei->change->target_element);
6951 putFile16BitBE(file, ei->change->delay_fixed);
6952 putFile16BitBE(file, ei->change->delay_random);
6953 putFile16BitBE(file, ei->change->delay_frames);
6955 putFile16BitBE(file, ei->change->initial_trigger_element);
6957 putFile8Bit(file, ei->change->explode);
6958 putFile8Bit(file, ei->change->use_target_content);
6959 putFile8Bit(file, ei->change->only_if_complete);
6960 putFile8Bit(file, ei->change->use_random_replace);
6962 putFile8Bit(file, ei->change->random_percentage);
6963 putFile8Bit(file, ei->change->replace_when);
6965 for (y = 0; y < 3; y++)
6966 for (x = 0; x < 3; x++)
6967 putFile16BitBE(file, ei->change->content.e[x][y]);
6969 putFile8Bit(file, ei->slippery_type);
6971 /* some free bytes for future properties and padding */
6972 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6979 if (check != num_changed_custom_elements) /* should not happen */
6980 Error(ERR_WARN, "inconsistent number of custom element properties");
6984 #if ENABLE_HISTORIC_CHUNKS
6985 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6987 struct ElementInfo *ei = &element_info[element];
6990 /* ---------- custom element base property values (96 bytes) ------------- */
6992 putFile16BitBE(file, element);
6994 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6995 putFile8Bit(file, ei->description[i]);
6997 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6999 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7001 putFile8Bit(file, ei->num_change_pages);
7003 putFile16BitBE(file, ei->ce_value_fixed_initial);
7004 putFile16BitBE(file, ei->ce_value_random_initial);
7005 putFile8Bit(file, ei->use_last_ce_value);
7007 putFile8Bit(file, ei->use_gfx_element);
7008 putFile16BitBE(file, ei->gfx_element_initial);
7010 putFile8Bit(file, ei->collect_score_initial);
7011 putFile8Bit(file, ei->collect_count_initial);
7013 putFile8Bit(file, ei->drop_delay_fixed);
7014 putFile8Bit(file, ei->push_delay_fixed);
7015 putFile8Bit(file, ei->drop_delay_random);
7016 putFile8Bit(file, ei->push_delay_random);
7017 putFile16BitBE(file, ei->move_delay_fixed);
7018 putFile16BitBE(file, ei->move_delay_random);
7020 /* bits 0 - 15 of "move_pattern" ... */
7021 putFile16BitBE(file, ei->move_pattern & 0xffff);
7022 putFile8Bit(file, ei->move_direction_initial);
7023 putFile8Bit(file, ei->move_stepsize);
7025 putFile8Bit(file, ei->slippery_type);
7027 for (y = 0; y < 3; y++)
7028 for (x = 0; x < 3; x++)
7029 putFile16BitBE(file, ei->content.e[x][y]);
7031 putFile16BitBE(file, ei->move_enter_element);
7032 putFile16BitBE(file, ei->move_leave_element);
7033 putFile8Bit(file, ei->move_leave_type);
7035 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7036 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7038 putFile8Bit(file, ei->access_direction);
7040 putFile8Bit(file, ei->explosion_delay);
7041 putFile8Bit(file, ei->ignition_delay);
7042 putFile8Bit(file, ei->explosion_type);
7044 /* some free bytes for future custom property values and padding */
7045 WriteUnusedBytesToFile(file, 1);
7047 /* ---------- change page property values (48 bytes) --------------------- */
7049 for (i = 0; i < ei->num_change_pages; i++)
7051 struct ElementChangeInfo *change = &ei->change_page[i];
7052 unsigned int event_bits;
7054 /* bits 0 - 31 of "has_event[]" ... */
7056 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7057 if (change->has_event[j])
7058 event_bits |= (1 << j);
7059 putFile32BitBE(file, event_bits);
7061 putFile16BitBE(file, change->target_element);
7063 putFile16BitBE(file, change->delay_fixed);
7064 putFile16BitBE(file, change->delay_random);
7065 putFile16BitBE(file, change->delay_frames);
7067 putFile16BitBE(file, change->initial_trigger_element);
7069 putFile8Bit(file, change->explode);
7070 putFile8Bit(file, change->use_target_content);
7071 putFile8Bit(file, change->only_if_complete);
7072 putFile8Bit(file, change->use_random_replace);
7074 putFile8Bit(file, change->random_percentage);
7075 putFile8Bit(file, change->replace_when);
7077 for (y = 0; y < 3; y++)
7078 for (x = 0; x < 3; x++)
7079 putFile16BitBE(file, change->target_content.e[x][y]);
7081 putFile8Bit(file, change->can_change);
7083 putFile8Bit(file, change->trigger_side);
7085 putFile8Bit(file, change->trigger_player);
7086 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7087 log_2(change->trigger_page)));
7089 putFile8Bit(file, change->has_action);
7090 putFile8Bit(file, change->action_type);
7091 putFile8Bit(file, change->action_mode);
7092 putFile16BitBE(file, change->action_arg);
7094 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7096 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7097 if (change->has_event[j])
7098 event_bits |= (1 << (j - 32));
7099 putFile8Bit(file, event_bits);
7104 #if ENABLE_HISTORIC_CHUNKS
7105 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7107 struct ElementInfo *ei = &element_info[element];
7108 struct ElementGroupInfo *group = ei->group;
7111 putFile16BitBE(file, element);
7113 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7114 putFile8Bit(file, ei->description[i]);
7116 putFile8Bit(file, group->num_elements);
7118 putFile8Bit(file, ei->use_gfx_element);
7119 putFile16BitBE(file, ei->gfx_element_initial);
7121 putFile8Bit(file, group->choice_mode);
7123 /* some free bytes for future values and padding */
7124 WriteUnusedBytesToFile(file, 3);
7126 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7127 putFile16BitBE(file, group->element[i]);
7131 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7132 boolean write_element)
7134 int save_type = entry->save_type;
7135 int data_type = entry->data_type;
7136 int conf_type = entry->conf_type;
7137 int byte_mask = conf_type & CONF_MASK_BYTES;
7138 int element = entry->element;
7139 int default_value = entry->default_value;
7141 boolean modified = FALSE;
7143 if (byte_mask != CONF_MASK_MULTI_BYTES)
7145 void *value_ptr = entry->value;
7146 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7149 /* check if any settings have been modified before saving them */
7150 if (value != default_value)
7153 /* do not save if explicitly told or if unmodified default settings */
7154 if ((save_type == SAVE_CONF_NEVER) ||
7155 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7159 num_bytes += putFile16BitBE(file, element);
7161 num_bytes += putFile8Bit(file, conf_type);
7162 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7163 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7164 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7167 else if (data_type == TYPE_STRING)
7169 char *default_string = entry->default_string;
7170 char *string = (char *)(entry->value);
7171 int string_length = strlen(string);
7174 /* check if any settings have been modified before saving them */
7175 if (!strEqual(string, default_string))
7178 /* do not save if explicitly told or if unmodified default settings */
7179 if ((save_type == SAVE_CONF_NEVER) ||
7180 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7184 num_bytes += putFile16BitBE(file, element);
7186 num_bytes += putFile8Bit(file, conf_type);
7187 num_bytes += putFile16BitBE(file, string_length);
7189 for (i = 0; i < string_length; i++)
7190 num_bytes += putFile8Bit(file, string[i]);
7192 else if (data_type == TYPE_ELEMENT_LIST)
7194 int *element_array = (int *)(entry->value);
7195 int num_elements = *(int *)(entry->num_entities);
7198 /* check if any settings have been modified before saving them */
7199 for (i = 0; i < num_elements; i++)
7200 if (element_array[i] != default_value)
7203 /* do not save if explicitly told or if unmodified default settings */
7204 if ((save_type == SAVE_CONF_NEVER) ||
7205 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7209 num_bytes += putFile16BitBE(file, element);
7211 num_bytes += putFile8Bit(file, conf_type);
7212 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7214 for (i = 0; i < num_elements; i++)
7215 num_bytes += putFile16BitBE(file, element_array[i]);
7217 else if (data_type == TYPE_CONTENT_LIST)
7219 struct Content *content = (struct Content *)(entry->value);
7220 int num_contents = *(int *)(entry->num_entities);
7223 /* check if any settings have been modified before saving them */
7224 for (i = 0; i < num_contents; i++)
7225 for (y = 0; y < 3; y++)
7226 for (x = 0; x < 3; x++)
7227 if (content[i].e[x][y] != default_value)
7230 /* do not save if explicitly told or if unmodified default settings */
7231 if ((save_type == SAVE_CONF_NEVER) ||
7232 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7236 num_bytes += putFile16BitBE(file, element);
7238 num_bytes += putFile8Bit(file, conf_type);
7239 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7241 for (i = 0; i < num_contents; i++)
7242 for (y = 0; y < 3; y++)
7243 for (x = 0; x < 3; x++)
7244 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7250 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7255 li = *level; /* copy level data into temporary buffer */
7257 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7258 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7263 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7268 li = *level; /* copy level data into temporary buffer */
7270 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7271 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7276 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7278 int envelope_nr = element - EL_ENVELOPE_1;
7282 chunk_size += putFile16BitBE(file, element);
7284 /* copy envelope data into temporary buffer */
7285 xx_envelope = level->envelope[envelope_nr];
7287 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7288 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7293 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7295 struct ElementInfo *ei = &element_info[element];
7299 chunk_size += putFile16BitBE(file, element);
7301 xx_ei = *ei; /* copy element data into temporary buffer */
7303 /* set default description string for this specific element */
7304 strcpy(xx_default_description, getDefaultElementDescription(ei));
7306 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7307 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7309 for (i = 0; i < ei->num_change_pages; i++)
7311 struct ElementChangeInfo *change = &ei->change_page[i];
7313 xx_current_change_page = i;
7315 xx_change = *change; /* copy change data into temporary buffer */
7318 setEventBitsFromEventFlags(change);
7320 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7321 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7328 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7330 struct ElementInfo *ei = &element_info[element];
7331 struct ElementGroupInfo *group = ei->group;
7335 chunk_size += putFile16BitBE(file, element);
7337 xx_ei = *ei; /* copy element data into temporary buffer */
7338 xx_group = *group; /* copy group data into temporary buffer */
7340 /* set default description string for this specific element */
7341 strcpy(xx_default_description, getDefaultElementDescription(ei));
7343 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7344 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7349 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7350 boolean save_as_template)
7356 if (!(file = fopen(filename, MODE_WRITE)))
7358 Error(ERR_WARN, "cannot save level file '%s'", filename);
7362 level->file_version = FILE_VERSION_ACTUAL;
7363 level->game_version = GAME_VERSION_ACTUAL;
7365 level->creation_date = getCurrentDate();
7367 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7368 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7370 chunk_size = SaveLevel_VERS(NULL, level);
7371 putFileChunkBE(file, "VERS", chunk_size);
7372 SaveLevel_VERS(file, level);
7374 chunk_size = SaveLevel_DATE(NULL, level);
7375 putFileChunkBE(file, "DATE", chunk_size);
7376 SaveLevel_DATE(file, level);
7378 chunk_size = SaveLevel_NAME(NULL, level);
7379 putFileChunkBE(file, "NAME", chunk_size);
7380 SaveLevel_NAME(file, level);
7382 chunk_size = SaveLevel_AUTH(NULL, level);
7383 putFileChunkBE(file, "AUTH", chunk_size);
7384 SaveLevel_AUTH(file, level);
7386 chunk_size = SaveLevel_INFO(NULL, level);
7387 putFileChunkBE(file, "INFO", chunk_size);
7388 SaveLevel_INFO(file, level);
7390 chunk_size = SaveLevel_BODY(NULL, level);
7391 putFileChunkBE(file, "BODY", chunk_size);
7392 SaveLevel_BODY(file, level);
7394 chunk_size = SaveLevel_ELEM(NULL, level);
7395 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7397 putFileChunkBE(file, "ELEM", chunk_size);
7398 SaveLevel_ELEM(file, level);
7401 for (i = 0; i < NUM_ENVELOPES; i++)
7403 int element = EL_ENVELOPE_1 + i;
7405 chunk_size = SaveLevel_NOTE(NULL, level, element);
7406 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7408 putFileChunkBE(file, "NOTE", chunk_size);
7409 SaveLevel_NOTE(file, level, element);
7413 /* if not using template level, check for non-default custom/group elements */
7414 if (!level->use_custom_template || save_as_template)
7416 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7418 int element = EL_CUSTOM_START + i;
7420 chunk_size = SaveLevel_CUSX(NULL, level, element);
7421 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7423 putFileChunkBE(file, "CUSX", chunk_size);
7424 SaveLevel_CUSX(file, level, element);
7428 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7430 int element = EL_GROUP_START + i;
7432 chunk_size = SaveLevel_GRPX(NULL, level, element);
7433 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7435 putFileChunkBE(file, "GRPX", chunk_size);
7436 SaveLevel_GRPX(file, level, element);
7443 SetFilePermissions(filename, PERMS_PRIVATE);
7446 void SaveLevel(int nr)
7448 char *filename = getDefaultLevelFilename(nr);
7450 SaveLevelFromFilename(&level, filename, FALSE);
7453 void SaveLevelTemplate()
7455 char *filename = getLocalLevelTemplateFilename();
7457 SaveLevelFromFilename(&level, filename, TRUE);
7460 boolean SaveLevelChecked(int nr)
7462 char *filename = getDefaultLevelFilename(nr);
7463 boolean new_level = !fileExists(filename);
7464 boolean level_saved = FALSE;
7466 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7471 Request("Level saved!", REQ_CONFIRM);
7479 void DumpLevel(struct LevelInfo *level)
7481 if (level->no_level_file || level->no_valid_file)
7483 Error(ERR_WARN, "cannot dump -- no valid level file found");
7489 Print("Level xxx (file version %08d, game version %08d)\n",
7490 level->file_version, level->game_version);
7493 Print("Level author: '%s'\n", level->author);
7494 Print("Level title: '%s'\n", level->name);
7496 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7498 Print("Level time: %d seconds\n", level->time);
7499 Print("Gems needed: %d\n", level->gems_needed);
7501 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7502 Print("Time for wheel: %d seconds\n", level->time_wheel);
7503 Print("Time for light: %d seconds\n", level->time_light);
7504 Print("Time for timegate: %d seconds\n", level->time_timegate);
7506 Print("Amoeba speed: %d\n", level->amoeba_speed);
7509 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7510 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7511 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7512 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7513 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7519 /* ========================================================================= */
7520 /* tape file functions */
7521 /* ========================================================================= */
7523 static void setTapeInfoToDefaults()
7527 /* always start with reliable default values (empty tape) */
7530 /* default values (also for pre-1.2 tapes) with only the first player */
7531 tape.player_participates[0] = TRUE;
7532 for (i = 1; i < MAX_PLAYERS; i++)
7533 tape.player_participates[i] = FALSE;
7535 /* at least one (default: the first) player participates in every tape */
7536 tape.num_participating_players = 1;
7538 tape.level_nr = level_nr;
7540 tape.changed = FALSE;
7542 tape.recording = FALSE;
7543 tape.playing = FALSE;
7544 tape.pausing = FALSE;
7546 tape.no_valid_file = FALSE;
7549 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7551 tape->file_version = getFileVersion(file);
7552 tape->game_version = getFileVersion(file);
7557 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7561 tape->random_seed = getFile32BitBE(file);
7562 tape->date = getFile32BitBE(file);
7563 tape->length = getFile32BitBE(file);
7565 /* read header fields that are new since version 1.2 */
7566 if (tape->file_version >= FILE_VERSION_1_2)
7568 byte store_participating_players = getFile8Bit(file);
7571 /* since version 1.2, tapes store which players participate in the tape */
7572 tape->num_participating_players = 0;
7573 for (i = 0; i < MAX_PLAYERS; i++)
7575 tape->player_participates[i] = FALSE;
7577 if (store_participating_players & (1 << i))
7579 tape->player_participates[i] = TRUE;
7580 tape->num_participating_players++;
7584 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7586 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7588 engine_version = getFileVersion(file);
7589 if (engine_version > 0)
7590 tape->engine_version = engine_version;
7592 tape->engine_version = tape->game_version;
7598 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7600 int level_identifier_size;
7603 level_identifier_size = getFile16BitBE(file);
7605 tape->level_identifier =
7606 checked_realloc(tape->level_identifier, level_identifier_size);
7608 for (i = 0; i < level_identifier_size; i++)
7609 tape->level_identifier[i] = getFile8Bit(file);
7611 tape->level_nr = getFile16BitBE(file);
7613 chunk_size = 2 + level_identifier_size + 2;
7618 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7622 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7623 int chunk_size_expected = tape_pos_size * tape->length;
7625 if (chunk_size_expected != chunk_size)
7627 ReadUnusedBytesFromFile(file, chunk_size);
7628 return chunk_size_expected;
7631 for (i = 0; i < tape->length; i++)
7633 if (i >= MAX_TAPE_LEN)
7635 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7638 // tape too large; read and ignore remaining tape data from this chunk
7639 for (;i < tape->length; i++)
7640 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7645 if (tape->use_mouse)
7647 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7648 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7649 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7651 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7655 for (j = 0; j < MAX_PLAYERS; j++)
7657 tape->pos[i].action[j] = MV_NONE;
7659 if (tape->player_participates[j])
7660 tape->pos[i].action[j] = getFile8Bit(file);
7664 tape->pos[i].delay = getFile8Bit(file);
7666 if (tape->file_version == FILE_VERSION_1_0)
7668 /* eliminate possible diagonal moves in old tapes */
7669 /* this is only for backward compatibility */
7671 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7672 byte action = tape->pos[i].action[0];
7673 int k, num_moves = 0;
7675 for (k = 0; k<4; k++)
7677 if (action & joy_dir[k])
7679 tape->pos[i + num_moves].action[0] = joy_dir[k];
7681 tape->pos[i + num_moves].delay = 0;
7690 tape->length += num_moves;
7693 else if (tape->file_version < FILE_VERSION_2_0)
7695 /* convert pre-2.0 tapes to new tape format */
7697 if (tape->pos[i].delay > 1)
7700 tape->pos[i + 1] = tape->pos[i];
7701 tape->pos[i + 1].delay = 1;
7704 for (j = 0; j < MAX_PLAYERS; j++)
7705 tape->pos[i].action[j] = MV_NONE;
7706 tape->pos[i].delay--;
7713 if (checkEndOfFile(file))
7717 if (i != tape->length)
7718 chunk_size = tape_pos_size * i;
7723 void LoadTape_SokobanSolution(char *filename)
7726 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7728 if (!(file = openFile(filename, MODE_READ)))
7730 tape.no_valid_file = TRUE;
7735 while (!checkEndOfFile(file))
7737 unsigned char c = getByteFromFile(file);
7739 if (checkEndOfFile(file))
7746 tape.pos[tape.length].action[0] = MV_UP;
7747 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7753 tape.pos[tape.length].action[0] = MV_DOWN;
7754 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7760 tape.pos[tape.length].action[0] = MV_LEFT;
7761 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7767 tape.pos[tape.length].action[0] = MV_RIGHT;
7768 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7776 /* ignore white-space characters */
7780 tape.no_valid_file = TRUE;
7782 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7790 if (tape.no_valid_file)
7793 tape.length_frames = GetTapeLengthFrames();
7794 tape.length_seconds = GetTapeLengthSeconds();
7797 void LoadTapeFromFilename(char *filename)
7799 char cookie[MAX_LINE_LEN];
7800 char chunk_name[CHUNK_ID_LEN + 1];
7804 /* always start with reliable default values */
7805 setTapeInfoToDefaults();
7807 if (strSuffix(filename, ".sln"))
7809 LoadTape_SokobanSolution(filename);
7814 if (!(file = openFile(filename, MODE_READ)))
7816 tape.no_valid_file = TRUE;
7821 getFileChunkBE(file, chunk_name, NULL);
7822 if (strEqual(chunk_name, "RND1"))
7824 getFile32BitBE(file); /* not used */
7826 getFileChunkBE(file, chunk_name, NULL);
7827 if (!strEqual(chunk_name, "TAPE"))
7829 tape.no_valid_file = TRUE;
7831 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7838 else /* check for pre-2.0 file format with cookie string */
7840 strcpy(cookie, chunk_name);
7841 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7843 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7844 cookie[strlen(cookie) - 1] = '\0';
7846 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7848 tape.no_valid_file = TRUE;
7850 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7857 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7859 tape.no_valid_file = TRUE;
7861 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7868 /* pre-2.0 tape files have no game version, so use file version here */
7869 tape.game_version = tape.file_version;
7872 if (tape.file_version < FILE_VERSION_1_2)
7874 /* tape files from versions before 1.2.0 without chunk structure */
7875 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7876 LoadTape_BODY(file, 2 * tape.length, &tape);
7884 int (*loader)(File *, int, struct TapeInfo *);
7888 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7889 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7890 { "INFO", -1, LoadTape_INFO },
7891 { "BODY", -1, LoadTape_BODY },
7895 while (getFileChunkBE(file, chunk_name, &chunk_size))
7899 while (chunk_info[i].name != NULL &&
7900 !strEqual(chunk_name, chunk_info[i].name))
7903 if (chunk_info[i].name == NULL)
7905 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7906 chunk_name, filename);
7907 ReadUnusedBytesFromFile(file, chunk_size);
7909 else if (chunk_info[i].size != -1 &&
7910 chunk_info[i].size != chunk_size)
7912 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7913 chunk_size, chunk_name, filename);
7914 ReadUnusedBytesFromFile(file, chunk_size);
7918 /* call function to load this tape chunk */
7919 int chunk_size_expected =
7920 (chunk_info[i].loader)(file, chunk_size, &tape);
7922 /* the size of some chunks cannot be checked before reading other
7923 chunks first (like "HEAD" and "BODY") that contain some header
7924 information, so check them here */
7925 if (chunk_size_expected != chunk_size)
7927 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7928 chunk_size, chunk_name, filename);
7936 tape.length_frames = GetTapeLengthFrames();
7937 tape.length_seconds = GetTapeLengthSeconds();
7940 printf("::: tape file version: %d\n", tape.file_version);
7941 printf("::: tape game version: %d\n", tape.game_version);
7942 printf("::: tape engine version: %d\n", tape.engine_version);
7946 void LoadTape(int nr)
7948 char *filename = getTapeFilename(nr);
7950 LoadTapeFromFilename(filename);
7953 void LoadSolutionTape(int nr)
7955 char *filename = getSolutionTapeFilename(nr);
7957 LoadTapeFromFilename(filename);
7959 if (TAPE_IS_EMPTY(tape) &&
7960 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7961 level.native_sp_level->demo.is_available)
7962 CopyNativeTape_SP_to_RND(&level);
7965 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7967 putFileVersion(file, tape->file_version);
7968 putFileVersion(file, tape->game_version);
7971 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7974 byte store_participating_players = 0;
7976 /* set bits for participating players for compact storage */
7977 for (i = 0; i < MAX_PLAYERS; i++)
7978 if (tape->player_participates[i])
7979 store_participating_players |= (1 << i);
7981 putFile32BitBE(file, tape->random_seed);
7982 putFile32BitBE(file, tape->date);
7983 putFile32BitBE(file, tape->length);
7985 putFile8Bit(file, store_participating_players);
7987 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7989 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7990 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7992 putFileVersion(file, tape->engine_version);
7995 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7997 int level_identifier_size = strlen(tape->level_identifier) + 1;
8000 putFile16BitBE(file, level_identifier_size);
8002 for (i = 0; i < level_identifier_size; i++)
8003 putFile8Bit(file, tape->level_identifier[i]);
8005 putFile16BitBE(file, tape->level_nr);
8008 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8012 for (i = 0; i < tape->length; i++)
8014 if (tape->use_mouse)
8016 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8017 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8018 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8022 for (j = 0; j < MAX_PLAYERS; j++)
8023 if (tape->player_participates[j])
8024 putFile8Bit(file, tape->pos[i].action[j]);
8027 putFile8Bit(file, tape->pos[i].delay);
8031 void SaveTape(int nr)
8033 char *filename = getTapeFilename(nr);
8035 int num_participating_players = 0;
8037 int info_chunk_size;
8038 int body_chunk_size;
8041 InitTapeDirectory(leveldir_current->subdir);
8043 if (!(file = fopen(filename, MODE_WRITE)))
8045 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8049 tape.file_version = FILE_VERSION_ACTUAL;
8050 tape.game_version = GAME_VERSION_ACTUAL;
8052 /* count number of participating players */
8053 for (i = 0; i < MAX_PLAYERS; i++)
8054 if (tape.player_participates[i])
8055 num_participating_players++;
8057 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8059 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8060 body_chunk_size = tape_pos_size * tape.length;
8062 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8063 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8065 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8066 SaveTape_VERS(file, &tape);
8068 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8069 SaveTape_HEAD(file, &tape);
8071 putFileChunkBE(file, "INFO", info_chunk_size);
8072 SaveTape_INFO(file, &tape);
8074 putFileChunkBE(file, "BODY", body_chunk_size);
8075 SaveTape_BODY(file, &tape);
8079 SetFilePermissions(filename, PERMS_PRIVATE);
8081 tape.changed = FALSE;
8084 boolean SaveTapeChecked(int nr)
8086 char *filename = getTapeFilename(nr);
8087 boolean new_tape = !fileExists(filename);
8088 boolean tape_saved = FALSE;
8090 if (new_tape || Request("Replace old tape?", REQ_ASK))
8095 Request("Tape saved!", REQ_CONFIRM);
8103 void DumpTape(struct TapeInfo *tape)
8105 int tape_frame_counter;
8108 if (tape->no_valid_file)
8110 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8116 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8117 tape->level_nr, tape->file_version, tape->game_version);
8118 Print(" (effective engine version %08d)\n",
8119 tape->engine_version);
8120 Print("Level series identifier: '%s'\n", tape->level_identifier);
8123 tape_frame_counter = 0;
8125 for (i = 0; i < tape->length; i++)
8127 if (i >= MAX_TAPE_LEN)
8132 for (j = 0; j < MAX_PLAYERS; j++)
8134 if (tape->player_participates[j])
8136 int action = tape->pos[i].action[j];
8138 Print("%d:%02x ", j, action);
8139 Print("[%c%c%c%c|%c%c] - ",
8140 (action & JOY_LEFT ? '<' : ' '),
8141 (action & JOY_RIGHT ? '>' : ' '),
8142 (action & JOY_UP ? '^' : ' '),
8143 (action & JOY_DOWN ? 'v' : ' '),
8144 (action & JOY_BUTTON_1 ? '1' : ' '),
8145 (action & JOY_BUTTON_2 ? '2' : ' '));
8149 Print("(%03d) ", tape->pos[i].delay);
8150 Print("[%05d]\n", tape_frame_counter);
8152 tape_frame_counter += tape->pos[i].delay;
8159 /* ========================================================================= */
8160 /* score file functions */
8161 /* ========================================================================= */
8163 void LoadScore(int nr)
8166 char *filename = getScoreFilename(nr);
8167 char cookie[MAX_LINE_LEN];
8168 char line[MAX_LINE_LEN];
8172 /* always start with reliable default values */
8173 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8175 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8176 highscore[i].Score = 0;
8179 if (!(file = fopen(filename, MODE_READ)))
8182 /* check file identifier */
8183 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8185 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8186 cookie[strlen(cookie) - 1] = '\0';
8188 if (!checkCookieString(cookie, SCORE_COOKIE))
8190 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8195 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8197 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8198 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8199 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8202 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8203 line[strlen(line) - 1] = '\0';
8205 for (line_ptr = line; *line_ptr; line_ptr++)
8207 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8209 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8210 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8219 void SaveScore(int nr)
8222 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8223 char *filename = getScoreFilename(nr);
8226 InitScoreDirectory(leveldir_current->subdir);
8228 if (!(file = fopen(filename, MODE_WRITE)))
8230 Error(ERR_WARN, "cannot save score for level %d", nr);
8234 fprintf(file, "%s\n\n", SCORE_COOKIE);
8236 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8237 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8241 SetFilePermissions(filename, permissions);
8245 /* ========================================================================= */
8246 /* setup file functions */
8247 /* ========================================================================= */
8249 #define TOKEN_STR_PLAYER_PREFIX "player_"
8252 #define SETUP_TOKEN_PLAYER_NAME 0
8253 #define SETUP_TOKEN_SOUND 1
8254 #define SETUP_TOKEN_SOUND_LOOPS 2
8255 #define SETUP_TOKEN_SOUND_MUSIC 3
8256 #define SETUP_TOKEN_SOUND_SIMPLE 4
8257 #define SETUP_TOKEN_TOONS 5
8258 #define SETUP_TOKEN_SCROLL_DELAY 6
8259 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8260 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8261 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8262 #define SETUP_TOKEN_FADE_SCREENS 10
8263 #define SETUP_TOKEN_AUTORECORD 11
8264 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8265 #define SETUP_TOKEN_QUICK_DOORS 13
8266 #define SETUP_TOKEN_TEAM_MODE 14
8267 #define SETUP_TOKEN_HANDICAP 15
8268 #define SETUP_TOKEN_SKIP_LEVELS 16
8269 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8270 #define SETUP_TOKEN_TIME_LIMIT 18
8271 #define SETUP_TOKEN_FULLSCREEN 19
8272 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8273 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8274 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8275 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8276 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8277 #define SETUP_TOKEN_QUICK_SWITCH 25
8278 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8279 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8280 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8281 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8282 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8283 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8284 #define SETUP_TOKEN_GRAPHICS_SET 32
8285 #define SETUP_TOKEN_SOUNDS_SET 33
8286 #define SETUP_TOKEN_MUSIC_SET 34
8287 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8288 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8289 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8290 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8291 #define SETUP_TOKEN_VOLUME_LOOPS 39
8292 #define SETUP_TOKEN_VOLUME_MUSIC 40
8293 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8294 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8295 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8297 #define NUM_GLOBAL_SETUP_TOKENS 44
8300 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0
8302 #define NUM_AUTO_SETUP_TOKENS 1
8305 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8306 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8307 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8308 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8309 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8310 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8312 #define NUM_EDITOR_SETUP_TOKENS 6
8314 /* editor cascade setup */
8315 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8316 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8317 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8318 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8319 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8320 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8321 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8322 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8323 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8324 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8325 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8326 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8327 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8328 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8329 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8331 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8333 /* shortcut setup */
8334 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8335 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8336 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8337 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8338 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8339 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8340 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8341 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8342 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8343 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8344 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8345 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8346 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8347 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8348 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8349 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8350 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8351 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8352 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8353 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8354 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8356 #define NUM_SHORTCUT_SETUP_TOKENS 21
8359 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8360 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8361 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8362 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8363 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8364 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8365 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8366 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8367 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8368 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8369 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8370 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8371 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8372 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8373 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8374 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8376 #define NUM_PLAYER_SETUP_TOKENS 16
8379 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8380 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8381 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8383 #define NUM_SYSTEM_SETUP_TOKENS 3
8385 /* internal setup */
8386 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8387 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8388 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8389 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8390 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8391 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8392 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8393 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8394 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8395 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8396 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8397 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8398 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8399 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8400 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8401 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8402 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8403 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8404 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8406 #define NUM_INTERNAL_SETUP_TOKENS 19
8409 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8410 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8411 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8412 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8413 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8414 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8415 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8416 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8417 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8418 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8419 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8420 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8421 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8422 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8423 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8424 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8425 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8426 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8427 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8428 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8429 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8430 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8431 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8433 #define NUM_DEBUG_SETUP_TOKENS 23
8436 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8438 #define NUM_OPTIONS_SETUP_TOKENS 1
8441 static struct SetupInfo si;
8442 static struct SetupAutoSetupInfo sasi;
8443 static struct SetupEditorInfo sei;
8444 static struct SetupEditorCascadeInfo seci;
8445 static struct SetupShortcutInfo ssi;
8446 static struct SetupInputInfo sii;
8447 static struct SetupSystemInfo syi;
8448 static struct SetupInternalInfo sxi;
8449 static struct SetupDebugInfo sdi;
8450 static struct OptionInfo soi;
8452 static struct TokenInfo global_setup_tokens[] =
8454 { TYPE_STRING, &si.player_name, "player_name" },
8455 { TYPE_SWITCH, &si.sound, "sound" },
8456 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8457 { TYPE_SWITCH, &si.sound_music, "background_music" },
8458 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8459 { TYPE_SWITCH, &si.toons, "toons" },
8460 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8461 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8462 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8463 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8464 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8465 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8466 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8467 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8468 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8469 { TYPE_SWITCH, &si.handicap, "handicap" },
8470 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8471 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8472 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8473 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8474 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8475 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8476 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8477 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8478 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8479 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8480 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8481 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8482 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8483 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8484 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8485 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8486 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8487 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8488 { TYPE_STRING, &si.music_set, "music_set" },
8489 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8490 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8491 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8492 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8493 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8494 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8495 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8496 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8497 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8500 static struct TokenInfo auto_setup_tokens[] =
8502 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8505 static struct TokenInfo editor_setup_tokens[] =
8507 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8508 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8509 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8510 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8511 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8512 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8515 static struct TokenInfo editor_cascade_setup_tokens[] =
8517 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8518 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8519 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8520 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8521 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8522 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8523 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8524 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8525 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8526 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8527 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8528 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8529 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8530 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8531 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8532 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8533 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8536 static struct TokenInfo shortcut_setup_tokens[] =
8538 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8539 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8540 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8541 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8542 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8543 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8544 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8545 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8546 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8547 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8548 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8549 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8550 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8551 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8552 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8553 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8554 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8555 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8556 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8557 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8558 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8561 static struct TokenInfo player_setup_tokens[] =
8563 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8564 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8565 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8566 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8567 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8568 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8569 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8570 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8571 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8572 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8573 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8574 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8575 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8576 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8577 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8578 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8581 static struct TokenInfo system_setup_tokens[] =
8583 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8584 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8585 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8588 static struct TokenInfo internal_setup_tokens[] =
8590 { TYPE_STRING, &sxi.program_title, "program_title" },
8591 { TYPE_STRING, &sxi.program_version, "program_version" },
8592 { TYPE_STRING, &sxi.program_author, "program_author" },
8593 { TYPE_STRING, &sxi.program_email, "program_email" },
8594 { TYPE_STRING, &sxi.program_website, "program_website" },
8595 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8596 { TYPE_STRING, &sxi.program_company, "program_company" },
8597 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8598 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8599 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8600 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8601 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8602 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8603 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8604 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8605 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8606 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8607 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8608 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8611 static struct TokenInfo debug_setup_tokens[] =
8613 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8614 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8615 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8616 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8617 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8618 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8619 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8620 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8621 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8622 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8623 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8624 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8625 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8626 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8627 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8628 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8629 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8630 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8631 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8632 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8633 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8634 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8635 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8638 static struct TokenInfo options_setup_tokens[] =
8640 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8643 static char *get_corrected_login_name(char *login_name)
8645 /* needed because player name must be a fixed length string */
8646 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8648 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8649 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8651 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8652 if (strchr(login_name_new, ' '))
8653 *strchr(login_name_new, ' ') = '\0';
8655 return login_name_new;
8658 static void setSetupInfoToDefaults(struct SetupInfo *si)
8662 si->player_name = get_corrected_login_name(getLoginName());
8665 si->sound_loops = TRUE;
8666 si->sound_music = TRUE;
8667 si->sound_simple = TRUE;
8669 si->scroll_delay = TRUE;
8670 si->scroll_delay_value = STD_SCROLL_DELAY;
8671 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8672 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8673 si->fade_screens = TRUE;
8674 si->autorecord = TRUE;
8675 si->show_titlescreen = TRUE;
8676 si->quick_doors = FALSE;
8677 si->team_mode = FALSE;
8678 si->handicap = TRUE;
8679 si->skip_levels = TRUE;
8680 si->increment_levels = TRUE;
8681 si->time_limit = TRUE;
8682 si->fullscreen = FALSE;
8683 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8684 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8685 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8686 si->ask_on_escape = TRUE;
8687 si->ask_on_escape_editor = TRUE;
8688 si->quick_switch = FALSE;
8689 si->input_on_focus = FALSE;
8690 si->prefer_aga_graphics = TRUE;
8691 si->game_frame_delay = GAME_FRAME_DELAY;
8692 si->sp_show_border_elements = FALSE;
8693 si->small_game_graphics = FALSE;
8694 si->show_snapshot_buttons = FALSE;
8696 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8697 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8698 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8700 si->override_level_graphics = FALSE;
8701 si->override_level_sounds = FALSE;
8702 si->override_level_music = FALSE;
8704 si->volume_simple = 100; /* percent */
8705 si->volume_loops = 100; /* percent */
8706 si->volume_music = 100; /* percent */
8708 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8709 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8710 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8712 si->editor.el_boulderdash = TRUE;
8713 si->editor.el_emerald_mine = TRUE;
8714 si->editor.el_emerald_mine_club = TRUE;
8715 si->editor.el_more = TRUE;
8716 si->editor.el_sokoban = TRUE;
8717 si->editor.el_supaplex = TRUE;
8718 si->editor.el_diamond_caves = TRUE;
8719 si->editor.el_dx_boulderdash = TRUE;
8721 si->editor.el_mirror_magic = TRUE;
8722 si->editor.el_deflektor = TRUE;
8724 si->editor.el_chars = TRUE;
8725 si->editor.el_steel_chars = TRUE;
8727 si->editor.el_classic = TRUE;
8728 si->editor.el_custom = TRUE;
8730 si->editor.el_user_defined = FALSE;
8731 si->editor.el_dynamic = TRUE;
8733 si->editor.el_headlines = TRUE;
8735 si->editor.show_element_token = FALSE;
8737 si->editor.use_template_for_new_levels = TRUE;
8739 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8740 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8741 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8743 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8744 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8745 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8746 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8747 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8749 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8750 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8751 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8752 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8753 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8754 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8756 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8757 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8758 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8760 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8761 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8762 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8763 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8765 for (i = 0; i < MAX_PLAYERS; i++)
8767 si->input[i].use_joystick = FALSE;
8768 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8769 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8770 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8771 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8772 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8773 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8774 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8775 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8776 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8777 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8778 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8779 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8780 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8781 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8782 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8785 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8786 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8787 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8789 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8790 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8791 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8792 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8793 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8794 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8795 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8797 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8799 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8800 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8801 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8803 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8804 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8805 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8807 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8808 si->internal.choose_from_top_leveldir = FALSE;
8809 si->internal.show_scaling_in_title = TRUE;
8811 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8812 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8814 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8815 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8816 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8817 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8818 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8819 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8820 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8821 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8822 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8823 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8825 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8826 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8827 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8828 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8829 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8830 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8831 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8832 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8833 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8834 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8836 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8837 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8839 si->debug.show_frames_per_second = FALSE;
8841 si->options.verbose = FALSE;
8843 #if defined(PLATFORM_ANDROID)
8844 si->fullscreen = TRUE;
8848 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8850 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8853 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8855 si->editor_cascade.el_bd = TRUE;
8856 si->editor_cascade.el_em = TRUE;
8857 si->editor_cascade.el_emc = TRUE;
8858 si->editor_cascade.el_rnd = TRUE;
8859 si->editor_cascade.el_sb = TRUE;
8860 si->editor_cascade.el_sp = TRUE;
8861 si->editor_cascade.el_dc = TRUE;
8862 si->editor_cascade.el_dx = TRUE;
8864 si->editor_cascade.el_mm = TRUE;
8865 si->editor_cascade.el_df = TRUE;
8867 si->editor_cascade.el_chars = FALSE;
8868 si->editor_cascade.el_steel_chars = FALSE;
8869 si->editor_cascade.el_ce = FALSE;
8870 si->editor_cascade.el_ge = FALSE;
8871 si->editor_cascade.el_ref = FALSE;
8872 si->editor_cascade.el_user = FALSE;
8873 si->editor_cascade.el_dynamic = FALSE;
8876 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8878 static char *getHideSetupToken(void *setup_value)
8880 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8882 if (setup_value != NULL)
8883 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8885 return hide_setup_token;
8888 static void setHideSetupEntry(void *setup_value_raw)
8890 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8891 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8893 char *hide_setup_token = getHideSetupToken(setup_value);
8895 if (setup_value != NULL)
8896 setHashEntry(hide_setup_hash, hide_setup_token, "");
8899 boolean hideSetupEntry(void *setup_value)
8901 char *hide_setup_token = getHideSetupToken(setup_value);
8903 return (setup_value != NULL &&
8904 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8907 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8908 struct TokenInfo *token_info,
8909 int token_nr, char *token_text)
8911 char *token_hide_text = getStringCat2(token_text, ".hide");
8912 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8914 /* set the value of this setup option in the setup option structure */
8915 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8917 /* check if this setup option should be hidden in the setup menu */
8918 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8919 setHideSetupEntry(token_info[token_nr].value);
8922 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8923 struct TokenInfo *token_info,
8926 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8927 token_info[token_nr].text);
8930 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8934 if (!setup_file_hash)
8937 if (hide_setup_hash == NULL)
8938 hide_setup_hash = newSetupFileHash();
8942 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8943 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8948 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8949 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8952 /* shortcut setup */
8953 ssi = setup.shortcut;
8954 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8955 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8956 setup.shortcut = ssi;
8959 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8963 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8965 sii = setup.input[pnr];
8966 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8968 char full_token[100];
8970 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8971 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8974 setup.input[pnr] = sii;
8979 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8980 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8983 /* internal setup */
8984 sxi = setup.internal;
8985 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8986 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8987 setup.internal = sxi;
8991 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8992 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8996 soi = setup.options;
8997 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8998 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8999 setup.options = soi;
9002 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9006 if (!setup_file_hash)
9010 sasi = setup.auto_setup;
9011 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9012 setSetupInfo(auto_setup_tokens, i,
9013 getHashEntry(setup_file_hash,
9014 auto_setup_tokens[i].text));
9015 setup.auto_setup = sasi;
9018 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9022 if (!setup_file_hash)
9025 /* editor cascade setup */
9026 seci = setup.editor_cascade;
9027 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9028 setSetupInfo(editor_cascade_setup_tokens, i,
9029 getHashEntry(setup_file_hash,
9030 editor_cascade_setup_tokens[i].text));
9031 setup.editor_cascade = seci;
9034 void LoadSetupFromFilename(char *filename)
9036 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9038 if (setup_file_hash)
9040 decodeSetupFileHash(setup_file_hash);
9042 freeSetupFileHash(setup_file_hash);
9046 Error(ERR_DEBUG, "using default setup values");
9050 static void LoadSetup_SpecialPostProcessing()
9052 char *player_name_new;
9054 /* needed to work around problems with fixed length strings */
9055 player_name_new = get_corrected_login_name(setup.player_name);
9056 free(setup.player_name);
9057 setup.player_name = player_name_new;
9059 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9060 if (setup.scroll_delay == FALSE)
9062 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9063 setup.scroll_delay = TRUE; /* now always "on" */
9066 /* make sure that scroll delay value stays inside valid range */
9067 setup.scroll_delay_value =
9068 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9075 /* always start with reliable default values */
9076 setSetupInfoToDefaults(&setup);
9078 /* try to load setup values from default setup file */
9079 filename = getDefaultSetupFilename();
9081 if (fileExists(filename))
9082 LoadSetupFromFilename(filename);
9084 /* try to load setup values from user setup file */
9085 filename = getSetupFilename();
9087 LoadSetupFromFilename(filename);
9089 LoadSetup_SpecialPostProcessing();
9092 void LoadSetup_AutoSetup()
9094 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9095 SetupFileHash *setup_file_hash = NULL;
9097 /* always start with reliable default values */
9098 setSetupInfoToDefaults_AutoSetup(&setup);
9100 setup_file_hash = loadSetupFileHash(filename);
9102 if (setup_file_hash)
9104 decodeSetupFileHash_AutoSetup(setup_file_hash);
9106 freeSetupFileHash(setup_file_hash);
9112 void LoadSetup_EditorCascade()
9114 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9115 SetupFileHash *setup_file_hash = NULL;
9117 /* always start with reliable default values */
9118 setSetupInfoToDefaults_EditorCascade(&setup);
9120 setup_file_hash = loadSetupFileHash(filename);
9122 if (setup_file_hash)
9124 decodeSetupFileHash_EditorCascade(setup_file_hash);
9126 freeSetupFileHash(setup_file_hash);
9132 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9135 char mapping_guid[MAX_LINE_LEN];
9136 char *mapping_start, *mapping_end;
9138 // get GUID from game controller mapping line: copy complete line
9139 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9140 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9142 // get GUID from game controller mapping line: cut after GUID part
9143 mapping_start = strchr(mapping_guid, ',');
9144 if (mapping_start != NULL)
9145 *mapping_start = '\0';
9147 // cut newline from game controller mapping line
9148 mapping_end = strchr(mapping_line, '\n');
9149 if (mapping_end != NULL)
9150 *mapping_end = '\0';
9152 // add mapping entry to game controller mappings hash
9153 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9156 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9161 if (!(file = fopen(filename, MODE_READ)))
9163 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9170 char line[MAX_LINE_LEN];
9172 if (!fgets(line, MAX_LINE_LEN, file))
9175 addGameControllerMappingToHash(mappings_hash, line);
9183 char *filename = getSetupFilename();
9187 InitUserDataDirectory();
9189 if (!(file = fopen(filename, MODE_WRITE)))
9191 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9195 fprintFileHeader(file, SETUP_FILENAME);
9199 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9201 /* just to make things nicer :) */
9202 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9203 i == SETUP_TOKEN_GRAPHICS_SET ||
9204 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9205 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9206 fprintf(file, "\n");
9208 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9213 fprintf(file, "\n");
9214 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9215 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9217 /* shortcut setup */
9218 ssi = setup.shortcut;
9219 fprintf(file, "\n");
9220 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9221 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9224 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9228 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9229 fprintf(file, "\n");
9231 sii = setup.input[pnr];
9232 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9233 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9238 fprintf(file, "\n");
9239 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9240 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9242 /* internal setup */
9243 /* (internal setup values not saved to user setup file) */
9247 fprintf(file, "\n");
9248 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9249 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9252 soi = setup.options;
9253 fprintf(file, "\n");
9254 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9255 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9259 SetFilePermissions(filename, PERMS_PRIVATE);
9262 void SaveSetup_AutoSetup()
9264 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9268 InitUserDataDirectory();
9270 if (!(file = fopen(filename, MODE_WRITE)))
9272 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9277 fprintFileHeader(file, AUTOSETUP_FILENAME);
9279 sasi = setup.auto_setup;
9280 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9281 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9285 SetFilePermissions(filename, PERMS_PRIVATE);
9290 void SaveSetup_EditorCascade()
9292 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9296 InitUserDataDirectory();
9298 if (!(file = fopen(filename, MODE_WRITE)))
9300 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9305 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9307 seci = setup.editor_cascade;
9308 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9309 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9313 SetFilePermissions(filename, PERMS_PRIVATE);
9318 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9323 if (!(file = fopen(filename, MODE_WRITE)))
9325 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9330 BEGIN_HASH_ITERATION(mappings_hash, itr)
9332 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9334 END_HASH_ITERATION(mappings_hash, itr)
9339 void SaveSetup_AddGameControllerMapping(char *mapping)
9341 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9342 SetupFileHash *mappings_hash = newSetupFileHash();
9344 InitUserDataDirectory();
9346 // load existing personal game controller mappings
9347 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9349 // add new mapping to personal game controller mappings
9350 addGameControllerMappingToHash(mappings_hash, mapping);
9352 // save updated personal game controller mappings
9353 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9355 freeSetupFileHash(mappings_hash);
9359 void LoadCustomElementDescriptions()
9361 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9362 SetupFileHash *setup_file_hash;
9365 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9367 if (element_info[i].custom_description != NULL)
9369 free(element_info[i].custom_description);
9370 element_info[i].custom_description = NULL;
9374 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9377 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9379 char *token = getStringCat2(element_info[i].token_name, ".name");
9380 char *value = getHashEntry(setup_file_hash, token);
9383 element_info[i].custom_description = getStringCopy(value);
9388 freeSetupFileHash(setup_file_hash);
9391 static int getElementFromToken(char *token)
9393 char *value = getHashEntry(element_token_hash, token);
9398 Error(ERR_WARN, "unknown element token '%s'", token);
9400 return EL_UNDEFINED;
9403 static int get_token_parameter_value(char *token, char *value_raw)
9407 if (token == NULL || value_raw == NULL)
9408 return ARG_UNDEFINED_VALUE;
9410 suffix = strrchr(token, '.');
9414 if (strEqual(suffix, ".element"))
9415 return getElementFromToken(value_raw);
9417 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9418 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9421 void InitMenuDesignSettings_Static()
9425 /* always start with reliable default values from static default config */
9426 for (i = 0; image_config_vars[i].token != NULL; i++)
9428 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9431 *image_config_vars[i].value =
9432 get_token_parameter_value(image_config_vars[i].token, value);
9436 static void InitMenuDesignSettings_SpecialPreProcessing()
9440 /* the following initializes hierarchical values from static configuration */
9442 /* special case: initialize "ARG_DEFAULT" values in static default config */
9443 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9444 titlescreen_initial_first_default.fade_mode =
9445 title_initial_first_default.fade_mode;
9446 titlescreen_initial_first_default.fade_delay =
9447 title_initial_first_default.fade_delay;
9448 titlescreen_initial_first_default.post_delay =
9449 title_initial_first_default.post_delay;
9450 titlescreen_initial_first_default.auto_delay =
9451 title_initial_first_default.auto_delay;
9452 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9453 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9454 titlescreen_first_default.post_delay = title_first_default.post_delay;
9455 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9456 titlemessage_initial_first_default.fade_mode =
9457 title_initial_first_default.fade_mode;
9458 titlemessage_initial_first_default.fade_delay =
9459 title_initial_first_default.fade_delay;
9460 titlemessage_initial_first_default.post_delay =
9461 title_initial_first_default.post_delay;
9462 titlemessage_initial_first_default.auto_delay =
9463 title_initial_first_default.auto_delay;
9464 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9465 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9466 titlemessage_first_default.post_delay = title_first_default.post_delay;
9467 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9469 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9470 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9471 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9472 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9473 titlescreen_default.fade_mode = title_default.fade_mode;
9474 titlescreen_default.fade_delay = title_default.fade_delay;
9475 titlescreen_default.post_delay = title_default.post_delay;
9476 titlescreen_default.auto_delay = title_default.auto_delay;
9477 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9478 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9479 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9480 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9481 titlemessage_default.fade_mode = title_default.fade_mode;
9482 titlemessage_default.fade_delay = title_default.fade_delay;
9483 titlemessage_default.post_delay = title_default.post_delay;
9484 titlemessage_default.auto_delay = title_default.auto_delay;
9486 /* special case: initialize "ARG_DEFAULT" values in static default config */
9487 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9488 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9490 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9491 titlescreen_first[i] = titlescreen_first_default;
9492 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9493 titlemessage_first[i] = titlemessage_first_default;
9495 titlescreen_initial[i] = titlescreen_initial_default;
9496 titlescreen[i] = titlescreen_default;
9497 titlemessage_initial[i] = titlemessage_initial_default;
9498 titlemessage[i] = titlemessage_default;
9501 /* special case: initialize "ARG_DEFAULT" values in static default config */
9502 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9503 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9505 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9508 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9509 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9510 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9513 /* special case: initialize "ARG_DEFAULT" values in static default config */
9514 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9515 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9517 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9518 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9519 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9521 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9524 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9528 static void InitMenuDesignSettings_SpecialPostProcessing()
9532 struct XY *dst, *src;
9536 { &game.button.save, &game.button.stop },
9537 { &game.button.pause2, &game.button.pause },
9538 { &game.button.load, &game.button.play },
9539 { &game.button.undo, &game.button.stop },
9540 { &game.button.redo, &game.button.play },
9546 /* special case: initialize later added SETUP list size from LEVELS value */
9547 if (menu.list_size[GAME_MODE_SETUP] == -1)
9548 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9550 /* set default position for snapshot buttons to stop/pause/play buttons */
9551 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9552 if ((*game_buttons_xy[i].dst).x == -1 &&
9553 (*game_buttons_xy[i].dst).y == -1)
9554 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9557 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9561 struct XYTileSize *dst, *src;
9564 editor_buttons_xy[] =
9567 &editor.button.element_left, &editor.palette.element_left,
9568 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9571 &editor.button.element_middle, &editor.palette.element_middle,
9572 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9575 &editor.button.element_right, &editor.palette.element_right,
9576 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9583 /* set default position for element buttons to element graphics */
9584 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9586 if ((*editor_buttons_xy[i].dst).x == -1 &&
9587 (*editor_buttons_xy[i].dst).y == -1)
9589 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9591 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9593 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9598 static void LoadMenuDesignSettingsFromFilename(char *filename)
9600 static struct TitleFadingInfo tfi;
9601 static struct TitleMessageInfo tmi;
9602 static struct TokenInfo title_tokens[] =
9604 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9605 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9606 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9607 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9611 static struct TokenInfo titlemessage_tokens[] =
9613 { TYPE_INTEGER, &tmi.x, ".x" },
9614 { TYPE_INTEGER, &tmi.y, ".y" },
9615 { TYPE_INTEGER, &tmi.width, ".width" },
9616 { TYPE_INTEGER, &tmi.height, ".height" },
9617 { TYPE_INTEGER, &tmi.chars, ".chars" },
9618 { TYPE_INTEGER, &tmi.lines, ".lines" },
9619 { TYPE_INTEGER, &tmi.align, ".align" },
9620 { TYPE_INTEGER, &tmi.valign, ".valign" },
9621 { TYPE_INTEGER, &tmi.font, ".font" },
9622 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9623 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9624 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9625 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9626 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9627 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9628 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9629 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9635 struct TitleFadingInfo *info;
9640 /* initialize first titles from "enter screen" definitions, if defined */
9641 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9642 { &title_first_default, "menu.enter_screen.TITLE" },
9644 /* initialize title screens from "next screen" definitions, if defined */
9645 { &title_initial_default, "menu.next_screen.TITLE" },
9646 { &title_default, "menu.next_screen.TITLE" },
9652 struct TitleMessageInfo *array;
9655 titlemessage_arrays[] =
9657 /* initialize first titles from "enter screen" definitions, if defined */
9658 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9659 { titlescreen_first, "menu.enter_screen.TITLE" },
9660 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9661 { titlemessage_first, "menu.enter_screen.TITLE" },
9663 /* initialize titles from "next screen" definitions, if defined */
9664 { titlescreen_initial, "menu.next_screen.TITLE" },
9665 { titlescreen, "menu.next_screen.TITLE" },
9666 { titlemessage_initial, "menu.next_screen.TITLE" },
9667 { titlemessage, "menu.next_screen.TITLE" },
9669 /* overwrite titles with title definitions, if defined */
9670 { titlescreen_initial_first, "[title_initial]" },
9671 { titlescreen_first, "[title]" },
9672 { titlemessage_initial_first, "[title_initial]" },
9673 { titlemessage_first, "[title]" },
9675 { titlescreen_initial, "[title_initial]" },
9676 { titlescreen, "[title]" },
9677 { titlemessage_initial, "[title_initial]" },
9678 { titlemessage, "[title]" },
9680 /* overwrite titles with title screen/message definitions, if defined */
9681 { titlescreen_initial_first, "[titlescreen_initial]" },
9682 { titlescreen_first, "[titlescreen]" },
9683 { titlemessage_initial_first, "[titlemessage_initial]" },
9684 { titlemessage_first, "[titlemessage]" },
9686 { titlescreen_initial, "[titlescreen_initial]" },
9687 { titlescreen, "[titlescreen]" },
9688 { titlemessage_initial, "[titlemessage_initial]" },
9689 { titlemessage, "[titlemessage]" },
9693 SetupFileHash *setup_file_hash;
9696 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9699 /* the following initializes hierarchical values from dynamic configuration */
9701 /* special case: initialize with default values that may be overwritten */
9702 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9703 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9705 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9706 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9707 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9709 if (value_1 != NULL)
9710 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9711 if (value_2 != NULL)
9712 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9713 if (value_3 != NULL)
9714 menu.list_size[i] = get_integer_from_string(value_3);
9717 /* special case: initialize with default values that may be overwritten */
9718 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9719 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9721 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9722 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9724 if (value_1 != NULL)
9725 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9726 if (value_2 != NULL)
9727 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9729 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9731 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9733 if (value_1 != NULL)
9734 menu.list_size_info[i] = get_integer_from_string(value_1);
9738 /* special case: initialize with default values that may be overwritten */
9739 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9740 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9742 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9743 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9745 if (value_1 != NULL)
9746 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9747 if (value_2 != NULL)
9748 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9751 /* special case: initialize with default values that may be overwritten */
9752 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9753 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9755 char *value_1 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
9756 char *value_2 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
9757 char *value_3 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
9758 char *value_4 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
9759 char *value_5 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
9761 if (value_1 != NULL)
9762 menu.paragraph_spacing_info[i] = get_integer_from_string(value_1);
9763 if (value_2 != NULL)
9764 menu.headline1_spacing_info[i] = get_integer_from_string(value_2);
9765 if (value_3 != NULL)
9766 menu.headline2_spacing_info[i] = get_integer_from_string(value_3);
9767 if (value_4 != NULL)
9768 menu.line_spacing_info[i] = get_integer_from_string(value_4);
9769 if (value_5 != NULL)
9770 menu.extra_spacing_info[i] = get_integer_from_string(value_5);
9773 /* special case: initialize with default values that may be overwritten */
9774 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9775 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9777 char *token_1 = "menu.enter_screen.fade_mode";
9778 char *token_2 = "menu.enter_screen.fade_delay";
9779 char *token_3 = "menu.enter_screen.post_delay";
9780 char *token_4 = "menu.leave_screen.fade_mode";
9781 char *token_5 = "menu.leave_screen.fade_delay";
9782 char *token_6 = "menu.leave_screen.post_delay";
9783 char *token_7 = "menu.next_screen.fade_mode";
9784 char *token_8 = "menu.next_screen.fade_delay";
9785 char *token_9 = "menu.next_screen.post_delay";
9786 char *value_1 = getHashEntry(setup_file_hash, token_1);
9787 char *value_2 = getHashEntry(setup_file_hash, token_2);
9788 char *value_3 = getHashEntry(setup_file_hash, token_3);
9789 char *value_4 = getHashEntry(setup_file_hash, token_4);
9790 char *value_5 = getHashEntry(setup_file_hash, token_5);
9791 char *value_6 = getHashEntry(setup_file_hash, token_6);
9792 char *value_7 = getHashEntry(setup_file_hash, token_7);
9793 char *value_8 = getHashEntry(setup_file_hash, token_8);
9794 char *value_9 = getHashEntry(setup_file_hash, token_9);
9796 if (value_1 != NULL)
9797 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9799 if (value_2 != NULL)
9800 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9802 if (value_3 != NULL)
9803 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9805 if (value_4 != NULL)
9806 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9808 if (value_5 != NULL)
9809 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9811 if (value_6 != NULL)
9812 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9814 if (value_7 != NULL)
9815 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9817 if (value_8 != NULL)
9818 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9820 if (value_9 != NULL)
9821 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9825 /* special case: initialize with default values that may be overwritten */
9826 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9827 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9829 char *token_w1 = "viewport.window.width";
9830 char *token_w2 = "viewport.window.height";
9831 char *token_01 = "viewport.playfield.x";
9832 char *token_02 = "viewport.playfield.y";
9833 char *token_03 = "viewport.playfield.width";
9834 char *token_04 = "viewport.playfield.height";
9835 char *token_05 = "viewport.playfield.border_size";
9836 char *token_06 = "viewport.door_1.x";
9837 char *token_07 = "viewport.door_1.y";
9838 char *token_08 = "viewport.door_1.width";
9839 char *token_09 = "viewport.door_1.height";
9840 char *token_10 = "viewport.door_1.border_size";
9841 char *token_11 = "viewport.door_2.x";
9842 char *token_12 = "viewport.door_2.y";
9843 char *token_13 = "viewport.door_2.width";
9844 char *token_14 = "viewport.door_2.height";
9845 char *token_15 = "viewport.door_2.border_size";
9846 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9847 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9848 char *value_01 = getHashEntry(setup_file_hash, token_01);
9849 char *value_02 = getHashEntry(setup_file_hash, token_02);
9850 char *value_03 = getHashEntry(setup_file_hash, token_03);
9851 char *value_04 = getHashEntry(setup_file_hash, token_04);
9852 char *value_05 = getHashEntry(setup_file_hash, token_05);
9853 char *value_06 = getHashEntry(setup_file_hash, token_06);
9854 char *value_07 = getHashEntry(setup_file_hash, token_07);
9855 char *value_08 = getHashEntry(setup_file_hash, token_08);
9856 char *value_09 = getHashEntry(setup_file_hash, token_09);
9857 char *value_10 = getHashEntry(setup_file_hash, token_10);
9858 char *value_11 = getHashEntry(setup_file_hash, token_11);
9859 char *value_12 = getHashEntry(setup_file_hash, token_12);
9860 char *value_13 = getHashEntry(setup_file_hash, token_13);
9861 char *value_14 = getHashEntry(setup_file_hash, token_14);
9862 char *value_15 = getHashEntry(setup_file_hash, token_15);
9864 if (value_w1 != NULL)
9865 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9866 if (value_w2 != NULL)
9867 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9868 if (value_01 != NULL)
9869 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9870 if (value_02 != NULL)
9871 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9872 if (value_03 != NULL)
9873 viewport.playfield[i].width = get_token_parameter_value(token_03,
9875 if (value_04 != NULL)
9876 viewport.playfield[i].height = get_token_parameter_value(token_04,
9878 if (value_05 != NULL)
9879 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9881 if (value_06 != NULL)
9882 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9883 if (value_07 != NULL)
9884 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9885 if (value_08 != NULL)
9886 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9887 if (value_09 != NULL)
9888 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9889 if (value_10 != NULL)
9890 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9892 if (value_11 != NULL)
9893 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9894 if (value_12 != NULL)
9895 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9896 if (value_13 != NULL)
9897 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9898 if (value_14 != NULL)
9899 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9900 if (value_15 != NULL)
9901 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9905 /* special case: initialize with default values that may be overwritten */
9906 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9907 for (i = 0; title_info[i].info != NULL; i++)
9909 struct TitleFadingInfo *info = title_info[i].info;
9910 char *base_token = title_info[i].text;
9912 for (j = 0; title_tokens[j].type != -1; j++)
9914 char *token = getStringCat2(base_token, title_tokens[j].text);
9915 char *value = getHashEntry(setup_file_hash, token);
9919 int parameter_value = get_token_parameter_value(token, value);
9923 *(int *)title_tokens[j].value = (int)parameter_value;
9932 /* special case: initialize with default values that may be overwritten */
9933 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9934 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9936 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9937 char *base_token = titlemessage_arrays[i].text;
9939 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9941 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9942 char *value = getHashEntry(setup_file_hash, token);
9946 int parameter_value = get_token_parameter_value(token, value);
9948 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9952 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9953 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9955 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9965 /* read (and overwrite with) values that may be specified in config file */
9966 for (i = 0; image_config_vars[i].token != NULL; i++)
9968 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9970 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9971 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9972 *image_config_vars[i].value =
9973 get_token_parameter_value(image_config_vars[i].token, value);
9976 freeSetupFileHash(setup_file_hash);
9979 void LoadMenuDesignSettings()
9981 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9983 InitMenuDesignSettings_Static();
9984 InitMenuDesignSettings_SpecialPreProcessing();
9986 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9988 /* first look for special settings configured in level series config */
9989 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9991 if (fileExists(filename_base))
9992 LoadMenuDesignSettingsFromFilename(filename_base);
9995 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9997 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9998 LoadMenuDesignSettingsFromFilename(filename_local);
10000 InitMenuDesignSettings_SpecialPostProcessing();
10003 void LoadMenuDesignSettings_AfterGraphics()
10005 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10008 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10010 char *filename = getEditorSetupFilename();
10011 SetupFileList *setup_file_list, *list;
10012 SetupFileHash *element_hash;
10013 int num_unknown_tokens = 0;
10016 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10019 element_hash = newSetupFileHash();
10021 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10022 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10024 /* determined size may be larger than needed (due to unknown elements) */
10026 for (list = setup_file_list; list != NULL; list = list->next)
10029 /* add space for up to 3 more elements for padding that may be needed */
10030 *num_elements += 3;
10032 /* free memory for old list of elements, if needed */
10033 checked_free(*elements);
10035 /* allocate memory for new list of elements */
10036 *elements = checked_malloc(*num_elements * sizeof(int));
10039 for (list = setup_file_list; list != NULL; list = list->next)
10041 char *value = getHashEntry(element_hash, list->token);
10043 if (value == NULL) /* try to find obsolete token mapping */
10045 char *mapped_token = get_mapped_token(list->token);
10047 if (mapped_token != NULL)
10049 value = getHashEntry(element_hash, mapped_token);
10051 free(mapped_token);
10057 (*elements)[(*num_elements)++] = atoi(value);
10061 if (num_unknown_tokens == 0)
10063 Error(ERR_INFO_LINE, "-");
10064 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10065 Error(ERR_INFO, "- config file: '%s'", filename);
10067 num_unknown_tokens++;
10070 Error(ERR_INFO, "- token: '%s'", list->token);
10074 if (num_unknown_tokens > 0)
10075 Error(ERR_INFO_LINE, "-");
10077 while (*num_elements % 4) /* pad with empty elements, if needed */
10078 (*elements)[(*num_elements)++] = EL_EMPTY;
10080 freeSetupFileList(setup_file_list);
10081 freeSetupFileHash(element_hash);
10084 for (i = 0; i < *num_elements; i++)
10085 printf("editor: element '%s' [%d]\n",
10086 element_info[(*elements)[i]].token_name, (*elements)[i]);
10090 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10093 SetupFileHash *setup_file_hash = NULL;
10094 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10095 char *filename_music, *filename_prefix, *filename_info;
10101 token_to_value_ptr[] =
10103 { "title_header", &tmp_music_file_info.title_header },
10104 { "artist_header", &tmp_music_file_info.artist_header },
10105 { "album_header", &tmp_music_file_info.album_header },
10106 { "year_header", &tmp_music_file_info.year_header },
10108 { "title", &tmp_music_file_info.title },
10109 { "artist", &tmp_music_file_info.artist },
10110 { "album", &tmp_music_file_info.album },
10111 { "year", &tmp_music_file_info.year },
10117 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10118 getCustomMusicFilename(basename));
10120 if (filename_music == NULL)
10123 /* ---------- try to replace file extension ---------- */
10125 filename_prefix = getStringCopy(filename_music);
10126 if (strrchr(filename_prefix, '.') != NULL)
10127 *strrchr(filename_prefix, '.') = '\0';
10128 filename_info = getStringCat2(filename_prefix, ".txt");
10130 if (fileExists(filename_info))
10131 setup_file_hash = loadSetupFileHash(filename_info);
10133 free(filename_prefix);
10134 free(filename_info);
10136 if (setup_file_hash == NULL)
10138 /* ---------- try to add file extension ---------- */
10140 filename_prefix = getStringCopy(filename_music);
10141 filename_info = getStringCat2(filename_prefix, ".txt");
10143 if (fileExists(filename_info))
10144 setup_file_hash = loadSetupFileHash(filename_info);
10146 free(filename_prefix);
10147 free(filename_info);
10150 if (setup_file_hash == NULL)
10153 /* ---------- music file info found ---------- */
10155 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10157 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10159 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10161 *token_to_value_ptr[i].value_ptr =
10162 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10165 tmp_music_file_info.basename = getStringCopy(basename);
10166 tmp_music_file_info.music = music;
10167 tmp_music_file_info.is_sound = is_sound;
10169 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10170 *new_music_file_info = tmp_music_file_info;
10172 return new_music_file_info;
10175 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10177 return get_music_file_info_ext(basename, music, FALSE);
10180 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10182 return get_music_file_info_ext(basename, sound, TRUE);
10185 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10186 char *basename, boolean is_sound)
10188 for (; list != NULL; list = list->next)
10189 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10195 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10197 return music_info_listed_ext(list, basename, FALSE);
10200 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10202 return music_info_listed_ext(list, basename, TRUE);
10205 void LoadMusicInfo()
10207 char *music_directory = getCustomMusicDirectory();
10208 int num_music = getMusicListSize();
10209 int num_music_noconf = 0;
10210 int num_sounds = getSoundListSize();
10212 DirectoryEntry *dir_entry;
10213 struct FileInfo *music, *sound;
10214 struct MusicFileInfo *next, **new;
10217 while (music_file_info != NULL)
10219 next = music_file_info->next;
10221 checked_free(music_file_info->basename);
10223 checked_free(music_file_info->title_header);
10224 checked_free(music_file_info->artist_header);
10225 checked_free(music_file_info->album_header);
10226 checked_free(music_file_info->year_header);
10228 checked_free(music_file_info->title);
10229 checked_free(music_file_info->artist);
10230 checked_free(music_file_info->album);
10231 checked_free(music_file_info->year);
10233 free(music_file_info);
10235 music_file_info = next;
10238 new = &music_file_info;
10240 for (i = 0; i < num_music; i++)
10242 music = getMusicListEntry(i);
10244 if (music->filename == NULL)
10247 if (strEqual(music->filename, UNDEFINED_FILENAME))
10250 /* a configured file may be not recognized as music */
10251 if (!FileIsMusic(music->filename))
10254 if (!music_info_listed(music_file_info, music->filename))
10256 *new = get_music_file_info(music->filename, i);
10259 new = &(*new)->next;
10263 if ((dir = openDirectory(music_directory)) == NULL)
10265 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10269 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10271 char *basename = dir_entry->basename;
10272 boolean music_already_used = FALSE;
10275 /* skip all music files that are configured in music config file */
10276 for (i = 0; i < num_music; i++)
10278 music = getMusicListEntry(i);
10280 if (music->filename == NULL)
10283 if (strEqual(basename, music->filename))
10285 music_already_used = TRUE;
10290 if (music_already_used)
10293 if (!FileIsMusic(dir_entry->filename))
10296 if (!music_info_listed(music_file_info, basename))
10298 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10301 new = &(*new)->next;
10304 num_music_noconf++;
10307 closeDirectory(dir);
10309 for (i = 0; i < num_sounds; i++)
10311 sound = getSoundListEntry(i);
10313 if (sound->filename == NULL)
10316 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10319 /* a configured file may be not recognized as sound */
10320 if (!FileIsSound(sound->filename))
10323 if (!sound_info_listed(music_file_info, sound->filename))
10325 *new = get_sound_file_info(sound->filename, i);
10327 new = &(*new)->next;
10332 void add_helpanim_entry(int element, int action, int direction, int delay,
10333 int *num_list_entries)
10335 struct HelpAnimInfo *new_list_entry;
10336 (*num_list_entries)++;
10339 checked_realloc(helpanim_info,
10340 *num_list_entries * sizeof(struct HelpAnimInfo));
10341 new_list_entry = &helpanim_info[*num_list_entries - 1];
10343 new_list_entry->element = element;
10344 new_list_entry->action = action;
10345 new_list_entry->direction = direction;
10346 new_list_entry->delay = delay;
10349 void print_unknown_token(char *filename, char *token, int token_nr)
10353 Error(ERR_INFO_LINE, "-");
10354 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10355 Error(ERR_INFO, "- config file: '%s'", filename);
10358 Error(ERR_INFO, "- token: '%s'", token);
10361 void print_unknown_token_end(int token_nr)
10364 Error(ERR_INFO_LINE, "-");
10367 void LoadHelpAnimInfo()
10369 char *filename = getHelpAnimFilename();
10370 SetupFileList *setup_file_list = NULL, *list;
10371 SetupFileHash *element_hash, *action_hash, *direction_hash;
10372 int num_list_entries = 0;
10373 int num_unknown_tokens = 0;
10376 if (fileExists(filename))
10377 setup_file_list = loadSetupFileList(filename);
10379 if (setup_file_list == NULL)
10381 /* use reliable default values from static configuration */
10382 SetupFileList *insert_ptr;
10384 insert_ptr = setup_file_list =
10385 newSetupFileList(helpanim_config[0].token,
10386 helpanim_config[0].value);
10388 for (i = 1; helpanim_config[i].token; i++)
10389 insert_ptr = addListEntry(insert_ptr,
10390 helpanim_config[i].token,
10391 helpanim_config[i].value);
10394 element_hash = newSetupFileHash();
10395 action_hash = newSetupFileHash();
10396 direction_hash = newSetupFileHash();
10398 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10399 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10401 for (i = 0; i < NUM_ACTIONS; i++)
10402 setHashEntry(action_hash, element_action_info[i].suffix,
10403 i_to_a(element_action_info[i].value));
10405 /* do not store direction index (bit) here, but direction value! */
10406 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10407 setHashEntry(direction_hash, element_direction_info[i].suffix,
10408 i_to_a(1 << element_direction_info[i].value));
10410 for (list = setup_file_list; list != NULL; list = list->next)
10412 char *element_token, *action_token, *direction_token;
10413 char *element_value, *action_value, *direction_value;
10414 int delay = atoi(list->value);
10416 if (strEqual(list->token, "end"))
10418 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10423 /* first try to break element into element/action/direction parts;
10424 if this does not work, also accept combined "element[.act][.dir]"
10425 elements (like "dynamite.active"), which are unique elements */
10427 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10429 element_value = getHashEntry(element_hash, list->token);
10430 if (element_value != NULL) /* element found */
10431 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10432 &num_list_entries);
10435 /* no further suffixes found -- this is not an element */
10436 print_unknown_token(filename, list->token, num_unknown_tokens++);
10442 /* token has format "<prefix>.<something>" */
10444 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10445 direction_token = action_token; /* ... or direction */
10447 element_token = getStringCopy(list->token);
10448 *strchr(element_token, '.') = '\0';
10450 element_value = getHashEntry(element_hash, element_token);
10452 if (element_value == NULL) /* this is no element */
10454 element_value = getHashEntry(element_hash, list->token);
10455 if (element_value != NULL) /* combined element found */
10456 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10457 &num_list_entries);
10459 print_unknown_token(filename, list->token, num_unknown_tokens++);
10461 free(element_token);
10466 action_value = getHashEntry(action_hash, action_token);
10468 if (action_value != NULL) /* action found */
10470 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10471 &num_list_entries);
10473 free(element_token);
10478 direction_value = getHashEntry(direction_hash, direction_token);
10480 if (direction_value != NULL) /* direction found */
10482 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10483 &num_list_entries);
10485 free(element_token);
10490 if (strchr(action_token + 1, '.') == NULL)
10492 /* no further suffixes found -- this is not an action nor direction */
10494 element_value = getHashEntry(element_hash, list->token);
10495 if (element_value != NULL) /* combined element found */
10496 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10497 &num_list_entries);
10499 print_unknown_token(filename, list->token, num_unknown_tokens++);
10501 free(element_token);
10506 /* token has format "<prefix>.<suffix>.<something>" */
10508 direction_token = strchr(action_token + 1, '.');
10510 action_token = getStringCopy(action_token);
10511 *strchr(action_token + 1, '.') = '\0';
10513 action_value = getHashEntry(action_hash, action_token);
10515 if (action_value == NULL) /* this is no action */
10517 element_value = getHashEntry(element_hash, list->token);
10518 if (element_value != NULL) /* combined element found */
10519 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10520 &num_list_entries);
10522 print_unknown_token(filename, list->token, num_unknown_tokens++);
10524 free(element_token);
10525 free(action_token);
10530 direction_value = getHashEntry(direction_hash, direction_token);
10532 if (direction_value != NULL) /* direction found */
10534 add_helpanim_entry(atoi(element_value), atoi(action_value),
10535 atoi(direction_value), delay, &num_list_entries);
10537 free(element_token);
10538 free(action_token);
10543 /* this is no direction */
10545 element_value = getHashEntry(element_hash, list->token);
10546 if (element_value != NULL) /* combined element found */
10547 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10548 &num_list_entries);
10550 print_unknown_token(filename, list->token, num_unknown_tokens++);
10552 free(element_token);
10553 free(action_token);
10556 print_unknown_token_end(num_unknown_tokens);
10558 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10559 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10561 freeSetupFileList(setup_file_list);
10562 freeSetupFileHash(element_hash);
10563 freeSetupFileHash(action_hash);
10564 freeSetupFileHash(direction_hash);
10567 for (i = 0; i < num_list_entries; i++)
10568 printf("::: '%s': %d, %d, %d => %d\n",
10569 EL_NAME(helpanim_info[i].element),
10570 helpanim_info[i].element,
10571 helpanim_info[i].action,
10572 helpanim_info[i].direction,
10573 helpanim_info[i].delay);
10577 void LoadHelpTextInfo()
10579 char *filename = getHelpTextFilename();
10582 if (helptext_info != NULL)
10584 freeSetupFileHash(helptext_info);
10585 helptext_info = NULL;
10588 if (fileExists(filename))
10589 helptext_info = loadSetupFileHash(filename);
10591 if (helptext_info == NULL)
10593 /* use reliable default values from static configuration */
10594 helptext_info = newSetupFileHash();
10596 for (i = 0; helptext_config[i].token; i++)
10597 setHashEntry(helptext_info,
10598 helptext_config[i].token,
10599 helptext_config[i].value);
10603 BEGIN_HASH_ITERATION(helptext_info, itr)
10605 printf("::: '%s' => '%s'\n",
10606 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10608 END_HASH_ITERATION(hash, itr)
10613 /* ------------------------------------------------------------------------- */
10614 /* convert levels */
10615 /* ------------------------------------------------------------------------- */
10617 #define MAX_NUM_CONVERT_LEVELS 1000
10619 void ConvertLevels()
10621 static LevelDirTree *convert_leveldir = NULL;
10622 static int convert_level_nr = -1;
10623 static int num_levels_handled = 0;
10624 static int num_levels_converted = 0;
10625 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10628 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10629 global.convert_leveldir);
10631 if (convert_leveldir == NULL)
10632 Error(ERR_EXIT, "no such level identifier: '%s'",
10633 global.convert_leveldir);
10635 leveldir_current = convert_leveldir;
10637 if (global.convert_level_nr != -1)
10639 convert_leveldir->first_level = global.convert_level_nr;
10640 convert_leveldir->last_level = global.convert_level_nr;
10643 convert_level_nr = convert_leveldir->first_level;
10645 PrintLine("=", 79);
10646 Print("Converting levels\n");
10647 PrintLine("-", 79);
10648 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10649 Print("Level series name: '%s'\n", convert_leveldir->name);
10650 Print("Level series author: '%s'\n", convert_leveldir->author);
10651 Print("Number of levels: %d\n", convert_leveldir->levels);
10652 PrintLine("=", 79);
10655 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10656 levels_failed[i] = FALSE;
10658 while (convert_level_nr <= convert_leveldir->last_level)
10660 char *level_filename;
10663 level_nr = convert_level_nr++;
10665 Print("Level %03d: ", level_nr);
10667 LoadLevel(level_nr);
10668 if (level.no_level_file || level.no_valid_file)
10670 Print("(no level)\n");
10674 Print("converting level ... ");
10676 level_filename = getDefaultLevelFilename(level_nr);
10677 new_level = !fileExists(level_filename);
10681 SaveLevel(level_nr);
10683 num_levels_converted++;
10685 Print("converted.\n");
10689 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10690 levels_failed[level_nr] = TRUE;
10692 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10695 num_levels_handled++;
10699 PrintLine("=", 79);
10700 Print("Number of levels handled: %d\n", num_levels_handled);
10701 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10702 (num_levels_handled ?
10703 num_levels_converted * 100 / num_levels_handled : 0));
10704 PrintLine("-", 79);
10705 Print("Summary (for automatic parsing by scripts):\n");
10706 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10707 convert_leveldir->identifier, num_levels_converted,
10708 num_levels_handled,
10709 (num_levels_handled ?
10710 num_levels_converted * 100 / num_levels_handled : 0));
10712 if (num_levels_handled != num_levels_converted)
10714 Print(", FAILED:");
10715 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10716 if (levels_failed[i])
10721 PrintLine("=", 79);
10723 CloseAllAndExit(0);
10727 /* ------------------------------------------------------------------------- */
10728 /* create and save images for use in level sketches (raw BMP format) */
10729 /* ------------------------------------------------------------------------- */
10731 void CreateLevelSketchImages()
10733 #if defined(TARGET_SDL)
10738 InitElementPropertiesGfxElement();
10740 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10741 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10743 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10745 Bitmap *src_bitmap;
10747 int element = getMappedElement(i);
10748 int graphic = el2edimg(element);
10749 char basename1[16];
10750 char basename2[16];
10754 sprintf(basename1, "%03d.bmp", i);
10755 sprintf(basename2, "%03ds.bmp", i);
10757 filename1 = getPath2(global.create_images_dir, basename1);
10758 filename2 = getPath2(global.create_images_dir, basename2);
10760 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10761 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10764 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10765 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10767 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10768 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10770 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10771 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10777 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10780 FreeBitmap(bitmap1);
10781 FreeBitmap(bitmap2);
10786 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10788 CloseAllAndExit(0);
10793 /* ------------------------------------------------------------------------- */
10794 /* create and save images for custom and group elements (raw BMP format) */
10795 /* ------------------------------------------------------------------------- */
10797 void CreateCustomElementImages(char *directory)
10799 #if defined(TARGET_SDL)
10800 char *src_basename = "RocksCE-template.ilbm";
10801 char *dst_basename = "RocksCE.bmp";
10802 char *src_filename = getPath2(directory, src_basename);
10803 char *dst_filename = getPath2(directory, dst_basename);
10804 Bitmap *src_bitmap;
10806 int yoffset_ce = 0;
10807 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10810 SDLInitVideoDisplay();
10812 ReCreateBitmap(&backbuffer, video.width, video.height);
10814 src_bitmap = LoadImage(src_filename);
10816 bitmap = CreateBitmap(TILEX * 16 * 2,
10817 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10820 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10827 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10828 TILEX * x, TILEY * y + yoffset_ce);
10830 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10832 TILEX * x + TILEX * 16,
10833 TILEY * y + yoffset_ce);
10835 for (j = 2; j >= 0; j--)
10839 BlitBitmap(src_bitmap, bitmap,
10840 TILEX + c * 7, 0, 6, 10,
10841 TILEX * x + 6 + j * 7,
10842 TILEY * y + 11 + yoffset_ce);
10844 BlitBitmap(src_bitmap, bitmap,
10845 TILEX + c * 8, TILEY, 6, 10,
10846 TILEX * 16 + TILEX * x + 6 + j * 8,
10847 TILEY * y + 10 + yoffset_ce);
10853 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10860 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10861 TILEX * x, TILEY * y + yoffset_ge);
10863 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10865 TILEX * x + TILEX * 16,
10866 TILEY * y + yoffset_ge);
10868 for (j = 1; j >= 0; j--)
10872 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10873 TILEX * x + 6 + j * 10,
10874 TILEY * y + 11 + yoffset_ge);
10876 BlitBitmap(src_bitmap, bitmap,
10877 TILEX + c * 8, TILEY + 12, 6, 10,
10878 TILEX * 16 + TILEX * x + 10 + j * 8,
10879 TILEY * y + 10 + yoffset_ge);
10885 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10886 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10888 FreeBitmap(bitmap);
10890 CloseAllAndExit(0);