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"
26 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
28 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
30 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
31 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
32 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
38 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
39 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
41 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
43 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
45 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
46 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
47 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
49 /* (element number, number of change pages, change page number) */
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 /* (element number only) */
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 /* (nothing at all if unchanged) */
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
60 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
61 #define TAPE_CHUNK_HEAD_UNUSED 2 /* unused tape header bytes */
63 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
64 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
65 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
67 /* file identifier strings */
68 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
69 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
70 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
72 /* values for deciding when (not) to save configuration data */
73 #define SAVE_CONF_NEVER 0
74 #define SAVE_CONF_ALWAYS 1
75 #define SAVE_CONF_WHEN_CHANGED -1
77 /* values for chunks using micro chunks */
78 #define CONF_MASK_1_BYTE 0x00
79 #define CONF_MASK_2_BYTE 0x40
80 #define CONF_MASK_4_BYTE 0x80
81 #define CONF_MASK_MULTI_BYTES 0xc0
83 #define CONF_MASK_BYTES 0xc0
84 #define CONF_MASK_TOKEN 0x3f
86 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
87 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
88 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
89 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
91 /* these definitions are just for convenience of use and readability */
92 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
93 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
94 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
95 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
97 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
98 (x) == CONF_MASK_2_BYTE ? 2 : \
99 (x) == CONF_MASK_4_BYTE ? 4 : 0)
101 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
102 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
103 #define CONF_ELEMENT_NUM_BYTES (2)
105 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
106 (t) == TYPE_ELEMENT_LIST ? \
107 CONF_ELEMENT_NUM_BYTES : \
108 (t) == TYPE_CONTENT || \
109 (t) == TYPE_CONTENT_LIST ? \
110 CONF_CONTENT_NUM_BYTES : 1)
112 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
113 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
114 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
116 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
118 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
119 CONF_ELEMENT_NUM_BYTES)
120 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
121 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
123 /* temporary variables used to store pointers to structure members */
124 static struct LevelInfo li;
125 static struct ElementInfo xx_ei, yy_ei;
126 static struct ElementChangeInfo xx_change;
127 static struct ElementGroupInfo xx_group;
128 static struct EnvelopeInfo xx_envelope;
129 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
130 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
131 static int xx_num_contents;
132 static int xx_current_change_page;
133 static char xx_default_string_empty[1] = "";
134 static int xx_string_length_unused;
136 struct LevelFileConfigInfo
138 int element; /* element for which data is to be stored */
139 int save_type; /* save data always, never or when changed */
140 int data_type; /* data type (used internally, not stored) */
141 int conf_type; /* micro chunk identifier (stored in file) */
144 void *value; /* variable that holds the data to be stored */
145 int default_value; /* initial default value for this variable */
148 void *value_copy; /* variable that holds the data to be copied */
149 void *num_entities; /* number of entities for multi-byte data */
150 int default_num_entities; /* default number of entities for this data */
151 int max_num_entities; /* maximal number of entities for this data */
152 char *default_string; /* optional default string for string data */
155 static struct LevelFileConfigInfo chunk_config_INFO[] =
157 /* ---------- values not related to single elements ----------------------- */
160 -1, SAVE_CONF_ALWAYS,
161 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
162 &li.game_engine_type, GAME_ENGINE_TYPE_RND
166 -1, SAVE_CONF_ALWAYS,
167 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
168 &li.fieldx, STD_LEV_FIELDX
171 -1, SAVE_CONF_ALWAYS,
172 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
173 &li.fieldy, STD_LEV_FIELDY
177 -1, SAVE_CONF_ALWAYS,
178 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
183 -1, SAVE_CONF_ALWAYS,
184 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
202 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
203 &li.wind_direction_initial, MV_NONE
208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
209 &li.em_slippery_gems, FALSE
214 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
215 &li.use_custom_template, FALSE
220 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
221 &li.can_move_into_acid_bits, ~0 /* default: everything can */
226 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
227 &li.dont_collide_with_bits, ~0 /* default: always deadly */
232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
233 &li.em_explodes_by_fire, FALSE
238 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
239 &li.score[SC_TIME_BONUS], 1
244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
245 &li.auto_exit_sokoban, FALSE
250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
251 &li.auto_count_gems, FALSE
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
257 &li.solved_by_one_player, FALSE
267 static struct LevelFileConfigInfo chunk_config_ELEM[] =
269 /* (these values are the same for each player) */
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
273 &li.block_last_field, FALSE /* default case for EM levels */
277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
278 &li.sp_block_last_field, TRUE /* default case for SP levels */
282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
283 &li.instant_relocation, FALSE
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
288 &li.can_pass_to_walkable, FALSE
292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
293 &li.block_snap_field, TRUE
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
298 &li.continuous_snapping, TRUE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
303 &li.shifted_relocation, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
308 &li.lazy_relocation, FALSE
311 /* (these values are different for each player) */
314 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
315 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
320 &li.initial_player_gravity[0], FALSE
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
325 &li.use_start_element[0], FALSE
329 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
330 &li.start_element[0], EL_PLAYER_1
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
335 &li.use_artwork_element[0], FALSE
339 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
340 &li.artwork_element[0], EL_PLAYER_1
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
345 &li.use_explosion_element[0], FALSE
349 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
350 &li.explosion_element[0], EL_PLAYER_1
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
355 &li.use_initial_inventory[0], FALSE
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
360 &li.initial_inventory_size[0], 1
364 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
365 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
366 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
371 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
372 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
377 &li.initial_player_gravity[1], FALSE
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
382 &li.use_start_element[1], FALSE
386 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
387 &li.start_element[1], EL_PLAYER_2
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
392 &li.use_artwork_element[1], FALSE
396 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
397 &li.artwork_element[1], EL_PLAYER_2
401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
402 &li.use_explosion_element[1], FALSE
406 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
407 &li.explosion_element[1], EL_PLAYER_2
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
412 &li.use_initial_inventory[1], FALSE
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
417 &li.initial_inventory_size[1], 1
421 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
422 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
423 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
428 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
429 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
434 &li.initial_player_gravity[2], FALSE
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
439 &li.use_start_element[2], FALSE
443 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
444 &li.start_element[2], EL_PLAYER_3
448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
449 &li.use_artwork_element[2], FALSE
453 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
454 &li.artwork_element[2], EL_PLAYER_3
458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
459 &li.use_explosion_element[2], FALSE
463 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
464 &li.explosion_element[2], EL_PLAYER_3
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
469 &li.use_initial_inventory[2], FALSE
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
474 &li.initial_inventory_size[2], 1
478 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
479 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
480 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
485 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
486 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
491 &li.initial_player_gravity[3], FALSE
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
496 &li.use_start_element[3], FALSE
500 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
501 &li.start_element[3], EL_PLAYER_4
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
506 &li.use_artwork_element[3], FALSE
510 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
511 &li.artwork_element[3], EL_PLAYER_4
515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
516 &li.use_explosion_element[3], FALSE
520 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
521 &li.explosion_element[3], EL_PLAYER_4
525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
526 &li.use_initial_inventory[3], FALSE
530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
531 &li.initial_inventory_size[3], 1
535 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
536 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
537 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
542 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
543 &li.score[SC_EMERALD], 10
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_DIAMOND], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_BUG], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_SPACESHIP], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_PACMAN], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_NUT], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_DYNAMITE], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_KEY], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_PEARL], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_CRYSTAL], 10
602 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
603 &li.amoeba_content, EL_DIAMOND
607 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
613 &li.grow_into_diggable, TRUE
618 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
619 &li.yamyam_content, EL_ROCK, NULL,
620 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
624 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 &li.score[SC_YAMYAM], 10
630 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
631 &li.score[SC_ROBOT], 10
635 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 &li.time_magic_wall, 10
653 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
654 &li.game_of_life[0], 2
658 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
659 &li.game_of_life[1], 3
663 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
664 &li.game_of_life[2], 3
668 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
669 &li.game_of_life[3], 3
674 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
679 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
684 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
689 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
694 EL_TIMEGATE_SWITCH, -1,
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
696 &li.time_timegate, 10
700 EL_LIGHT_SWITCH_ACTIVE, -1,
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
706 EL_SHIELD_NORMAL, -1,
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
708 &li.shield_normal_time, 10
711 EL_SHIELD_NORMAL, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
713 &li.score[SC_SHIELD], 10
717 EL_SHIELD_DEADLY, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
719 &li.shield_deadly_time, 10
722 EL_SHIELD_DEADLY, -1,
723 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
724 &li.score[SC_SHIELD], 10
729 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
734 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
735 &li.extra_time_score, 10
739 EL_TIME_ORB_FULL, -1,
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
741 &li.time_orb_time, 10
744 EL_TIME_ORB_FULL, -1,
745 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
746 &li.use_time_orb_bug, FALSE
751 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
752 &li.use_spring_bug, FALSE
757 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
758 &li.android_move_time, 10
762 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
763 &li.android_clone_time, 10
767 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
768 &li.android_clone_element[0], EL_EMPTY, NULL,
769 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
774 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
779 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
784 EL_EMC_MAGNIFIER, -1,
785 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 &li.magnify_score, 10
789 EL_EMC_MAGNIFIER, -1,
790 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
795 EL_EMC_MAGIC_BALL, -1,
796 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
800 EL_EMC_MAGIC_BALL, -1,
801 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
802 &li.ball_random, FALSE
805 EL_EMC_MAGIC_BALL, -1,
806 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
807 &li.ball_state_initial, FALSE
810 EL_EMC_MAGIC_BALL, -1,
811 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
812 &li.ball_content, EL_EMPTY, NULL,
813 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
819 &li.mm_laser_red, FALSE
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
824 &li.mm_laser_green, FALSE
828 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
829 &li.mm_laser_blue, TRUE
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
835 &li.df_laser_red, TRUE
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
840 &li.df_laser_green, TRUE
844 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
845 &li.df_laser_blue, FALSE
849 EL_MM_FUSE_ACTIVE, -1,
850 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
855 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
864 EL_MM_STEEL_BLOCK, -1,
865 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
866 &li.mm_time_block, 75
870 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
871 &li.score[SC_ELEM_BONUS], 10
874 /* ---------- unused values ----------------------------------------------- */
877 EL_UNKNOWN, SAVE_CONF_NEVER,
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 &li.score[SC_UNKNOWN_15], 10
889 static struct LevelFileConfigInfo chunk_config_NOTE[] =
893 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
894 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
898 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
899 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
904 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
905 &xx_envelope.autowrap, FALSE
909 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
910 &xx_envelope.centered, FALSE
915 TYPE_STRING, CONF_VALUE_BYTES(1),
916 &xx_envelope.text, -1, NULL,
917 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
918 &xx_default_string_empty[0]
928 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
932 TYPE_STRING, CONF_VALUE_BYTES(1),
933 &xx_ei.description[0], -1,
934 &yy_ei.description[0],
935 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
936 &xx_default_description[0]
941 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
942 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
943 &yy_ei.properties[EP_BITFIELD_BASE_NR]
945 #if ENABLE_RESERVED_CODE
946 /* (reserved for later use) */
949 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
950 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
951 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
957 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
958 &xx_ei.use_gfx_element, FALSE,
959 &yy_ei.use_gfx_element
963 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
964 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
965 &yy_ei.gfx_element_initial
970 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
971 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
972 &yy_ei.access_direction
977 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
978 &xx_ei.collect_score_initial, 10,
979 &yy_ei.collect_score_initial
983 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
984 &xx_ei.collect_count_initial, 1,
985 &yy_ei.collect_count_initial
990 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
991 &xx_ei.ce_value_fixed_initial, 0,
992 &yy_ei.ce_value_fixed_initial
996 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
997 &xx_ei.ce_value_random_initial, 0,
998 &yy_ei.ce_value_random_initial
1002 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1003 &xx_ei.use_last_ce_value, FALSE,
1004 &yy_ei.use_last_ce_value
1009 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1010 &xx_ei.push_delay_fixed, 8,
1011 &yy_ei.push_delay_fixed
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1016 &xx_ei.push_delay_random, 8,
1017 &yy_ei.push_delay_random
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1022 &xx_ei.drop_delay_fixed, 0,
1023 &yy_ei.drop_delay_fixed
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1028 &xx_ei.drop_delay_random, 0,
1029 &yy_ei.drop_delay_random
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1034 &xx_ei.move_delay_fixed, 0,
1035 &yy_ei.move_delay_fixed
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1040 &xx_ei.move_delay_random, 0,
1041 &yy_ei.move_delay_random
1046 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1047 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1052 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1053 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1054 &yy_ei.move_direction_initial
1058 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1059 &xx_ei.move_stepsize, TILEX / 8,
1060 &yy_ei.move_stepsize
1065 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1066 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1067 &yy_ei.move_enter_element
1071 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1072 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1073 &yy_ei.move_leave_element
1077 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1078 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1079 &yy_ei.move_leave_type
1084 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1085 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1086 &yy_ei.slippery_type
1091 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1092 &xx_ei.explosion_type, EXPLODES_3X3,
1093 &yy_ei.explosion_type
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1098 &xx_ei.explosion_delay, 16,
1099 &yy_ei.explosion_delay
1103 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1104 &xx_ei.ignition_delay, 8,
1105 &yy_ei.ignition_delay
1110 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1111 &xx_ei.content, EL_EMPTY_SPACE,
1113 &xx_num_contents, 1, 1
1116 /* ---------- "num_change_pages" must be the last entry ------------------- */
1119 -1, SAVE_CONF_ALWAYS,
1120 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1121 &xx_ei.num_change_pages, 1,
1122 &yy_ei.num_change_pages
1133 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1135 /* ---------- "current_change_page" must be the first entry --------------- */
1138 -1, SAVE_CONF_ALWAYS,
1139 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1140 &xx_current_change_page, -1
1143 /* ---------- (the remaining entries can be in any order) ----------------- */
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1148 &xx_change.can_change, FALSE
1153 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1154 &xx_event_bits[0], 0
1158 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1159 &xx_event_bits[1], 0
1164 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1165 &xx_change.trigger_player, CH_PLAYER_ANY
1169 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1170 &xx_change.trigger_side, CH_SIDE_ANY
1174 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1175 &xx_change.trigger_page, CH_PAGE_ANY
1180 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1181 &xx_change.target_element, EL_EMPTY_SPACE
1186 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1187 &xx_change.delay_fixed, 0
1191 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1192 &xx_change.delay_random, 0
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1197 &xx_change.delay_frames, FRAMES_PER_SECOND
1202 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1203 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1209 &xx_change.explode, FALSE
1213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1214 &xx_change.use_target_content, FALSE
1218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1219 &xx_change.only_if_complete, FALSE
1223 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1224 &xx_change.use_random_replace, FALSE
1228 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1229 &xx_change.random_percentage, 100
1233 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1234 &xx_change.replace_when, CP_WHEN_EMPTY
1239 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1240 &xx_change.has_action, FALSE
1244 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1245 &xx_change.action_type, CA_NO_ACTION
1249 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1250 &xx_change.action_mode, CA_MODE_UNDEFINED
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1255 &xx_change.action_arg, CA_ARG_UNDEFINED
1260 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1261 &xx_change.action_element, EL_EMPTY_SPACE
1266 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1267 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1268 &xx_num_contents, 1, 1
1278 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1282 TYPE_STRING, CONF_VALUE_BYTES(1),
1283 &xx_ei.description[0], -1, NULL,
1284 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1285 &xx_default_description[0]
1290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1291 &xx_ei.use_gfx_element, FALSE
1295 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1296 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1301 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1302 &xx_group.choice_mode, ANIM_RANDOM
1307 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1308 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1309 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1319 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1324 &li.block_snap_field, TRUE
1328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1329 &li.continuous_snapping, TRUE
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1334 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1339 &li.use_start_element[0], FALSE
1343 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1344 &li.start_element[0], EL_PLAYER_1
1348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1349 &li.use_artwork_element[0], FALSE
1353 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1354 &li.artwork_element[0], EL_PLAYER_1
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1359 &li.use_explosion_element[0], FALSE
1363 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1364 &li.explosion_element[0], EL_PLAYER_1
1379 filetype_id_list[] =
1381 { LEVEL_FILE_TYPE_RND, "RND" },
1382 { LEVEL_FILE_TYPE_BD, "BD" },
1383 { LEVEL_FILE_TYPE_EM, "EM" },
1384 { LEVEL_FILE_TYPE_SP, "SP" },
1385 { LEVEL_FILE_TYPE_DX, "DX" },
1386 { LEVEL_FILE_TYPE_SB, "SB" },
1387 { LEVEL_FILE_TYPE_DC, "DC" },
1388 { LEVEL_FILE_TYPE_MM, "MM" },
1389 { LEVEL_FILE_TYPE_MM, "DF" },
1394 /* ========================================================================= */
1395 /* level file functions */
1396 /* ========================================================================= */
1398 static boolean check_special_flags(char *flag)
1400 if (strEqual(options.special_flags, flag) ||
1401 strEqual(leveldir_current->special_flags, flag))
1407 static struct DateInfo getCurrentDate(void)
1409 time_t epoch_seconds = time(NULL);
1410 struct tm *now = localtime(&epoch_seconds);
1411 struct DateInfo date;
1413 date.year = now->tm_year + 1900;
1414 date.month = now->tm_mon + 1;
1415 date.day = now->tm_mday;
1417 date.src = DATE_SRC_CLOCK;
1422 static void resetEventFlags(struct ElementChangeInfo *change)
1426 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1427 change->has_event[i] = FALSE;
1430 static void resetEventBits(void)
1434 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1435 xx_event_bits[i] = 0;
1438 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1442 /* important: only change event flag if corresponding event bit is set
1443 (this is because all xx_event_bits[] values are loaded separately,
1444 and all xx_event_bits[] values are set back to zero before loading
1445 another value xx_event_bits[x] (each value representing 32 flags)) */
1447 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1448 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1449 change->has_event[i] = TRUE;
1452 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1456 /* in contrast to the above function setEventFlagsFromEventBits(), it
1457 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1458 depending on the corresponding change->has_event[i] values here, as
1459 all xx_event_bits[] values are reset in resetEventBits() before */
1461 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1462 if (change->has_event[i])
1463 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1466 static char *getDefaultElementDescription(struct ElementInfo *ei)
1468 static char description[MAX_ELEMENT_NAME_LEN + 1];
1469 char *default_description = (ei->custom_description != NULL ?
1470 ei->custom_description :
1471 ei->editor_description);
1474 /* always start with reliable default values */
1475 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1476 description[i] = '\0';
1478 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1479 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1481 return &description[0];
1484 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1486 char *default_description = getDefaultElementDescription(ei);
1489 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1490 ei->description[i] = default_description[i];
1493 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1497 for (i = 0; conf[i].data_type != -1; i++)
1499 int default_value = conf[i].default_value;
1500 int data_type = conf[i].data_type;
1501 int conf_type = conf[i].conf_type;
1502 int byte_mask = conf_type & CONF_MASK_BYTES;
1504 if (byte_mask == CONF_MASK_MULTI_BYTES)
1506 int default_num_entities = conf[i].default_num_entities;
1507 int max_num_entities = conf[i].max_num_entities;
1509 *(int *)(conf[i].num_entities) = default_num_entities;
1511 if (data_type == TYPE_STRING)
1513 char *default_string = conf[i].default_string;
1514 char *string = (char *)(conf[i].value);
1516 strncpy(string, default_string, max_num_entities);
1518 else if (data_type == TYPE_ELEMENT_LIST)
1520 int *element_array = (int *)(conf[i].value);
1523 for (j = 0; j < max_num_entities; j++)
1524 element_array[j] = default_value;
1526 else if (data_type == TYPE_CONTENT_LIST)
1528 struct Content *content = (struct Content *)(conf[i].value);
1531 for (c = 0; c < max_num_entities; c++)
1532 for (y = 0; y < 3; y++)
1533 for (x = 0; x < 3; x++)
1534 content[c].e[x][y] = default_value;
1537 else /* constant size configuration data (1, 2 or 4 bytes) */
1539 if (data_type == TYPE_BOOLEAN)
1540 *(boolean *)(conf[i].value) = default_value;
1542 *(int *) (conf[i].value) = default_value;
1547 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1551 for (i = 0; conf[i].data_type != -1; i++)
1553 int data_type = conf[i].data_type;
1554 int conf_type = conf[i].conf_type;
1555 int byte_mask = conf_type & CONF_MASK_BYTES;
1557 if (byte_mask == CONF_MASK_MULTI_BYTES)
1559 int max_num_entities = conf[i].max_num_entities;
1561 if (data_type == TYPE_STRING)
1563 char *string = (char *)(conf[i].value);
1564 char *string_copy = (char *)(conf[i].value_copy);
1566 strncpy(string_copy, string, max_num_entities);
1568 else if (data_type == TYPE_ELEMENT_LIST)
1570 int *element_array = (int *)(conf[i].value);
1571 int *element_array_copy = (int *)(conf[i].value_copy);
1574 for (j = 0; j < max_num_entities; j++)
1575 element_array_copy[j] = element_array[j];
1577 else if (data_type == TYPE_CONTENT_LIST)
1579 struct Content *content = (struct Content *)(conf[i].value);
1580 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1583 for (c = 0; c < max_num_entities; c++)
1584 for (y = 0; y < 3; y++)
1585 for (x = 0; x < 3; x++)
1586 content_copy[c].e[x][y] = content[c].e[x][y];
1589 else /* constant size configuration data (1, 2 or 4 bytes) */
1591 if (data_type == TYPE_BOOLEAN)
1592 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1594 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1599 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1603 xx_ei = *ei_from; /* copy element data into temporary buffer */
1604 yy_ei = *ei_to; /* copy element data into temporary buffer */
1606 copyConfigFromConfigList(chunk_config_CUSX_base);
1611 /* ---------- reinitialize and copy change pages ---------- */
1613 ei_to->num_change_pages = ei_from->num_change_pages;
1614 ei_to->current_change_page = ei_from->current_change_page;
1616 setElementChangePages(ei_to, ei_to->num_change_pages);
1618 for (i = 0; i < ei_to->num_change_pages; i++)
1619 ei_to->change_page[i] = ei_from->change_page[i];
1621 /* ---------- copy group element info ---------- */
1622 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1623 *ei_to->group = *ei_from->group;
1625 /* mark this custom element as modified */
1626 ei_to->modified_settings = TRUE;
1629 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1631 int change_page_size = sizeof(struct ElementChangeInfo);
1633 ei->num_change_pages = MAX(1, change_pages);
1636 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1638 if (ei->current_change_page >= ei->num_change_pages)
1639 ei->current_change_page = ei->num_change_pages - 1;
1641 ei->change = &ei->change_page[ei->current_change_page];
1644 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1646 xx_change = *change; /* copy change data into temporary buffer */
1648 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1650 *change = xx_change;
1652 resetEventFlags(change);
1654 change->direct_action = 0;
1655 change->other_action = 0;
1657 change->pre_change_function = NULL;
1658 change->change_function = NULL;
1659 change->post_change_function = NULL;
1662 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1666 li = *level; /* copy level data into temporary buffer */
1667 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1668 *level = li; /* copy temporary buffer back to level data */
1670 setLevelInfoToDefaults_EM();
1671 setLevelInfoToDefaults_SP();
1672 setLevelInfoToDefaults_MM();
1674 level->native_em_level = &native_em_level;
1675 level->native_sp_level = &native_sp_level;
1676 level->native_mm_level = &native_mm_level;
1678 level->file_version = FILE_VERSION_ACTUAL;
1679 level->game_version = GAME_VERSION_ACTUAL;
1681 level->creation_date = getCurrentDate();
1683 level->encoding_16bit_field = TRUE;
1684 level->encoding_16bit_yamyam = TRUE;
1685 level->encoding_16bit_amoeba = TRUE;
1687 /* clear level name and level author string buffers */
1688 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1689 level->name[i] = '\0';
1690 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1691 level->author[i] = '\0';
1693 /* set level name and level author to default values */
1694 strcpy(level->name, NAMELESS_LEVEL_NAME);
1695 strcpy(level->author, ANONYMOUS_NAME);
1697 /* set level playfield to playable default level with player and exit */
1698 for (x = 0; x < MAX_LEV_FIELDX; x++)
1699 for (y = 0; y < MAX_LEV_FIELDY; y++)
1700 level->field[x][y] = EL_SAND;
1702 level->field[0][0] = EL_PLAYER_1;
1703 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1705 BorderElement = EL_STEELWALL;
1707 /* detect custom elements when loading them */
1708 level->file_has_custom_elements = FALSE;
1710 /* set all bug compatibility flags to "false" => do not emulate this bug */
1711 level->use_action_after_change_bug = FALSE;
1713 if (leveldir_current)
1715 /* try to determine better author name than 'anonymous' */
1716 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1718 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1719 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1723 switch (LEVELCLASS(leveldir_current))
1725 case LEVELCLASS_TUTORIAL:
1726 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1729 case LEVELCLASS_CONTRIB:
1730 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1731 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1734 case LEVELCLASS_PRIVATE:
1735 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1736 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1740 /* keep default value */
1747 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1749 static boolean clipboard_elements_initialized = FALSE;
1752 InitElementPropertiesStatic();
1754 li = *level; /* copy level data into temporary buffer */
1755 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1756 *level = li; /* copy temporary buffer back to level data */
1758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1761 struct ElementInfo *ei = &element_info[element];
1763 /* never initialize clipboard elements after the very first time */
1764 /* (to be able to use clipboard elements between several levels) */
1765 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1768 if (IS_ENVELOPE(element))
1770 int envelope_nr = element - EL_ENVELOPE_1;
1772 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1774 level->envelope[envelope_nr] = xx_envelope;
1777 if (IS_CUSTOM_ELEMENT(element) ||
1778 IS_GROUP_ELEMENT(element) ||
1779 IS_INTERNAL_ELEMENT(element))
1781 xx_ei = *ei; /* copy element data into temporary buffer */
1783 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1788 setElementChangePages(ei, 1);
1789 setElementChangeInfoToDefaults(ei->change);
1791 if (IS_CUSTOM_ELEMENT(element) ||
1792 IS_GROUP_ELEMENT(element) ||
1793 IS_INTERNAL_ELEMENT(element))
1795 setElementDescriptionToDefault(ei);
1797 ei->modified_settings = FALSE;
1800 if (IS_CUSTOM_ELEMENT(element) ||
1801 IS_INTERNAL_ELEMENT(element))
1803 /* internal values used in level editor */
1805 ei->access_type = 0;
1806 ei->access_layer = 0;
1807 ei->access_protected = 0;
1808 ei->walk_to_action = 0;
1809 ei->smash_targets = 0;
1812 ei->can_explode_by_fire = FALSE;
1813 ei->can_explode_smashed = FALSE;
1814 ei->can_explode_impact = FALSE;
1816 ei->current_change_page = 0;
1819 if (IS_GROUP_ELEMENT(element) ||
1820 IS_INTERNAL_ELEMENT(element))
1822 struct ElementGroupInfo *group;
1824 /* initialize memory for list of elements in group */
1825 if (ei->group == NULL)
1826 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1830 xx_group = *group; /* copy group data into temporary buffer */
1832 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1838 clipboard_elements_initialized = TRUE;
1841 static void setLevelInfoToDefaults(struct LevelInfo *level,
1842 boolean level_info_only,
1843 boolean reset_file_status)
1845 setLevelInfoToDefaults_Level(level);
1847 if (!level_info_only)
1848 setLevelInfoToDefaults_Elements(level);
1850 if (reset_file_status)
1852 level->no_valid_file = FALSE;
1853 level->no_level_file = FALSE;
1856 level->changed = FALSE;
1859 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1861 level_file_info->nr = 0;
1862 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1863 level_file_info->packed = FALSE;
1865 setString(&level_file_info->basename, NULL);
1866 setString(&level_file_info->filename, NULL);
1869 int getMappedElement_SB(int, boolean);
1871 static void ActivateLevelTemplate(void)
1875 if (check_special_flags("load_xsb_to_ces"))
1877 /* fill smaller playfields with padding "beyond border wall" elements */
1878 if (level.fieldx < level_template.fieldx ||
1879 level.fieldy < level_template.fieldy)
1881 short field[level.fieldx][level.fieldy];
1882 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1883 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1884 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1885 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1887 /* copy old playfield (which is smaller than the visible area) */
1888 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1889 field[x][y] = level.field[x][y];
1891 /* fill new, larger playfield with "beyond border wall" elements */
1892 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1893 level.field[x][y] = getMappedElement_SB('_', TRUE);
1895 /* copy the old playfield to the middle of the new playfield */
1896 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1897 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1899 level.fieldx = new_fieldx;
1900 level.fieldy = new_fieldy;
1904 /* Currently there is no special action needed to activate the template
1905 data, because 'element_info' property settings overwrite the original
1906 level data, while all other variables do not change. */
1908 /* Exception: 'from_level_template' elements in the original level playfield
1909 are overwritten with the corresponding elements at the same position in
1910 playfield from the level template. */
1912 for (x = 0; x < level.fieldx; x++)
1913 for (y = 0; y < level.fieldy; y++)
1914 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1915 level.field[x][y] = level_template.field[x][y];
1917 if (check_special_flags("load_xsb_to_ces"))
1919 struct LevelInfo level_backup = level;
1921 /* overwrite all individual level settings from template level settings */
1922 level = level_template;
1924 /* restore level file info */
1925 level.file_info = level_backup.file_info;
1927 /* restore playfield size */
1928 level.fieldx = level_backup.fieldx;
1929 level.fieldy = level_backup.fieldy;
1931 /* restore playfield content */
1932 for (x = 0; x < level.fieldx; x++)
1933 for (y = 0; y < level.fieldy; y++)
1934 level.field[x][y] = level_backup.field[x][y];
1936 /* restore name and author from individual level */
1937 strcpy(level.name, level_backup.name);
1938 strcpy(level.author, level_backup.author);
1940 /* restore flag "use_custom_template" */
1941 level.use_custom_template = level_backup.use_custom_template;
1945 static char *getLevelFilenameFromBasename(char *basename)
1947 static char *filename = NULL;
1949 checked_free(filename);
1951 filename = getPath2(getCurrentLevelDir(), basename);
1956 static int getFileTypeFromBasename(char *basename)
1958 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1960 static char *filename = NULL;
1961 struct stat file_status;
1963 /* ---------- try to determine file type from filename ---------- */
1965 /* check for typical filename of a Supaplex level package file */
1966 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1967 return LEVEL_FILE_TYPE_SP;
1969 /* check for typical filename of a Diamond Caves II level package file */
1970 if (strSuffixLower(basename, ".dc") ||
1971 strSuffixLower(basename, ".dc2"))
1972 return LEVEL_FILE_TYPE_DC;
1974 /* check for typical filename of a Sokoban level package file */
1975 if (strSuffixLower(basename, ".xsb") &&
1976 strchr(basename, '%') == NULL)
1977 return LEVEL_FILE_TYPE_SB;
1979 /* ---------- try to determine file type from filesize ---------- */
1981 checked_free(filename);
1982 filename = getPath2(getCurrentLevelDir(), basename);
1984 if (stat(filename, &file_status) == 0)
1986 /* check for typical filesize of a Supaplex level package file */
1987 if (file_status.st_size == 170496)
1988 return LEVEL_FILE_TYPE_SP;
1991 return LEVEL_FILE_TYPE_UNKNOWN;
1994 static int getFileTypeFromMagicBytes(char *filename, int type)
1998 if ((file = openFile(filename, MODE_READ)))
2000 char chunk_name[CHUNK_ID_LEN + 1];
2002 getFileChunkBE(file, chunk_name, NULL);
2004 if (strEqual(chunk_name, "MMII") ||
2005 strEqual(chunk_name, "MIRR"))
2006 type = LEVEL_FILE_TYPE_MM;
2014 static boolean checkForPackageFromBasename(char *basename)
2016 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2017 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2019 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2022 static char *getSingleLevelBasenameExt(int nr, char *extension)
2024 static char basename[MAX_FILENAME_LEN];
2027 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2029 sprintf(basename, "%03d.%s", nr, extension);
2034 static char *getSingleLevelBasename(int nr)
2036 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2039 static char *getPackedLevelBasename(int type)
2041 static char basename[MAX_FILENAME_LEN];
2042 char *directory = getCurrentLevelDir();
2044 DirectoryEntry *dir_entry;
2046 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2048 if ((dir = openDirectory(directory)) == NULL)
2050 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2055 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2057 char *entry_basename = dir_entry->basename;
2058 int entry_type = getFileTypeFromBasename(entry_basename);
2060 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2062 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2065 strcpy(basename, entry_basename);
2072 closeDirectory(dir);
2077 static char *getSingleLevelFilename(int nr)
2079 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2082 #if ENABLE_UNUSED_CODE
2083 static char *getPackedLevelFilename(int type)
2085 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2089 char *getDefaultLevelFilename(int nr)
2091 return getSingleLevelFilename(nr);
2094 #if ENABLE_UNUSED_CODE
2095 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2099 lfi->packed = FALSE;
2101 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2102 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2106 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2107 int type, char *format, ...)
2109 static char basename[MAX_FILENAME_LEN];
2112 va_start(ap, format);
2113 vsprintf(basename, format, ap);
2117 lfi->packed = FALSE;
2119 setString(&lfi->basename, basename);
2120 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2123 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2129 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2130 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2133 static int getFiletypeFromID(char *filetype_id)
2135 char *filetype_id_lower;
2136 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2139 if (filetype_id == NULL)
2140 return LEVEL_FILE_TYPE_UNKNOWN;
2142 filetype_id_lower = getStringToLower(filetype_id);
2144 for (i = 0; filetype_id_list[i].id != NULL; i++)
2146 char *id_lower = getStringToLower(filetype_id_list[i].id);
2148 if (strEqual(filetype_id_lower, id_lower))
2149 filetype = filetype_id_list[i].filetype;
2153 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2157 free(filetype_id_lower);
2162 char *getLocalLevelTemplateFilename(void)
2164 return getDefaultLevelFilename(-1);
2167 char *getGlobalLevelTemplateFilename(void)
2169 /* global variable "leveldir_current" must be modified in the loop below */
2170 LevelDirTree *leveldir_current_last = leveldir_current;
2171 char *filename = NULL;
2173 /* check for template level in path from current to topmost tree node */
2175 while (leveldir_current != NULL)
2177 filename = getDefaultLevelFilename(-1);
2179 if (fileExists(filename))
2182 leveldir_current = leveldir_current->node_parent;
2185 /* restore global variable "leveldir_current" modified in above loop */
2186 leveldir_current = leveldir_current_last;
2191 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2195 /* special case: level number is negative => check for level template file */
2198 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2199 getSingleLevelBasename(-1));
2201 /* replace local level template filename with global template filename */
2202 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2204 /* no fallback if template file not existing */
2208 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2209 if (leveldir_current->level_filename != NULL)
2211 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2213 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2214 leveldir_current->level_filename, nr);
2216 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2218 if (fileExists(lfi->filename))
2221 else if (leveldir_current->level_filetype != NULL)
2223 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2225 /* check for specified native level file with standard file name */
2226 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2227 "%03d.%s", nr, LEVELFILE_EXTENSION);
2228 if (fileExists(lfi->filename))
2232 /* check for native Rocks'n'Diamonds level file */
2233 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2234 "%03d.%s", nr, LEVELFILE_EXTENSION);
2235 if (fileExists(lfi->filename))
2238 /* check for Emerald Mine level file (V1) */
2239 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2240 'a' + (nr / 10) % 26, '0' + nr % 10);
2241 if (fileExists(lfi->filename))
2243 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2244 'A' + (nr / 10) % 26, '0' + nr % 10);
2245 if (fileExists(lfi->filename))
2248 /* check for Emerald Mine level file (V2 to V5) */
2249 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2250 if (fileExists(lfi->filename))
2253 /* check for Emerald Mine level file (V6 / single mode) */
2254 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2255 if (fileExists(lfi->filename))
2257 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2258 if (fileExists(lfi->filename))
2261 /* check for Emerald Mine level file (V6 / teamwork mode) */
2262 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2263 if (fileExists(lfi->filename))
2265 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2266 if (fileExists(lfi->filename))
2269 /* check for various packed level file formats */
2270 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2271 if (fileExists(lfi->filename))
2274 /* no known level file found -- use default values (and fail later) */
2275 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2276 "%03d.%s", nr, LEVELFILE_EXTENSION);
2279 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2281 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2282 lfi->type = getFileTypeFromBasename(lfi->basename);
2284 if (lfi->type == LEVEL_FILE_TYPE_RND)
2285 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2288 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2290 /* always start with reliable default values */
2291 setFileInfoToDefaults(level_file_info);
2293 level_file_info->nr = nr; /* set requested level number */
2295 determineLevelFileInfo_Filename(level_file_info);
2296 determineLevelFileInfo_Filetype(level_file_info);
2299 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2300 struct LevelFileInfo *lfi_to)
2302 lfi_to->nr = lfi_from->nr;
2303 lfi_to->type = lfi_from->type;
2304 lfi_to->packed = lfi_from->packed;
2306 setString(&lfi_to->basename, lfi_from->basename);
2307 setString(&lfi_to->filename, lfi_from->filename);
2310 /* ------------------------------------------------------------------------- */
2311 /* functions for loading R'n'D level */
2312 /* ------------------------------------------------------------------------- */
2314 static int getMappedElement(int element)
2316 /* remap some (historic, now obsolete) elements */
2320 case EL_PLAYER_OBSOLETE:
2321 element = EL_PLAYER_1;
2324 case EL_KEY_OBSOLETE:
2328 case EL_EM_KEY_1_FILE_OBSOLETE:
2329 element = EL_EM_KEY_1;
2332 case EL_EM_KEY_2_FILE_OBSOLETE:
2333 element = EL_EM_KEY_2;
2336 case EL_EM_KEY_3_FILE_OBSOLETE:
2337 element = EL_EM_KEY_3;
2340 case EL_EM_KEY_4_FILE_OBSOLETE:
2341 element = EL_EM_KEY_4;
2344 case EL_ENVELOPE_OBSOLETE:
2345 element = EL_ENVELOPE_1;
2353 if (element >= NUM_FILE_ELEMENTS)
2355 Error(ERR_WARN, "invalid level element %d", element);
2357 element = EL_UNKNOWN;
2365 static int getMappedElementByVersion(int element, int game_version)
2367 /* remap some elements due to certain game version */
2369 if (game_version <= VERSION_IDENT(2,2,0,0))
2371 /* map game font elements */
2372 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2373 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2374 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2375 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2378 if (game_version < VERSION_IDENT(3,0,0,0))
2380 /* map Supaplex gravity tube elements */
2381 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2382 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2383 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2384 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2391 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2393 level->file_version = getFileVersion(file);
2394 level->game_version = getFileVersion(file);
2399 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2401 level->creation_date.year = getFile16BitBE(file);
2402 level->creation_date.month = getFile8Bit(file);
2403 level->creation_date.day = getFile8Bit(file);
2405 level->creation_date.src = DATE_SRC_LEVELFILE;
2410 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2412 int initial_player_stepsize;
2413 int initial_player_gravity;
2416 level->fieldx = getFile8Bit(file);
2417 level->fieldy = getFile8Bit(file);
2419 level->time = getFile16BitBE(file);
2420 level->gems_needed = getFile16BitBE(file);
2422 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2423 level->name[i] = getFile8Bit(file);
2424 level->name[MAX_LEVEL_NAME_LEN] = 0;
2426 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2427 level->score[i] = getFile8Bit(file);
2429 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2430 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2431 for (y = 0; y < 3; y++)
2432 for (x = 0; x < 3; x++)
2433 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2435 level->amoeba_speed = getFile8Bit(file);
2436 level->time_magic_wall = getFile8Bit(file);
2437 level->time_wheel = getFile8Bit(file);
2438 level->amoeba_content = getMappedElement(getFile8Bit(file));
2440 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2443 for (i = 0; i < MAX_PLAYERS; i++)
2444 level->initial_player_stepsize[i] = initial_player_stepsize;
2446 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2448 for (i = 0; i < MAX_PLAYERS; i++)
2449 level->initial_player_gravity[i] = initial_player_gravity;
2451 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2452 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2454 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2456 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2457 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2458 level->can_move_into_acid_bits = getFile32BitBE(file);
2459 level->dont_collide_with_bits = getFile8Bit(file);
2461 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2462 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2464 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2465 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2466 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2468 level->game_engine_type = getFile8Bit(file);
2470 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2475 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2479 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2480 level->name[i] = getFile8Bit(file);
2481 level->name[MAX_LEVEL_NAME_LEN] = 0;
2486 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2490 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2491 level->author[i] = getFile8Bit(file);
2492 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2497 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2500 int chunk_size_expected = level->fieldx * level->fieldy;
2502 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2503 stored with 16-bit encoding (and should be twice as big then).
2504 Even worse, playfield data was stored 16-bit when only yamyam content
2505 contained 16-bit elements and vice versa. */
2507 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2508 chunk_size_expected *= 2;
2510 if (chunk_size_expected != chunk_size)
2512 ReadUnusedBytesFromFile(file, chunk_size);
2513 return chunk_size_expected;
2516 for (y = 0; y < level->fieldy; y++)
2517 for (x = 0; x < level->fieldx; x++)
2518 level->field[x][y] =
2519 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2524 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2527 int header_size = 4;
2528 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2529 int chunk_size_expected = header_size + content_size;
2531 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2532 stored with 16-bit encoding (and should be twice as big then).
2533 Even worse, playfield data was stored 16-bit when only yamyam content
2534 contained 16-bit elements and vice versa. */
2536 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2537 chunk_size_expected += content_size;
2539 if (chunk_size_expected != chunk_size)
2541 ReadUnusedBytesFromFile(file, chunk_size);
2542 return chunk_size_expected;
2546 level->num_yamyam_contents = getFile8Bit(file);
2550 /* correct invalid number of content fields -- should never happen */
2551 if (level->num_yamyam_contents < 1 ||
2552 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2553 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2555 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 level->yamyam_content[i].e[x][y] =
2559 getMappedElement(level->encoding_16bit_field ?
2560 getFile16BitBE(file) : getFile8Bit(file));
2564 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2569 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2571 element = getMappedElement(getFile16BitBE(file));
2572 num_contents = getFile8Bit(file);
2574 getFile8Bit(file); /* content x size (unused) */
2575 getFile8Bit(file); /* content y size (unused) */
2577 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2579 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2580 for (y = 0; y < 3; y++)
2581 for (x = 0; x < 3; x++)
2582 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2584 /* correct invalid number of content fields -- should never happen */
2585 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2586 num_contents = STD_ELEMENT_CONTENTS;
2588 if (element == EL_YAMYAM)
2590 level->num_yamyam_contents = num_contents;
2592 for (i = 0; i < num_contents; i++)
2593 for (y = 0; y < 3; y++)
2594 for (x = 0; x < 3; x++)
2595 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2597 else if (element == EL_BD_AMOEBA)
2599 level->amoeba_content = content_array[0][0][0];
2603 Error(ERR_WARN, "cannot load content for element '%d'", element);
2609 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2615 int chunk_size_expected;
2617 element = getMappedElement(getFile16BitBE(file));
2618 if (!IS_ENVELOPE(element))
2619 element = EL_ENVELOPE_1;
2621 envelope_nr = element - EL_ENVELOPE_1;
2623 envelope_len = getFile16BitBE(file);
2625 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2626 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2628 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2630 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2631 if (chunk_size_expected != chunk_size)
2633 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2634 return chunk_size_expected;
2637 for (i = 0; i < envelope_len; i++)
2638 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2643 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2645 int num_changed_custom_elements = getFile16BitBE(file);
2646 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2649 if (chunk_size_expected != chunk_size)
2651 ReadUnusedBytesFromFile(file, chunk_size - 2);
2652 return chunk_size_expected;
2655 for (i = 0; i < num_changed_custom_elements; i++)
2657 int element = getMappedElement(getFile16BitBE(file));
2658 int properties = getFile32BitBE(file);
2660 if (IS_CUSTOM_ELEMENT(element))
2661 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2663 Error(ERR_WARN, "invalid custom element number %d", element);
2665 /* older game versions that wrote level files with CUS1 chunks used
2666 different default push delay values (not yet stored in level file) */
2667 element_info[element].push_delay_fixed = 2;
2668 element_info[element].push_delay_random = 8;
2671 level->file_has_custom_elements = TRUE;
2676 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2678 int num_changed_custom_elements = getFile16BitBE(file);
2679 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2682 if (chunk_size_expected != chunk_size)
2684 ReadUnusedBytesFromFile(file, chunk_size - 2);
2685 return chunk_size_expected;
2688 for (i = 0; i < num_changed_custom_elements; i++)
2690 int element = getMappedElement(getFile16BitBE(file));
2691 int custom_target_element = getMappedElement(getFile16BitBE(file));
2693 if (IS_CUSTOM_ELEMENT(element))
2694 element_info[element].change->target_element = custom_target_element;
2696 Error(ERR_WARN, "invalid custom element number %d", element);
2699 level->file_has_custom_elements = TRUE;
2704 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2706 int num_changed_custom_elements = getFile16BitBE(file);
2707 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2710 if (chunk_size_expected != chunk_size)
2712 ReadUnusedBytesFromFile(file, chunk_size - 2);
2713 return chunk_size_expected;
2716 for (i = 0; i < num_changed_custom_elements; i++)
2718 int element = getMappedElement(getFile16BitBE(file));
2719 struct ElementInfo *ei = &element_info[element];
2720 unsigned int event_bits;
2722 if (!IS_CUSTOM_ELEMENT(element))
2724 Error(ERR_WARN, "invalid custom element number %d", element);
2726 element = EL_INTERNAL_DUMMY;
2729 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2730 ei->description[j] = getFile8Bit(file);
2731 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2733 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2735 /* some free bytes for future properties and padding */
2736 ReadUnusedBytesFromFile(file, 7);
2738 ei->use_gfx_element = getFile8Bit(file);
2739 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2741 ei->collect_score_initial = getFile8Bit(file);
2742 ei->collect_count_initial = getFile8Bit(file);
2744 ei->push_delay_fixed = getFile16BitBE(file);
2745 ei->push_delay_random = getFile16BitBE(file);
2746 ei->move_delay_fixed = getFile16BitBE(file);
2747 ei->move_delay_random = getFile16BitBE(file);
2749 ei->move_pattern = getFile16BitBE(file);
2750 ei->move_direction_initial = getFile8Bit(file);
2751 ei->move_stepsize = getFile8Bit(file);
2753 for (y = 0; y < 3; y++)
2754 for (x = 0; x < 3; x++)
2755 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2757 event_bits = getFile32BitBE(file);
2758 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2759 if (event_bits & (1 << j))
2760 ei->change->has_event[j] = TRUE;
2762 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2764 ei->change->delay_fixed = getFile16BitBE(file);
2765 ei->change->delay_random = getFile16BitBE(file);
2766 ei->change->delay_frames = getFile16BitBE(file);
2768 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2770 ei->change->explode = getFile8Bit(file);
2771 ei->change->use_target_content = getFile8Bit(file);
2772 ei->change->only_if_complete = getFile8Bit(file);
2773 ei->change->use_random_replace = getFile8Bit(file);
2775 ei->change->random_percentage = getFile8Bit(file);
2776 ei->change->replace_when = getFile8Bit(file);
2778 for (y = 0; y < 3; y++)
2779 for (x = 0; x < 3; x++)
2780 ei->change->target_content.e[x][y] =
2781 getMappedElement(getFile16BitBE(file));
2783 ei->slippery_type = getFile8Bit(file);
2785 /* some free bytes for future properties and padding */
2786 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2788 /* mark that this custom element has been modified */
2789 ei->modified_settings = TRUE;
2792 level->file_has_custom_elements = TRUE;
2797 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2799 struct ElementInfo *ei;
2800 int chunk_size_expected;
2804 /* ---------- custom element base property values (96 bytes) ------------- */
2806 element = getMappedElement(getFile16BitBE(file));
2808 if (!IS_CUSTOM_ELEMENT(element))
2810 Error(ERR_WARN, "invalid custom element number %d", element);
2812 ReadUnusedBytesFromFile(file, chunk_size - 2);
2816 ei = &element_info[element];
2818 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2819 ei->description[i] = getFile8Bit(file);
2820 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2822 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2824 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2826 ei->num_change_pages = getFile8Bit(file);
2828 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2829 if (chunk_size_expected != chunk_size)
2831 ReadUnusedBytesFromFile(file, chunk_size - 43);
2832 return chunk_size_expected;
2835 ei->ce_value_fixed_initial = getFile16BitBE(file);
2836 ei->ce_value_random_initial = getFile16BitBE(file);
2837 ei->use_last_ce_value = getFile8Bit(file);
2839 ei->use_gfx_element = getFile8Bit(file);
2840 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2842 ei->collect_score_initial = getFile8Bit(file);
2843 ei->collect_count_initial = getFile8Bit(file);
2845 ei->drop_delay_fixed = getFile8Bit(file);
2846 ei->push_delay_fixed = getFile8Bit(file);
2847 ei->drop_delay_random = getFile8Bit(file);
2848 ei->push_delay_random = getFile8Bit(file);
2849 ei->move_delay_fixed = getFile16BitBE(file);
2850 ei->move_delay_random = getFile16BitBE(file);
2852 /* bits 0 - 15 of "move_pattern" ... */
2853 ei->move_pattern = getFile16BitBE(file);
2854 ei->move_direction_initial = getFile8Bit(file);
2855 ei->move_stepsize = getFile8Bit(file);
2857 ei->slippery_type = getFile8Bit(file);
2859 for (y = 0; y < 3; y++)
2860 for (x = 0; x < 3; x++)
2861 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2863 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2864 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2865 ei->move_leave_type = getFile8Bit(file);
2867 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2868 ei->move_pattern |= (getFile16BitBE(file) << 16);
2870 ei->access_direction = getFile8Bit(file);
2872 ei->explosion_delay = getFile8Bit(file);
2873 ei->ignition_delay = getFile8Bit(file);
2874 ei->explosion_type = getFile8Bit(file);
2876 /* some free bytes for future custom property values and padding */
2877 ReadUnusedBytesFromFile(file, 1);
2879 /* ---------- change page property values (48 bytes) --------------------- */
2881 setElementChangePages(ei, ei->num_change_pages);
2883 for (i = 0; i < ei->num_change_pages; i++)
2885 struct ElementChangeInfo *change = &ei->change_page[i];
2886 unsigned int event_bits;
2888 /* always start with reliable default values */
2889 setElementChangeInfoToDefaults(change);
2891 /* bits 0 - 31 of "has_event[]" ... */
2892 event_bits = getFile32BitBE(file);
2893 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2894 if (event_bits & (1 << j))
2895 change->has_event[j] = TRUE;
2897 change->target_element = getMappedElement(getFile16BitBE(file));
2899 change->delay_fixed = getFile16BitBE(file);
2900 change->delay_random = getFile16BitBE(file);
2901 change->delay_frames = getFile16BitBE(file);
2903 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2905 change->explode = getFile8Bit(file);
2906 change->use_target_content = getFile8Bit(file);
2907 change->only_if_complete = getFile8Bit(file);
2908 change->use_random_replace = getFile8Bit(file);
2910 change->random_percentage = getFile8Bit(file);
2911 change->replace_when = getFile8Bit(file);
2913 for (y = 0; y < 3; y++)
2914 for (x = 0; x < 3; x++)
2915 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2917 change->can_change = getFile8Bit(file);
2919 change->trigger_side = getFile8Bit(file);
2921 change->trigger_player = getFile8Bit(file);
2922 change->trigger_page = getFile8Bit(file);
2924 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2925 CH_PAGE_ANY : (1 << change->trigger_page));
2927 change->has_action = getFile8Bit(file);
2928 change->action_type = getFile8Bit(file);
2929 change->action_mode = getFile8Bit(file);
2930 change->action_arg = getFile16BitBE(file);
2932 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2933 event_bits = getFile8Bit(file);
2934 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2935 if (event_bits & (1 << (j - 32)))
2936 change->has_event[j] = TRUE;
2939 /* mark this custom element as modified */
2940 ei->modified_settings = TRUE;
2942 level->file_has_custom_elements = TRUE;
2947 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2949 struct ElementInfo *ei;
2950 struct ElementGroupInfo *group;
2954 element = getMappedElement(getFile16BitBE(file));
2956 if (!IS_GROUP_ELEMENT(element))
2958 Error(ERR_WARN, "invalid group element number %d", element);
2960 ReadUnusedBytesFromFile(file, chunk_size - 2);
2964 ei = &element_info[element];
2966 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2967 ei->description[i] = getFile8Bit(file);
2968 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2970 group = element_info[element].group;
2972 group->num_elements = getFile8Bit(file);
2974 ei->use_gfx_element = getFile8Bit(file);
2975 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2977 group->choice_mode = getFile8Bit(file);
2979 /* some free bytes for future values and padding */
2980 ReadUnusedBytesFromFile(file, 3);
2982 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2983 group->element[i] = getMappedElement(getFile16BitBE(file));
2985 /* mark this group element as modified */
2986 element_info[element].modified_settings = TRUE;
2988 level->file_has_custom_elements = TRUE;
2993 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2994 int element, int real_element)
2996 int micro_chunk_size = 0;
2997 int conf_type = getFile8Bit(file);
2998 int byte_mask = conf_type & CONF_MASK_BYTES;
2999 boolean element_found = FALSE;
3002 micro_chunk_size += 1;
3004 if (byte_mask == CONF_MASK_MULTI_BYTES)
3006 int num_bytes = getFile16BitBE(file);
3007 byte *buffer = checked_malloc(num_bytes);
3009 ReadBytesFromFile(file, buffer, num_bytes);
3011 for (i = 0; conf[i].data_type != -1; i++)
3013 if (conf[i].element == element &&
3014 conf[i].conf_type == conf_type)
3016 int data_type = conf[i].data_type;
3017 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3018 int max_num_entities = conf[i].max_num_entities;
3020 if (num_entities > max_num_entities)
3023 "truncating number of entities for element %d from %d to %d",
3024 element, num_entities, max_num_entities);
3026 num_entities = max_num_entities;
3029 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3030 data_type == TYPE_CONTENT_LIST))
3032 /* for element and content lists, zero entities are not allowed */
3033 Error(ERR_WARN, "found empty list of entities for element %d",
3036 /* do not set "num_entities" here to prevent reading behind buffer */
3038 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3042 *(int *)(conf[i].num_entities) = num_entities;
3045 element_found = TRUE;
3047 if (data_type == TYPE_STRING)
3049 char *string = (char *)(conf[i].value);
3052 for (j = 0; j < max_num_entities; j++)
3053 string[j] = (j < num_entities ? buffer[j] : '\0');
3055 else if (data_type == TYPE_ELEMENT_LIST)
3057 int *element_array = (int *)(conf[i].value);
3060 for (j = 0; j < num_entities; j++)
3062 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3064 else if (data_type == TYPE_CONTENT_LIST)
3066 struct Content *content= (struct Content *)(conf[i].value);
3069 for (c = 0; c < num_entities; c++)
3070 for (y = 0; y < 3; y++)
3071 for (x = 0; x < 3; x++)
3072 content[c].e[x][y] =
3073 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3076 element_found = FALSE;
3082 checked_free(buffer);
3084 micro_chunk_size += 2 + num_bytes;
3086 else /* constant size configuration data (1, 2 or 4 bytes) */
3088 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3089 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3090 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3092 for (i = 0; conf[i].data_type != -1; i++)
3094 if (conf[i].element == element &&
3095 conf[i].conf_type == conf_type)
3097 int data_type = conf[i].data_type;
3099 if (data_type == TYPE_ELEMENT)
3100 value = getMappedElement(value);
3102 if (data_type == TYPE_BOOLEAN)
3103 *(boolean *)(conf[i].value) = value;
3105 *(int *) (conf[i].value) = value;
3107 element_found = TRUE;
3113 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3118 char *error_conf_chunk_bytes =
3119 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3120 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3121 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3122 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3123 int error_element = real_element;
3125 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3126 error_conf_chunk_bytes, error_conf_chunk_token,
3127 error_element, EL_NAME(error_element));
3130 return micro_chunk_size;
3133 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3135 int real_chunk_size = 0;
3137 li = *level; /* copy level data into temporary buffer */
3139 while (!checkEndOfFile(file))
3141 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3143 if (real_chunk_size >= chunk_size)
3147 *level = li; /* copy temporary buffer back to level data */
3149 return real_chunk_size;
3152 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3154 int real_chunk_size = 0;
3156 li = *level; /* copy level data into temporary buffer */
3158 while (!checkEndOfFile(file))
3160 int element = getMappedElement(getFile16BitBE(file));
3162 real_chunk_size += 2;
3163 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3165 if (real_chunk_size >= chunk_size)
3169 *level = li; /* copy temporary buffer back to level data */
3171 return real_chunk_size;
3174 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3176 int real_chunk_size = 0;
3178 li = *level; /* copy level data into temporary buffer */
3180 while (!checkEndOfFile(file))
3182 int element = getMappedElement(getFile16BitBE(file));
3184 real_chunk_size += 2;
3185 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3187 if (real_chunk_size >= chunk_size)
3191 *level = li; /* copy temporary buffer back to level data */
3193 return real_chunk_size;
3196 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3198 int element = getMappedElement(getFile16BitBE(file));
3199 int envelope_nr = element - EL_ENVELOPE_1;
3200 int real_chunk_size = 2;
3202 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3204 while (!checkEndOfFile(file))
3206 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3209 if (real_chunk_size >= chunk_size)
3213 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3215 return real_chunk_size;
3218 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3220 int element = getMappedElement(getFile16BitBE(file));
3221 int real_chunk_size = 2;
3222 struct ElementInfo *ei = &element_info[element];
3225 xx_ei = *ei; /* copy element data into temporary buffer */
3227 xx_ei.num_change_pages = -1;
3229 while (!checkEndOfFile(file))
3231 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3233 if (xx_ei.num_change_pages != -1)
3236 if (real_chunk_size >= chunk_size)
3242 if (ei->num_change_pages == -1)
3244 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3247 ei->num_change_pages = 1;
3249 setElementChangePages(ei, 1);
3250 setElementChangeInfoToDefaults(ei->change);
3252 return real_chunk_size;
3255 /* initialize number of change pages stored for this custom element */
3256 setElementChangePages(ei, ei->num_change_pages);
3257 for (i = 0; i < ei->num_change_pages; i++)
3258 setElementChangeInfoToDefaults(&ei->change_page[i]);
3260 /* start with reading properties for the first change page */
3261 xx_current_change_page = 0;
3263 while (!checkEndOfFile(file))
3265 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3267 xx_change = *change; /* copy change data into temporary buffer */
3269 resetEventBits(); /* reset bits; change page might have changed */
3271 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3274 *change = xx_change;
3276 setEventFlagsFromEventBits(change);
3278 if (real_chunk_size >= chunk_size)
3282 level->file_has_custom_elements = TRUE;
3284 return real_chunk_size;
3287 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3289 int element = getMappedElement(getFile16BitBE(file));
3290 int real_chunk_size = 2;
3291 struct ElementInfo *ei = &element_info[element];
3292 struct ElementGroupInfo *group = ei->group;
3294 xx_ei = *ei; /* copy element data into temporary buffer */
3295 xx_group = *group; /* copy group data into temporary buffer */
3297 while (!checkEndOfFile(file))
3299 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3302 if (real_chunk_size >= chunk_size)
3309 level->file_has_custom_elements = TRUE;
3311 return real_chunk_size;
3314 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3315 struct LevelFileInfo *level_file_info,
3316 boolean level_info_only)
3318 char *filename = level_file_info->filename;
3319 char cookie[MAX_LINE_LEN];
3320 char chunk_name[CHUNK_ID_LEN + 1];
3324 if (!(file = openFile(filename, MODE_READ)))
3326 level->no_valid_file = TRUE;
3327 level->no_level_file = TRUE;
3329 if (level_info_only)
3332 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3334 if (!setup.editor.use_template_for_new_levels)
3337 /* if level file not found, try to initialize level data from template */
3338 filename = getGlobalLevelTemplateFilename();
3340 if (!(file = openFile(filename, MODE_READ)))
3343 /* default: for empty levels, use level template for custom elements */
3344 level->use_custom_template = TRUE;
3346 level->no_valid_file = FALSE;
3349 getFileChunkBE(file, chunk_name, NULL);
3350 if (strEqual(chunk_name, "RND1"))
3352 getFile32BitBE(file); /* not used */
3354 getFileChunkBE(file, chunk_name, NULL);
3355 if (!strEqual(chunk_name, "CAVE"))
3357 level->no_valid_file = TRUE;
3359 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3366 else /* check for pre-2.0 file format with cookie string */
3368 strcpy(cookie, chunk_name);
3369 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3371 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3372 cookie[strlen(cookie) - 1] = '\0';
3374 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3376 level->no_valid_file = TRUE;
3378 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3385 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3387 level->no_valid_file = TRUE;
3389 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3396 /* pre-2.0 level files have no game version, so use file version here */
3397 level->game_version = level->file_version;
3400 if (level->file_version < FILE_VERSION_1_2)
3402 /* level files from versions before 1.2.0 without chunk structure */
3403 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3404 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3412 int (*loader)(File *, int, struct LevelInfo *);
3416 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3417 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3418 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3419 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3420 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3421 { "INFO", -1, LoadLevel_INFO },
3422 { "BODY", -1, LoadLevel_BODY },
3423 { "CONT", -1, LoadLevel_CONT },
3424 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3425 { "CNT3", -1, LoadLevel_CNT3 },
3426 { "CUS1", -1, LoadLevel_CUS1 },
3427 { "CUS2", -1, LoadLevel_CUS2 },
3428 { "CUS3", -1, LoadLevel_CUS3 },
3429 { "CUS4", -1, LoadLevel_CUS4 },
3430 { "GRP1", -1, LoadLevel_GRP1 },
3431 { "CONF", -1, LoadLevel_CONF },
3432 { "ELEM", -1, LoadLevel_ELEM },
3433 { "NOTE", -1, LoadLevel_NOTE },
3434 { "CUSX", -1, LoadLevel_CUSX },
3435 { "GRPX", -1, LoadLevel_GRPX },
3440 while (getFileChunkBE(file, chunk_name, &chunk_size))
3444 while (chunk_info[i].name != NULL &&
3445 !strEqual(chunk_name, chunk_info[i].name))
3448 if (chunk_info[i].name == NULL)
3450 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3451 chunk_name, filename);
3452 ReadUnusedBytesFromFile(file, chunk_size);
3454 else if (chunk_info[i].size != -1 &&
3455 chunk_info[i].size != chunk_size)
3457 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3458 chunk_size, chunk_name, filename);
3459 ReadUnusedBytesFromFile(file, chunk_size);
3463 /* call function to load this level chunk */
3464 int chunk_size_expected =
3465 (chunk_info[i].loader)(file, chunk_size, level);
3467 /* the size of some chunks cannot be checked before reading other
3468 chunks first (like "HEAD" and "BODY") that contain some header
3469 information, so check them here */
3470 if (chunk_size_expected != chunk_size)
3472 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3473 chunk_size, chunk_name, filename);
3483 /* ------------------------------------------------------------------------- */
3484 /* functions for loading EM level */
3485 /* ------------------------------------------------------------------------- */
3487 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3489 static int ball_xy[8][2] =
3500 struct LevelInfo_EM *level_em = level->native_em_level;
3501 struct LEVEL *lev = level_em->lev;
3502 struct PLAYER **ply = level_em->ply;
3505 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3506 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3508 lev->time_seconds = level->time;
3509 lev->required_initial = level->gems_needed;
3511 lev->emerald_score = level->score[SC_EMERALD];
3512 lev->diamond_score = level->score[SC_DIAMOND];
3513 lev->alien_score = level->score[SC_ROBOT];
3514 lev->tank_score = level->score[SC_SPACESHIP];
3515 lev->bug_score = level->score[SC_BUG];
3516 lev->eater_score = level->score[SC_YAMYAM];
3517 lev->nut_score = level->score[SC_NUT];
3518 lev->dynamite_score = level->score[SC_DYNAMITE];
3519 lev->key_score = level->score[SC_KEY];
3520 lev->exit_score = level->score[SC_TIME_BONUS];
3522 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3523 for (y = 0; y < 3; y++)
3524 for (x = 0; x < 3; x++)
3525 lev->eater_array[i][y * 3 + x] =
3526 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3528 lev->amoeba_time = level->amoeba_speed;
3529 lev->wonderwall_time_initial = level->time_magic_wall;
3530 lev->wheel_time = level->time_wheel;
3532 lev->android_move_time = level->android_move_time;
3533 lev->android_clone_time = level->android_clone_time;
3534 lev->ball_random = level->ball_random;
3535 lev->ball_state_initial = level->ball_state_initial;
3536 lev->ball_time = level->ball_time;
3537 lev->num_ball_arrays = level->num_ball_contents;
3539 lev->lenses_score = level->lenses_score;
3540 lev->magnify_score = level->magnify_score;
3541 lev->slurp_score = level->slurp_score;
3543 lev->lenses_time = level->lenses_time;
3544 lev->magnify_time = level->magnify_time;
3546 lev->wind_direction_initial =
3547 map_direction_RND_to_EM(level->wind_direction_initial);
3548 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3549 lev->wind_time : 0);
3551 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3552 for (j = 0; j < 8; j++)
3553 lev->ball_array[i][j] =
3554 map_element_RND_to_EM(level->
3555 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3557 map_android_clone_elements_RND_to_EM(level);
3559 /* first fill the complete playfield with the default border element */
3560 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3561 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3562 level_em->cave[x][y] = ZBORDER;
3564 if (BorderElement == EL_STEELWALL)
3566 for (y = 0; y < lev->height + 2; y++)
3567 for (x = 0; x < lev->width + 2; x++)
3568 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3571 /* then copy the real level contents from level file into the playfield */
3572 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3574 int new_element = map_element_RND_to_EM(level->field[x][y]);
3575 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3576 int xx = x + 1 + offset;
3577 int yy = y + 1 + offset;
3579 if (level->field[x][y] == EL_AMOEBA_DEAD)
3580 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3582 level_em->cave[xx][yy] = new_element;
3585 for (i = 0; i < MAX_PLAYERS; i++)
3587 ply[i]->x_initial = 0;
3588 ply[i]->y_initial = 0;
3591 /* initialize player positions and delete players from the playfield */
3592 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3594 if (ELEM_IS_PLAYER(level->field[x][y]))
3596 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3597 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3598 int xx = x + 1 + offset;
3599 int yy = y + 1 + offset;
3601 ply[player_nr]->x_initial = xx;
3602 ply[player_nr]->y_initial = yy;
3604 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3608 if (BorderElement == EL_STEELWALL)
3615 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3617 static int ball_xy[8][2] =
3628 struct LevelInfo_EM *level_em = level->native_em_level;
3629 struct LEVEL *lev = level_em->lev;
3630 struct PLAYER **ply = level_em->ply;
3633 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3634 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3636 level->time = lev->time_seconds;
3637 level->gems_needed = lev->required_initial;
3639 sprintf(level->name, "Level %d", level->file_info.nr);
3641 level->score[SC_EMERALD] = lev->emerald_score;
3642 level->score[SC_DIAMOND] = lev->diamond_score;
3643 level->score[SC_ROBOT] = lev->alien_score;
3644 level->score[SC_SPACESHIP] = lev->tank_score;
3645 level->score[SC_BUG] = lev->bug_score;
3646 level->score[SC_YAMYAM] = lev->eater_score;
3647 level->score[SC_NUT] = lev->nut_score;
3648 level->score[SC_DYNAMITE] = lev->dynamite_score;
3649 level->score[SC_KEY] = lev->key_score;
3650 level->score[SC_TIME_BONUS] = lev->exit_score;
3652 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3654 for (i = 0; i < level->num_yamyam_contents; i++)
3655 for (y = 0; y < 3; y++)
3656 for (x = 0; x < 3; x++)
3657 level->yamyam_content[i].e[x][y] =
3658 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3660 level->amoeba_speed = lev->amoeba_time;
3661 level->time_magic_wall = lev->wonderwall_time_initial;
3662 level->time_wheel = lev->wheel_time;
3664 level->android_move_time = lev->android_move_time;
3665 level->android_clone_time = lev->android_clone_time;
3666 level->ball_random = lev->ball_random;
3667 level->ball_state_initial = lev->ball_state_initial;
3668 level->ball_time = lev->ball_time;
3669 level->num_ball_contents = lev->num_ball_arrays;
3671 level->lenses_score = lev->lenses_score;
3672 level->magnify_score = lev->magnify_score;
3673 level->slurp_score = lev->slurp_score;
3675 level->lenses_time = lev->lenses_time;
3676 level->magnify_time = lev->magnify_time;
3678 level->wind_direction_initial =
3679 map_direction_EM_to_RND(lev->wind_direction_initial);
3681 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3682 for (j = 0; j < 8; j++)
3683 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3684 map_element_EM_to_RND(lev->ball_array[i][j]);
3686 map_android_clone_elements_EM_to_RND(level);
3688 /* convert the playfield (some elements need special treatment) */
3689 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3691 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3693 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3694 new_element = EL_AMOEBA_DEAD;
3696 level->field[x][y] = new_element;
3699 for (i = 0; i < MAX_PLAYERS; i++)
3701 /* in case of all players set to the same field, use the first player */
3702 int nr = MAX_PLAYERS - i - 1;
3703 int jx = ply[nr]->x_initial - 1;
3704 int jy = ply[nr]->y_initial - 1;
3706 if (jx != -1 && jy != -1)
3707 level->field[jx][jy] = EL_PLAYER_1 + nr;
3712 /* ------------------------------------------------------------------------- */
3713 /* functions for loading SP level */
3714 /* ------------------------------------------------------------------------- */
3716 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3718 struct LevelInfo_SP *level_sp = level->native_sp_level;
3719 LevelInfoType *header = &level_sp->header;
3722 level_sp->width = level->fieldx;
3723 level_sp->height = level->fieldy;
3725 for (x = 0; x < level->fieldx; x++)
3726 for (y = 0; y < level->fieldy; y++)
3727 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3729 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3731 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3732 header->LevelTitle[i] = level->name[i];
3733 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3735 header->InfotronsNeeded = level->gems_needed;
3737 header->SpecialPortCount = 0;
3739 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3741 boolean gravity_port_found = FALSE;
3742 boolean gravity_port_valid = FALSE;
3743 int gravity_port_flag;
3744 int gravity_port_base_element;
3745 int element = level->field[x][y];
3747 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3748 element <= EL_SP_GRAVITY_ON_PORT_UP)
3750 gravity_port_found = TRUE;
3751 gravity_port_valid = TRUE;
3752 gravity_port_flag = 1;
3753 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3755 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3756 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3758 gravity_port_found = TRUE;
3759 gravity_port_valid = TRUE;
3760 gravity_port_flag = 0;
3761 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3763 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3764 element <= EL_SP_GRAVITY_PORT_UP)
3766 /* change R'n'D style gravity inverting special port to normal port
3767 (there are no gravity inverting ports in native Supaplex engine) */
3769 gravity_port_found = TRUE;
3770 gravity_port_valid = FALSE;
3771 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3774 if (gravity_port_found)
3776 if (gravity_port_valid &&
3777 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3779 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3781 port->PortLocation = (y * level->fieldx + x) * 2;
3782 port->Gravity = gravity_port_flag;
3784 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3786 header->SpecialPortCount++;
3790 /* change special gravity port to normal port */
3792 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3795 level_sp->playfield[x][y] = element - EL_SP_START;
3800 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3802 struct LevelInfo_SP *level_sp = level->native_sp_level;
3803 LevelInfoType *header = &level_sp->header;
3804 boolean num_invalid_elements = 0;
3807 level->fieldx = level_sp->width;
3808 level->fieldy = level_sp->height;
3810 for (x = 0; x < level->fieldx; x++)
3812 for (y = 0; y < level->fieldy; y++)
3814 int element_old = level_sp->playfield[x][y];
3815 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3817 if (element_new == EL_UNKNOWN)
3819 num_invalid_elements++;
3821 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3825 level->field[x][y] = element_new;
3829 if (num_invalid_elements > 0)
3830 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3831 (!options.debug ? " (use '--debug' for more details)" : ""));
3833 for (i = 0; i < MAX_PLAYERS; i++)
3834 level->initial_player_gravity[i] =
3835 (header->InitialGravity == 1 ? TRUE : FALSE);
3837 /* skip leading spaces */
3838 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3839 if (header->LevelTitle[i] != ' ')
3842 /* copy level title */
3843 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3844 level->name[j] = header->LevelTitle[i];
3845 level->name[j] = '\0';
3847 /* cut trailing spaces */
3849 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3850 level->name[j - 1] = '\0';
3852 level->gems_needed = header->InfotronsNeeded;
3854 for (i = 0; i < header->SpecialPortCount; i++)
3856 SpecialPortType *port = &header->SpecialPort[i];
3857 int port_location = port->PortLocation;
3858 int gravity = port->Gravity;
3859 int port_x, port_y, port_element;
3861 port_x = (port_location / 2) % level->fieldx;
3862 port_y = (port_location / 2) / level->fieldx;
3864 if (port_x < 0 || port_x >= level->fieldx ||
3865 port_y < 0 || port_y >= level->fieldy)
3867 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3873 port_element = level->field[port_x][port_y];
3875 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3876 port_element > EL_SP_GRAVITY_PORT_UP)
3878 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3883 /* change previous (wrong) gravity inverting special port to either
3884 gravity enabling special port or gravity disabling special port */
3885 level->field[port_x][port_y] +=
3886 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3887 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3890 /* change special gravity ports without database entries to normal ports */
3891 for (x = 0; x < level->fieldx; x++)
3892 for (y = 0; y < level->fieldy; y++)
3893 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3894 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3895 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3897 level->time = 0; /* no time limit */
3898 level->amoeba_speed = 0;
3899 level->time_magic_wall = 0;
3900 level->time_wheel = 0;
3901 level->amoeba_content = EL_EMPTY;
3904 /* original Supaplex does not use score values -- use default values */
3906 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3907 level->score[i] = 0;
3910 /* there are no yamyams in supaplex levels */
3911 for (i = 0; i < level->num_yamyam_contents; i++)
3912 for (x = 0; x < 3; x++)
3913 for (y = 0; y < 3; y++)
3914 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3917 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3919 struct LevelInfo_SP *level_sp = level->native_sp_level;
3920 struct DemoInfo_SP *demo = &level_sp->demo;
3923 /* always start with reliable default values */
3924 demo->is_available = FALSE;
3927 if (TAPE_IS_EMPTY(tape))
3930 demo->level_nr = tape.level_nr; /* (currently not used) */
3932 level_sp->header.DemoRandomSeed = tape.random_seed;
3936 for (i = 0; i < tape.length; i++)
3938 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3939 int demo_repeat = tape.pos[i].delay;
3940 int demo_entries = (demo_repeat + 15) / 16;
3942 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3944 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3950 for (j = 0; j < demo_repeat / 16; j++)
3951 demo->data[demo->length++] = 0xf0 | demo_action;
3953 if (demo_repeat % 16)
3954 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3957 demo->is_available = TRUE;
3960 static void setTapeInfoToDefaults(void);
3962 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3964 struct LevelInfo_SP *level_sp = level->native_sp_level;
3965 struct DemoInfo_SP *demo = &level_sp->demo;
3966 char *filename = level->file_info.filename;
3969 /* always start with reliable default values */
3970 setTapeInfoToDefaults();
3972 if (!demo->is_available)
3975 tape.level_nr = demo->level_nr; /* (currently not used) */
3976 tape.random_seed = level_sp->header.DemoRandomSeed;
3978 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3981 tape.pos[tape.counter].delay = 0;
3983 for (i = 0; i < demo->length; i++)
3985 int demo_action = demo->data[i] & 0x0f;
3986 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3987 int tape_action = map_key_SP_to_RND(demo_action);
3988 int tape_repeat = demo_repeat + 1;
3989 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3990 boolean success = 0;
3993 for (j = 0; j < tape_repeat; j++)
3994 success = TapeAddAction(action);
3998 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
4005 TapeHaltRecording();
4009 /* ------------------------------------------------------------------------- */
4010 /* functions for loading MM level */
4011 /* ------------------------------------------------------------------------- */
4013 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4015 struct LevelInfo_MM *level_mm = level->native_mm_level;
4018 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4019 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4021 level_mm->time = level->time;
4022 level_mm->kettles_needed = level->gems_needed;
4023 level_mm->auto_count_kettles = level->auto_count_gems;
4025 level_mm->laser_red = level->mm_laser_red;
4026 level_mm->laser_green = level->mm_laser_green;
4027 level_mm->laser_blue = level->mm_laser_blue;
4029 strcpy(level_mm->name, level->name);
4030 strcpy(level_mm->author, level->author);
4032 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4033 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4034 level_mm->score[SC_KEY] = level->score[SC_KEY];
4035 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4036 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4038 level_mm->amoeba_speed = level->amoeba_speed;
4039 level_mm->time_fuse = level->mm_time_fuse;
4040 level_mm->time_bomb = level->mm_time_bomb;
4041 level_mm->time_ball = level->mm_time_ball;
4042 level_mm->time_block = level->mm_time_block;
4044 for (x = 0; x < level->fieldx; x++)
4045 for (y = 0; y < level->fieldy; y++)
4047 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4050 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4052 struct LevelInfo_MM *level_mm = level->native_mm_level;
4055 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4056 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4058 level->time = level_mm->time;
4059 level->gems_needed = level_mm->kettles_needed;
4060 level->auto_count_gems = level_mm->auto_count_kettles;
4062 level->mm_laser_red = level_mm->laser_red;
4063 level->mm_laser_green = level_mm->laser_green;
4064 level->mm_laser_blue = level_mm->laser_blue;
4066 strcpy(level->name, level_mm->name);
4068 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4069 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4070 strcpy(level->author, level_mm->author);
4072 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4073 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4074 level->score[SC_KEY] = level_mm->score[SC_KEY];
4075 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4076 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4078 level->amoeba_speed = level_mm->amoeba_speed;
4079 level->mm_time_fuse = level_mm->time_fuse;
4080 level->mm_time_bomb = level_mm->time_bomb;
4081 level->mm_time_ball = level_mm->time_ball;
4082 level->mm_time_block = level_mm->time_block;
4084 for (x = 0; x < level->fieldx; x++)
4085 for (y = 0; y < level->fieldy; y++)
4086 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4090 /* ------------------------------------------------------------------------- */
4091 /* functions for loading DC level */
4092 /* ------------------------------------------------------------------------- */
4094 #define DC_LEVEL_HEADER_SIZE 344
4096 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4099 static int last_data_encoded;
4103 int diff_hi, diff_lo;
4104 int data_hi, data_lo;
4105 unsigned short data_decoded;
4109 last_data_encoded = 0;
4116 diff = data_encoded - last_data_encoded;
4117 diff_hi = diff & ~0xff;
4118 diff_lo = diff & 0xff;
4122 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4123 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4124 data_hi = data_hi & 0xff00;
4126 data_decoded = data_hi | data_lo;
4128 last_data_encoded = data_encoded;
4130 offset1 = (offset1 + 1) % 31;
4131 offset2 = offset2 & 0xff;
4133 return data_decoded;
4136 static int getMappedElement_DC(int element)
4144 /* 0x0117 - 0x036e: (?) */
4147 /* 0x042d - 0x0684: (?) */
4163 element = EL_CRYSTAL;
4166 case 0x0e77: /* quicksand (boulder) */
4167 element = EL_QUICKSAND_FAST_FULL;
4170 case 0x0e99: /* slow quicksand (boulder) */
4171 element = EL_QUICKSAND_FULL;
4175 element = EL_EM_EXIT_OPEN;
4179 element = EL_EM_EXIT_CLOSED;
4183 element = EL_EM_STEEL_EXIT_OPEN;
4187 element = EL_EM_STEEL_EXIT_CLOSED;
4190 case 0x0f4f: /* dynamite (lit 1) */
4191 element = EL_EM_DYNAMITE_ACTIVE;
4194 case 0x0f57: /* dynamite (lit 2) */
4195 element = EL_EM_DYNAMITE_ACTIVE;
4198 case 0x0f5f: /* dynamite (lit 3) */
4199 element = EL_EM_DYNAMITE_ACTIVE;
4202 case 0x0f67: /* dynamite (lit 4) */
4203 element = EL_EM_DYNAMITE_ACTIVE;
4210 element = EL_AMOEBA_WET;
4214 element = EL_AMOEBA_DROP;
4218 element = EL_DC_MAGIC_WALL;
4222 element = EL_SPACESHIP_UP;
4226 element = EL_SPACESHIP_DOWN;
4230 element = EL_SPACESHIP_LEFT;
4234 element = EL_SPACESHIP_RIGHT;
4238 element = EL_BUG_UP;
4242 element = EL_BUG_DOWN;
4246 element = EL_BUG_LEFT;
4250 element = EL_BUG_RIGHT;
4254 element = EL_MOLE_UP;
4258 element = EL_MOLE_DOWN;
4262 element = EL_MOLE_LEFT;
4266 element = EL_MOLE_RIGHT;
4274 element = EL_YAMYAM;
4278 element = EL_SWITCHGATE_OPEN;
4282 element = EL_SWITCHGATE_CLOSED;
4286 element = EL_DC_SWITCHGATE_SWITCH_UP;
4290 element = EL_TIMEGATE_CLOSED;
4293 case 0x144c: /* conveyor belt switch (green) */
4294 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4297 case 0x144f: /* conveyor belt switch (red) */
4298 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4301 case 0x1452: /* conveyor belt switch (blue) */
4302 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4306 element = EL_CONVEYOR_BELT_3_MIDDLE;
4310 element = EL_CONVEYOR_BELT_3_LEFT;
4314 element = EL_CONVEYOR_BELT_3_RIGHT;
4318 element = EL_CONVEYOR_BELT_1_MIDDLE;
4322 element = EL_CONVEYOR_BELT_1_LEFT;
4326 element = EL_CONVEYOR_BELT_1_RIGHT;
4330 element = EL_CONVEYOR_BELT_4_MIDDLE;
4334 element = EL_CONVEYOR_BELT_4_LEFT;
4338 element = EL_CONVEYOR_BELT_4_RIGHT;
4342 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4346 element = EL_EXPANDABLE_WALL_VERTICAL;
4350 element = EL_EXPANDABLE_WALL_ANY;
4353 case 0x14ce: /* growing steel wall (left/right) */
4354 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4357 case 0x14df: /* growing steel wall (up/down) */
4358 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4361 case 0x14e8: /* growing steel wall (up/down/left/right) */
4362 element = EL_EXPANDABLE_STEELWALL_ANY;
4366 element = EL_SHIELD_DEADLY;
4370 element = EL_EXTRA_TIME;
4378 element = EL_EMPTY_SPACE;
4381 case 0x1578: /* quicksand (empty) */
4382 element = EL_QUICKSAND_FAST_EMPTY;
4385 case 0x1579: /* slow quicksand (empty) */
4386 element = EL_QUICKSAND_EMPTY;
4389 /* 0x157c - 0x158b: */
4392 /* 0x1590 - 0x159f: */
4393 /* EL_DC_LANDMINE */
4396 element = EL_EM_DYNAMITE;
4399 case 0x15a1: /* key (red) */
4400 element = EL_EM_KEY_1;
4403 case 0x15a2: /* key (yellow) */
4404 element = EL_EM_KEY_2;
4407 case 0x15a3: /* key (blue) */
4408 element = EL_EM_KEY_4;
4411 case 0x15a4: /* key (green) */
4412 element = EL_EM_KEY_3;
4415 case 0x15a5: /* key (white) */
4416 element = EL_DC_KEY_WHITE;
4420 element = EL_WALL_SLIPPERY;
4427 case 0x15a8: /* wall (not round) */
4431 case 0x15a9: /* (blue) */
4432 element = EL_CHAR_A;
4435 case 0x15aa: /* (blue) */
4436 element = EL_CHAR_B;
4439 case 0x15ab: /* (blue) */
4440 element = EL_CHAR_C;
4443 case 0x15ac: /* (blue) */
4444 element = EL_CHAR_D;
4447 case 0x15ad: /* (blue) */
4448 element = EL_CHAR_E;
4451 case 0x15ae: /* (blue) */
4452 element = EL_CHAR_F;
4455 case 0x15af: /* (blue) */
4456 element = EL_CHAR_G;
4459 case 0x15b0: /* (blue) */
4460 element = EL_CHAR_H;
4463 case 0x15b1: /* (blue) */
4464 element = EL_CHAR_I;
4467 case 0x15b2: /* (blue) */
4468 element = EL_CHAR_J;
4471 case 0x15b3: /* (blue) */
4472 element = EL_CHAR_K;
4475 case 0x15b4: /* (blue) */
4476 element = EL_CHAR_L;
4479 case 0x15b5: /* (blue) */
4480 element = EL_CHAR_M;
4483 case 0x15b6: /* (blue) */
4484 element = EL_CHAR_N;
4487 case 0x15b7: /* (blue) */
4488 element = EL_CHAR_O;
4491 case 0x15b8: /* (blue) */
4492 element = EL_CHAR_P;
4495 case 0x15b9: /* (blue) */
4496 element = EL_CHAR_Q;
4499 case 0x15ba: /* (blue) */
4500 element = EL_CHAR_R;
4503 case 0x15bb: /* (blue) */
4504 element = EL_CHAR_S;
4507 case 0x15bc: /* (blue) */
4508 element = EL_CHAR_T;
4511 case 0x15bd: /* (blue) */
4512 element = EL_CHAR_U;
4515 case 0x15be: /* (blue) */
4516 element = EL_CHAR_V;
4519 case 0x15bf: /* (blue) */
4520 element = EL_CHAR_W;
4523 case 0x15c0: /* (blue) */
4524 element = EL_CHAR_X;
4527 case 0x15c1: /* (blue) */
4528 element = EL_CHAR_Y;
4531 case 0x15c2: /* (blue) */
4532 element = EL_CHAR_Z;
4535 case 0x15c3: /* (blue) */
4536 element = EL_CHAR_AUMLAUT;
4539 case 0x15c4: /* (blue) */
4540 element = EL_CHAR_OUMLAUT;
4543 case 0x15c5: /* (blue) */
4544 element = EL_CHAR_UUMLAUT;
4547 case 0x15c6: /* (blue) */
4548 element = EL_CHAR_0;
4551 case 0x15c7: /* (blue) */
4552 element = EL_CHAR_1;
4555 case 0x15c8: /* (blue) */
4556 element = EL_CHAR_2;
4559 case 0x15c9: /* (blue) */
4560 element = EL_CHAR_3;
4563 case 0x15ca: /* (blue) */
4564 element = EL_CHAR_4;
4567 case 0x15cb: /* (blue) */
4568 element = EL_CHAR_5;
4571 case 0x15cc: /* (blue) */
4572 element = EL_CHAR_6;
4575 case 0x15cd: /* (blue) */
4576 element = EL_CHAR_7;
4579 case 0x15ce: /* (blue) */
4580 element = EL_CHAR_8;
4583 case 0x15cf: /* (blue) */
4584 element = EL_CHAR_9;
4587 case 0x15d0: /* (blue) */
4588 element = EL_CHAR_PERIOD;
4591 case 0x15d1: /* (blue) */
4592 element = EL_CHAR_EXCLAM;
4595 case 0x15d2: /* (blue) */
4596 element = EL_CHAR_COLON;
4599 case 0x15d3: /* (blue) */
4600 element = EL_CHAR_LESS;
4603 case 0x15d4: /* (blue) */
4604 element = EL_CHAR_GREATER;
4607 case 0x15d5: /* (blue) */
4608 element = EL_CHAR_QUESTION;
4611 case 0x15d6: /* (blue) */
4612 element = EL_CHAR_COPYRIGHT;
4615 case 0x15d7: /* (blue) */
4616 element = EL_CHAR_UP;
4619 case 0x15d8: /* (blue) */
4620 element = EL_CHAR_DOWN;
4623 case 0x15d9: /* (blue) */
4624 element = EL_CHAR_BUTTON;
4627 case 0x15da: /* (blue) */
4628 element = EL_CHAR_PLUS;
4631 case 0x15db: /* (blue) */
4632 element = EL_CHAR_MINUS;
4635 case 0x15dc: /* (blue) */
4636 element = EL_CHAR_APOSTROPHE;
4639 case 0x15dd: /* (blue) */
4640 element = EL_CHAR_PARENLEFT;
4643 case 0x15de: /* (blue) */
4644 element = EL_CHAR_PARENRIGHT;
4647 case 0x15df: /* (green) */
4648 element = EL_CHAR_A;
4651 case 0x15e0: /* (green) */
4652 element = EL_CHAR_B;
4655 case 0x15e1: /* (green) */
4656 element = EL_CHAR_C;
4659 case 0x15e2: /* (green) */
4660 element = EL_CHAR_D;
4663 case 0x15e3: /* (green) */
4664 element = EL_CHAR_E;
4667 case 0x15e4: /* (green) */
4668 element = EL_CHAR_F;
4671 case 0x15e5: /* (green) */
4672 element = EL_CHAR_G;
4675 case 0x15e6: /* (green) */
4676 element = EL_CHAR_H;
4679 case 0x15e7: /* (green) */
4680 element = EL_CHAR_I;
4683 case 0x15e8: /* (green) */
4684 element = EL_CHAR_J;
4687 case 0x15e9: /* (green) */
4688 element = EL_CHAR_K;
4691 case 0x15ea: /* (green) */
4692 element = EL_CHAR_L;
4695 case 0x15eb: /* (green) */
4696 element = EL_CHAR_M;
4699 case 0x15ec: /* (green) */
4700 element = EL_CHAR_N;
4703 case 0x15ed: /* (green) */
4704 element = EL_CHAR_O;
4707 case 0x15ee: /* (green) */
4708 element = EL_CHAR_P;
4711 case 0x15ef: /* (green) */
4712 element = EL_CHAR_Q;
4715 case 0x15f0: /* (green) */
4716 element = EL_CHAR_R;
4719 case 0x15f1: /* (green) */
4720 element = EL_CHAR_S;
4723 case 0x15f2: /* (green) */
4724 element = EL_CHAR_T;
4727 case 0x15f3: /* (green) */
4728 element = EL_CHAR_U;
4731 case 0x15f4: /* (green) */
4732 element = EL_CHAR_V;
4735 case 0x15f5: /* (green) */
4736 element = EL_CHAR_W;
4739 case 0x15f6: /* (green) */
4740 element = EL_CHAR_X;
4743 case 0x15f7: /* (green) */
4744 element = EL_CHAR_Y;
4747 case 0x15f8: /* (green) */
4748 element = EL_CHAR_Z;
4751 case 0x15f9: /* (green) */
4752 element = EL_CHAR_AUMLAUT;
4755 case 0x15fa: /* (green) */
4756 element = EL_CHAR_OUMLAUT;
4759 case 0x15fb: /* (green) */
4760 element = EL_CHAR_UUMLAUT;
4763 case 0x15fc: /* (green) */
4764 element = EL_CHAR_0;
4767 case 0x15fd: /* (green) */
4768 element = EL_CHAR_1;
4771 case 0x15fe: /* (green) */
4772 element = EL_CHAR_2;
4775 case 0x15ff: /* (green) */
4776 element = EL_CHAR_3;
4779 case 0x1600: /* (green) */
4780 element = EL_CHAR_4;
4783 case 0x1601: /* (green) */
4784 element = EL_CHAR_5;
4787 case 0x1602: /* (green) */
4788 element = EL_CHAR_6;
4791 case 0x1603: /* (green) */
4792 element = EL_CHAR_7;
4795 case 0x1604: /* (green) */
4796 element = EL_CHAR_8;
4799 case 0x1605: /* (green) */
4800 element = EL_CHAR_9;
4803 case 0x1606: /* (green) */
4804 element = EL_CHAR_PERIOD;
4807 case 0x1607: /* (green) */
4808 element = EL_CHAR_EXCLAM;
4811 case 0x1608: /* (green) */
4812 element = EL_CHAR_COLON;
4815 case 0x1609: /* (green) */
4816 element = EL_CHAR_LESS;
4819 case 0x160a: /* (green) */
4820 element = EL_CHAR_GREATER;
4823 case 0x160b: /* (green) */
4824 element = EL_CHAR_QUESTION;
4827 case 0x160c: /* (green) */
4828 element = EL_CHAR_COPYRIGHT;
4831 case 0x160d: /* (green) */
4832 element = EL_CHAR_UP;
4835 case 0x160e: /* (green) */
4836 element = EL_CHAR_DOWN;
4839 case 0x160f: /* (green) */
4840 element = EL_CHAR_BUTTON;
4843 case 0x1610: /* (green) */
4844 element = EL_CHAR_PLUS;
4847 case 0x1611: /* (green) */
4848 element = EL_CHAR_MINUS;
4851 case 0x1612: /* (green) */
4852 element = EL_CHAR_APOSTROPHE;
4855 case 0x1613: /* (green) */
4856 element = EL_CHAR_PARENLEFT;
4859 case 0x1614: /* (green) */
4860 element = EL_CHAR_PARENRIGHT;
4863 case 0x1615: /* (blue steel) */
4864 element = EL_STEEL_CHAR_A;
4867 case 0x1616: /* (blue steel) */
4868 element = EL_STEEL_CHAR_B;
4871 case 0x1617: /* (blue steel) */
4872 element = EL_STEEL_CHAR_C;
4875 case 0x1618: /* (blue steel) */
4876 element = EL_STEEL_CHAR_D;
4879 case 0x1619: /* (blue steel) */
4880 element = EL_STEEL_CHAR_E;
4883 case 0x161a: /* (blue steel) */
4884 element = EL_STEEL_CHAR_F;
4887 case 0x161b: /* (blue steel) */
4888 element = EL_STEEL_CHAR_G;
4891 case 0x161c: /* (blue steel) */
4892 element = EL_STEEL_CHAR_H;
4895 case 0x161d: /* (blue steel) */
4896 element = EL_STEEL_CHAR_I;
4899 case 0x161e: /* (blue steel) */
4900 element = EL_STEEL_CHAR_J;
4903 case 0x161f: /* (blue steel) */
4904 element = EL_STEEL_CHAR_K;
4907 case 0x1620: /* (blue steel) */
4908 element = EL_STEEL_CHAR_L;
4911 case 0x1621: /* (blue steel) */
4912 element = EL_STEEL_CHAR_M;
4915 case 0x1622: /* (blue steel) */
4916 element = EL_STEEL_CHAR_N;
4919 case 0x1623: /* (blue steel) */
4920 element = EL_STEEL_CHAR_O;
4923 case 0x1624: /* (blue steel) */
4924 element = EL_STEEL_CHAR_P;
4927 case 0x1625: /* (blue steel) */
4928 element = EL_STEEL_CHAR_Q;
4931 case 0x1626: /* (blue steel) */
4932 element = EL_STEEL_CHAR_R;
4935 case 0x1627: /* (blue steel) */
4936 element = EL_STEEL_CHAR_S;
4939 case 0x1628: /* (blue steel) */
4940 element = EL_STEEL_CHAR_T;
4943 case 0x1629: /* (blue steel) */
4944 element = EL_STEEL_CHAR_U;
4947 case 0x162a: /* (blue steel) */
4948 element = EL_STEEL_CHAR_V;
4951 case 0x162b: /* (blue steel) */
4952 element = EL_STEEL_CHAR_W;
4955 case 0x162c: /* (blue steel) */
4956 element = EL_STEEL_CHAR_X;
4959 case 0x162d: /* (blue steel) */
4960 element = EL_STEEL_CHAR_Y;
4963 case 0x162e: /* (blue steel) */
4964 element = EL_STEEL_CHAR_Z;
4967 case 0x162f: /* (blue steel) */
4968 element = EL_STEEL_CHAR_AUMLAUT;
4971 case 0x1630: /* (blue steel) */
4972 element = EL_STEEL_CHAR_OUMLAUT;
4975 case 0x1631: /* (blue steel) */
4976 element = EL_STEEL_CHAR_UUMLAUT;
4979 case 0x1632: /* (blue steel) */
4980 element = EL_STEEL_CHAR_0;
4983 case 0x1633: /* (blue steel) */
4984 element = EL_STEEL_CHAR_1;
4987 case 0x1634: /* (blue steel) */
4988 element = EL_STEEL_CHAR_2;
4991 case 0x1635: /* (blue steel) */
4992 element = EL_STEEL_CHAR_3;
4995 case 0x1636: /* (blue steel) */
4996 element = EL_STEEL_CHAR_4;
4999 case 0x1637: /* (blue steel) */
5000 element = EL_STEEL_CHAR_5;
5003 case 0x1638: /* (blue steel) */
5004 element = EL_STEEL_CHAR_6;
5007 case 0x1639: /* (blue steel) */
5008 element = EL_STEEL_CHAR_7;
5011 case 0x163a: /* (blue steel) */
5012 element = EL_STEEL_CHAR_8;
5015 case 0x163b: /* (blue steel) */
5016 element = EL_STEEL_CHAR_9;
5019 case 0x163c: /* (blue steel) */
5020 element = EL_STEEL_CHAR_PERIOD;
5023 case 0x163d: /* (blue steel) */
5024 element = EL_STEEL_CHAR_EXCLAM;
5027 case 0x163e: /* (blue steel) */
5028 element = EL_STEEL_CHAR_COLON;
5031 case 0x163f: /* (blue steel) */
5032 element = EL_STEEL_CHAR_LESS;
5035 case 0x1640: /* (blue steel) */
5036 element = EL_STEEL_CHAR_GREATER;
5039 case 0x1641: /* (blue steel) */
5040 element = EL_STEEL_CHAR_QUESTION;
5043 case 0x1642: /* (blue steel) */
5044 element = EL_STEEL_CHAR_COPYRIGHT;
5047 case 0x1643: /* (blue steel) */
5048 element = EL_STEEL_CHAR_UP;
5051 case 0x1644: /* (blue steel) */
5052 element = EL_STEEL_CHAR_DOWN;
5055 case 0x1645: /* (blue steel) */
5056 element = EL_STEEL_CHAR_BUTTON;
5059 case 0x1646: /* (blue steel) */
5060 element = EL_STEEL_CHAR_PLUS;
5063 case 0x1647: /* (blue steel) */
5064 element = EL_STEEL_CHAR_MINUS;
5067 case 0x1648: /* (blue steel) */
5068 element = EL_STEEL_CHAR_APOSTROPHE;
5071 case 0x1649: /* (blue steel) */
5072 element = EL_STEEL_CHAR_PARENLEFT;
5075 case 0x164a: /* (blue steel) */
5076 element = EL_STEEL_CHAR_PARENRIGHT;
5079 case 0x164b: /* (green steel) */
5080 element = EL_STEEL_CHAR_A;
5083 case 0x164c: /* (green steel) */
5084 element = EL_STEEL_CHAR_B;
5087 case 0x164d: /* (green steel) */
5088 element = EL_STEEL_CHAR_C;
5091 case 0x164e: /* (green steel) */
5092 element = EL_STEEL_CHAR_D;
5095 case 0x164f: /* (green steel) */
5096 element = EL_STEEL_CHAR_E;
5099 case 0x1650: /* (green steel) */
5100 element = EL_STEEL_CHAR_F;
5103 case 0x1651: /* (green steel) */
5104 element = EL_STEEL_CHAR_G;
5107 case 0x1652: /* (green steel) */
5108 element = EL_STEEL_CHAR_H;
5111 case 0x1653: /* (green steel) */
5112 element = EL_STEEL_CHAR_I;
5115 case 0x1654: /* (green steel) */
5116 element = EL_STEEL_CHAR_J;
5119 case 0x1655: /* (green steel) */
5120 element = EL_STEEL_CHAR_K;
5123 case 0x1656: /* (green steel) */
5124 element = EL_STEEL_CHAR_L;
5127 case 0x1657: /* (green steel) */
5128 element = EL_STEEL_CHAR_M;
5131 case 0x1658: /* (green steel) */
5132 element = EL_STEEL_CHAR_N;
5135 case 0x1659: /* (green steel) */
5136 element = EL_STEEL_CHAR_O;
5139 case 0x165a: /* (green steel) */
5140 element = EL_STEEL_CHAR_P;
5143 case 0x165b: /* (green steel) */
5144 element = EL_STEEL_CHAR_Q;
5147 case 0x165c: /* (green steel) */
5148 element = EL_STEEL_CHAR_R;
5151 case 0x165d: /* (green steel) */
5152 element = EL_STEEL_CHAR_S;
5155 case 0x165e: /* (green steel) */
5156 element = EL_STEEL_CHAR_T;
5159 case 0x165f: /* (green steel) */
5160 element = EL_STEEL_CHAR_U;
5163 case 0x1660: /* (green steel) */
5164 element = EL_STEEL_CHAR_V;
5167 case 0x1661: /* (green steel) */
5168 element = EL_STEEL_CHAR_W;
5171 case 0x1662: /* (green steel) */
5172 element = EL_STEEL_CHAR_X;
5175 case 0x1663: /* (green steel) */
5176 element = EL_STEEL_CHAR_Y;
5179 case 0x1664: /* (green steel) */
5180 element = EL_STEEL_CHAR_Z;
5183 case 0x1665: /* (green steel) */
5184 element = EL_STEEL_CHAR_AUMLAUT;
5187 case 0x1666: /* (green steel) */
5188 element = EL_STEEL_CHAR_OUMLAUT;
5191 case 0x1667: /* (green steel) */
5192 element = EL_STEEL_CHAR_UUMLAUT;
5195 case 0x1668: /* (green steel) */
5196 element = EL_STEEL_CHAR_0;
5199 case 0x1669: /* (green steel) */
5200 element = EL_STEEL_CHAR_1;
5203 case 0x166a: /* (green steel) */
5204 element = EL_STEEL_CHAR_2;
5207 case 0x166b: /* (green steel) */
5208 element = EL_STEEL_CHAR_3;
5211 case 0x166c: /* (green steel) */
5212 element = EL_STEEL_CHAR_4;
5215 case 0x166d: /* (green steel) */
5216 element = EL_STEEL_CHAR_5;
5219 case 0x166e: /* (green steel) */
5220 element = EL_STEEL_CHAR_6;
5223 case 0x166f: /* (green steel) */
5224 element = EL_STEEL_CHAR_7;
5227 case 0x1670: /* (green steel) */
5228 element = EL_STEEL_CHAR_8;
5231 case 0x1671: /* (green steel) */
5232 element = EL_STEEL_CHAR_9;
5235 case 0x1672: /* (green steel) */
5236 element = EL_STEEL_CHAR_PERIOD;
5239 case 0x1673: /* (green steel) */
5240 element = EL_STEEL_CHAR_EXCLAM;
5243 case 0x1674: /* (green steel) */
5244 element = EL_STEEL_CHAR_COLON;
5247 case 0x1675: /* (green steel) */
5248 element = EL_STEEL_CHAR_LESS;
5251 case 0x1676: /* (green steel) */
5252 element = EL_STEEL_CHAR_GREATER;
5255 case 0x1677: /* (green steel) */
5256 element = EL_STEEL_CHAR_QUESTION;
5259 case 0x1678: /* (green steel) */
5260 element = EL_STEEL_CHAR_COPYRIGHT;
5263 case 0x1679: /* (green steel) */
5264 element = EL_STEEL_CHAR_UP;
5267 case 0x167a: /* (green steel) */
5268 element = EL_STEEL_CHAR_DOWN;
5271 case 0x167b: /* (green steel) */
5272 element = EL_STEEL_CHAR_BUTTON;
5275 case 0x167c: /* (green steel) */
5276 element = EL_STEEL_CHAR_PLUS;
5279 case 0x167d: /* (green steel) */
5280 element = EL_STEEL_CHAR_MINUS;
5283 case 0x167e: /* (green steel) */
5284 element = EL_STEEL_CHAR_APOSTROPHE;
5287 case 0x167f: /* (green steel) */
5288 element = EL_STEEL_CHAR_PARENLEFT;
5291 case 0x1680: /* (green steel) */
5292 element = EL_STEEL_CHAR_PARENRIGHT;
5295 case 0x1681: /* gate (red) */
5296 element = EL_EM_GATE_1;
5299 case 0x1682: /* secret gate (red) */
5300 element = EL_GATE_1_GRAY;
5303 case 0x1683: /* gate (yellow) */
5304 element = EL_EM_GATE_2;
5307 case 0x1684: /* secret gate (yellow) */
5308 element = EL_GATE_2_GRAY;
5311 case 0x1685: /* gate (blue) */
5312 element = EL_EM_GATE_4;
5315 case 0x1686: /* secret gate (blue) */
5316 element = EL_GATE_4_GRAY;
5319 case 0x1687: /* gate (green) */
5320 element = EL_EM_GATE_3;
5323 case 0x1688: /* secret gate (green) */
5324 element = EL_GATE_3_GRAY;
5327 case 0x1689: /* gate (white) */
5328 element = EL_DC_GATE_WHITE;
5331 case 0x168a: /* secret gate (white) */
5332 element = EL_DC_GATE_WHITE_GRAY;
5335 case 0x168b: /* secret gate (no key) */
5336 element = EL_DC_GATE_FAKE_GRAY;
5340 element = EL_ROBOT_WHEEL;
5344 element = EL_DC_TIMEGATE_SWITCH;
5348 element = EL_ACID_POOL_BOTTOM;
5352 element = EL_ACID_POOL_TOPLEFT;
5356 element = EL_ACID_POOL_TOPRIGHT;
5360 element = EL_ACID_POOL_BOTTOMLEFT;
5364 element = EL_ACID_POOL_BOTTOMRIGHT;
5368 element = EL_STEELWALL;
5372 element = EL_STEELWALL_SLIPPERY;
5375 case 0x1695: /* steel wall (not round) */
5376 element = EL_STEELWALL;
5379 case 0x1696: /* steel wall (left) */
5380 element = EL_DC_STEELWALL_1_LEFT;
5383 case 0x1697: /* steel wall (bottom) */
5384 element = EL_DC_STEELWALL_1_BOTTOM;
5387 case 0x1698: /* steel wall (right) */
5388 element = EL_DC_STEELWALL_1_RIGHT;
5391 case 0x1699: /* steel wall (top) */
5392 element = EL_DC_STEELWALL_1_TOP;
5395 case 0x169a: /* steel wall (left/bottom) */
5396 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5399 case 0x169b: /* steel wall (right/bottom) */
5400 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5403 case 0x169c: /* steel wall (right/top) */
5404 element = EL_DC_STEELWALL_1_TOPRIGHT;
5407 case 0x169d: /* steel wall (left/top) */
5408 element = EL_DC_STEELWALL_1_TOPLEFT;
5411 case 0x169e: /* steel wall (right/bottom small) */
5412 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5415 case 0x169f: /* steel wall (left/bottom small) */
5416 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5419 case 0x16a0: /* steel wall (right/top small) */
5420 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5423 case 0x16a1: /* steel wall (left/top small) */
5424 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5427 case 0x16a2: /* steel wall (left/right) */
5428 element = EL_DC_STEELWALL_1_VERTICAL;
5431 case 0x16a3: /* steel wall (top/bottom) */
5432 element = EL_DC_STEELWALL_1_HORIZONTAL;
5435 case 0x16a4: /* steel wall 2 (left end) */
5436 element = EL_DC_STEELWALL_2_LEFT;
5439 case 0x16a5: /* steel wall 2 (right end) */
5440 element = EL_DC_STEELWALL_2_RIGHT;
5443 case 0x16a6: /* steel wall 2 (top end) */
5444 element = EL_DC_STEELWALL_2_TOP;
5447 case 0x16a7: /* steel wall 2 (bottom end) */
5448 element = EL_DC_STEELWALL_2_BOTTOM;
5451 case 0x16a8: /* steel wall 2 (left/right) */
5452 element = EL_DC_STEELWALL_2_HORIZONTAL;
5455 case 0x16a9: /* steel wall 2 (up/down) */
5456 element = EL_DC_STEELWALL_2_VERTICAL;
5459 case 0x16aa: /* steel wall 2 (mid) */
5460 element = EL_DC_STEELWALL_2_MIDDLE;
5464 element = EL_SIGN_EXCLAMATION;
5468 element = EL_SIGN_RADIOACTIVITY;
5472 element = EL_SIGN_STOP;
5476 element = EL_SIGN_WHEELCHAIR;
5480 element = EL_SIGN_PARKING;
5484 element = EL_SIGN_NO_ENTRY;
5488 element = EL_SIGN_HEART;
5492 element = EL_SIGN_GIVE_WAY;
5496 element = EL_SIGN_ENTRY_FORBIDDEN;
5500 element = EL_SIGN_EMERGENCY_EXIT;
5504 element = EL_SIGN_YIN_YANG;
5508 element = EL_WALL_EMERALD;
5512 element = EL_WALL_DIAMOND;
5516 element = EL_WALL_PEARL;
5520 element = EL_WALL_CRYSTAL;
5524 element = EL_INVISIBLE_WALL;
5528 element = EL_INVISIBLE_STEELWALL;
5531 /* 0x16bc - 0x16cb: */
5532 /* EL_INVISIBLE_SAND */
5535 element = EL_LIGHT_SWITCH;
5539 element = EL_ENVELOPE_1;
5543 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5544 element = EL_DIAMOND;
5545 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5546 element = EL_EMERALD;
5547 else if (element >= 0x157c && element <= 0x158b)
5549 else if (element >= 0x1590 && element <= 0x159f)
5550 element = EL_DC_LANDMINE;
5551 else if (element >= 0x16bc && element <= 0x16cb)
5552 element = EL_INVISIBLE_SAND;
5555 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5556 element = EL_UNKNOWN;
5561 return getMappedElement(element);
5564 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5567 byte header[DC_LEVEL_HEADER_SIZE];
5569 int envelope_header_pos = 62;
5570 int envelope_content_pos = 94;
5571 int level_name_pos = 251;
5572 int level_author_pos = 292;
5573 int envelope_header_len;
5574 int envelope_content_len;
5576 int level_author_len;
5578 int num_yamyam_contents;
5581 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5583 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5585 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5587 header[i * 2 + 0] = header_word >> 8;
5588 header[i * 2 + 1] = header_word & 0xff;
5591 /* read some values from level header to check level decoding integrity */
5592 fieldx = header[6] | (header[7] << 8);
5593 fieldy = header[8] | (header[9] << 8);
5594 num_yamyam_contents = header[60] | (header[61] << 8);
5596 /* do some simple sanity checks to ensure that level was correctly decoded */
5597 if (fieldx < 1 || fieldx > 256 ||
5598 fieldy < 1 || fieldy > 256 ||
5599 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5601 level->no_valid_file = TRUE;
5603 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5608 /* maximum envelope header size is 31 bytes */
5609 envelope_header_len = header[envelope_header_pos];
5610 /* maximum envelope content size is 110 (156?) bytes */
5611 envelope_content_len = header[envelope_content_pos];
5613 /* maximum level title size is 40 bytes */
5614 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5615 /* maximum level author size is 30 (51?) bytes */
5616 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5620 for (i = 0; i < envelope_header_len; i++)
5621 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5622 level->envelope[0].text[envelope_size++] =
5623 header[envelope_header_pos + 1 + i];
5625 if (envelope_header_len > 0 && envelope_content_len > 0)
5627 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5628 level->envelope[0].text[envelope_size++] = '\n';
5629 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5630 level->envelope[0].text[envelope_size++] = '\n';
5633 for (i = 0; i < envelope_content_len; i++)
5634 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5635 level->envelope[0].text[envelope_size++] =
5636 header[envelope_content_pos + 1 + i];
5638 level->envelope[0].text[envelope_size] = '\0';
5640 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5641 level->envelope[0].ysize = 10;
5642 level->envelope[0].autowrap = TRUE;
5643 level->envelope[0].centered = TRUE;
5645 for (i = 0; i < level_name_len; i++)
5646 level->name[i] = header[level_name_pos + 1 + i];
5647 level->name[level_name_len] = '\0';
5649 for (i = 0; i < level_author_len; i++)
5650 level->author[i] = header[level_author_pos + 1 + i];
5651 level->author[level_author_len] = '\0';
5653 num_yamyam_contents = header[60] | (header[61] << 8);
5654 level->num_yamyam_contents =
5655 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5657 for (i = 0; i < num_yamyam_contents; i++)
5659 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5661 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5662 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5664 if (i < MAX_ELEMENT_CONTENTS)
5665 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5669 fieldx = header[6] | (header[7] << 8);
5670 fieldy = header[8] | (header[9] << 8);
5671 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5672 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5674 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5676 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5677 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5679 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5680 level->field[x][y] = getMappedElement_DC(element_dc);
5683 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5684 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5685 level->field[x][y] = EL_PLAYER_1;
5687 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5688 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5689 level->field[x][y] = EL_PLAYER_2;
5691 level->gems_needed = header[18] | (header[19] << 8);
5693 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5694 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5695 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5696 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5697 level->score[SC_NUT] = header[28] | (header[29] << 8);
5698 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5699 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5700 level->score[SC_BUG] = header[34] | (header[35] << 8);
5701 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5702 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5703 level->score[SC_KEY] = header[40] | (header[41] << 8);
5704 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5706 level->time = header[44] | (header[45] << 8);
5708 level->amoeba_speed = header[46] | (header[47] << 8);
5709 level->time_light = header[48] | (header[49] << 8);
5710 level->time_timegate = header[50] | (header[51] << 8);
5711 level->time_wheel = header[52] | (header[53] << 8);
5712 level->time_magic_wall = header[54] | (header[55] << 8);
5713 level->extra_time = header[56] | (header[57] << 8);
5714 level->shield_normal_time = header[58] | (header[59] << 8);
5716 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5717 can slip down from flat walls, like normal walls and steel walls */
5718 level->em_slippery_gems = TRUE;
5721 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5722 struct LevelFileInfo *level_file_info,
5723 boolean level_info_only)
5725 char *filename = level_file_info->filename;
5727 int num_magic_bytes = 8;
5728 char magic_bytes[num_magic_bytes + 1];
5729 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5731 if (!(file = openFile(filename, MODE_READ)))
5733 level->no_valid_file = TRUE;
5735 if (!level_info_only)
5736 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5741 // fseek(file, 0x0000, SEEK_SET);
5743 if (level_file_info->packed)
5745 /* read "magic bytes" from start of file */
5746 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5747 magic_bytes[0] = '\0';
5749 /* check "magic bytes" for correct file format */
5750 if (!strPrefix(magic_bytes, "DC2"))
5752 level->no_valid_file = TRUE;
5754 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5760 if (strPrefix(magic_bytes, "DC2Win95") ||
5761 strPrefix(magic_bytes, "DC2Win98"))
5763 int position_first_level = 0x00fa;
5764 int extra_bytes = 4;
5767 /* advance file stream to first level inside the level package */
5768 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5770 /* each block of level data is followed by block of non-level data */
5771 num_levels_to_skip *= 2;
5773 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5774 while (num_levels_to_skip >= 0)
5776 /* advance file stream to next level inside the level package */
5777 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5779 level->no_valid_file = TRUE;
5781 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5787 /* skip apparently unused extra bytes following each level */
5788 ReadUnusedBytesFromFile(file, extra_bytes);
5790 /* read size of next level in level package */
5791 skip_bytes = getFile32BitLE(file);
5793 num_levels_to_skip--;
5798 level->no_valid_file = TRUE;
5800 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5807 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5813 /* ------------------------------------------------------------------------- */
5814 /* functions for loading SB level */
5815 /* ------------------------------------------------------------------------- */
5817 int getMappedElement_SB(int element_ascii, boolean use_ces)
5825 sb_element_mapping[] =
5827 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5828 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5829 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5830 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5831 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5832 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5833 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5834 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5841 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5842 if (element_ascii == sb_element_mapping[i].ascii)
5843 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5845 return EL_UNDEFINED;
5848 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5849 struct LevelFileInfo *level_file_info,
5850 boolean level_info_only)
5852 char *filename = level_file_info->filename;
5853 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5854 char last_comment[MAX_LINE_LEN];
5855 char level_name[MAX_LINE_LEN];
5858 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5859 boolean read_continued_line = FALSE;
5860 boolean reading_playfield = FALSE;
5861 boolean got_valid_playfield_line = FALSE;
5862 boolean invalid_playfield_char = FALSE;
5863 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5864 int file_level_nr = 0;
5866 int x = 0, y = 0; /* initialized to make compilers happy */
5868 last_comment[0] = '\0';
5869 level_name[0] = '\0';
5871 if (!(file = openFile(filename, MODE_READ)))
5873 level->no_valid_file = TRUE;
5875 if (!level_info_only)
5876 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5881 while (!checkEndOfFile(file))
5883 /* level successfully read, but next level may follow here */
5884 if (!got_valid_playfield_line && reading_playfield)
5886 /* read playfield from single level file -- skip remaining file */
5887 if (!level_file_info->packed)
5890 if (file_level_nr >= num_levels_to_skip)
5895 last_comment[0] = '\0';
5896 level_name[0] = '\0';
5898 reading_playfield = FALSE;
5901 got_valid_playfield_line = FALSE;
5903 /* read next line of input file */
5904 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5907 /* check if line was completely read and is terminated by line break */
5908 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5911 /* cut trailing line break (this can be newline and/or carriage return) */
5912 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5913 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5916 /* copy raw input line for later use (mainly debugging output) */
5917 strcpy(line_raw, line);
5919 if (read_continued_line)
5921 /* append new line to existing line, if there is enough space */
5922 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5923 strcat(previous_line, line_ptr);
5925 strcpy(line, previous_line); /* copy storage buffer to line */
5927 read_continued_line = FALSE;
5930 /* if the last character is '\', continue at next line */
5931 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5933 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5934 strcpy(previous_line, line); /* copy line to storage buffer */
5936 read_continued_line = TRUE;
5941 /* skip empty lines */
5942 if (line[0] == '\0')
5945 /* extract comment text from comment line */
5948 for (line_ptr = line; *line_ptr; line_ptr++)
5949 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5952 strcpy(last_comment, line_ptr);
5957 /* extract level title text from line containing level title */
5958 if (line[0] == '\'')
5960 strcpy(level_name, &line[1]);
5962 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5963 level_name[strlen(level_name) - 1] = '\0';
5968 /* skip lines containing only spaces (or empty lines) */
5969 for (line_ptr = line; *line_ptr; line_ptr++)
5970 if (*line_ptr != ' ')
5972 if (*line_ptr == '\0')
5975 /* at this point, we have found a line containing part of a playfield */
5977 got_valid_playfield_line = TRUE;
5979 if (!reading_playfield)
5981 reading_playfield = TRUE;
5982 invalid_playfield_char = FALSE;
5984 for (x = 0; x < MAX_LEV_FIELDX; x++)
5985 for (y = 0; y < MAX_LEV_FIELDY; y++)
5986 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5991 /* start with topmost tile row */
5995 /* skip playfield line if larger row than allowed */
5996 if (y >= MAX_LEV_FIELDY)
5999 /* start with leftmost tile column */
6002 /* read playfield elements from line */
6003 for (line_ptr = line; *line_ptr; line_ptr++)
6005 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6007 /* stop parsing playfield line if larger column than allowed */
6008 if (x >= MAX_LEV_FIELDX)
6011 if (mapped_sb_element == EL_UNDEFINED)
6013 invalid_playfield_char = TRUE;
6018 level->field[x][y] = mapped_sb_element;
6020 /* continue with next tile column */
6023 level->fieldx = MAX(x, level->fieldx);
6026 if (invalid_playfield_char)
6028 /* if first playfield line, treat invalid lines as comment lines */
6030 reading_playfield = FALSE;
6035 /* continue with next tile row */
6043 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6044 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6046 if (!reading_playfield)
6048 level->no_valid_file = TRUE;
6050 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6055 if (*level_name != '\0')
6057 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6058 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6060 else if (*last_comment != '\0')
6062 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6063 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6067 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6070 /* set all empty fields beyond the border walls to invisible steel wall */
6071 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6073 if ((x == 0 || x == level->fieldx - 1 ||
6074 y == 0 || y == level->fieldy - 1) &&
6075 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6076 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6077 level->field, level->fieldx, level->fieldy);
6080 /* set special level settings for Sokoban levels */
6083 level->use_step_counter = TRUE;
6085 if (load_xsb_to_ces)
6087 /* special global settings can now be set in level template */
6089 level->use_custom_template = TRUE;
6094 /* ------------------------------------------------------------------------- */
6095 /* functions for handling native levels */
6096 /* ------------------------------------------------------------------------- */
6098 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6099 struct LevelFileInfo *level_file_info,
6100 boolean level_info_only)
6102 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6103 level->no_valid_file = TRUE;
6106 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6107 struct LevelFileInfo *level_file_info,
6108 boolean level_info_only)
6112 /* determine position of requested level inside level package */
6113 if (level_file_info->packed)
6114 pos = level_file_info->nr - leveldir_current->first_level;
6116 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6117 level->no_valid_file = TRUE;
6120 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6121 struct LevelFileInfo *level_file_info,
6122 boolean level_info_only)
6124 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6125 level->no_valid_file = TRUE;
6128 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6130 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6131 CopyNativeLevel_RND_to_EM(level);
6132 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6133 CopyNativeLevel_RND_to_SP(level);
6134 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6135 CopyNativeLevel_RND_to_MM(level);
6138 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6140 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6141 CopyNativeLevel_EM_to_RND(level);
6142 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6143 CopyNativeLevel_SP_to_RND(level);
6144 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6145 CopyNativeLevel_MM_to_RND(level);
6148 void SaveNativeLevel(struct LevelInfo *level)
6150 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6152 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6153 char *filename = getLevelFilenameFromBasename(basename);
6155 CopyNativeLevel_RND_to_SP(level);
6156 CopyNativeTape_RND_to_SP(level);
6158 SaveNativeLevel_SP(filename);
6163 /* ------------------------------------------------------------------------- */
6164 /* functions for loading generic level */
6165 /* ------------------------------------------------------------------------- */
6167 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6168 struct LevelFileInfo *level_file_info,
6169 boolean level_info_only)
6171 /* always start with reliable default values */
6172 setLevelInfoToDefaults(level, level_info_only, TRUE);
6174 switch (level_file_info->type)
6176 case LEVEL_FILE_TYPE_RND:
6177 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6180 case LEVEL_FILE_TYPE_EM:
6181 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6182 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6185 case LEVEL_FILE_TYPE_SP:
6186 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6187 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6190 case LEVEL_FILE_TYPE_MM:
6191 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6192 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6195 case LEVEL_FILE_TYPE_DC:
6196 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6199 case LEVEL_FILE_TYPE_SB:
6200 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6204 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6208 /* if level file is invalid, restore level structure to default values */
6209 if (level->no_valid_file)
6210 setLevelInfoToDefaults(level, level_info_only, FALSE);
6212 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6213 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6215 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6216 CopyNativeLevel_Native_to_RND(level);
6219 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6221 static struct LevelFileInfo level_file_info;
6223 /* always start with reliable default values */
6224 setFileInfoToDefaults(&level_file_info);
6226 level_file_info.nr = 0; /* unknown level number */
6227 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6229 setString(&level_file_info.filename, filename);
6231 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6234 static void LoadLevel_InitVersion(struct LevelInfo *level)
6238 if (leveldir_current == NULL) /* only when dumping level */
6241 /* all engine modifications also valid for levels which use latest engine */
6242 if (level->game_version < VERSION_IDENT(3,2,0,5))
6244 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6245 level->score[SC_TIME_BONUS] /= 10;
6248 if (leveldir_current->latest_engine)
6250 /* ---------- use latest game engine ----------------------------------- */
6252 /* For all levels which are forced to use the latest game engine version
6253 (normally all but user contributed, private and undefined levels), set
6254 the game engine version to the actual version; this allows for actual
6255 corrections in the game engine to take effect for existing, converted
6256 levels (from "classic" or other existing games) to make the emulation
6257 of the corresponding game more accurate, while (hopefully) not breaking
6258 existing levels created from other players. */
6260 level->game_version = GAME_VERSION_ACTUAL;
6262 /* Set special EM style gems behaviour: EM style gems slip down from
6263 normal, steel and growing wall. As this is a more fundamental change,
6264 it seems better to set the default behaviour to "off" (as it is more
6265 natural) and make it configurable in the level editor (as a property
6266 of gem style elements). Already existing converted levels (neither
6267 private nor contributed levels) are changed to the new behaviour. */
6269 if (level->file_version < FILE_VERSION_2_0)
6270 level->em_slippery_gems = TRUE;
6275 /* ---------- use game engine the level was created with ----------------- */
6277 /* For all levels which are not forced to use the latest game engine
6278 version (normally user contributed, private and undefined levels),
6279 use the version of the game engine the levels were created for.
6281 Since 2.0.1, the game engine version is now directly stored
6282 in the level file (chunk "VERS"), so there is no need anymore
6283 to set the game version from the file version (except for old,
6284 pre-2.0 levels, where the game version is still taken from the
6285 file format version used to store the level -- see above). */
6287 /* player was faster than enemies in 1.0.0 and before */
6288 if (level->file_version == FILE_VERSION_1_0)
6289 for (i = 0; i < MAX_PLAYERS; i++)
6290 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6292 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6293 if (level->game_version == VERSION_IDENT(2,0,1,0))
6294 level->em_slippery_gems = TRUE;
6296 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6297 if (level->game_version < VERSION_IDENT(2,2,0,0))
6298 level->use_spring_bug = TRUE;
6300 if (level->game_version < VERSION_IDENT(3,2,0,5))
6302 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6303 level->use_time_orb_bug = TRUE;
6305 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6306 level->block_snap_field = FALSE;
6308 /* extra time score was same value as time left score before 3.2.0-5 */
6309 level->extra_time_score = level->score[SC_TIME_BONUS];
6312 if (level->game_version < VERSION_IDENT(3,2,0,7))
6314 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6315 level->continuous_snapping = FALSE;
6318 /* only few elements were able to actively move into acid before 3.1.0 */
6319 /* trigger settings did not exist before 3.1.0; set to default "any" */
6320 if (level->game_version < VERSION_IDENT(3,1,0,0))
6322 /* correct "can move into acid" settings (all zero in old levels) */
6324 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6325 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6327 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6328 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6329 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6330 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6332 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6333 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6335 /* correct trigger settings (stored as zero == "none" in old levels) */
6337 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6339 int element = EL_CUSTOM_START + i;
6340 struct ElementInfo *ei = &element_info[element];
6342 for (j = 0; j < ei->num_change_pages; j++)
6344 struct ElementChangeInfo *change = &ei->change_page[j];
6346 change->trigger_player = CH_PLAYER_ANY;
6347 change->trigger_page = CH_PAGE_ANY;
6352 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6354 int element = EL_CUSTOM_256;
6355 struct ElementInfo *ei = &element_info[element];
6356 struct ElementChangeInfo *change = &ei->change_page[0];
6358 /* This is needed to fix a problem that was caused by a bugfix in function
6359 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6360 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6361 not replace walkable elements, but instead just placed the player on it,
6362 without placing the Sokoban field under the player). Unfortunately, this
6363 breaks "Snake Bite" style levels when the snake is halfway through a door
6364 that just closes (the snake head is still alive and can be moved in this
6365 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6366 player (without Sokoban element) which then gets killed as designed). */
6368 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6369 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6370 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6371 change->target_element = EL_PLAYER_1;
6374 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6375 if (level->game_version < VERSION_IDENT(3,2,5,0))
6377 /* This is needed to fix a problem that was caused by a bugfix in function
6378 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6379 corrects the behaviour when a custom element changes to another custom
6380 element with a higher element number that has change actions defined.
6381 Normally, only one change per frame is allowed for custom elements.
6382 Therefore, it is checked if a custom element already changed in the
6383 current frame; if it did, subsequent changes are suppressed.
6384 Unfortunately, this is only checked for element changes, but not for
6385 change actions, which are still executed. As the function above loops
6386 through all custom elements from lower to higher, an element change
6387 resulting in a lower CE number won't be checked again, while a target
6388 element with a higher number will also be checked, and potential change
6389 actions will get executed for this CE, too (which is wrong), while
6390 further changes are ignored (which is correct). As this bugfix breaks
6391 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6392 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6393 behaviour for existing levels and tapes that make use of this bug */
6395 level->use_action_after_change_bug = TRUE;
6398 /* not centering level after relocating player was default only in 3.2.3 */
6399 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6400 level->shifted_relocation = TRUE;
6402 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6403 if (level->game_version < VERSION_IDENT(3,2,6,0))
6404 level->em_explodes_by_fire = TRUE;
6406 /* levels were solved by the first player entering an exit up to 4.1.0.0 */
6407 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6408 level->solved_by_one_player = TRUE;
6411 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6415 /* map elements that have changed in newer versions */
6416 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6417 level->game_version);
6418 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6419 for (x = 0; x < 3; x++)
6420 for (y = 0; y < 3; y++)
6421 level->yamyam_content[i].e[x][y] =
6422 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6423 level->game_version);
6427 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6431 /* map custom element change events that have changed in newer versions
6432 (these following values were accidentally changed in version 3.0.1)
6433 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6434 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6436 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6438 int element = EL_CUSTOM_START + i;
6440 /* order of checking and copying events to be mapped is important */
6441 /* (do not change the start and end value -- they are constant) */
6442 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6444 if (HAS_CHANGE_EVENT(element, j - 2))
6446 SET_CHANGE_EVENT(element, j - 2, FALSE);
6447 SET_CHANGE_EVENT(element, j, TRUE);
6451 /* order of checking and copying events to be mapped is important */
6452 /* (do not change the start and end value -- they are constant) */
6453 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6455 if (HAS_CHANGE_EVENT(element, j - 1))
6457 SET_CHANGE_EVENT(element, j - 1, FALSE);
6458 SET_CHANGE_EVENT(element, j, TRUE);
6464 /* initialize "can_change" field for old levels with only one change page */
6465 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6469 int element = EL_CUSTOM_START + i;
6471 if (CAN_CHANGE(element))
6472 element_info[element].change->can_change = TRUE;
6476 /* correct custom element values (for old levels without these options) */
6477 if (level->game_version < VERSION_IDENT(3,1,1,0))
6479 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6481 int element = EL_CUSTOM_START + i;
6482 struct ElementInfo *ei = &element_info[element];
6484 if (ei->access_direction == MV_NO_DIRECTION)
6485 ei->access_direction = MV_ALL_DIRECTIONS;
6489 /* correct custom element values (fix invalid values for all versions) */
6492 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6494 int element = EL_CUSTOM_START + i;
6495 struct ElementInfo *ei = &element_info[element];
6497 for (j = 0; j < ei->num_change_pages; j++)
6499 struct ElementChangeInfo *change = &ei->change_page[j];
6501 if (change->trigger_player == CH_PLAYER_NONE)
6502 change->trigger_player = CH_PLAYER_ANY;
6504 if (change->trigger_side == CH_SIDE_NONE)
6505 change->trigger_side = CH_SIDE_ANY;
6510 /* initialize "can_explode" field for old levels which did not store this */
6511 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6512 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6514 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6516 int element = EL_CUSTOM_START + i;
6518 if (EXPLODES_1X1_OLD(element))
6519 element_info[element].explosion_type = EXPLODES_1X1;
6521 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6522 EXPLODES_SMASHED(element) ||
6523 EXPLODES_IMPACT(element)));
6527 /* correct previously hard-coded move delay values for maze runner style */
6528 if (level->game_version < VERSION_IDENT(3,1,1,0))
6530 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6532 int element = EL_CUSTOM_START + i;
6534 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6536 /* previously hard-coded and therefore ignored */
6537 element_info[element].move_delay_fixed = 9;
6538 element_info[element].move_delay_random = 0;
6543 /* set some other uninitialized values of custom elements in older levels */
6544 if (level->game_version < VERSION_IDENT(3,1,0,0))
6546 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6548 int element = EL_CUSTOM_START + i;
6550 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6552 element_info[element].explosion_delay = 17;
6553 element_info[element].ignition_delay = 8;
6558 static void LoadLevel_InitElements(struct LevelInfo *level)
6560 LoadLevel_InitStandardElements(level);
6562 if (level->file_has_custom_elements)
6563 LoadLevel_InitCustomElements(level);
6565 /* initialize element properties for level editor etc. */
6566 InitElementPropertiesEngine(level->game_version);
6567 InitElementPropertiesGfxElement();
6570 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6574 /* map elements that have changed in newer versions */
6575 for (y = 0; y < level->fieldy; y++)
6576 for (x = 0; x < level->fieldx; x++)
6577 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6578 level->game_version);
6580 /* clear unused playfield data (nicer if level gets resized in editor) */
6581 for (x = 0; x < MAX_LEV_FIELDX; x++)
6582 for (y = 0; y < MAX_LEV_FIELDY; y++)
6583 if (x >= level->fieldx || y >= level->fieldy)
6584 level->field[x][y] = EL_EMPTY;
6586 /* copy elements to runtime playfield array */
6587 for (x = 0; x < MAX_LEV_FIELDX; x++)
6588 for (y = 0; y < MAX_LEV_FIELDY; y++)
6589 Feld[x][y] = level->field[x][y];
6591 /* initialize level size variables for faster access */
6592 lev_fieldx = level->fieldx;
6593 lev_fieldy = level->fieldy;
6595 /* determine border element for this level */
6596 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6597 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6602 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6604 struct LevelFileInfo *level_file_info = &level->file_info;
6606 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6607 CopyNativeLevel_RND_to_Native(level);
6610 static void LoadLevelTemplate_LoadAndInit(void)
6612 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6614 LoadLevel_InitVersion(&level_template);
6615 LoadLevel_InitElements(&level_template);
6617 ActivateLevelTemplate();
6620 void LoadLevelTemplate(int nr)
6622 if (!fileExists(getGlobalLevelTemplateFilename()))
6624 Error(ERR_WARN, "no level template found for this level");
6629 setLevelFileInfo(&level_template.file_info, nr);
6631 LoadLevelTemplate_LoadAndInit();
6634 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6636 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6638 LoadLevelTemplate_LoadAndInit();
6641 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6643 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6645 if (level.use_custom_template)
6647 if (network_level != NULL)
6648 LoadNetworkLevelTemplate(network_level);
6650 LoadLevelTemplate(-1);
6653 LoadLevel_InitVersion(&level);
6654 LoadLevel_InitElements(&level);
6655 LoadLevel_InitPlayfield(&level);
6657 LoadLevel_InitNativeEngines(&level);
6660 void LoadLevel(int nr)
6662 SetLevelSetInfo(leveldir_current->identifier, nr);
6664 setLevelFileInfo(&level.file_info, nr);
6666 LoadLevel_LoadAndInit(NULL);
6669 void LoadLevelInfoOnly(int nr)
6671 setLevelFileInfo(&level.file_info, nr);
6673 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6676 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6678 SetLevelSetInfo(network_level->leveldir_identifier,
6679 network_level->file_info.nr);
6681 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6683 LoadLevel_LoadAndInit(network_level);
6686 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6690 chunk_size += putFileVersion(file, level->file_version);
6691 chunk_size += putFileVersion(file, level->game_version);
6696 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6700 chunk_size += putFile16BitBE(file, level->creation_date.year);
6701 chunk_size += putFile8Bit(file, level->creation_date.month);
6702 chunk_size += putFile8Bit(file, level->creation_date.day);
6707 #if ENABLE_HISTORIC_CHUNKS
6708 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6712 putFile8Bit(file, level->fieldx);
6713 putFile8Bit(file, level->fieldy);
6715 putFile16BitBE(file, level->time);
6716 putFile16BitBE(file, level->gems_needed);
6718 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6719 putFile8Bit(file, level->name[i]);
6721 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6722 putFile8Bit(file, level->score[i]);
6724 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6725 for (y = 0; y < 3; y++)
6726 for (x = 0; x < 3; x++)
6727 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6728 level->yamyam_content[i].e[x][y]));
6729 putFile8Bit(file, level->amoeba_speed);
6730 putFile8Bit(file, level->time_magic_wall);
6731 putFile8Bit(file, level->time_wheel);
6732 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6733 level->amoeba_content));
6734 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6735 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6736 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6737 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6739 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6741 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6742 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6743 putFile32BitBE(file, level->can_move_into_acid_bits);
6744 putFile8Bit(file, level->dont_collide_with_bits);
6746 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6747 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6749 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6750 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6751 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6753 putFile8Bit(file, level->game_engine_type);
6755 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6759 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6764 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6765 chunk_size += putFile8Bit(file, level->name[i]);
6770 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6775 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6776 chunk_size += putFile8Bit(file, level->author[i]);
6781 #if ENABLE_HISTORIC_CHUNKS
6782 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6787 for (y = 0; y < level->fieldy; y++)
6788 for (x = 0; x < level->fieldx; x++)
6789 if (level->encoding_16bit_field)
6790 chunk_size += putFile16BitBE(file, level->field[x][y]);
6792 chunk_size += putFile8Bit(file, level->field[x][y]);
6798 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6803 for (y = 0; y < level->fieldy; y++)
6804 for (x = 0; x < level->fieldx; x++)
6805 chunk_size += putFile16BitBE(file, level->field[x][y]);
6810 #if ENABLE_HISTORIC_CHUNKS
6811 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6815 putFile8Bit(file, EL_YAMYAM);
6816 putFile8Bit(file, level->num_yamyam_contents);
6817 putFile8Bit(file, 0);
6818 putFile8Bit(file, 0);
6820 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6821 for (y = 0; y < 3; y++)
6822 for (x = 0; x < 3; x++)
6823 if (level->encoding_16bit_field)
6824 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6826 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6830 #if ENABLE_HISTORIC_CHUNKS
6831 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6834 int num_contents, content_xsize, content_ysize;
6835 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6837 if (element == EL_YAMYAM)
6839 num_contents = level->num_yamyam_contents;
6843 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6844 for (y = 0; y < 3; y++)
6845 for (x = 0; x < 3; x++)
6846 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6848 else if (element == EL_BD_AMOEBA)
6854 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6855 for (y = 0; y < 3; y++)
6856 for (x = 0; x < 3; x++)
6857 content_array[i][x][y] = EL_EMPTY;
6858 content_array[0][0][0] = level->amoeba_content;
6862 /* chunk header already written -- write empty chunk data */
6863 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6865 Error(ERR_WARN, "cannot save content for element '%d'", element);
6869 putFile16BitBE(file, element);
6870 putFile8Bit(file, num_contents);
6871 putFile8Bit(file, content_xsize);
6872 putFile8Bit(file, content_ysize);
6874 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6876 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6877 for (y = 0; y < 3; y++)
6878 for (x = 0; x < 3; x++)
6879 putFile16BitBE(file, content_array[i][x][y]);
6883 #if ENABLE_HISTORIC_CHUNKS
6884 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6886 int envelope_nr = element - EL_ENVELOPE_1;
6887 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6891 chunk_size += putFile16BitBE(file, element);
6892 chunk_size += putFile16BitBE(file, envelope_len);
6893 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6894 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6896 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6897 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6899 for (i = 0; i < envelope_len; i++)
6900 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6906 #if ENABLE_HISTORIC_CHUNKS
6907 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6908 int num_changed_custom_elements)
6912 putFile16BitBE(file, num_changed_custom_elements);
6914 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6916 int element = EL_CUSTOM_START + i;
6918 struct ElementInfo *ei = &element_info[element];
6920 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6922 if (check < num_changed_custom_elements)
6924 putFile16BitBE(file, element);
6925 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6932 if (check != num_changed_custom_elements) /* should not happen */
6933 Error(ERR_WARN, "inconsistent number of custom element properties");
6937 #if ENABLE_HISTORIC_CHUNKS
6938 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6939 int num_changed_custom_elements)
6943 putFile16BitBE(file, num_changed_custom_elements);
6945 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6947 int element = EL_CUSTOM_START + i;
6949 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6951 if (check < num_changed_custom_elements)
6953 putFile16BitBE(file, element);
6954 putFile16BitBE(file, element_info[element].change->target_element);
6961 if (check != num_changed_custom_elements) /* should not happen */
6962 Error(ERR_WARN, "inconsistent number of custom target elements");
6966 #if ENABLE_HISTORIC_CHUNKS
6967 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6968 int num_changed_custom_elements)
6970 int i, j, x, y, check = 0;
6972 putFile16BitBE(file, num_changed_custom_elements);
6974 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6976 int element = EL_CUSTOM_START + i;
6977 struct ElementInfo *ei = &element_info[element];
6979 if (ei->modified_settings)
6981 if (check < num_changed_custom_elements)
6983 putFile16BitBE(file, element);
6985 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6986 putFile8Bit(file, ei->description[j]);
6988 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6990 /* some free bytes for future properties and padding */
6991 WriteUnusedBytesToFile(file, 7);
6993 putFile8Bit(file, ei->use_gfx_element);
6994 putFile16BitBE(file, ei->gfx_element_initial);
6996 putFile8Bit(file, ei->collect_score_initial);
6997 putFile8Bit(file, ei->collect_count_initial);
6999 putFile16BitBE(file, ei->push_delay_fixed);
7000 putFile16BitBE(file, ei->push_delay_random);
7001 putFile16BitBE(file, ei->move_delay_fixed);
7002 putFile16BitBE(file, ei->move_delay_random);
7004 putFile16BitBE(file, ei->move_pattern);
7005 putFile8Bit(file, ei->move_direction_initial);
7006 putFile8Bit(file, ei->move_stepsize);
7008 for (y = 0; y < 3; y++)
7009 for (x = 0; x < 3; x++)
7010 putFile16BitBE(file, ei->content.e[x][y]);
7012 putFile32BitBE(file, ei->change->events);
7014 putFile16BitBE(file, ei->change->target_element);
7016 putFile16BitBE(file, ei->change->delay_fixed);
7017 putFile16BitBE(file, ei->change->delay_random);
7018 putFile16BitBE(file, ei->change->delay_frames);
7020 putFile16BitBE(file, ei->change->initial_trigger_element);
7022 putFile8Bit(file, ei->change->explode);
7023 putFile8Bit(file, ei->change->use_target_content);
7024 putFile8Bit(file, ei->change->only_if_complete);
7025 putFile8Bit(file, ei->change->use_random_replace);
7027 putFile8Bit(file, ei->change->random_percentage);
7028 putFile8Bit(file, ei->change->replace_when);
7030 for (y = 0; y < 3; y++)
7031 for (x = 0; x < 3; x++)
7032 putFile16BitBE(file, ei->change->content.e[x][y]);
7034 putFile8Bit(file, ei->slippery_type);
7036 /* some free bytes for future properties and padding */
7037 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7044 if (check != num_changed_custom_elements) /* should not happen */
7045 Error(ERR_WARN, "inconsistent number of custom element properties");
7049 #if ENABLE_HISTORIC_CHUNKS
7050 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7052 struct ElementInfo *ei = &element_info[element];
7055 /* ---------- custom element base property values (96 bytes) ------------- */
7057 putFile16BitBE(file, element);
7059 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7060 putFile8Bit(file, ei->description[i]);
7062 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7064 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7066 putFile8Bit(file, ei->num_change_pages);
7068 putFile16BitBE(file, ei->ce_value_fixed_initial);
7069 putFile16BitBE(file, ei->ce_value_random_initial);
7070 putFile8Bit(file, ei->use_last_ce_value);
7072 putFile8Bit(file, ei->use_gfx_element);
7073 putFile16BitBE(file, ei->gfx_element_initial);
7075 putFile8Bit(file, ei->collect_score_initial);
7076 putFile8Bit(file, ei->collect_count_initial);
7078 putFile8Bit(file, ei->drop_delay_fixed);
7079 putFile8Bit(file, ei->push_delay_fixed);
7080 putFile8Bit(file, ei->drop_delay_random);
7081 putFile8Bit(file, ei->push_delay_random);
7082 putFile16BitBE(file, ei->move_delay_fixed);
7083 putFile16BitBE(file, ei->move_delay_random);
7085 /* bits 0 - 15 of "move_pattern" ... */
7086 putFile16BitBE(file, ei->move_pattern & 0xffff);
7087 putFile8Bit(file, ei->move_direction_initial);
7088 putFile8Bit(file, ei->move_stepsize);
7090 putFile8Bit(file, ei->slippery_type);
7092 for (y = 0; y < 3; y++)
7093 for (x = 0; x < 3; x++)
7094 putFile16BitBE(file, ei->content.e[x][y]);
7096 putFile16BitBE(file, ei->move_enter_element);
7097 putFile16BitBE(file, ei->move_leave_element);
7098 putFile8Bit(file, ei->move_leave_type);
7100 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7101 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7103 putFile8Bit(file, ei->access_direction);
7105 putFile8Bit(file, ei->explosion_delay);
7106 putFile8Bit(file, ei->ignition_delay);
7107 putFile8Bit(file, ei->explosion_type);
7109 /* some free bytes for future custom property values and padding */
7110 WriteUnusedBytesToFile(file, 1);
7112 /* ---------- change page property values (48 bytes) --------------------- */
7114 for (i = 0; i < ei->num_change_pages; i++)
7116 struct ElementChangeInfo *change = &ei->change_page[i];
7117 unsigned int event_bits;
7119 /* bits 0 - 31 of "has_event[]" ... */
7121 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7122 if (change->has_event[j])
7123 event_bits |= (1 << j);
7124 putFile32BitBE(file, event_bits);
7126 putFile16BitBE(file, change->target_element);
7128 putFile16BitBE(file, change->delay_fixed);
7129 putFile16BitBE(file, change->delay_random);
7130 putFile16BitBE(file, change->delay_frames);
7132 putFile16BitBE(file, change->initial_trigger_element);
7134 putFile8Bit(file, change->explode);
7135 putFile8Bit(file, change->use_target_content);
7136 putFile8Bit(file, change->only_if_complete);
7137 putFile8Bit(file, change->use_random_replace);
7139 putFile8Bit(file, change->random_percentage);
7140 putFile8Bit(file, change->replace_when);
7142 for (y = 0; y < 3; y++)
7143 for (x = 0; x < 3; x++)
7144 putFile16BitBE(file, change->target_content.e[x][y]);
7146 putFile8Bit(file, change->can_change);
7148 putFile8Bit(file, change->trigger_side);
7150 putFile8Bit(file, change->trigger_player);
7151 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7152 log_2(change->trigger_page)));
7154 putFile8Bit(file, change->has_action);
7155 putFile8Bit(file, change->action_type);
7156 putFile8Bit(file, change->action_mode);
7157 putFile16BitBE(file, change->action_arg);
7159 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7161 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7162 if (change->has_event[j])
7163 event_bits |= (1 << (j - 32));
7164 putFile8Bit(file, event_bits);
7169 #if ENABLE_HISTORIC_CHUNKS
7170 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7172 struct ElementInfo *ei = &element_info[element];
7173 struct ElementGroupInfo *group = ei->group;
7176 putFile16BitBE(file, element);
7178 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7179 putFile8Bit(file, ei->description[i]);
7181 putFile8Bit(file, group->num_elements);
7183 putFile8Bit(file, ei->use_gfx_element);
7184 putFile16BitBE(file, ei->gfx_element_initial);
7186 putFile8Bit(file, group->choice_mode);
7188 /* some free bytes for future values and padding */
7189 WriteUnusedBytesToFile(file, 3);
7191 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7192 putFile16BitBE(file, group->element[i]);
7196 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7197 boolean write_element)
7199 int save_type = entry->save_type;
7200 int data_type = entry->data_type;
7201 int conf_type = entry->conf_type;
7202 int byte_mask = conf_type & CONF_MASK_BYTES;
7203 int element = entry->element;
7204 int default_value = entry->default_value;
7206 boolean modified = FALSE;
7208 if (byte_mask != CONF_MASK_MULTI_BYTES)
7210 void *value_ptr = entry->value;
7211 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7214 /* check if any settings have been modified before saving them */
7215 if (value != default_value)
7218 /* do not save if explicitly told or if unmodified default settings */
7219 if ((save_type == SAVE_CONF_NEVER) ||
7220 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7224 num_bytes += putFile16BitBE(file, element);
7226 num_bytes += putFile8Bit(file, conf_type);
7227 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7228 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7229 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7232 else if (data_type == TYPE_STRING)
7234 char *default_string = entry->default_string;
7235 char *string = (char *)(entry->value);
7236 int string_length = strlen(string);
7239 /* check if any settings have been modified before saving them */
7240 if (!strEqual(string, default_string))
7243 /* do not save if explicitly told or if unmodified default settings */
7244 if ((save_type == SAVE_CONF_NEVER) ||
7245 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7249 num_bytes += putFile16BitBE(file, element);
7251 num_bytes += putFile8Bit(file, conf_type);
7252 num_bytes += putFile16BitBE(file, string_length);
7254 for (i = 0; i < string_length; i++)
7255 num_bytes += putFile8Bit(file, string[i]);
7257 else if (data_type == TYPE_ELEMENT_LIST)
7259 int *element_array = (int *)(entry->value);
7260 int num_elements = *(int *)(entry->num_entities);
7263 /* check if any settings have been modified before saving them */
7264 for (i = 0; i < num_elements; i++)
7265 if (element_array[i] != default_value)
7268 /* do not save if explicitly told or if unmodified default settings */
7269 if ((save_type == SAVE_CONF_NEVER) ||
7270 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7274 num_bytes += putFile16BitBE(file, element);
7276 num_bytes += putFile8Bit(file, conf_type);
7277 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7279 for (i = 0; i < num_elements; i++)
7280 num_bytes += putFile16BitBE(file, element_array[i]);
7282 else if (data_type == TYPE_CONTENT_LIST)
7284 struct Content *content = (struct Content *)(entry->value);
7285 int num_contents = *(int *)(entry->num_entities);
7288 /* check if any settings have been modified before saving them */
7289 for (i = 0; i < num_contents; i++)
7290 for (y = 0; y < 3; y++)
7291 for (x = 0; x < 3; x++)
7292 if (content[i].e[x][y] != default_value)
7295 /* do not save if explicitly told or if unmodified default settings */
7296 if ((save_type == SAVE_CONF_NEVER) ||
7297 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7301 num_bytes += putFile16BitBE(file, element);
7303 num_bytes += putFile8Bit(file, conf_type);
7304 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7306 for (i = 0; i < num_contents; i++)
7307 for (y = 0; y < 3; y++)
7308 for (x = 0; x < 3; x++)
7309 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7315 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7320 li = *level; /* copy level data into temporary buffer */
7322 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7323 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7328 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7333 li = *level; /* copy level data into temporary buffer */
7335 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7336 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7341 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7343 int envelope_nr = element - EL_ENVELOPE_1;
7347 chunk_size += putFile16BitBE(file, element);
7349 /* copy envelope data into temporary buffer */
7350 xx_envelope = level->envelope[envelope_nr];
7352 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7353 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7358 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7360 struct ElementInfo *ei = &element_info[element];
7364 chunk_size += putFile16BitBE(file, element);
7366 xx_ei = *ei; /* copy element data into temporary buffer */
7368 /* set default description string for this specific element */
7369 strcpy(xx_default_description, getDefaultElementDescription(ei));
7371 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7372 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7374 for (i = 0; i < ei->num_change_pages; i++)
7376 struct ElementChangeInfo *change = &ei->change_page[i];
7378 xx_current_change_page = i;
7380 xx_change = *change; /* copy change data into temporary buffer */
7383 setEventBitsFromEventFlags(change);
7385 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7386 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7393 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7395 struct ElementInfo *ei = &element_info[element];
7396 struct ElementGroupInfo *group = ei->group;
7400 chunk_size += putFile16BitBE(file, element);
7402 xx_ei = *ei; /* copy element data into temporary buffer */
7403 xx_group = *group; /* copy group data into temporary buffer */
7405 /* set default description string for this specific element */
7406 strcpy(xx_default_description, getDefaultElementDescription(ei));
7408 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7409 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7414 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7415 boolean save_as_template)
7421 if (!(file = fopen(filename, MODE_WRITE)))
7423 Error(ERR_WARN, "cannot save level file '%s'", filename);
7427 level->file_version = FILE_VERSION_ACTUAL;
7428 level->game_version = GAME_VERSION_ACTUAL;
7430 level->creation_date = getCurrentDate();
7432 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7433 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7435 chunk_size = SaveLevel_VERS(NULL, level);
7436 putFileChunkBE(file, "VERS", chunk_size);
7437 SaveLevel_VERS(file, level);
7439 chunk_size = SaveLevel_DATE(NULL, level);
7440 putFileChunkBE(file, "DATE", chunk_size);
7441 SaveLevel_DATE(file, level);
7443 chunk_size = SaveLevel_NAME(NULL, level);
7444 putFileChunkBE(file, "NAME", chunk_size);
7445 SaveLevel_NAME(file, level);
7447 chunk_size = SaveLevel_AUTH(NULL, level);
7448 putFileChunkBE(file, "AUTH", chunk_size);
7449 SaveLevel_AUTH(file, level);
7451 chunk_size = SaveLevel_INFO(NULL, level);
7452 putFileChunkBE(file, "INFO", chunk_size);
7453 SaveLevel_INFO(file, level);
7455 chunk_size = SaveLevel_BODY(NULL, level);
7456 putFileChunkBE(file, "BODY", chunk_size);
7457 SaveLevel_BODY(file, level);
7459 chunk_size = SaveLevel_ELEM(NULL, level);
7460 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7462 putFileChunkBE(file, "ELEM", chunk_size);
7463 SaveLevel_ELEM(file, level);
7466 for (i = 0; i < NUM_ENVELOPES; i++)
7468 int element = EL_ENVELOPE_1 + i;
7470 chunk_size = SaveLevel_NOTE(NULL, level, element);
7471 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7473 putFileChunkBE(file, "NOTE", chunk_size);
7474 SaveLevel_NOTE(file, level, element);
7478 /* if not using template level, check for non-default custom/group elements */
7479 if (!level->use_custom_template || save_as_template)
7481 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7483 int element = EL_CUSTOM_START + i;
7485 chunk_size = SaveLevel_CUSX(NULL, level, element);
7486 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7488 putFileChunkBE(file, "CUSX", chunk_size);
7489 SaveLevel_CUSX(file, level, element);
7493 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7495 int element = EL_GROUP_START + i;
7497 chunk_size = SaveLevel_GRPX(NULL, level, element);
7498 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7500 putFileChunkBE(file, "GRPX", chunk_size);
7501 SaveLevel_GRPX(file, level, element);
7508 SetFilePermissions(filename, PERMS_PRIVATE);
7511 void SaveLevel(int nr)
7513 char *filename = getDefaultLevelFilename(nr);
7515 SaveLevelFromFilename(&level, filename, FALSE);
7518 void SaveLevelTemplate(void)
7520 char *filename = getLocalLevelTemplateFilename();
7522 SaveLevelFromFilename(&level, filename, TRUE);
7525 boolean SaveLevelChecked(int nr)
7527 char *filename = getDefaultLevelFilename(nr);
7528 boolean new_level = !fileExists(filename);
7529 boolean level_saved = FALSE;
7531 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7536 Request("Level saved!", REQ_CONFIRM);
7544 void DumpLevel(struct LevelInfo *level)
7546 if (level->no_level_file || level->no_valid_file)
7548 Error(ERR_WARN, "cannot dump -- no valid level file found");
7554 Print("Level xxx (file version %08d, game version %08d)\n",
7555 level->file_version, level->game_version);
7558 Print("Level author: '%s'\n", level->author);
7559 Print("Level title: '%s'\n", level->name);
7561 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7563 Print("Level time: %d seconds\n", level->time);
7564 Print("Gems needed: %d\n", level->gems_needed);
7566 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7567 Print("Time for wheel: %d seconds\n", level->time_wheel);
7568 Print("Time for light: %d seconds\n", level->time_light);
7569 Print("Time for timegate: %d seconds\n", level->time_timegate);
7571 Print("Amoeba speed: %d\n", level->amoeba_speed);
7574 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7575 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7576 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7577 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7578 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7584 /* ========================================================================= */
7585 /* tape file functions */
7586 /* ========================================================================= */
7588 static void setTapeInfoToDefaults(void)
7592 /* always start with reliable default values (empty tape) */
7595 /* default values (also for pre-1.2 tapes) with only the first player */
7596 tape.player_participates[0] = TRUE;
7597 for (i = 1; i < MAX_PLAYERS; i++)
7598 tape.player_participates[i] = FALSE;
7600 /* at least one (default: the first) player participates in every tape */
7601 tape.num_participating_players = 1;
7603 tape.level_nr = level_nr;
7605 tape.changed = FALSE;
7607 tape.recording = FALSE;
7608 tape.playing = FALSE;
7609 tape.pausing = FALSE;
7611 tape.no_valid_file = FALSE;
7614 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7616 tape->file_version = getFileVersion(file);
7617 tape->game_version = getFileVersion(file);
7622 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7626 tape->random_seed = getFile32BitBE(file);
7627 tape->date = getFile32BitBE(file);
7628 tape->length = getFile32BitBE(file);
7630 /* read header fields that are new since version 1.2 */
7631 if (tape->file_version >= FILE_VERSION_1_2)
7633 byte store_participating_players = getFile8Bit(file);
7636 /* since version 1.2, tapes store which players participate in the tape */
7637 tape->num_participating_players = 0;
7638 for (i = 0; i < MAX_PLAYERS; i++)
7640 tape->player_participates[i] = FALSE;
7642 if (store_participating_players & (1 << i))
7644 tape->player_participates[i] = TRUE;
7645 tape->num_participating_players++;
7649 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7651 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7653 engine_version = getFileVersion(file);
7654 if (engine_version > 0)
7655 tape->engine_version = engine_version;
7657 tape->engine_version = tape->game_version;
7663 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7665 int level_identifier_size;
7668 level_identifier_size = getFile16BitBE(file);
7670 tape->level_identifier =
7671 checked_realloc(tape->level_identifier, level_identifier_size);
7673 for (i = 0; i < level_identifier_size; i++)
7674 tape->level_identifier[i] = getFile8Bit(file);
7676 tape->level_nr = getFile16BitBE(file);
7678 chunk_size = 2 + level_identifier_size + 2;
7683 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7687 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7688 int chunk_size_expected = tape_pos_size * tape->length;
7690 if (chunk_size_expected != chunk_size)
7692 ReadUnusedBytesFromFile(file, chunk_size);
7693 return chunk_size_expected;
7696 for (i = 0; i < tape->length; i++)
7698 if (i >= MAX_TAPE_LEN)
7700 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7703 // tape too large; read and ignore remaining tape data from this chunk
7704 for (;i < tape->length; i++)
7705 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7710 if (tape->use_mouse)
7712 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7713 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7714 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7716 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7720 for (j = 0; j < MAX_PLAYERS; j++)
7722 tape->pos[i].action[j] = MV_NONE;
7724 if (tape->player_participates[j])
7725 tape->pos[i].action[j] = getFile8Bit(file);
7729 tape->pos[i].delay = getFile8Bit(file);
7731 if (tape->file_version == FILE_VERSION_1_0)
7733 /* eliminate possible diagonal moves in old tapes */
7734 /* this is only for backward compatibility */
7736 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7737 byte action = tape->pos[i].action[0];
7738 int k, num_moves = 0;
7740 for (k = 0; k<4; k++)
7742 if (action & joy_dir[k])
7744 tape->pos[i + num_moves].action[0] = joy_dir[k];
7746 tape->pos[i + num_moves].delay = 0;
7755 tape->length += num_moves;
7758 else if (tape->file_version < FILE_VERSION_2_0)
7760 /* convert pre-2.0 tapes to new tape format */
7762 if (tape->pos[i].delay > 1)
7765 tape->pos[i + 1] = tape->pos[i];
7766 tape->pos[i + 1].delay = 1;
7769 for (j = 0; j < MAX_PLAYERS; j++)
7770 tape->pos[i].action[j] = MV_NONE;
7771 tape->pos[i].delay--;
7778 if (checkEndOfFile(file))
7782 if (i != tape->length)
7783 chunk_size = tape_pos_size * i;
7788 static void LoadTape_SokobanSolution(char *filename)
7791 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7793 if (!(file = openFile(filename, MODE_READ)))
7795 tape.no_valid_file = TRUE;
7800 while (!checkEndOfFile(file))
7802 unsigned char c = getByteFromFile(file);
7804 if (checkEndOfFile(file))
7811 tape.pos[tape.length].action[0] = MV_UP;
7812 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7818 tape.pos[tape.length].action[0] = MV_DOWN;
7819 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7825 tape.pos[tape.length].action[0] = MV_LEFT;
7826 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7832 tape.pos[tape.length].action[0] = MV_RIGHT;
7833 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7841 /* ignore white-space characters */
7845 tape.no_valid_file = TRUE;
7847 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7855 if (tape.no_valid_file)
7858 tape.length_frames = GetTapeLengthFrames();
7859 tape.length_seconds = GetTapeLengthSeconds();
7862 void LoadTapeFromFilename(char *filename)
7864 char cookie[MAX_LINE_LEN];
7865 char chunk_name[CHUNK_ID_LEN + 1];
7869 /* always start with reliable default values */
7870 setTapeInfoToDefaults();
7872 if (strSuffix(filename, ".sln"))
7874 LoadTape_SokobanSolution(filename);
7879 if (!(file = openFile(filename, MODE_READ)))
7881 tape.no_valid_file = TRUE;
7886 getFileChunkBE(file, chunk_name, NULL);
7887 if (strEqual(chunk_name, "RND1"))
7889 getFile32BitBE(file); /* not used */
7891 getFileChunkBE(file, chunk_name, NULL);
7892 if (!strEqual(chunk_name, "TAPE"))
7894 tape.no_valid_file = TRUE;
7896 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7903 else /* check for pre-2.0 file format with cookie string */
7905 strcpy(cookie, chunk_name);
7906 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7908 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7909 cookie[strlen(cookie) - 1] = '\0';
7911 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7913 tape.no_valid_file = TRUE;
7915 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7922 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7924 tape.no_valid_file = TRUE;
7926 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7933 /* pre-2.0 tape files have no game version, so use file version here */
7934 tape.game_version = tape.file_version;
7937 if (tape.file_version < FILE_VERSION_1_2)
7939 /* tape files from versions before 1.2.0 without chunk structure */
7940 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7941 LoadTape_BODY(file, 2 * tape.length, &tape);
7949 int (*loader)(File *, int, struct TapeInfo *);
7953 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7954 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7955 { "INFO", -1, LoadTape_INFO },
7956 { "BODY", -1, LoadTape_BODY },
7960 while (getFileChunkBE(file, chunk_name, &chunk_size))
7964 while (chunk_info[i].name != NULL &&
7965 !strEqual(chunk_name, chunk_info[i].name))
7968 if (chunk_info[i].name == NULL)
7970 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7971 chunk_name, filename);
7972 ReadUnusedBytesFromFile(file, chunk_size);
7974 else if (chunk_info[i].size != -1 &&
7975 chunk_info[i].size != chunk_size)
7977 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7978 chunk_size, chunk_name, filename);
7979 ReadUnusedBytesFromFile(file, chunk_size);
7983 /* call function to load this tape chunk */
7984 int chunk_size_expected =
7985 (chunk_info[i].loader)(file, chunk_size, &tape);
7987 /* the size of some chunks cannot be checked before reading other
7988 chunks first (like "HEAD" and "BODY") that contain some header
7989 information, so check them here */
7990 if (chunk_size_expected != chunk_size)
7992 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7993 chunk_size, chunk_name, filename);
8001 tape.length_frames = GetTapeLengthFrames();
8002 tape.length_seconds = GetTapeLengthSeconds();
8005 printf("::: tape file version: %d\n", tape.file_version);
8006 printf("::: tape game version: %d\n", tape.game_version);
8007 printf("::: tape engine version: %d\n", tape.engine_version);
8011 void LoadTape(int nr)
8013 char *filename = getTapeFilename(nr);
8015 LoadTapeFromFilename(filename);
8018 void LoadSolutionTape(int nr)
8020 char *filename = getSolutionTapeFilename(nr);
8022 LoadTapeFromFilename(filename);
8024 if (TAPE_IS_EMPTY(tape) &&
8025 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8026 level.native_sp_level->demo.is_available)
8027 CopyNativeTape_SP_to_RND(&level);
8030 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8032 putFileVersion(file, tape->file_version);
8033 putFileVersion(file, tape->game_version);
8036 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8039 byte store_participating_players = 0;
8041 /* set bits for participating players for compact storage */
8042 for (i = 0; i < MAX_PLAYERS; i++)
8043 if (tape->player_participates[i])
8044 store_participating_players |= (1 << i);
8046 putFile32BitBE(file, tape->random_seed);
8047 putFile32BitBE(file, tape->date);
8048 putFile32BitBE(file, tape->length);
8050 putFile8Bit(file, store_participating_players);
8052 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8054 /* unused bytes not at the end here for 4-byte alignment of engine_version */
8055 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8057 putFileVersion(file, tape->engine_version);
8060 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8062 int level_identifier_size = strlen(tape->level_identifier) + 1;
8065 putFile16BitBE(file, level_identifier_size);
8067 for (i = 0; i < level_identifier_size; i++)
8068 putFile8Bit(file, tape->level_identifier[i]);
8070 putFile16BitBE(file, tape->level_nr);
8073 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8077 for (i = 0; i < tape->length; i++)
8079 if (tape->use_mouse)
8081 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8082 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8083 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8087 for (j = 0; j < MAX_PLAYERS; j++)
8088 if (tape->player_participates[j])
8089 putFile8Bit(file, tape->pos[i].action[j]);
8092 putFile8Bit(file, tape->pos[i].delay);
8096 void SaveTape(int nr)
8098 char *filename = getTapeFilename(nr);
8100 int num_participating_players = 0;
8102 int info_chunk_size;
8103 int body_chunk_size;
8106 InitTapeDirectory(leveldir_current->subdir);
8108 if (!(file = fopen(filename, MODE_WRITE)))
8110 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8114 tape.file_version = FILE_VERSION_ACTUAL;
8115 tape.game_version = GAME_VERSION_ACTUAL;
8117 /* count number of participating players */
8118 for (i = 0; i < MAX_PLAYERS; i++)
8119 if (tape.player_participates[i])
8120 num_participating_players++;
8122 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8124 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8125 body_chunk_size = tape_pos_size * tape.length;
8127 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8128 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8130 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8131 SaveTape_VERS(file, &tape);
8133 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8134 SaveTape_HEAD(file, &tape);
8136 putFileChunkBE(file, "INFO", info_chunk_size);
8137 SaveTape_INFO(file, &tape);
8139 putFileChunkBE(file, "BODY", body_chunk_size);
8140 SaveTape_BODY(file, &tape);
8144 SetFilePermissions(filename, PERMS_PRIVATE);
8146 tape.changed = FALSE;
8149 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8151 char *filename = getTapeFilename(nr);
8152 boolean new_tape = !fileExists(filename);
8153 boolean tape_saved = FALSE;
8155 if (new_tape || Request(msg_replace, REQ_ASK))
8160 Request(msg_saved, REQ_CONFIRM);
8168 boolean SaveTapeChecked(int nr)
8170 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8173 boolean SaveTapeChecked_LevelSolved(int nr)
8175 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8176 "Level solved! Tape saved!");
8179 void DumpTape(struct TapeInfo *tape)
8181 int tape_frame_counter;
8184 if (tape->no_valid_file)
8186 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8192 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8193 tape->level_nr, tape->file_version, tape->game_version);
8194 Print(" (effective engine version %08d)\n",
8195 tape->engine_version);
8196 Print("Level series identifier: '%s'\n", tape->level_identifier);
8199 tape_frame_counter = 0;
8201 for (i = 0; i < tape->length; i++)
8203 if (i >= MAX_TAPE_LEN)
8208 for (j = 0; j < MAX_PLAYERS; j++)
8210 if (tape->player_participates[j])
8212 int action = tape->pos[i].action[j];
8214 Print("%d:%02x ", j, action);
8215 Print("[%c%c%c%c|%c%c] - ",
8216 (action & JOY_LEFT ? '<' : ' '),
8217 (action & JOY_RIGHT ? '>' : ' '),
8218 (action & JOY_UP ? '^' : ' '),
8219 (action & JOY_DOWN ? 'v' : ' '),
8220 (action & JOY_BUTTON_1 ? '1' : ' '),
8221 (action & JOY_BUTTON_2 ? '2' : ' '));
8225 Print("(%03d) ", tape->pos[i].delay);
8226 Print("[%05d]\n", tape_frame_counter);
8228 tape_frame_counter += tape->pos[i].delay;
8235 /* ========================================================================= */
8236 /* score file functions */
8237 /* ========================================================================= */
8239 void LoadScore(int nr)
8242 char *filename = getScoreFilename(nr);
8243 char cookie[MAX_LINE_LEN];
8244 char line[MAX_LINE_LEN];
8248 /* always start with reliable default values */
8249 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8251 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8252 highscore[i].Score = 0;
8255 if (!(file = fopen(filename, MODE_READ)))
8258 /* check file identifier */
8259 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8261 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8262 cookie[strlen(cookie) - 1] = '\0';
8264 if (!checkCookieString(cookie, SCORE_COOKIE))
8266 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8271 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8273 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8274 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8275 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8278 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8279 line[strlen(line) - 1] = '\0';
8281 for (line_ptr = line; *line_ptr; line_ptr++)
8283 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8285 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8286 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8295 void SaveScore(int nr)
8298 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8299 char *filename = getScoreFilename(nr);
8302 /* used instead of "leveldir_current->subdir" (for network games) */
8303 InitScoreDirectory(levelset.identifier);
8305 if (!(file = fopen(filename, MODE_WRITE)))
8307 Error(ERR_WARN, "cannot save score for level %d", nr);
8311 fprintf(file, "%s\n\n", SCORE_COOKIE);
8313 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8314 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8318 SetFilePermissions(filename, permissions);
8322 /* ========================================================================= */
8323 /* setup file functions */
8324 /* ========================================================================= */
8326 #define TOKEN_STR_PLAYER_PREFIX "player_"
8331 SETUP_TOKEN_PLAYER_NAME = 0,
8333 SETUP_TOKEN_SOUND_LOOPS,
8334 SETUP_TOKEN_SOUND_MUSIC,
8335 SETUP_TOKEN_SOUND_SIMPLE,
8337 SETUP_TOKEN_SCROLL_DELAY,
8338 SETUP_TOKEN_SCROLL_DELAY_VALUE,
8339 SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8340 SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8341 SETUP_TOKEN_FADE_SCREENS,
8342 SETUP_TOKEN_AUTORECORD,
8343 SETUP_TOKEN_SHOW_TITLESCREEN,
8344 SETUP_TOKEN_QUICK_DOORS,
8345 SETUP_TOKEN_TEAM_MODE,
8346 SETUP_TOKEN_HANDICAP,
8347 SETUP_TOKEN_SKIP_LEVELS,
8348 SETUP_TOKEN_INCREMENT_LEVELS,
8349 SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8350 SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8351 SETUP_TOKEN_TIME_LIMIT,
8352 SETUP_TOKEN_FULLSCREEN,
8353 SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8354 SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8355 SETUP_TOKEN_SCREEN_RENDERING_MODE,
8356 SETUP_TOKEN_VSYNC_MODE,
8357 SETUP_TOKEN_ASK_ON_ESCAPE,
8358 SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8359 SETUP_TOKEN_QUICK_SWITCH,
8360 SETUP_TOKEN_INPUT_ON_FOCUS,
8361 SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8362 SETUP_TOKEN_GAME_SPEED_EXTENDED,
8363 SETUP_TOKEN_GAME_FRAME_DELAY,
8364 SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8365 SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8366 SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8367 SETUP_TOKEN_GRAPHICS_SET,
8368 SETUP_TOKEN_SOUNDS_SET,
8369 SETUP_TOKEN_MUSIC_SET,
8370 SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8371 SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8372 SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8373 SETUP_TOKEN_VOLUME_SIMPLE,
8374 SETUP_TOKEN_VOLUME_LOOPS,
8375 SETUP_TOKEN_VOLUME_MUSIC,
8376 SETUP_TOKEN_NETWORK_MODE,
8377 SETUP_TOKEN_NETWORK_PLAYER_NR,
8378 SETUP_TOKEN_NETWORK_SERVER_HOSTNAME,
8379 SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8380 SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8381 SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8382 SETUP_TOKEN_TOUCH_TRANSPARENCY,
8383 SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8384 SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8385 SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8386 SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8387 SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8388 SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8390 NUM_GLOBAL_SETUP_TOKENS
8396 SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8398 NUM_AUTO_SETUP_TOKENS
8404 SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8405 SETUP_TOKEN_EDITOR_EL_CUSTOM,
8406 SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8407 SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8408 SETUP_TOKEN_EDITOR_EL_HEADLINES,
8409 SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8411 NUM_EDITOR_SETUP_TOKENS
8414 /* editor cascade setup */
8417 SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8418 SETUP_TOKEN_EDITOR_CASCADE_EM,
8419 SETUP_TOKEN_EDITOR_CASCADE_EMC,
8420 SETUP_TOKEN_EDITOR_CASCADE_RND,
8421 SETUP_TOKEN_EDITOR_CASCADE_SB,
8422 SETUP_TOKEN_EDITOR_CASCADE_SP,
8423 SETUP_TOKEN_EDITOR_CASCADE_DC,
8424 SETUP_TOKEN_EDITOR_CASCADE_DX,
8425 SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8426 SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8427 SETUP_TOKEN_EDITOR_CASCADE_CE,
8428 SETUP_TOKEN_EDITOR_CASCADE_GE,
8429 SETUP_TOKEN_EDITOR_CASCADE_REF,
8430 SETUP_TOKEN_EDITOR_CASCADE_USER,
8431 SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8433 NUM_EDITOR_CASCADE_SETUP_TOKENS
8436 /* shortcut setup */
8439 SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8440 SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8441 SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8442 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8443 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8444 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8445 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8446 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8447 SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8448 SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8449 SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8450 SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8451 SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8452 SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8453 SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8454 SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8455 SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8456 SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8457 SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8458 SETUP_TOKEN_SHORTCUT_SNAP_UP,
8459 SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8461 NUM_SHORTCUT_SETUP_TOKENS
8467 SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8468 SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8469 SETUP_TOKEN_PLAYER_JOY_XLEFT,
8470 SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8471 SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8472 SETUP_TOKEN_PLAYER_JOY_YUPPER,
8473 SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8474 SETUP_TOKEN_PLAYER_JOY_YLOWER,
8475 SETUP_TOKEN_PLAYER_JOY_SNAP,
8476 SETUP_TOKEN_PLAYER_JOY_DROP,
8477 SETUP_TOKEN_PLAYER_KEY_LEFT,
8478 SETUP_TOKEN_PLAYER_KEY_RIGHT,
8479 SETUP_TOKEN_PLAYER_KEY_UP,
8480 SETUP_TOKEN_PLAYER_KEY_DOWN,
8481 SETUP_TOKEN_PLAYER_KEY_SNAP,
8482 SETUP_TOKEN_PLAYER_KEY_DROP,
8484 NUM_PLAYER_SETUP_TOKENS
8490 SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8491 SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8492 SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8494 NUM_SYSTEM_SETUP_TOKENS
8497 /* internal setup */
8500 SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8501 SETUP_TOKEN_INT_PROGRAM_VERSION,
8502 SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8503 SETUP_TOKEN_INT_PROGRAM_EMAIL,
8504 SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8505 SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8506 SETUP_TOKEN_INT_PROGRAM_COMPANY,
8507 SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8508 SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8509 SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8510 SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8511 SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8512 SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8513 SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8514 SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8515 SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8516 SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8517 SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8518 SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8520 NUM_INTERNAL_SETUP_TOKENS
8526 SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8527 SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8528 SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8529 SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8530 SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8531 SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8532 SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8533 SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8534 SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8535 SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8536 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8537 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8538 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8539 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8540 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8541 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8542 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8543 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8544 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8545 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8546 SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8547 SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8548 SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8550 NUM_DEBUG_SETUP_TOKENS
8556 SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8558 NUM_OPTIONS_SETUP_TOKENS
8562 static struct SetupInfo si;
8563 static struct SetupAutoSetupInfo sasi;
8564 static struct SetupEditorInfo sei;
8565 static struct SetupEditorCascadeInfo seci;
8566 static struct SetupShortcutInfo ssi;
8567 static struct SetupInputInfo sii;
8568 static struct SetupSystemInfo syi;
8569 static struct SetupInternalInfo sxi;
8570 static struct SetupDebugInfo sdi;
8571 static struct OptionInfo soi;
8573 static struct TokenInfo global_setup_tokens[] =
8575 { TYPE_STRING, &si.player_name, "player_name" },
8576 { TYPE_SWITCH, &si.sound, "sound" },
8577 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8578 { TYPE_SWITCH, &si.sound_music, "background_music" },
8579 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8580 { TYPE_SWITCH, &si.toons, "toons" },
8581 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8582 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8583 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8584 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8585 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8586 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8587 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8588 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8589 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8590 { TYPE_SWITCH, &si.handicap, "handicap" },
8591 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8592 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8593 { TYPE_SWITCH, &si.auto_play_next_level, "auto_play_next_level" },
8594 { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
8595 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8596 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8597 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8598 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8599 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8600 { TYPE_STRING, &si.vsync_mode, "vsync_mode" },
8601 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8602 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8603 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8604 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8605 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8606 { TYPE_SWITCH, &si.game_speed_extended, "game_speed_extended" },
8607 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8608 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8609 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8610 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8611 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8612 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8613 { TYPE_STRING, &si.music_set, "music_set" },
8614 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8615 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8616 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8617 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8618 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8619 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8620 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8621 { TYPE_PLAYER, &si.network_player_nr, "network_player" },
8622 { TYPE_STRING, &si.network_server_hostname, "network_server_hostname" },
8623 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8624 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8625 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8626 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8627 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8628 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8629 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8630 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8631 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8632 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8635 static struct TokenInfo auto_setup_tokens[] =
8637 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8640 static struct TokenInfo editor_setup_tokens[] =
8642 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8643 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8644 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8645 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8646 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8647 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8650 static struct TokenInfo editor_cascade_setup_tokens[] =
8652 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8653 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8654 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8655 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8656 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8657 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8658 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8659 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8660 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8661 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8662 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8663 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8664 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8665 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8666 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8667 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8668 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8671 static struct TokenInfo shortcut_setup_tokens[] =
8673 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8674 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8675 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8676 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8677 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8678 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8679 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8680 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8681 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8682 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8683 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8684 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8685 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8686 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8687 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8688 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8689 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8690 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8691 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8692 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8693 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8696 static struct TokenInfo player_setup_tokens[] =
8698 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8699 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8700 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8701 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8702 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8703 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8704 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8705 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8706 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8707 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8708 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8709 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8710 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8711 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8712 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8713 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8716 static struct TokenInfo system_setup_tokens[] =
8718 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8719 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8720 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8723 static struct TokenInfo internal_setup_tokens[] =
8725 { TYPE_STRING, &sxi.program_title, "program_title" },
8726 { TYPE_STRING, &sxi.program_version, "program_version" },
8727 { TYPE_STRING, &sxi.program_author, "program_author" },
8728 { TYPE_STRING, &sxi.program_email, "program_email" },
8729 { TYPE_STRING, &sxi.program_website, "program_website" },
8730 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8731 { TYPE_STRING, &sxi.program_company, "program_company" },
8732 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8733 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8734 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8735 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8736 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8737 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8738 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8739 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8740 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8741 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8742 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8743 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8746 static struct TokenInfo debug_setup_tokens[] =
8748 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8749 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8750 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8751 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8752 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8753 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8754 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8755 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8756 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8757 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8758 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8759 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8760 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8761 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8762 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8763 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8764 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8765 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8766 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8767 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8768 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8769 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8770 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8773 static struct TokenInfo options_setup_tokens[] =
8775 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8778 static char *get_corrected_login_name(char *login_name)
8780 /* needed because player name must be a fixed length string */
8781 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8783 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8784 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8786 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8787 if (strchr(login_name_new, ' '))
8788 *strchr(login_name_new, ' ') = '\0';
8790 return login_name_new;
8793 static void setSetupInfoToDefaults(struct SetupInfo *si)
8797 si->player_name = get_corrected_login_name(getLoginName());
8800 si->sound_loops = TRUE;
8801 si->sound_music = TRUE;
8802 si->sound_simple = TRUE;
8804 si->scroll_delay = TRUE;
8805 si->scroll_delay_value = STD_SCROLL_DELAY;
8806 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8807 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8808 si->fade_screens = TRUE;
8809 si->autorecord = TRUE;
8810 si->show_titlescreen = TRUE;
8811 si->quick_doors = FALSE;
8812 si->team_mode = FALSE;
8813 si->handicap = TRUE;
8814 si->skip_levels = TRUE;
8815 si->increment_levels = TRUE;
8816 si->auto_play_next_level = TRUE;
8817 si->skip_scores_after_game = FALSE;
8818 si->time_limit = TRUE;
8819 si->fullscreen = FALSE;
8820 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8821 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8822 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8823 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
8824 si->ask_on_escape = TRUE;
8825 si->ask_on_escape_editor = TRUE;
8826 si->quick_switch = FALSE;
8827 si->input_on_focus = FALSE;
8828 si->prefer_aga_graphics = TRUE;
8829 si->game_speed_extended = FALSE;
8830 si->game_frame_delay = GAME_FRAME_DELAY;
8831 si->sp_show_border_elements = FALSE;
8832 si->small_game_graphics = FALSE;
8833 si->show_snapshot_buttons = FALSE;
8835 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8836 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8837 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8839 si->override_level_graphics = FALSE;
8840 si->override_level_sounds = FALSE;
8841 si->override_level_music = FALSE;
8843 si->volume_simple = 100; /* percent */
8844 si->volume_loops = 100; /* percent */
8845 si->volume_music = 100; /* percent */
8847 si->network_mode = FALSE;
8848 si->network_player_nr = 0; /* first player */
8849 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
8851 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8852 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8853 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8854 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8855 si->touch.draw_outlined = TRUE;
8856 si->touch.draw_pressed = TRUE;
8858 for (i = 0; i < 2; i++)
8860 char *default_grid_button[6][2] =
8866 { "111222", " vv " },
8867 { "111222", " vv " }
8869 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8870 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8871 int min_xsize = MIN(6, grid_xsize);
8872 int min_ysize = MIN(6, grid_ysize);
8873 int startx = grid_xsize - min_xsize;
8874 int starty = grid_ysize - min_ysize;
8877 // virtual buttons grid can only be set to defaults if video is initialized
8878 // (this will be repeated if virtual buttons are not loaded from setup file)
8879 if (video.initialized)
8881 si->touch.grid_xsize[i] = grid_xsize;
8882 si->touch.grid_ysize[i] = grid_ysize;
8886 si->touch.grid_xsize[i] = -1;
8887 si->touch.grid_ysize[i] = -1;
8890 for (x = 0; x < MAX_GRID_XSIZE; x++)
8891 for (y = 0; y < MAX_GRID_YSIZE; y++)
8892 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8894 for (x = 0; x < min_xsize; x++)
8895 for (y = 0; y < min_ysize; y++)
8896 si->touch.grid_button[i][x][starty + y] =
8897 default_grid_button[y][0][x];
8899 for (x = 0; x < min_xsize; x++)
8900 for (y = 0; y < min_ysize; y++)
8901 si->touch.grid_button[i][startx + x][starty + y] =
8902 default_grid_button[y][1][x];
8905 si->touch.grid_initialized = video.initialized;
8907 si->editor.el_boulderdash = TRUE;
8908 si->editor.el_emerald_mine = TRUE;
8909 si->editor.el_emerald_mine_club = TRUE;
8910 si->editor.el_more = TRUE;
8911 si->editor.el_sokoban = TRUE;
8912 si->editor.el_supaplex = TRUE;
8913 si->editor.el_diamond_caves = TRUE;
8914 si->editor.el_dx_boulderdash = TRUE;
8916 si->editor.el_mirror_magic = TRUE;
8917 si->editor.el_deflektor = TRUE;
8919 si->editor.el_chars = TRUE;
8920 si->editor.el_steel_chars = TRUE;
8922 si->editor.el_classic = TRUE;
8923 si->editor.el_custom = TRUE;
8925 si->editor.el_user_defined = FALSE;
8926 si->editor.el_dynamic = TRUE;
8928 si->editor.el_headlines = TRUE;
8930 si->editor.show_element_token = FALSE;
8932 si->editor.use_template_for_new_levels = TRUE;
8934 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8935 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8936 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8938 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8939 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8940 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8941 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8942 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8944 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8945 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8946 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8947 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8948 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8949 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8951 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8952 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8953 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8955 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8956 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8957 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8958 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8960 for (i = 0; i < MAX_PLAYERS; i++)
8962 si->input[i].use_joystick = FALSE;
8963 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8964 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8965 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8966 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8967 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8968 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8969 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8970 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8971 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8972 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8973 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8974 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8975 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8976 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8977 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8980 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8981 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8982 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8984 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8985 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8986 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8987 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8988 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8989 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8990 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8992 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8994 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8995 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8996 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8998 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8999 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9000 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9002 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9003 si->internal.choose_from_top_leveldir = FALSE;
9004 si->internal.show_scaling_in_title = TRUE;
9006 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9007 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9009 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9010 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9011 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9012 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9013 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9014 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9015 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9016 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9017 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9018 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9020 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9021 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9022 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9023 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9024 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9025 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9026 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9027 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9028 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9029 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9031 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9032 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9034 si->debug.show_frames_per_second = FALSE;
9036 si->options.verbose = FALSE;
9038 #if defined(PLATFORM_ANDROID)
9039 si->fullscreen = TRUE;
9043 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9045 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9048 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9050 si->editor_cascade.el_bd = TRUE;
9051 si->editor_cascade.el_em = TRUE;
9052 si->editor_cascade.el_emc = TRUE;
9053 si->editor_cascade.el_rnd = TRUE;
9054 si->editor_cascade.el_sb = TRUE;
9055 si->editor_cascade.el_sp = TRUE;
9056 si->editor_cascade.el_dc = TRUE;
9057 si->editor_cascade.el_dx = TRUE;
9059 si->editor_cascade.el_mm = TRUE;
9060 si->editor_cascade.el_df = TRUE;
9062 si->editor_cascade.el_chars = FALSE;
9063 si->editor_cascade.el_steel_chars = FALSE;
9064 si->editor_cascade.el_ce = FALSE;
9065 si->editor_cascade.el_ge = FALSE;
9066 si->editor_cascade.el_ref = FALSE;
9067 si->editor_cascade.el_user = FALSE;
9068 si->editor_cascade.el_dynamic = FALSE;
9071 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9073 static char *getHideSetupToken(void *setup_value)
9075 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9077 if (setup_value != NULL)
9078 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9080 return hide_setup_token;
9083 void setHideSetupEntry(void *setup_value)
9085 char *hide_setup_token = getHideSetupToken(setup_value);
9087 if (setup_value != NULL)
9088 setHashEntry(hide_setup_hash, hide_setup_token, "");
9091 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9093 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9094 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9096 setHideSetupEntry(setup_value);
9099 boolean hideSetupEntry(void *setup_value)
9101 char *hide_setup_token = getHideSetupToken(setup_value);
9103 return (setup_value != NULL &&
9104 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9107 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9108 struct TokenInfo *token_info,
9109 int token_nr, char *token_text)
9111 char *token_hide_text = getStringCat2(token_text, ".hide");
9112 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9114 /* set the value of this setup option in the setup option structure */
9115 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9117 /* check if this setup option should be hidden in the setup menu */
9118 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9119 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9122 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9123 struct TokenInfo *token_info,
9126 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9127 token_info[token_nr].text);
9130 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9134 if (!setup_file_hash)
9137 if (hide_setup_hash == NULL)
9138 hide_setup_hash = newSetupFileHash();
9142 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9143 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9146 /* virtual buttons setup */
9147 setup.touch.grid_initialized = TRUE;
9148 for (i = 0; i < 2; i++)
9150 int grid_xsize = setup.touch.grid_xsize[i];
9151 int grid_ysize = setup.touch.grid_ysize[i];
9154 // if virtual buttons are not loaded from setup file, repeat initializing
9155 // virtual buttons grid with default values later when video is initialized
9156 if (grid_xsize == -1 ||
9159 setup.touch.grid_initialized = FALSE;
9164 for (y = 0; y < grid_ysize; y++)
9166 char token_string[MAX_LINE_LEN];
9168 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9170 char *value_string = getHashEntry(setup_file_hash, token_string);
9172 if (value_string == NULL)
9175 for (x = 0; x < grid_xsize; x++)
9177 char c = value_string[x];
9179 setup.touch.grid_button[i][x][y] =
9180 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9187 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9188 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9191 /* shortcut setup */
9192 ssi = setup.shortcut;
9193 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9194 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9195 setup.shortcut = ssi;
9198 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9202 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9204 sii = setup.input[pnr];
9205 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9207 char full_token[100];
9209 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9210 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9213 setup.input[pnr] = sii;
9218 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9219 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9222 /* internal setup */
9223 sxi = setup.internal;
9224 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9225 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9226 setup.internal = sxi;
9230 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9231 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9235 soi = setup.options;
9236 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9237 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9238 setup.options = soi;
9240 setHideRelatedSetupEntries();
9243 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9247 if (!setup_file_hash)
9251 sasi = setup.auto_setup;
9252 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9253 setSetupInfo(auto_setup_tokens, i,
9254 getHashEntry(setup_file_hash,
9255 auto_setup_tokens[i].text));
9256 setup.auto_setup = sasi;
9259 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9263 if (!setup_file_hash)
9266 /* editor cascade setup */
9267 seci = setup.editor_cascade;
9268 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9269 setSetupInfo(editor_cascade_setup_tokens, i,
9270 getHashEntry(setup_file_hash,
9271 editor_cascade_setup_tokens[i].text));
9272 setup.editor_cascade = seci;
9275 void LoadSetupFromFilename(char *filename)
9277 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9279 if (setup_file_hash)
9281 decodeSetupFileHash(setup_file_hash);
9283 freeSetupFileHash(setup_file_hash);
9287 Error(ERR_DEBUG, "using default setup values");
9291 static void LoadSetup_SpecialPostProcessing(void)
9293 char *player_name_new;
9295 /* needed to work around problems with fixed length strings */
9296 player_name_new = get_corrected_login_name(setup.player_name);
9297 free(setup.player_name);
9298 setup.player_name = player_name_new;
9300 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9301 if (setup.scroll_delay == FALSE)
9303 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9304 setup.scroll_delay = TRUE; /* now always "on" */
9307 /* make sure that scroll delay value stays inside valid range */
9308 setup.scroll_delay_value =
9309 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9312 void LoadSetup(void)
9316 /* always start with reliable default values */
9317 setSetupInfoToDefaults(&setup);
9319 /* try to load setup values from default setup file */
9320 filename = getDefaultSetupFilename();
9322 if (fileExists(filename))
9323 LoadSetupFromFilename(filename);
9325 /* try to load setup values from user setup file */
9326 filename = getSetupFilename();
9328 LoadSetupFromFilename(filename);
9330 LoadSetup_SpecialPostProcessing();
9333 void LoadSetup_AutoSetup(void)
9335 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9336 SetupFileHash *setup_file_hash = NULL;
9338 /* always start with reliable default values */
9339 setSetupInfoToDefaults_AutoSetup(&setup);
9341 setup_file_hash = loadSetupFileHash(filename);
9343 if (setup_file_hash)
9345 decodeSetupFileHash_AutoSetup(setup_file_hash);
9347 freeSetupFileHash(setup_file_hash);
9353 void LoadSetup_EditorCascade(void)
9355 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9356 SetupFileHash *setup_file_hash = NULL;
9358 /* always start with reliable default values */
9359 setSetupInfoToDefaults_EditorCascade(&setup);
9361 setup_file_hash = loadSetupFileHash(filename);
9363 if (setup_file_hash)
9365 decodeSetupFileHash_EditorCascade(setup_file_hash);
9367 freeSetupFileHash(setup_file_hash);
9373 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9376 char mapping_guid[MAX_LINE_LEN];
9377 char *mapping_start, *mapping_end;
9379 // get GUID from game controller mapping line: copy complete line
9380 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9381 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9383 // get GUID from game controller mapping line: cut after GUID part
9384 mapping_start = strchr(mapping_guid, ',');
9385 if (mapping_start != NULL)
9386 *mapping_start = '\0';
9388 // cut newline from game controller mapping line
9389 mapping_end = strchr(mapping_line, '\n');
9390 if (mapping_end != NULL)
9391 *mapping_end = '\0';
9393 // add mapping entry to game controller mappings hash
9394 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9397 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9402 if (!(file = fopen(filename, MODE_READ)))
9404 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9411 char line[MAX_LINE_LEN];
9413 if (!fgets(line, MAX_LINE_LEN, file))
9416 addGameControllerMappingToHash(mappings_hash, line);
9422 void SaveSetup(void)
9424 char *filename = getSetupFilename();
9428 InitUserDataDirectory();
9430 if (!(file = fopen(filename, MODE_WRITE)))
9432 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9436 fprintFileHeader(file, SETUP_FILENAME);
9440 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9442 /* just to make things nicer :) */
9443 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9444 i == SETUP_TOKEN_GRAPHICS_SET ||
9445 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9446 i == SETUP_TOKEN_NETWORK_MODE ||
9447 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9448 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9449 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9450 fprintf(file, "\n");
9452 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9455 /* virtual buttons setup */
9456 for (i = 0; i < 2; i++)
9458 int grid_xsize = setup.touch.grid_xsize[i];
9459 int grid_ysize = setup.touch.grid_ysize[i];
9462 fprintf(file, "\n");
9464 for (y = 0; y < grid_ysize; y++)
9466 char token_string[MAX_LINE_LEN];
9467 char value_string[MAX_LINE_LEN];
9469 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9471 for (x = 0; x < grid_xsize; x++)
9473 char c = setup.touch.grid_button[i][x][y];
9475 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9478 value_string[grid_xsize] = '\0';
9480 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9486 fprintf(file, "\n");
9487 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9488 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9490 /* shortcut setup */
9491 ssi = setup.shortcut;
9492 fprintf(file, "\n");
9493 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9494 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9497 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9501 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9502 fprintf(file, "\n");
9504 sii = setup.input[pnr];
9505 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9506 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9511 fprintf(file, "\n");
9512 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9513 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9515 /* internal setup */
9516 /* (internal setup values not saved to user setup file) */
9520 fprintf(file, "\n");
9521 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9522 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9525 soi = setup.options;
9526 fprintf(file, "\n");
9527 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9528 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9532 SetFilePermissions(filename, PERMS_PRIVATE);
9535 void SaveSetup_AutoSetup(void)
9537 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9541 InitUserDataDirectory();
9543 if (!(file = fopen(filename, MODE_WRITE)))
9545 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9550 fprintFileHeader(file, AUTOSETUP_FILENAME);
9552 sasi = setup.auto_setup;
9553 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9554 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9558 SetFilePermissions(filename, PERMS_PRIVATE);
9563 void SaveSetup_EditorCascade(void)
9565 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9569 InitUserDataDirectory();
9571 if (!(file = fopen(filename, MODE_WRITE)))
9573 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9578 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9580 seci = setup.editor_cascade;
9581 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9582 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9586 SetFilePermissions(filename, PERMS_PRIVATE);
9591 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9596 if (!(file = fopen(filename, MODE_WRITE)))
9598 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9603 BEGIN_HASH_ITERATION(mappings_hash, itr)
9605 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9607 END_HASH_ITERATION(mappings_hash, itr)
9612 void SaveSetup_AddGameControllerMapping(char *mapping)
9614 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9615 SetupFileHash *mappings_hash = newSetupFileHash();
9617 InitUserDataDirectory();
9619 // load existing personal game controller mappings
9620 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9622 // add new mapping to personal game controller mappings
9623 addGameControllerMappingToHash(mappings_hash, mapping);
9625 // save updated personal game controller mappings
9626 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9628 freeSetupFileHash(mappings_hash);
9632 void LoadCustomElementDescriptions(void)
9634 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9635 SetupFileHash *setup_file_hash;
9638 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9640 if (element_info[i].custom_description != NULL)
9642 free(element_info[i].custom_description);
9643 element_info[i].custom_description = NULL;
9647 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9650 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9652 char *token = getStringCat2(element_info[i].token_name, ".name");
9653 char *value = getHashEntry(setup_file_hash, token);
9656 element_info[i].custom_description = getStringCopy(value);
9661 freeSetupFileHash(setup_file_hash);
9664 static int getElementFromToken(char *token)
9666 char *value = getHashEntry(element_token_hash, token);
9671 Error(ERR_WARN, "unknown element token '%s'", token);
9673 return EL_UNDEFINED;
9676 static int get_token_parameter_value(char *token, char *value_raw)
9680 if (token == NULL || value_raw == NULL)
9681 return ARG_UNDEFINED_VALUE;
9683 suffix = strrchr(token, '.');
9687 if (strEqual(suffix, ".element"))
9688 return getElementFromToken(value_raw);
9690 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9691 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9694 void InitMenuDesignSettings_Static(void)
9698 /* always start with reliable default values from static default config */
9699 for (i = 0; image_config_vars[i].token != NULL; i++)
9701 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9704 *image_config_vars[i].value =
9705 get_token_parameter_value(image_config_vars[i].token, value);
9709 static void InitMenuDesignSettings_SpecialPreProcessing(void)
9713 /* the following initializes hierarchical values from static configuration */
9715 /* special case: initialize "ARG_DEFAULT" values in static default config */
9716 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9717 titlescreen_initial_first_default.fade_mode =
9718 title_initial_first_default.fade_mode;
9719 titlescreen_initial_first_default.fade_delay =
9720 title_initial_first_default.fade_delay;
9721 titlescreen_initial_first_default.post_delay =
9722 title_initial_first_default.post_delay;
9723 titlescreen_initial_first_default.auto_delay =
9724 title_initial_first_default.auto_delay;
9725 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9726 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9727 titlescreen_first_default.post_delay = title_first_default.post_delay;
9728 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9729 titlemessage_initial_first_default.fade_mode =
9730 title_initial_first_default.fade_mode;
9731 titlemessage_initial_first_default.fade_delay =
9732 title_initial_first_default.fade_delay;
9733 titlemessage_initial_first_default.post_delay =
9734 title_initial_first_default.post_delay;
9735 titlemessage_initial_first_default.auto_delay =
9736 title_initial_first_default.auto_delay;
9737 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9738 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9739 titlemessage_first_default.post_delay = title_first_default.post_delay;
9740 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9742 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9743 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9744 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9745 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9746 titlescreen_default.fade_mode = title_default.fade_mode;
9747 titlescreen_default.fade_delay = title_default.fade_delay;
9748 titlescreen_default.post_delay = title_default.post_delay;
9749 titlescreen_default.auto_delay = title_default.auto_delay;
9750 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9751 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9752 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9753 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9754 titlemessage_default.fade_mode = title_default.fade_mode;
9755 titlemessage_default.fade_delay = title_default.fade_delay;
9756 titlemessage_default.post_delay = title_default.post_delay;
9757 titlemessage_default.auto_delay = title_default.auto_delay;
9759 /* special case: initialize "ARG_DEFAULT" values in static default config */
9760 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9761 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9763 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9764 titlescreen_first[i] = titlescreen_first_default;
9765 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9766 titlemessage_first[i] = titlemessage_first_default;
9768 titlescreen_initial[i] = titlescreen_initial_default;
9769 titlescreen[i] = titlescreen_default;
9770 titlemessage_initial[i] = titlemessage_initial_default;
9771 titlemessage[i] = titlemessage_default;
9774 /* special case: initialize "ARG_DEFAULT" values in static default config */
9775 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9776 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9778 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9781 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9782 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9783 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9786 /* special case: initialize "ARG_DEFAULT" values in static default config */
9787 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9788 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9790 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9791 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9792 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9794 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9797 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9801 static void InitMenuDesignSettings_SpecialPostProcessing(void)
9805 struct XY *dst, *src;
9809 { &game.button.save, &game.button.stop },
9810 { &game.button.pause2, &game.button.pause },
9811 { &game.button.load, &game.button.play },
9812 { &game.button.undo, &game.button.stop },
9813 { &game.button.redo, &game.button.play },
9819 /* special case: initialize later added SETUP list size from LEVELS value */
9820 if (menu.list_size[GAME_MODE_SETUP] == -1)
9821 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9823 /* set default position for snapshot buttons to stop/pause/play buttons */
9824 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9825 if ((*game_buttons_xy[i].dst).x == -1 &&
9826 (*game_buttons_xy[i].dst).y == -1)
9827 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9830 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
9834 struct XYTileSize *dst, *src;
9837 editor_buttons_xy[] =
9840 &editor.button.element_left, &editor.palette.element_left,
9841 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9844 &editor.button.element_middle, &editor.palette.element_middle,
9845 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9848 &editor.button.element_right, &editor.palette.element_right,
9849 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9856 /* set default position for element buttons to element graphics */
9857 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9859 if ((*editor_buttons_xy[i].dst).x == -1 &&
9860 (*editor_buttons_xy[i].dst).y == -1)
9862 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9864 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9866 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9871 static void LoadMenuDesignSettingsFromFilename(char *filename)
9873 static struct TitleFadingInfo tfi;
9874 static struct TitleMessageInfo tmi;
9875 static struct TokenInfo title_tokens[] =
9877 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9878 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9879 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9880 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9884 static struct TokenInfo titlemessage_tokens[] =
9886 { TYPE_INTEGER, &tmi.x, ".x" },
9887 { TYPE_INTEGER, &tmi.y, ".y" },
9888 { TYPE_INTEGER, &tmi.width, ".width" },
9889 { TYPE_INTEGER, &tmi.height, ".height" },
9890 { TYPE_INTEGER, &tmi.chars, ".chars" },
9891 { TYPE_INTEGER, &tmi.lines, ".lines" },
9892 { TYPE_INTEGER, &tmi.align, ".align" },
9893 { TYPE_INTEGER, &tmi.valign, ".valign" },
9894 { TYPE_INTEGER, &tmi.font, ".font" },
9895 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9896 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9897 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9898 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9899 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9900 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9901 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9902 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9908 struct TitleFadingInfo *info;
9913 /* initialize first titles from "enter screen" definitions, if defined */
9914 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9915 { &title_first_default, "menu.enter_screen.TITLE" },
9917 /* initialize title screens from "next screen" definitions, if defined */
9918 { &title_initial_default, "menu.next_screen.TITLE" },
9919 { &title_default, "menu.next_screen.TITLE" },
9925 struct TitleMessageInfo *array;
9928 titlemessage_arrays[] =
9930 /* initialize first titles from "enter screen" definitions, if defined */
9931 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9932 { titlescreen_first, "menu.enter_screen.TITLE" },
9933 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9934 { titlemessage_first, "menu.enter_screen.TITLE" },
9936 /* initialize titles from "next screen" definitions, if defined */
9937 { titlescreen_initial, "menu.next_screen.TITLE" },
9938 { titlescreen, "menu.next_screen.TITLE" },
9939 { titlemessage_initial, "menu.next_screen.TITLE" },
9940 { titlemessage, "menu.next_screen.TITLE" },
9942 /* overwrite titles with title definitions, if defined */
9943 { titlescreen_initial_first, "[title_initial]" },
9944 { titlescreen_first, "[title]" },
9945 { titlemessage_initial_first, "[title_initial]" },
9946 { titlemessage_first, "[title]" },
9948 { titlescreen_initial, "[title_initial]" },
9949 { titlescreen, "[title]" },
9950 { titlemessage_initial, "[title_initial]" },
9951 { titlemessage, "[title]" },
9953 /* overwrite titles with title screen/message definitions, if defined */
9954 { titlescreen_initial_first, "[titlescreen_initial]" },
9955 { titlescreen_first, "[titlescreen]" },
9956 { titlemessage_initial_first, "[titlemessage_initial]" },
9957 { titlemessage_first, "[titlemessage]" },
9959 { titlescreen_initial, "[titlescreen_initial]" },
9960 { titlescreen, "[titlescreen]" },
9961 { titlemessage_initial, "[titlemessage_initial]" },
9962 { titlemessage, "[titlemessage]" },
9966 SetupFileHash *setup_file_hash;
9969 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9972 /* the following initializes hierarchical values from dynamic configuration */
9974 /* special case: initialize with default values that may be overwritten */
9975 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9976 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9978 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9979 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9980 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9982 if (value_1 != NULL)
9983 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9984 if (value_2 != NULL)
9985 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9986 if (value_3 != NULL)
9987 menu.list_size[i] = get_integer_from_string(value_3);
9990 /* special case: initialize with default values that may be overwritten */
9991 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9992 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9994 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9995 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9997 if (value_1 != NULL)
9998 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9999 if (value_2 != NULL)
10000 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
10002 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
10004 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
10006 if (value_1 != NULL)
10007 menu.list_size_info[i] = get_integer_from_string(value_1);
10011 /* special case: initialize with default values that may be overwritten */
10012 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
10013 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10015 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
10016 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
10018 if (value_1 != NULL)
10019 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
10020 if (value_2 != NULL)
10021 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
10024 /* special case: initialize with default values that may be overwritten */
10025 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
10026 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10028 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
10029 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
10030 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
10031 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
10032 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
10033 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
10034 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
10035 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
10036 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
10038 if (value_1 != NULL)
10039 menu.left_spacing_info[i] = get_integer_from_string(value_1);
10040 if (value_2 != NULL)
10041 menu.right_spacing_info[i] = get_integer_from_string(value_2);
10042 if (value_3 != NULL)
10043 menu.top_spacing_info[i] = get_integer_from_string(value_3);
10044 if (value_4 != NULL)
10045 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
10046 if (value_5 != NULL)
10047 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
10048 if (value_6 != NULL)
10049 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
10050 if (value_7 != NULL)
10051 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
10052 if (value_8 != NULL)
10053 menu.line_spacing_info[i] = get_integer_from_string(value_8);
10054 if (value_9 != NULL)
10055 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
10058 /* special case: initialize with default values that may be overwritten */
10059 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10060 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10062 char *token_1 = "menu.enter_screen.fade_mode";
10063 char *token_2 = "menu.enter_screen.fade_delay";
10064 char *token_3 = "menu.enter_screen.post_delay";
10065 char *token_4 = "menu.leave_screen.fade_mode";
10066 char *token_5 = "menu.leave_screen.fade_delay";
10067 char *token_6 = "menu.leave_screen.post_delay";
10068 char *token_7 = "menu.next_screen.fade_mode";
10069 char *token_8 = "menu.next_screen.fade_delay";
10070 char *token_9 = "menu.next_screen.post_delay";
10071 char *value_1 = getHashEntry(setup_file_hash, token_1);
10072 char *value_2 = getHashEntry(setup_file_hash, token_2);
10073 char *value_3 = getHashEntry(setup_file_hash, token_3);
10074 char *value_4 = getHashEntry(setup_file_hash, token_4);
10075 char *value_5 = getHashEntry(setup_file_hash, token_5);
10076 char *value_6 = getHashEntry(setup_file_hash, token_6);
10077 char *value_7 = getHashEntry(setup_file_hash, token_7);
10078 char *value_8 = getHashEntry(setup_file_hash, token_8);
10079 char *value_9 = getHashEntry(setup_file_hash, token_9);
10081 if (value_1 != NULL)
10082 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10084 if (value_2 != NULL)
10085 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10087 if (value_3 != NULL)
10088 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10090 if (value_4 != NULL)
10091 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10093 if (value_5 != NULL)
10094 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10096 if (value_6 != NULL)
10097 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10099 if (value_7 != NULL)
10100 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10102 if (value_8 != NULL)
10103 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10105 if (value_9 != NULL)
10106 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10110 /* special case: initialize with default values that may be overwritten */
10111 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10112 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10114 char *token_w1 = "viewport.window.width";
10115 char *token_w2 = "viewport.window.height";
10116 char *token_01 = "viewport.playfield.x";
10117 char *token_02 = "viewport.playfield.y";
10118 char *token_03 = "viewport.playfield.width";
10119 char *token_04 = "viewport.playfield.height";
10120 char *token_05 = "viewport.playfield.border_size";
10121 char *token_06 = "viewport.door_1.x";
10122 char *token_07 = "viewport.door_1.y";
10123 char *token_08 = "viewport.door_1.width";
10124 char *token_09 = "viewport.door_1.height";
10125 char *token_10 = "viewport.door_1.border_size";
10126 char *token_11 = "viewport.door_2.x";
10127 char *token_12 = "viewport.door_2.y";
10128 char *token_13 = "viewport.door_2.width";
10129 char *token_14 = "viewport.door_2.height";
10130 char *token_15 = "viewport.door_2.border_size";
10131 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10132 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10133 char *value_01 = getHashEntry(setup_file_hash, token_01);
10134 char *value_02 = getHashEntry(setup_file_hash, token_02);
10135 char *value_03 = getHashEntry(setup_file_hash, token_03);
10136 char *value_04 = getHashEntry(setup_file_hash, token_04);
10137 char *value_05 = getHashEntry(setup_file_hash, token_05);
10138 char *value_06 = getHashEntry(setup_file_hash, token_06);
10139 char *value_07 = getHashEntry(setup_file_hash, token_07);
10140 char *value_08 = getHashEntry(setup_file_hash, token_08);
10141 char *value_09 = getHashEntry(setup_file_hash, token_09);
10142 char *value_10 = getHashEntry(setup_file_hash, token_10);
10143 char *value_11 = getHashEntry(setup_file_hash, token_11);
10144 char *value_12 = getHashEntry(setup_file_hash, token_12);
10145 char *value_13 = getHashEntry(setup_file_hash, token_13);
10146 char *value_14 = getHashEntry(setup_file_hash, token_14);
10147 char *value_15 = getHashEntry(setup_file_hash, token_15);
10149 if (value_w1 != NULL)
10150 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10151 if (value_w2 != NULL)
10152 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10153 if (value_01 != NULL)
10154 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10155 if (value_02 != NULL)
10156 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10157 if (value_03 != NULL)
10158 viewport.playfield[i].width = get_token_parameter_value(token_03,
10160 if (value_04 != NULL)
10161 viewport.playfield[i].height = get_token_parameter_value(token_04,
10163 if (value_05 != NULL)
10164 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10166 if (value_06 != NULL)
10167 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10168 if (value_07 != NULL)
10169 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10170 if (value_08 != NULL)
10171 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10172 if (value_09 != NULL)
10173 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10174 if (value_10 != NULL)
10175 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10177 if (value_11 != NULL)
10178 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10179 if (value_12 != NULL)
10180 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10181 if (value_13 != NULL)
10182 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10183 if (value_14 != NULL)
10184 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10185 if (value_15 != NULL)
10186 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10190 /* special case: initialize with default values that may be overwritten */
10191 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10192 for (i = 0; title_info[i].info != NULL; i++)
10194 struct TitleFadingInfo *info = title_info[i].info;
10195 char *base_token = title_info[i].text;
10197 for (j = 0; title_tokens[j].type != -1; j++)
10199 char *token = getStringCat2(base_token, title_tokens[j].text);
10200 char *value = getHashEntry(setup_file_hash, token);
10204 int parameter_value = get_token_parameter_value(token, value);
10208 *(int *)title_tokens[j].value = (int)parameter_value;
10217 /* special case: initialize with default values that may be overwritten */
10218 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10219 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10221 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10222 char *base_token = titlemessage_arrays[i].text;
10224 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10226 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10227 char *value = getHashEntry(setup_file_hash, token);
10231 int parameter_value = get_token_parameter_value(token, value);
10233 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10237 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10238 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10240 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10250 /* read (and overwrite with) values that may be specified in config file */
10251 for (i = 0; image_config_vars[i].token != NULL; i++)
10253 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10255 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10256 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10257 *image_config_vars[i].value =
10258 get_token_parameter_value(image_config_vars[i].token, value);
10261 freeSetupFileHash(setup_file_hash);
10264 void LoadMenuDesignSettings(void)
10266 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10268 InitMenuDesignSettings_Static();
10269 InitMenuDesignSettings_SpecialPreProcessing();
10271 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10273 /* first look for special settings configured in level series config */
10274 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10276 if (fileExists(filename_base))
10277 LoadMenuDesignSettingsFromFilename(filename_base);
10280 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10282 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10283 LoadMenuDesignSettingsFromFilename(filename_local);
10285 InitMenuDesignSettings_SpecialPostProcessing();
10288 void LoadMenuDesignSettings_AfterGraphics(void)
10290 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10293 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10295 char *filename = getEditorSetupFilename();
10296 SetupFileList *setup_file_list, *list;
10297 SetupFileHash *element_hash;
10298 int num_unknown_tokens = 0;
10301 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10304 element_hash = newSetupFileHash();
10306 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10307 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10309 /* determined size may be larger than needed (due to unknown elements) */
10311 for (list = setup_file_list; list != NULL; list = list->next)
10314 /* add space for up to 3 more elements for padding that may be needed */
10315 *num_elements += 3;
10317 /* free memory for old list of elements, if needed */
10318 checked_free(*elements);
10320 /* allocate memory for new list of elements */
10321 *elements = checked_malloc(*num_elements * sizeof(int));
10324 for (list = setup_file_list; list != NULL; list = list->next)
10326 char *value = getHashEntry(element_hash, list->token);
10328 if (value == NULL) /* try to find obsolete token mapping */
10330 char *mapped_token = get_mapped_token(list->token);
10332 if (mapped_token != NULL)
10334 value = getHashEntry(element_hash, mapped_token);
10336 free(mapped_token);
10342 (*elements)[(*num_elements)++] = atoi(value);
10346 if (num_unknown_tokens == 0)
10348 Error(ERR_INFO_LINE, "-");
10349 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10350 Error(ERR_INFO, "- config file: '%s'", filename);
10352 num_unknown_tokens++;
10355 Error(ERR_INFO, "- token: '%s'", list->token);
10359 if (num_unknown_tokens > 0)
10360 Error(ERR_INFO_LINE, "-");
10362 while (*num_elements % 4) /* pad with empty elements, if needed */
10363 (*elements)[(*num_elements)++] = EL_EMPTY;
10365 freeSetupFileList(setup_file_list);
10366 freeSetupFileHash(element_hash);
10369 for (i = 0; i < *num_elements; i++)
10370 printf("editor: element '%s' [%d]\n",
10371 element_info[(*elements)[i]].token_name, (*elements)[i]);
10375 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10378 SetupFileHash *setup_file_hash = NULL;
10379 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10380 char *filename_music, *filename_prefix, *filename_info;
10386 token_to_value_ptr[] =
10388 { "title_header", &tmp_music_file_info.title_header },
10389 { "artist_header", &tmp_music_file_info.artist_header },
10390 { "album_header", &tmp_music_file_info.album_header },
10391 { "year_header", &tmp_music_file_info.year_header },
10393 { "title", &tmp_music_file_info.title },
10394 { "artist", &tmp_music_file_info.artist },
10395 { "album", &tmp_music_file_info.album },
10396 { "year", &tmp_music_file_info.year },
10402 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10403 getCustomMusicFilename(basename));
10405 if (filename_music == NULL)
10408 /* ---------- try to replace file extension ---------- */
10410 filename_prefix = getStringCopy(filename_music);
10411 if (strrchr(filename_prefix, '.') != NULL)
10412 *strrchr(filename_prefix, '.') = '\0';
10413 filename_info = getStringCat2(filename_prefix, ".txt");
10415 if (fileExists(filename_info))
10416 setup_file_hash = loadSetupFileHash(filename_info);
10418 free(filename_prefix);
10419 free(filename_info);
10421 if (setup_file_hash == NULL)
10423 /* ---------- try to add file extension ---------- */
10425 filename_prefix = getStringCopy(filename_music);
10426 filename_info = getStringCat2(filename_prefix, ".txt");
10428 if (fileExists(filename_info))
10429 setup_file_hash = loadSetupFileHash(filename_info);
10431 free(filename_prefix);
10432 free(filename_info);
10435 if (setup_file_hash == NULL)
10438 /* ---------- music file info found ---------- */
10440 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10442 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10444 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10446 *token_to_value_ptr[i].value_ptr =
10447 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10450 tmp_music_file_info.basename = getStringCopy(basename);
10451 tmp_music_file_info.music = music;
10452 tmp_music_file_info.is_sound = is_sound;
10454 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10455 *new_music_file_info = tmp_music_file_info;
10457 return new_music_file_info;
10460 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10462 return get_music_file_info_ext(basename, music, FALSE);
10465 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10467 return get_music_file_info_ext(basename, sound, TRUE);
10470 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10471 char *basename, boolean is_sound)
10473 for (; list != NULL; list = list->next)
10474 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10480 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10482 return music_info_listed_ext(list, basename, FALSE);
10485 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10487 return music_info_listed_ext(list, basename, TRUE);
10490 void LoadMusicInfo(void)
10492 char *music_directory = getCustomMusicDirectory();
10493 int num_music = getMusicListSize();
10494 int num_music_noconf = 0;
10495 int num_sounds = getSoundListSize();
10497 DirectoryEntry *dir_entry;
10498 struct FileInfo *music, *sound;
10499 struct MusicFileInfo *next, **new;
10502 while (music_file_info != NULL)
10504 next = music_file_info->next;
10506 checked_free(music_file_info->basename);
10508 checked_free(music_file_info->title_header);
10509 checked_free(music_file_info->artist_header);
10510 checked_free(music_file_info->album_header);
10511 checked_free(music_file_info->year_header);
10513 checked_free(music_file_info->title);
10514 checked_free(music_file_info->artist);
10515 checked_free(music_file_info->album);
10516 checked_free(music_file_info->year);
10518 free(music_file_info);
10520 music_file_info = next;
10523 new = &music_file_info;
10525 for (i = 0; i < num_music; i++)
10527 music = getMusicListEntry(i);
10529 if (music->filename == NULL)
10532 if (strEqual(music->filename, UNDEFINED_FILENAME))
10535 /* a configured file may be not recognized as music */
10536 if (!FileIsMusic(music->filename))
10539 if (!music_info_listed(music_file_info, music->filename))
10541 *new = get_music_file_info(music->filename, i);
10544 new = &(*new)->next;
10548 if ((dir = openDirectory(music_directory)) == NULL)
10550 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10554 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10556 char *basename = dir_entry->basename;
10557 boolean music_already_used = FALSE;
10560 /* skip all music files that are configured in music config file */
10561 for (i = 0; i < num_music; i++)
10563 music = getMusicListEntry(i);
10565 if (music->filename == NULL)
10568 if (strEqual(basename, music->filename))
10570 music_already_used = TRUE;
10575 if (music_already_used)
10578 if (!FileIsMusic(dir_entry->filename))
10581 if (!music_info_listed(music_file_info, basename))
10583 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10586 new = &(*new)->next;
10589 num_music_noconf++;
10592 closeDirectory(dir);
10594 for (i = 0; i < num_sounds; i++)
10596 sound = getSoundListEntry(i);
10598 if (sound->filename == NULL)
10601 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10604 /* a configured file may be not recognized as sound */
10605 if (!FileIsSound(sound->filename))
10608 if (!sound_info_listed(music_file_info, sound->filename))
10610 *new = get_sound_file_info(sound->filename, i);
10612 new = &(*new)->next;
10617 static void add_helpanim_entry(int element, int action, int direction,
10618 int delay, int *num_list_entries)
10620 struct HelpAnimInfo *new_list_entry;
10621 (*num_list_entries)++;
10624 checked_realloc(helpanim_info,
10625 *num_list_entries * sizeof(struct HelpAnimInfo));
10626 new_list_entry = &helpanim_info[*num_list_entries - 1];
10628 new_list_entry->element = element;
10629 new_list_entry->action = action;
10630 new_list_entry->direction = direction;
10631 new_list_entry->delay = delay;
10634 static void print_unknown_token(char *filename, char *token, int token_nr)
10638 Error(ERR_INFO_LINE, "-");
10639 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10640 Error(ERR_INFO, "- config file: '%s'", filename);
10643 Error(ERR_INFO, "- token: '%s'", token);
10646 static void print_unknown_token_end(int token_nr)
10649 Error(ERR_INFO_LINE, "-");
10652 void LoadHelpAnimInfo(void)
10654 char *filename = getHelpAnimFilename();
10655 SetupFileList *setup_file_list = NULL, *list;
10656 SetupFileHash *element_hash, *action_hash, *direction_hash;
10657 int num_list_entries = 0;
10658 int num_unknown_tokens = 0;
10661 if (fileExists(filename))
10662 setup_file_list = loadSetupFileList(filename);
10664 if (setup_file_list == NULL)
10666 /* use reliable default values from static configuration */
10667 SetupFileList *insert_ptr;
10669 insert_ptr = setup_file_list =
10670 newSetupFileList(helpanim_config[0].token,
10671 helpanim_config[0].value);
10673 for (i = 1; helpanim_config[i].token; i++)
10674 insert_ptr = addListEntry(insert_ptr,
10675 helpanim_config[i].token,
10676 helpanim_config[i].value);
10679 element_hash = newSetupFileHash();
10680 action_hash = newSetupFileHash();
10681 direction_hash = newSetupFileHash();
10683 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10684 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10686 for (i = 0; i < NUM_ACTIONS; i++)
10687 setHashEntry(action_hash, element_action_info[i].suffix,
10688 i_to_a(element_action_info[i].value));
10690 /* do not store direction index (bit) here, but direction value! */
10691 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10692 setHashEntry(direction_hash, element_direction_info[i].suffix,
10693 i_to_a(1 << element_direction_info[i].value));
10695 for (list = setup_file_list; list != NULL; list = list->next)
10697 char *element_token, *action_token, *direction_token;
10698 char *element_value, *action_value, *direction_value;
10699 int delay = atoi(list->value);
10701 if (strEqual(list->token, "end"))
10703 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10708 /* first try to break element into element/action/direction parts;
10709 if this does not work, also accept combined "element[.act][.dir]"
10710 elements (like "dynamite.active"), which are unique elements */
10712 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10714 element_value = getHashEntry(element_hash, list->token);
10715 if (element_value != NULL) /* element found */
10716 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10717 &num_list_entries);
10720 /* no further suffixes found -- this is not an element */
10721 print_unknown_token(filename, list->token, num_unknown_tokens++);
10727 /* token has format "<prefix>.<something>" */
10729 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10730 direction_token = action_token; /* ... or direction */
10732 element_token = getStringCopy(list->token);
10733 *strchr(element_token, '.') = '\0';
10735 element_value = getHashEntry(element_hash, element_token);
10737 if (element_value == NULL) /* this is no element */
10739 element_value = getHashEntry(element_hash, list->token);
10740 if (element_value != NULL) /* combined element found */
10741 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10742 &num_list_entries);
10744 print_unknown_token(filename, list->token, num_unknown_tokens++);
10746 free(element_token);
10751 action_value = getHashEntry(action_hash, action_token);
10753 if (action_value != NULL) /* action found */
10755 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10756 &num_list_entries);
10758 free(element_token);
10763 direction_value = getHashEntry(direction_hash, direction_token);
10765 if (direction_value != NULL) /* direction found */
10767 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10768 &num_list_entries);
10770 free(element_token);
10775 if (strchr(action_token + 1, '.') == NULL)
10777 /* no further suffixes found -- this is not an action nor direction */
10779 element_value = getHashEntry(element_hash, list->token);
10780 if (element_value != NULL) /* combined element found */
10781 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10782 &num_list_entries);
10784 print_unknown_token(filename, list->token, num_unknown_tokens++);
10786 free(element_token);
10791 /* token has format "<prefix>.<suffix>.<something>" */
10793 direction_token = strchr(action_token + 1, '.');
10795 action_token = getStringCopy(action_token);
10796 *strchr(action_token + 1, '.') = '\0';
10798 action_value = getHashEntry(action_hash, action_token);
10800 if (action_value == NULL) /* this is no action */
10802 element_value = getHashEntry(element_hash, list->token);
10803 if (element_value != NULL) /* combined element found */
10804 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10805 &num_list_entries);
10807 print_unknown_token(filename, list->token, num_unknown_tokens++);
10809 free(element_token);
10810 free(action_token);
10815 direction_value = getHashEntry(direction_hash, direction_token);
10817 if (direction_value != NULL) /* direction found */
10819 add_helpanim_entry(atoi(element_value), atoi(action_value),
10820 atoi(direction_value), delay, &num_list_entries);
10822 free(element_token);
10823 free(action_token);
10828 /* this is no direction */
10830 element_value = getHashEntry(element_hash, list->token);
10831 if (element_value != NULL) /* combined element found */
10832 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10833 &num_list_entries);
10835 print_unknown_token(filename, list->token, num_unknown_tokens++);
10837 free(element_token);
10838 free(action_token);
10841 print_unknown_token_end(num_unknown_tokens);
10843 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10844 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10846 freeSetupFileList(setup_file_list);
10847 freeSetupFileHash(element_hash);
10848 freeSetupFileHash(action_hash);
10849 freeSetupFileHash(direction_hash);
10852 for (i = 0; i < num_list_entries; i++)
10853 printf("::: '%s': %d, %d, %d => %d\n",
10854 EL_NAME(helpanim_info[i].element),
10855 helpanim_info[i].element,
10856 helpanim_info[i].action,
10857 helpanim_info[i].direction,
10858 helpanim_info[i].delay);
10862 void LoadHelpTextInfo(void)
10864 char *filename = getHelpTextFilename();
10867 if (helptext_info != NULL)
10869 freeSetupFileHash(helptext_info);
10870 helptext_info = NULL;
10873 if (fileExists(filename))
10874 helptext_info = loadSetupFileHash(filename);
10876 if (helptext_info == NULL)
10878 /* use reliable default values from static configuration */
10879 helptext_info = newSetupFileHash();
10881 for (i = 0; helptext_config[i].token; i++)
10882 setHashEntry(helptext_info,
10883 helptext_config[i].token,
10884 helptext_config[i].value);
10888 BEGIN_HASH_ITERATION(helptext_info, itr)
10890 printf("::: '%s' => '%s'\n",
10891 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10893 END_HASH_ITERATION(hash, itr)
10898 /* ------------------------------------------------------------------------- */
10899 /* convert levels */
10900 /* ------------------------------------------------------------------------- */
10902 #define MAX_NUM_CONVERT_LEVELS 1000
10904 void ConvertLevels(void)
10906 static LevelDirTree *convert_leveldir = NULL;
10907 static int convert_level_nr = -1;
10908 static int num_levels_handled = 0;
10909 static int num_levels_converted = 0;
10910 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10913 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10914 global.convert_leveldir);
10916 if (convert_leveldir == NULL)
10917 Error(ERR_EXIT, "no such level identifier: '%s'",
10918 global.convert_leveldir);
10920 leveldir_current = convert_leveldir;
10922 if (global.convert_level_nr != -1)
10924 convert_leveldir->first_level = global.convert_level_nr;
10925 convert_leveldir->last_level = global.convert_level_nr;
10928 convert_level_nr = convert_leveldir->first_level;
10930 PrintLine("=", 79);
10931 Print("Converting levels\n");
10932 PrintLine("-", 79);
10933 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10934 Print("Level series name: '%s'\n", convert_leveldir->name);
10935 Print("Level series author: '%s'\n", convert_leveldir->author);
10936 Print("Number of levels: %d\n", convert_leveldir->levels);
10937 PrintLine("=", 79);
10940 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10941 levels_failed[i] = FALSE;
10943 while (convert_level_nr <= convert_leveldir->last_level)
10945 char *level_filename;
10948 level_nr = convert_level_nr++;
10950 Print("Level %03d: ", level_nr);
10952 LoadLevel(level_nr);
10953 if (level.no_level_file || level.no_valid_file)
10955 Print("(no level)\n");
10959 Print("converting level ... ");
10961 level_filename = getDefaultLevelFilename(level_nr);
10962 new_level = !fileExists(level_filename);
10966 SaveLevel(level_nr);
10968 num_levels_converted++;
10970 Print("converted.\n");
10974 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10975 levels_failed[level_nr] = TRUE;
10977 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10980 num_levels_handled++;
10984 PrintLine("=", 79);
10985 Print("Number of levels handled: %d\n", num_levels_handled);
10986 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10987 (num_levels_handled ?
10988 num_levels_converted * 100 / num_levels_handled : 0));
10989 PrintLine("-", 79);
10990 Print("Summary (for automatic parsing by scripts):\n");
10991 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10992 convert_leveldir->identifier, num_levels_converted,
10993 num_levels_handled,
10994 (num_levels_handled ?
10995 num_levels_converted * 100 / num_levels_handled : 0));
10997 if (num_levels_handled != num_levels_converted)
10999 Print(", FAILED:");
11000 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11001 if (levels_failed[i])
11006 PrintLine("=", 79);
11008 CloseAllAndExit(0);
11012 /* ------------------------------------------------------------------------- */
11013 /* create and save images for use in level sketches (raw BMP format) */
11014 /* ------------------------------------------------------------------------- */
11016 void CreateLevelSketchImages(void)
11018 #if defined(TARGET_SDL)
11023 InitElementPropertiesGfxElement();
11025 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11026 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11028 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11030 Bitmap *src_bitmap;
11032 int element = getMappedElement(i);
11033 int graphic = el2edimg(element);
11034 char basename1[16];
11035 char basename2[16];
11039 sprintf(basename1, "%03d.bmp", i);
11040 sprintf(basename2, "%03ds.bmp", i);
11042 filename1 = getPath2(global.create_images_dir, basename1);
11043 filename2 = getPath2(global.create_images_dir, basename2);
11045 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
11046 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
11049 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11050 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11052 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
11053 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
11055 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11056 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11062 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11065 FreeBitmap(bitmap1);
11066 FreeBitmap(bitmap2);
11071 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11073 CloseAllAndExit(0);
11078 /* ------------------------------------------------------------------------- */
11079 /* create and save images for custom and group elements (raw BMP format) */
11080 /* ------------------------------------------------------------------------- */
11082 void CreateCustomElementImages(char *directory)
11084 #if defined(TARGET_SDL)
11085 char *src_basename = "RocksCE-template.ilbm";
11086 char *dst_basename = "RocksCE.bmp";
11087 char *src_filename = getPath2(directory, src_basename);
11088 char *dst_filename = getPath2(directory, dst_basename);
11089 Bitmap *src_bitmap;
11091 int yoffset_ce = 0;
11092 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11095 SDLInitVideoDisplay();
11097 ReCreateBitmap(&backbuffer, video.width, video.height);
11099 src_bitmap = LoadImage(src_filename);
11101 bitmap = CreateBitmap(TILEX * 16 * 2,
11102 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11105 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11112 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11113 TILEX * x, TILEY * y + yoffset_ce);
11115 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11117 TILEX * x + TILEX * 16,
11118 TILEY * y + yoffset_ce);
11120 for (j = 2; j >= 0; j--)
11124 BlitBitmap(src_bitmap, bitmap,
11125 TILEX + c * 7, 0, 6, 10,
11126 TILEX * x + 6 + j * 7,
11127 TILEY * y + 11 + yoffset_ce);
11129 BlitBitmap(src_bitmap, bitmap,
11130 TILEX + c * 8, TILEY, 6, 10,
11131 TILEX * 16 + TILEX * x + 6 + j * 8,
11132 TILEY * y + 10 + yoffset_ce);
11138 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11145 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11146 TILEX * x, TILEY * y + yoffset_ge);
11148 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11150 TILEX * x + TILEX * 16,
11151 TILEY * y + yoffset_ge);
11153 for (j = 1; j >= 0; j--)
11157 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11158 TILEX * x + 6 + j * 10,
11159 TILEY * y + 11 + yoffset_ge);
11161 BlitBitmap(src_bitmap, bitmap,
11162 TILEX + c * 8, TILEY + 12, 6, 10,
11163 TILEX * 16 + TILEX * x + 10 + j * 8,
11164 TILEY * y + 10 + yoffset_ge);
11170 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11171 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11173 FreeBitmap(bitmap);
11175 CloseAllAndExit(0);