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
261 static struct LevelFileConfigInfo chunk_config_ELEM[] =
263 /* (these values are the same for each player) */
266 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
267 &li.block_last_field, FALSE /* default case for EM levels */
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
272 &li.sp_block_last_field, TRUE /* default case for SP levels */
276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
277 &li.instant_relocation, FALSE
281 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
282 &li.can_pass_to_walkable, FALSE
286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
287 &li.block_snap_field, TRUE
291 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
292 &li.continuous_snapping, TRUE
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
297 &li.shifted_relocation, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
302 &li.lazy_relocation, FALSE
305 /* (these values are different for each player) */
308 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
309 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
314 &li.initial_player_gravity[0], FALSE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
319 &li.use_start_element[0], FALSE
323 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
324 &li.start_element[0], EL_PLAYER_1
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
329 &li.use_artwork_element[0], FALSE
333 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
334 &li.artwork_element[0], EL_PLAYER_1
338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
339 &li.use_explosion_element[0], FALSE
343 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
344 &li.explosion_element[0], EL_PLAYER_1
348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
349 &li.use_initial_inventory[0], FALSE
353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
354 &li.initial_inventory_size[0], 1
358 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
359 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
360 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
365 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
366 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
371 &li.initial_player_gravity[1], FALSE
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
376 &li.use_start_element[1], FALSE
380 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
381 &li.start_element[1], EL_PLAYER_2
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
386 &li.use_artwork_element[1], FALSE
390 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
391 &li.artwork_element[1], EL_PLAYER_2
395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
396 &li.use_explosion_element[1], FALSE
400 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
401 &li.explosion_element[1], EL_PLAYER_2
405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
406 &li.use_initial_inventory[1], FALSE
410 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
411 &li.initial_inventory_size[1], 1
415 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
416 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
417 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
422 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
423 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
428 &li.initial_player_gravity[2], FALSE
432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
433 &li.use_start_element[2], FALSE
437 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
438 &li.start_element[2], EL_PLAYER_3
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
443 &li.use_artwork_element[2], FALSE
447 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
448 &li.artwork_element[2], EL_PLAYER_3
452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
453 &li.use_explosion_element[2], FALSE
457 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
458 &li.explosion_element[2], EL_PLAYER_3
462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
463 &li.use_initial_inventory[2], FALSE
467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
468 &li.initial_inventory_size[2], 1
472 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
473 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
474 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
479 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
480 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
485 &li.initial_player_gravity[3], FALSE
489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
490 &li.use_start_element[3], FALSE
494 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
495 &li.start_element[3], EL_PLAYER_4
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
500 &li.use_artwork_element[3], FALSE
504 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
505 &li.artwork_element[3], EL_PLAYER_4
509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
510 &li.use_explosion_element[3], FALSE
514 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
515 &li.explosion_element[3], EL_PLAYER_4
519 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
520 &li.use_initial_inventory[3], FALSE
524 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
525 &li.initial_inventory_size[3], 1
529 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
530 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
531 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
536 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
537 &li.score[SC_EMERALD], 10
542 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
543 &li.score[SC_DIAMOND], 10
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_BUG], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_SPACESHIP], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_PACMAN], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_NUT], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_DYNAMITE], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_KEY], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_PEARL], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_CRYSTAL], 10
596 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
597 &li.amoeba_content, EL_DIAMOND
601 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
606 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
607 &li.grow_into_diggable, TRUE
612 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
613 &li.yamyam_content, EL_ROCK, NULL,
614 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
618 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
619 &li.score[SC_YAMYAM], 10
624 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 &li.score[SC_ROBOT], 10
629 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
635 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
642 &li.time_magic_wall, 10
647 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
648 &li.game_of_life[0], 2
652 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
653 &li.game_of_life[1], 3
657 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
658 &li.game_of_life[2], 3
662 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
663 &li.game_of_life[3], 3
668 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
678 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
683 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
688 EL_TIMEGATE_SWITCH, -1,
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
690 &li.time_timegate, 10
694 EL_LIGHT_SWITCH_ACTIVE, -1,
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
700 EL_SHIELD_NORMAL, -1,
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
702 &li.shield_normal_time, 10
705 EL_SHIELD_NORMAL, -1,
706 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
707 &li.score[SC_SHIELD], 10
711 EL_SHIELD_DEADLY, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.shield_deadly_time, 10
716 EL_SHIELD_DEADLY, -1,
717 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
718 &li.score[SC_SHIELD], 10
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
728 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
729 &li.extra_time_score, 10
733 EL_TIME_ORB_FULL, -1,
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
735 &li.time_orb_time, 10
738 EL_TIME_ORB_FULL, -1,
739 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
740 &li.use_time_orb_bug, FALSE
745 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
746 &li.use_spring_bug, FALSE
751 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
752 &li.android_move_time, 10
756 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
757 &li.android_clone_time, 10
761 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
762 &li.android_clone_element[0], EL_EMPTY, NULL,
763 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
768 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
773 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
778 EL_EMC_MAGNIFIER, -1,
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 &li.magnify_score, 10
783 EL_EMC_MAGNIFIER, -1,
784 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
789 EL_EMC_MAGIC_BALL, -1,
790 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
794 EL_EMC_MAGIC_BALL, -1,
795 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
796 &li.ball_random, FALSE
799 EL_EMC_MAGIC_BALL, -1,
800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
801 &li.ball_state_initial, FALSE
804 EL_EMC_MAGIC_BALL, -1,
805 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
806 &li.ball_content, EL_EMPTY, NULL,
807 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.mm_laser_red, FALSE
817 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
818 &li.mm_laser_green, FALSE
822 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
823 &li.mm_laser_blue, TRUE
828 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
829 &li.df_laser_red, TRUE
833 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
834 &li.df_laser_green, TRUE
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
839 &li.df_laser_blue, FALSE
843 EL_MM_FUSE_ACTIVE, -1,
844 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
854 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
858 EL_MM_STEEL_BLOCK, -1,
859 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
860 &li.mm_time_block, 75
864 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
865 &li.score[SC_ELEM_BONUS], 10
868 /* ---------- unused values ----------------------------------------------- */
871 EL_UNKNOWN, SAVE_CONF_NEVER,
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 &li.score[SC_UNKNOWN_15], 10
883 static struct LevelFileConfigInfo chunk_config_NOTE[] =
887 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
888 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
892 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
893 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
898 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
899 &xx_envelope.autowrap, FALSE
903 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
904 &xx_envelope.centered, FALSE
909 TYPE_STRING, CONF_VALUE_BYTES(1),
910 &xx_envelope.text, -1, NULL,
911 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
912 &xx_default_string_empty[0]
922 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
926 TYPE_STRING, CONF_VALUE_BYTES(1),
927 &xx_ei.description[0], -1,
928 &yy_ei.description[0],
929 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
930 &xx_default_description[0]
935 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
936 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
937 &yy_ei.properties[EP_BITFIELD_BASE_NR]
939 #if ENABLE_RESERVED_CODE
940 /* (reserved for later use) */
943 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
944 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
945 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &xx_ei.use_gfx_element, FALSE,
953 &yy_ei.use_gfx_element
957 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
958 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
959 &yy_ei.gfx_element_initial
964 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
965 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
966 &yy_ei.access_direction
971 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
972 &xx_ei.collect_score_initial, 10,
973 &yy_ei.collect_score_initial
977 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
978 &xx_ei.collect_count_initial, 1,
979 &yy_ei.collect_count_initial
984 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
985 &xx_ei.ce_value_fixed_initial, 0,
986 &yy_ei.ce_value_fixed_initial
990 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
991 &xx_ei.ce_value_random_initial, 0,
992 &yy_ei.ce_value_random_initial
996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
997 &xx_ei.use_last_ce_value, FALSE,
998 &yy_ei.use_last_ce_value
1003 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1004 &xx_ei.push_delay_fixed, 8,
1005 &yy_ei.push_delay_fixed
1009 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1010 &xx_ei.push_delay_random, 8,
1011 &yy_ei.push_delay_random
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1016 &xx_ei.drop_delay_fixed, 0,
1017 &yy_ei.drop_delay_fixed
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1022 &xx_ei.drop_delay_random, 0,
1023 &yy_ei.drop_delay_random
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1028 &xx_ei.move_delay_fixed, 0,
1029 &yy_ei.move_delay_fixed
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1034 &xx_ei.move_delay_random, 0,
1035 &yy_ei.move_delay_random
1040 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1041 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1046 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1047 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1048 &yy_ei.move_direction_initial
1052 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1053 &xx_ei.move_stepsize, TILEX / 8,
1054 &yy_ei.move_stepsize
1059 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1060 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1061 &yy_ei.move_enter_element
1065 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1066 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1067 &yy_ei.move_leave_element
1071 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1072 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1073 &yy_ei.move_leave_type
1078 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1079 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1080 &yy_ei.slippery_type
1085 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1086 &xx_ei.explosion_type, EXPLODES_3X3,
1087 &yy_ei.explosion_type
1091 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1092 &xx_ei.explosion_delay, 16,
1093 &yy_ei.explosion_delay
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1098 &xx_ei.ignition_delay, 8,
1099 &yy_ei.ignition_delay
1104 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1105 &xx_ei.content, EL_EMPTY_SPACE,
1107 &xx_num_contents, 1, 1
1110 /* ---------- "num_change_pages" must be the last entry ------------------- */
1113 -1, SAVE_CONF_ALWAYS,
1114 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1115 &xx_ei.num_change_pages, 1,
1116 &yy_ei.num_change_pages
1127 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1129 /* ---------- "current_change_page" must be the first entry --------------- */
1132 -1, SAVE_CONF_ALWAYS,
1133 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1134 &xx_current_change_page, -1
1137 /* ---------- (the remaining entries can be in any order) ----------------- */
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1142 &xx_change.can_change, FALSE
1147 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1148 &xx_event_bits[0], 0
1152 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1153 &xx_event_bits[1], 0
1158 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1159 &xx_change.trigger_player, CH_PLAYER_ANY
1163 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1164 &xx_change.trigger_side, CH_SIDE_ANY
1168 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1169 &xx_change.trigger_page, CH_PAGE_ANY
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1175 &xx_change.target_element, EL_EMPTY_SPACE
1180 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1181 &xx_change.delay_fixed, 0
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1186 &xx_change.delay_random, 0
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1191 &xx_change.delay_frames, FRAMES_PER_SECOND
1196 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1197 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1202 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1203 &xx_change.explode, FALSE
1207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1208 &xx_change.use_target_content, FALSE
1212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1213 &xx_change.only_if_complete, FALSE
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1218 &xx_change.use_random_replace, FALSE
1222 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1223 &xx_change.random_percentage, 100
1227 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1228 &xx_change.replace_when, CP_WHEN_EMPTY
1233 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1234 &xx_change.has_action, FALSE
1238 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1239 &xx_change.action_type, CA_NO_ACTION
1243 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1244 &xx_change.action_mode, CA_MODE_UNDEFINED
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1249 &xx_change.action_arg, CA_ARG_UNDEFINED
1254 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1255 &xx_change.action_element, EL_EMPTY_SPACE
1260 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1261 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1262 &xx_num_contents, 1, 1
1272 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1276 TYPE_STRING, CONF_VALUE_BYTES(1),
1277 &xx_ei.description[0], -1, NULL,
1278 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1279 &xx_default_description[0]
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1285 &xx_ei.use_gfx_element, FALSE
1289 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1290 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1295 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1296 &xx_group.choice_mode, ANIM_RANDOM
1301 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1302 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1303 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1313 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1318 &li.block_snap_field, TRUE
1322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1323 &li.continuous_snapping, TRUE
1327 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1328 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1333 &li.use_start_element[0], FALSE
1337 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1338 &li.start_element[0], EL_PLAYER_1
1342 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1343 &li.use_artwork_element[0], FALSE
1347 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1348 &li.artwork_element[0], EL_PLAYER_1
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1353 &li.use_explosion_element[0], FALSE
1357 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1358 &li.explosion_element[0], EL_PLAYER_1
1373 filetype_id_list[] =
1375 { LEVEL_FILE_TYPE_RND, "RND" },
1376 { LEVEL_FILE_TYPE_BD, "BD" },
1377 { LEVEL_FILE_TYPE_EM, "EM" },
1378 { LEVEL_FILE_TYPE_SP, "SP" },
1379 { LEVEL_FILE_TYPE_DX, "DX" },
1380 { LEVEL_FILE_TYPE_SB, "SB" },
1381 { LEVEL_FILE_TYPE_DC, "DC" },
1382 { LEVEL_FILE_TYPE_MM, "MM" },
1383 { LEVEL_FILE_TYPE_MM, "DF" },
1388 /* ========================================================================= */
1389 /* level file functions */
1390 /* ========================================================================= */
1392 static boolean check_special_flags(char *flag)
1394 if (strEqual(options.special_flags, flag) ||
1395 strEqual(leveldir_current->special_flags, flag))
1401 static struct DateInfo getCurrentDate()
1403 time_t epoch_seconds = time(NULL);
1404 struct tm *now = localtime(&epoch_seconds);
1405 struct DateInfo date;
1407 date.year = now->tm_year + 1900;
1408 date.month = now->tm_mon + 1;
1409 date.day = now->tm_mday;
1411 date.src = DATE_SRC_CLOCK;
1416 static void resetEventFlags(struct ElementChangeInfo *change)
1420 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1421 change->has_event[i] = FALSE;
1424 static void resetEventBits()
1428 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1429 xx_event_bits[i] = 0;
1432 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1436 /* important: only change event flag if corresponding event bit is set
1437 (this is because all xx_event_bits[] values are loaded separately,
1438 and all xx_event_bits[] values are set back to zero before loading
1439 another value xx_event_bits[x] (each value representing 32 flags)) */
1441 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1442 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1443 change->has_event[i] = TRUE;
1446 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1450 /* in contrast to the above function setEventFlagsFromEventBits(), it
1451 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1452 depending on the corresponding change->has_event[i] values here, as
1453 all xx_event_bits[] values are reset in resetEventBits() before */
1455 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1456 if (change->has_event[i])
1457 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1460 static char *getDefaultElementDescription(struct ElementInfo *ei)
1462 static char description[MAX_ELEMENT_NAME_LEN + 1];
1463 char *default_description = (ei->custom_description != NULL ?
1464 ei->custom_description :
1465 ei->editor_description);
1468 /* always start with reliable default values */
1469 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1470 description[i] = '\0';
1472 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1473 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1475 return &description[0];
1478 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1480 char *default_description = getDefaultElementDescription(ei);
1483 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1484 ei->description[i] = default_description[i];
1487 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1491 for (i = 0; conf[i].data_type != -1; i++)
1493 int default_value = conf[i].default_value;
1494 int data_type = conf[i].data_type;
1495 int conf_type = conf[i].conf_type;
1496 int byte_mask = conf_type & CONF_MASK_BYTES;
1498 if (byte_mask == CONF_MASK_MULTI_BYTES)
1500 int default_num_entities = conf[i].default_num_entities;
1501 int max_num_entities = conf[i].max_num_entities;
1503 *(int *)(conf[i].num_entities) = default_num_entities;
1505 if (data_type == TYPE_STRING)
1507 char *default_string = conf[i].default_string;
1508 char *string = (char *)(conf[i].value);
1510 strncpy(string, default_string, max_num_entities);
1512 else if (data_type == TYPE_ELEMENT_LIST)
1514 int *element_array = (int *)(conf[i].value);
1517 for (j = 0; j < max_num_entities; j++)
1518 element_array[j] = default_value;
1520 else if (data_type == TYPE_CONTENT_LIST)
1522 struct Content *content = (struct Content *)(conf[i].value);
1525 for (c = 0; c < max_num_entities; c++)
1526 for (y = 0; y < 3; y++)
1527 for (x = 0; x < 3; x++)
1528 content[c].e[x][y] = default_value;
1531 else /* constant size configuration data (1, 2 or 4 bytes) */
1533 if (data_type == TYPE_BOOLEAN)
1534 *(boolean *)(conf[i].value) = default_value;
1536 *(int *) (conf[i].value) = default_value;
1541 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1545 for (i = 0; conf[i].data_type != -1; i++)
1547 int data_type = conf[i].data_type;
1548 int conf_type = conf[i].conf_type;
1549 int byte_mask = conf_type & CONF_MASK_BYTES;
1551 if (byte_mask == CONF_MASK_MULTI_BYTES)
1553 int max_num_entities = conf[i].max_num_entities;
1555 if (data_type == TYPE_STRING)
1557 char *string = (char *)(conf[i].value);
1558 char *string_copy = (char *)(conf[i].value_copy);
1560 strncpy(string_copy, string, max_num_entities);
1562 else if (data_type == TYPE_ELEMENT_LIST)
1564 int *element_array = (int *)(conf[i].value);
1565 int *element_array_copy = (int *)(conf[i].value_copy);
1568 for (j = 0; j < max_num_entities; j++)
1569 element_array_copy[j] = element_array[j];
1571 else if (data_type == TYPE_CONTENT_LIST)
1573 struct Content *content = (struct Content *)(conf[i].value);
1574 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1577 for (c = 0; c < max_num_entities; c++)
1578 for (y = 0; y < 3; y++)
1579 for (x = 0; x < 3; x++)
1580 content_copy[c].e[x][y] = content[c].e[x][y];
1583 else /* constant size configuration data (1, 2 or 4 bytes) */
1585 if (data_type == TYPE_BOOLEAN)
1586 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1588 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1593 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1597 xx_ei = *ei_from; /* copy element data into temporary buffer */
1598 yy_ei = *ei_to; /* copy element data into temporary buffer */
1600 copyConfigFromConfigList(chunk_config_CUSX_base);
1605 /* ---------- reinitialize and copy change pages ---------- */
1607 ei_to->num_change_pages = ei_from->num_change_pages;
1608 ei_to->current_change_page = ei_from->current_change_page;
1610 setElementChangePages(ei_to, ei_to->num_change_pages);
1612 for (i = 0; i < ei_to->num_change_pages; i++)
1613 ei_to->change_page[i] = ei_from->change_page[i];
1615 /* ---------- copy group element info ---------- */
1616 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1617 *ei_to->group = *ei_from->group;
1619 /* mark this custom element as modified */
1620 ei_to->modified_settings = TRUE;
1623 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1625 int change_page_size = sizeof(struct ElementChangeInfo);
1627 ei->num_change_pages = MAX(1, change_pages);
1630 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1632 if (ei->current_change_page >= ei->num_change_pages)
1633 ei->current_change_page = ei->num_change_pages - 1;
1635 ei->change = &ei->change_page[ei->current_change_page];
1638 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1640 xx_change = *change; /* copy change data into temporary buffer */
1642 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1644 *change = xx_change;
1646 resetEventFlags(change);
1648 change->direct_action = 0;
1649 change->other_action = 0;
1651 change->pre_change_function = NULL;
1652 change->change_function = NULL;
1653 change->post_change_function = NULL;
1656 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1660 li = *level; /* copy level data into temporary buffer */
1661 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1662 *level = li; /* copy temporary buffer back to level data */
1664 setLevelInfoToDefaults_EM();
1665 setLevelInfoToDefaults_SP();
1666 setLevelInfoToDefaults_MM();
1668 level->native_em_level = &native_em_level;
1669 level->native_sp_level = &native_sp_level;
1670 level->native_mm_level = &native_mm_level;
1672 level->file_version = FILE_VERSION_ACTUAL;
1673 level->game_version = GAME_VERSION_ACTUAL;
1675 level->creation_date = getCurrentDate();
1677 level->encoding_16bit_field = TRUE;
1678 level->encoding_16bit_yamyam = TRUE;
1679 level->encoding_16bit_amoeba = TRUE;
1681 /* clear level name and level author string buffers */
1682 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1683 level->name[i] = '\0';
1684 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1685 level->author[i] = '\0';
1687 /* set level name and level author to default values */
1688 strcpy(level->name, NAMELESS_LEVEL_NAME);
1689 strcpy(level->author, ANONYMOUS_NAME);
1691 /* set level playfield to playable default level with player and exit */
1692 for (x = 0; x < MAX_LEV_FIELDX; x++)
1693 for (y = 0; y < MAX_LEV_FIELDY; y++)
1694 level->field[x][y] = EL_SAND;
1696 level->field[0][0] = EL_PLAYER_1;
1697 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1699 BorderElement = EL_STEELWALL;
1701 /* detect custom elements when loading them */
1702 level->file_has_custom_elements = FALSE;
1704 /* set all bug compatibility flags to "false" => do not emulate this bug */
1705 level->use_action_after_change_bug = FALSE;
1707 if (leveldir_current)
1709 /* try to determine better author name than 'anonymous' */
1710 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1712 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1713 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1717 switch (LEVELCLASS(leveldir_current))
1719 case LEVELCLASS_TUTORIAL:
1720 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1723 case LEVELCLASS_CONTRIB:
1724 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1725 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1728 case LEVELCLASS_PRIVATE:
1729 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1730 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1734 /* keep default value */
1741 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1743 static boolean clipboard_elements_initialized = FALSE;
1746 InitElementPropertiesStatic();
1748 li = *level; /* copy level data into temporary buffer */
1749 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1750 *level = li; /* copy temporary buffer back to level data */
1752 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1755 struct ElementInfo *ei = &element_info[element];
1757 /* never initialize clipboard elements after the very first time */
1758 /* (to be able to use clipboard elements between several levels) */
1759 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1762 if (IS_ENVELOPE(element))
1764 int envelope_nr = element - EL_ENVELOPE_1;
1766 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1768 level->envelope[envelope_nr] = xx_envelope;
1771 if (IS_CUSTOM_ELEMENT(element) ||
1772 IS_GROUP_ELEMENT(element) ||
1773 IS_INTERNAL_ELEMENT(element))
1775 xx_ei = *ei; /* copy element data into temporary buffer */
1777 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1782 setElementChangePages(ei, 1);
1783 setElementChangeInfoToDefaults(ei->change);
1785 if (IS_CUSTOM_ELEMENT(element) ||
1786 IS_GROUP_ELEMENT(element) ||
1787 IS_INTERNAL_ELEMENT(element))
1789 setElementDescriptionToDefault(ei);
1791 ei->modified_settings = FALSE;
1794 if (IS_CUSTOM_ELEMENT(element) ||
1795 IS_INTERNAL_ELEMENT(element))
1797 /* internal values used in level editor */
1799 ei->access_type = 0;
1800 ei->access_layer = 0;
1801 ei->access_protected = 0;
1802 ei->walk_to_action = 0;
1803 ei->smash_targets = 0;
1806 ei->can_explode_by_fire = FALSE;
1807 ei->can_explode_smashed = FALSE;
1808 ei->can_explode_impact = FALSE;
1810 ei->current_change_page = 0;
1813 if (IS_GROUP_ELEMENT(element) ||
1814 IS_INTERNAL_ELEMENT(element))
1816 struct ElementGroupInfo *group;
1818 /* initialize memory for list of elements in group */
1819 if (ei->group == NULL)
1820 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1824 xx_group = *group; /* copy group data into temporary buffer */
1826 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1832 clipboard_elements_initialized = TRUE;
1835 static void setLevelInfoToDefaults(struct LevelInfo *level,
1836 boolean level_info_only,
1837 boolean reset_file_status)
1839 setLevelInfoToDefaults_Level(level);
1841 if (!level_info_only)
1842 setLevelInfoToDefaults_Elements(level);
1844 if (reset_file_status)
1846 level->no_valid_file = FALSE;
1847 level->no_level_file = FALSE;
1850 level->changed = FALSE;
1853 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1855 level_file_info->nr = 0;
1856 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1857 level_file_info->packed = FALSE;
1859 setString(&level_file_info->basename, NULL);
1860 setString(&level_file_info->filename, NULL);
1863 int getMappedElement_SB(int, boolean);
1865 static void ActivateLevelTemplate()
1869 if (check_special_flags("load_xsb_to_ces"))
1871 /* fill smaller playfields with padding "beyond border wall" elements */
1872 if (level.fieldx < level_template.fieldx ||
1873 level.fieldy < level_template.fieldy)
1875 short field[level.fieldx][level.fieldy];
1876 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1877 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1878 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1879 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1881 /* copy old playfield (which is smaller than the visible area) */
1882 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1883 field[x][y] = level.field[x][y];
1885 /* fill new, larger playfield with "beyond border wall" elements */
1886 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1887 level.field[x][y] = getMappedElement_SB('_', TRUE);
1889 /* copy the old playfield to the middle of the new playfield */
1890 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1891 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1893 level.fieldx = new_fieldx;
1894 level.fieldy = new_fieldy;
1898 /* Currently there is no special action needed to activate the template
1899 data, because 'element_info' property settings overwrite the original
1900 level data, while all other variables do not change. */
1902 /* Exception: 'from_level_template' elements in the original level playfield
1903 are overwritten with the corresponding elements at the same position in
1904 playfield from the level template. */
1906 for (x = 0; x < level.fieldx; x++)
1907 for (y = 0; y < level.fieldy; y++)
1908 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1909 level.field[x][y] = level_template.field[x][y];
1911 if (check_special_flags("load_xsb_to_ces"))
1913 struct LevelInfo level_backup = level;
1915 /* overwrite all individual level settings from template level settings */
1916 level = level_template;
1918 /* restore playfield size */
1919 level.fieldx = level_backup.fieldx;
1920 level.fieldy = level_backup.fieldy;
1922 /* restore playfield content */
1923 for (x = 0; x < level.fieldx; x++)
1924 for (y = 0; y < level.fieldy; y++)
1925 level.field[x][y] = level_backup.field[x][y];
1927 /* restore name and author from individual level */
1928 strcpy(level.name, level_backup.name);
1929 strcpy(level.author, level_backup.author);
1931 /* restore flag "use_custom_template" */
1932 level.use_custom_template = level_backup.use_custom_template;
1936 static char *getLevelFilenameFromBasename(char *basename)
1938 static char *filename = NULL;
1940 checked_free(filename);
1942 filename = getPath2(getCurrentLevelDir(), basename);
1947 static int getFileTypeFromBasename(char *basename)
1949 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1951 static char *filename = NULL;
1952 struct stat file_status;
1954 /* ---------- try to determine file type from filename ---------- */
1956 /* check for typical filename of a Supaplex level package file */
1957 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1958 return LEVEL_FILE_TYPE_SP;
1960 /* check for typical filename of a Diamond Caves II level package file */
1961 if (strSuffixLower(basename, ".dc") ||
1962 strSuffixLower(basename, ".dc2"))
1963 return LEVEL_FILE_TYPE_DC;
1965 /* check for typical filename of a Sokoban level package file */
1966 if (strSuffixLower(basename, ".xsb") &&
1967 strchr(basename, '%') == NULL)
1968 return LEVEL_FILE_TYPE_SB;
1970 /* ---------- try to determine file type from filesize ---------- */
1972 checked_free(filename);
1973 filename = getPath2(getCurrentLevelDir(), basename);
1975 if (stat(filename, &file_status) == 0)
1977 /* check for typical filesize of a Supaplex level package file */
1978 if (file_status.st_size == 170496)
1979 return LEVEL_FILE_TYPE_SP;
1982 return LEVEL_FILE_TYPE_UNKNOWN;
1985 static int getFileTypeFromMagicBytes(char *filename, int type)
1989 if ((file = openFile(filename, MODE_READ)))
1991 char chunk_name[CHUNK_ID_LEN + 1];
1993 getFileChunkBE(file, chunk_name, NULL);
1995 if (strEqual(chunk_name, "MMII") ||
1996 strEqual(chunk_name, "MIRR"))
1997 type = LEVEL_FILE_TYPE_MM;
2005 static boolean checkForPackageFromBasename(char *basename)
2007 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2008 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2010 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2013 static char *getSingleLevelBasenameExt(int nr, char *extension)
2015 static char basename[MAX_FILENAME_LEN];
2018 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2020 sprintf(basename, "%03d.%s", nr, extension);
2025 static char *getSingleLevelBasename(int nr)
2027 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2030 static char *getPackedLevelBasename(int type)
2032 static char basename[MAX_FILENAME_LEN];
2033 char *directory = getCurrentLevelDir();
2035 DirectoryEntry *dir_entry;
2037 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2039 if ((dir = openDirectory(directory)) == NULL)
2041 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2046 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2048 char *entry_basename = dir_entry->basename;
2049 int entry_type = getFileTypeFromBasename(entry_basename);
2051 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2053 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2056 strcpy(basename, entry_basename);
2063 closeDirectory(dir);
2068 static char *getSingleLevelFilename(int nr)
2070 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2073 #if ENABLE_UNUSED_CODE
2074 static char *getPackedLevelFilename(int type)
2076 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2080 char *getDefaultLevelFilename(int nr)
2082 return getSingleLevelFilename(nr);
2085 #if ENABLE_UNUSED_CODE
2086 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2090 lfi->packed = FALSE;
2092 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2093 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2097 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2098 int type, char *format, ...)
2100 static char basename[MAX_FILENAME_LEN];
2103 va_start(ap, format);
2104 vsprintf(basename, format, ap);
2108 lfi->packed = FALSE;
2110 setString(&lfi->basename, basename);
2111 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2114 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2120 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2121 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2124 static int getFiletypeFromID(char *filetype_id)
2126 char *filetype_id_lower;
2127 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2130 if (filetype_id == NULL)
2131 return LEVEL_FILE_TYPE_UNKNOWN;
2133 filetype_id_lower = getStringToLower(filetype_id);
2135 for (i = 0; filetype_id_list[i].id != NULL; i++)
2137 char *id_lower = getStringToLower(filetype_id_list[i].id);
2139 if (strEqual(filetype_id_lower, id_lower))
2140 filetype = filetype_id_list[i].filetype;
2144 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2148 free(filetype_id_lower);
2153 char *getLocalLevelTemplateFilename()
2155 return getDefaultLevelFilename(-1);
2158 char *getGlobalLevelTemplateFilename()
2160 /* global variable "leveldir_current" must be modified in the loop below */
2161 LevelDirTree *leveldir_current_last = leveldir_current;
2162 char *filename = NULL;
2164 /* check for template level in path from current to topmost tree node */
2166 while (leveldir_current != NULL)
2168 filename = getDefaultLevelFilename(-1);
2170 if (fileExists(filename))
2173 leveldir_current = leveldir_current->node_parent;
2176 /* restore global variable "leveldir_current" modified in above loop */
2177 leveldir_current = leveldir_current_last;
2182 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2186 /* special case: level number is negative => check for level template file */
2189 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2190 getSingleLevelBasename(-1));
2192 /* replace local level template filename with global template filename */
2193 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2195 /* no fallback if template file not existing */
2199 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2200 if (leveldir_current->level_filename != NULL)
2202 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2204 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2205 leveldir_current->level_filename, nr);
2207 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2209 if (fileExists(lfi->filename))
2212 else if (leveldir_current->level_filetype != NULL)
2214 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2216 /* check for specified native level file with standard file name */
2217 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2218 "%03d.%s", nr, LEVELFILE_EXTENSION);
2219 if (fileExists(lfi->filename))
2223 /* check for native Rocks'n'Diamonds level file */
2224 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2225 "%03d.%s", nr, LEVELFILE_EXTENSION);
2226 if (fileExists(lfi->filename))
2229 /* check for Emerald Mine level file (V1) */
2230 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2231 'a' + (nr / 10) % 26, '0' + nr % 10);
2232 if (fileExists(lfi->filename))
2234 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2235 'A' + (nr / 10) % 26, '0' + nr % 10);
2236 if (fileExists(lfi->filename))
2239 /* check for Emerald Mine level file (V2 to V5) */
2240 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2241 if (fileExists(lfi->filename))
2244 /* check for Emerald Mine level file (V6 / single mode) */
2245 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2246 if (fileExists(lfi->filename))
2248 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2249 if (fileExists(lfi->filename))
2252 /* check for Emerald Mine level file (V6 / teamwork mode) */
2253 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2254 if (fileExists(lfi->filename))
2256 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2257 if (fileExists(lfi->filename))
2260 /* check for various packed level file formats */
2261 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2262 if (fileExists(lfi->filename))
2265 /* no known level file found -- use default values (and fail later) */
2266 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2267 "%03d.%s", nr, LEVELFILE_EXTENSION);
2270 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2272 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2273 lfi->type = getFileTypeFromBasename(lfi->basename);
2275 if (lfi->type == LEVEL_FILE_TYPE_RND)
2276 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2279 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2281 /* always start with reliable default values */
2282 setFileInfoToDefaults(level_file_info);
2284 level_file_info->nr = nr; /* set requested level number */
2286 determineLevelFileInfo_Filename(level_file_info);
2287 determineLevelFileInfo_Filetype(level_file_info);
2290 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2291 struct LevelFileInfo *lfi_to)
2293 lfi_to->nr = lfi_from->nr;
2294 lfi_to->type = lfi_from->type;
2295 lfi_to->packed = lfi_from->packed;
2297 setString(&lfi_to->basename, lfi_from->basename);
2298 setString(&lfi_to->filename, lfi_from->filename);
2301 /* ------------------------------------------------------------------------- */
2302 /* functions for loading R'n'D level */
2303 /* ------------------------------------------------------------------------- */
2305 int getMappedElement(int element)
2307 /* remap some (historic, now obsolete) elements */
2311 case EL_PLAYER_OBSOLETE:
2312 element = EL_PLAYER_1;
2315 case EL_KEY_OBSOLETE:
2319 case EL_EM_KEY_1_FILE_OBSOLETE:
2320 element = EL_EM_KEY_1;
2323 case EL_EM_KEY_2_FILE_OBSOLETE:
2324 element = EL_EM_KEY_2;
2327 case EL_EM_KEY_3_FILE_OBSOLETE:
2328 element = EL_EM_KEY_3;
2331 case EL_EM_KEY_4_FILE_OBSOLETE:
2332 element = EL_EM_KEY_4;
2335 case EL_ENVELOPE_OBSOLETE:
2336 element = EL_ENVELOPE_1;
2344 if (element >= NUM_FILE_ELEMENTS)
2346 Error(ERR_WARN, "invalid level element %d", element);
2348 element = EL_UNKNOWN;
2356 int getMappedElementByVersion(int element, int game_version)
2358 /* remap some elements due to certain game version */
2360 if (game_version <= VERSION_IDENT(2,2,0,0))
2362 /* map game font elements */
2363 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2364 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2365 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2366 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2369 if (game_version < VERSION_IDENT(3,0,0,0))
2371 /* map Supaplex gravity tube elements */
2372 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2373 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2374 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2375 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2382 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2384 level->file_version = getFileVersion(file);
2385 level->game_version = getFileVersion(file);
2390 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2392 level->creation_date.year = getFile16BitBE(file);
2393 level->creation_date.month = getFile8Bit(file);
2394 level->creation_date.day = getFile8Bit(file);
2396 level->creation_date.src = DATE_SRC_LEVELFILE;
2401 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2403 int initial_player_stepsize;
2404 int initial_player_gravity;
2407 level->fieldx = getFile8Bit(file);
2408 level->fieldy = getFile8Bit(file);
2410 level->time = getFile16BitBE(file);
2411 level->gems_needed = getFile16BitBE(file);
2413 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2414 level->name[i] = getFile8Bit(file);
2415 level->name[MAX_LEVEL_NAME_LEN] = 0;
2417 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2418 level->score[i] = getFile8Bit(file);
2420 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2421 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2422 for (y = 0; y < 3; y++)
2423 for (x = 0; x < 3; x++)
2424 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2426 level->amoeba_speed = getFile8Bit(file);
2427 level->time_magic_wall = getFile8Bit(file);
2428 level->time_wheel = getFile8Bit(file);
2429 level->amoeba_content = getMappedElement(getFile8Bit(file));
2431 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2434 for (i = 0; i < MAX_PLAYERS; i++)
2435 level->initial_player_stepsize[i] = initial_player_stepsize;
2437 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2439 for (i = 0; i < MAX_PLAYERS; i++)
2440 level->initial_player_gravity[i] = initial_player_gravity;
2442 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2443 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2445 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2447 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2448 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2449 level->can_move_into_acid_bits = getFile32BitBE(file);
2450 level->dont_collide_with_bits = getFile8Bit(file);
2452 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2453 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2455 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2456 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2457 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2459 level->game_engine_type = getFile8Bit(file);
2461 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2466 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2470 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2471 level->name[i] = getFile8Bit(file);
2472 level->name[MAX_LEVEL_NAME_LEN] = 0;
2477 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2481 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2482 level->author[i] = getFile8Bit(file);
2483 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2488 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2491 int chunk_size_expected = level->fieldx * level->fieldy;
2493 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2494 stored with 16-bit encoding (and should be twice as big then).
2495 Even worse, playfield data was stored 16-bit when only yamyam content
2496 contained 16-bit elements and vice versa. */
2498 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2499 chunk_size_expected *= 2;
2501 if (chunk_size_expected != chunk_size)
2503 ReadUnusedBytesFromFile(file, chunk_size);
2504 return chunk_size_expected;
2507 for (y = 0; y < level->fieldy; y++)
2508 for (x = 0; x < level->fieldx; x++)
2509 level->field[x][y] =
2510 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2515 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2518 int header_size = 4;
2519 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2520 int chunk_size_expected = header_size + content_size;
2522 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2523 stored with 16-bit encoding (and should be twice as big then).
2524 Even worse, playfield data was stored 16-bit when only yamyam content
2525 contained 16-bit elements and vice versa. */
2527 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2528 chunk_size_expected += content_size;
2530 if (chunk_size_expected != chunk_size)
2532 ReadUnusedBytesFromFile(file, chunk_size);
2533 return chunk_size_expected;
2537 level->num_yamyam_contents = getFile8Bit(file);
2541 /* correct invalid number of content fields -- should never happen */
2542 if (level->num_yamyam_contents < 1 ||
2543 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2544 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2546 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2547 for (y = 0; y < 3; y++)
2548 for (x = 0; x < 3; x++)
2549 level->yamyam_content[i].e[x][y] =
2550 getMappedElement(level->encoding_16bit_field ?
2551 getFile16BitBE(file) : getFile8Bit(file));
2555 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2560 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2562 element = getMappedElement(getFile16BitBE(file));
2563 num_contents = getFile8Bit(file);
2565 getFile8Bit(file); /* content x size (unused) */
2566 getFile8Bit(file); /* content y size (unused) */
2568 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2570 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2571 for (y = 0; y < 3; y++)
2572 for (x = 0; x < 3; x++)
2573 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2575 /* correct invalid number of content fields -- should never happen */
2576 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2577 num_contents = STD_ELEMENT_CONTENTS;
2579 if (element == EL_YAMYAM)
2581 level->num_yamyam_contents = num_contents;
2583 for (i = 0; i < num_contents; i++)
2584 for (y = 0; y < 3; y++)
2585 for (x = 0; x < 3; x++)
2586 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2588 else if (element == EL_BD_AMOEBA)
2590 level->amoeba_content = content_array[0][0][0];
2594 Error(ERR_WARN, "cannot load content for element '%d'", element);
2600 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2606 int chunk_size_expected;
2608 element = getMappedElement(getFile16BitBE(file));
2609 if (!IS_ENVELOPE(element))
2610 element = EL_ENVELOPE_1;
2612 envelope_nr = element - EL_ENVELOPE_1;
2614 envelope_len = getFile16BitBE(file);
2616 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2617 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2619 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2621 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2622 if (chunk_size_expected != chunk_size)
2624 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2625 return chunk_size_expected;
2628 for (i = 0; i < envelope_len; i++)
2629 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2634 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2636 int num_changed_custom_elements = getFile16BitBE(file);
2637 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2640 if (chunk_size_expected != chunk_size)
2642 ReadUnusedBytesFromFile(file, chunk_size - 2);
2643 return chunk_size_expected;
2646 for (i = 0; i < num_changed_custom_elements; i++)
2648 int element = getMappedElement(getFile16BitBE(file));
2649 int properties = getFile32BitBE(file);
2651 if (IS_CUSTOM_ELEMENT(element))
2652 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2654 Error(ERR_WARN, "invalid custom element number %d", element);
2656 /* older game versions that wrote level files with CUS1 chunks used
2657 different default push delay values (not yet stored in level file) */
2658 element_info[element].push_delay_fixed = 2;
2659 element_info[element].push_delay_random = 8;
2662 level->file_has_custom_elements = TRUE;
2667 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2669 int num_changed_custom_elements = getFile16BitBE(file);
2670 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2673 if (chunk_size_expected != chunk_size)
2675 ReadUnusedBytesFromFile(file, chunk_size - 2);
2676 return chunk_size_expected;
2679 for (i = 0; i < num_changed_custom_elements; i++)
2681 int element = getMappedElement(getFile16BitBE(file));
2682 int custom_target_element = getMappedElement(getFile16BitBE(file));
2684 if (IS_CUSTOM_ELEMENT(element))
2685 element_info[element].change->target_element = custom_target_element;
2687 Error(ERR_WARN, "invalid custom element number %d", element);
2690 level->file_has_custom_elements = TRUE;
2695 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2697 int num_changed_custom_elements = getFile16BitBE(file);
2698 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2701 if (chunk_size_expected != chunk_size)
2703 ReadUnusedBytesFromFile(file, chunk_size - 2);
2704 return chunk_size_expected;
2707 for (i = 0; i < num_changed_custom_elements; i++)
2709 int element = getMappedElement(getFile16BitBE(file));
2710 struct ElementInfo *ei = &element_info[element];
2711 unsigned int event_bits;
2713 if (!IS_CUSTOM_ELEMENT(element))
2715 Error(ERR_WARN, "invalid custom element number %d", element);
2717 element = EL_INTERNAL_DUMMY;
2720 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2721 ei->description[j] = getFile8Bit(file);
2722 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2724 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2726 /* some free bytes for future properties and padding */
2727 ReadUnusedBytesFromFile(file, 7);
2729 ei->use_gfx_element = getFile8Bit(file);
2730 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2732 ei->collect_score_initial = getFile8Bit(file);
2733 ei->collect_count_initial = getFile8Bit(file);
2735 ei->push_delay_fixed = getFile16BitBE(file);
2736 ei->push_delay_random = getFile16BitBE(file);
2737 ei->move_delay_fixed = getFile16BitBE(file);
2738 ei->move_delay_random = getFile16BitBE(file);
2740 ei->move_pattern = getFile16BitBE(file);
2741 ei->move_direction_initial = getFile8Bit(file);
2742 ei->move_stepsize = getFile8Bit(file);
2744 for (y = 0; y < 3; y++)
2745 for (x = 0; x < 3; x++)
2746 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2748 event_bits = getFile32BitBE(file);
2749 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2750 if (event_bits & (1 << j))
2751 ei->change->has_event[j] = TRUE;
2753 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2755 ei->change->delay_fixed = getFile16BitBE(file);
2756 ei->change->delay_random = getFile16BitBE(file);
2757 ei->change->delay_frames = getFile16BitBE(file);
2759 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2761 ei->change->explode = getFile8Bit(file);
2762 ei->change->use_target_content = getFile8Bit(file);
2763 ei->change->only_if_complete = getFile8Bit(file);
2764 ei->change->use_random_replace = getFile8Bit(file);
2766 ei->change->random_percentage = getFile8Bit(file);
2767 ei->change->replace_when = getFile8Bit(file);
2769 for (y = 0; y < 3; y++)
2770 for (x = 0; x < 3; x++)
2771 ei->change->target_content.e[x][y] =
2772 getMappedElement(getFile16BitBE(file));
2774 ei->slippery_type = getFile8Bit(file);
2776 /* some free bytes for future properties and padding */
2777 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2779 /* mark that this custom element has been modified */
2780 ei->modified_settings = TRUE;
2783 level->file_has_custom_elements = TRUE;
2788 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2790 struct ElementInfo *ei;
2791 int chunk_size_expected;
2795 /* ---------- custom element base property values (96 bytes) ------------- */
2797 element = getMappedElement(getFile16BitBE(file));
2799 if (!IS_CUSTOM_ELEMENT(element))
2801 Error(ERR_WARN, "invalid custom element number %d", element);
2803 ReadUnusedBytesFromFile(file, chunk_size - 2);
2807 ei = &element_info[element];
2809 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2810 ei->description[i] = getFile8Bit(file);
2811 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2813 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2815 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2817 ei->num_change_pages = getFile8Bit(file);
2819 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2820 if (chunk_size_expected != chunk_size)
2822 ReadUnusedBytesFromFile(file, chunk_size - 43);
2823 return chunk_size_expected;
2826 ei->ce_value_fixed_initial = getFile16BitBE(file);
2827 ei->ce_value_random_initial = getFile16BitBE(file);
2828 ei->use_last_ce_value = getFile8Bit(file);
2830 ei->use_gfx_element = getFile8Bit(file);
2831 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2833 ei->collect_score_initial = getFile8Bit(file);
2834 ei->collect_count_initial = getFile8Bit(file);
2836 ei->drop_delay_fixed = getFile8Bit(file);
2837 ei->push_delay_fixed = getFile8Bit(file);
2838 ei->drop_delay_random = getFile8Bit(file);
2839 ei->push_delay_random = getFile8Bit(file);
2840 ei->move_delay_fixed = getFile16BitBE(file);
2841 ei->move_delay_random = getFile16BitBE(file);
2843 /* bits 0 - 15 of "move_pattern" ... */
2844 ei->move_pattern = getFile16BitBE(file);
2845 ei->move_direction_initial = getFile8Bit(file);
2846 ei->move_stepsize = getFile8Bit(file);
2848 ei->slippery_type = getFile8Bit(file);
2850 for (y = 0; y < 3; y++)
2851 for (x = 0; x < 3; x++)
2852 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2854 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2855 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2856 ei->move_leave_type = getFile8Bit(file);
2858 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2859 ei->move_pattern |= (getFile16BitBE(file) << 16);
2861 ei->access_direction = getFile8Bit(file);
2863 ei->explosion_delay = getFile8Bit(file);
2864 ei->ignition_delay = getFile8Bit(file);
2865 ei->explosion_type = getFile8Bit(file);
2867 /* some free bytes for future custom property values and padding */
2868 ReadUnusedBytesFromFile(file, 1);
2870 /* ---------- change page property values (48 bytes) --------------------- */
2872 setElementChangePages(ei, ei->num_change_pages);
2874 for (i = 0; i < ei->num_change_pages; i++)
2876 struct ElementChangeInfo *change = &ei->change_page[i];
2877 unsigned int event_bits;
2879 /* always start with reliable default values */
2880 setElementChangeInfoToDefaults(change);
2882 /* bits 0 - 31 of "has_event[]" ... */
2883 event_bits = getFile32BitBE(file);
2884 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2885 if (event_bits & (1 << j))
2886 change->has_event[j] = TRUE;
2888 change->target_element = getMappedElement(getFile16BitBE(file));
2890 change->delay_fixed = getFile16BitBE(file);
2891 change->delay_random = getFile16BitBE(file);
2892 change->delay_frames = getFile16BitBE(file);
2894 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2896 change->explode = getFile8Bit(file);
2897 change->use_target_content = getFile8Bit(file);
2898 change->only_if_complete = getFile8Bit(file);
2899 change->use_random_replace = getFile8Bit(file);
2901 change->random_percentage = getFile8Bit(file);
2902 change->replace_when = getFile8Bit(file);
2904 for (y = 0; y < 3; y++)
2905 for (x = 0; x < 3; x++)
2906 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2908 change->can_change = getFile8Bit(file);
2910 change->trigger_side = getFile8Bit(file);
2912 change->trigger_player = getFile8Bit(file);
2913 change->trigger_page = getFile8Bit(file);
2915 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2916 CH_PAGE_ANY : (1 << change->trigger_page));
2918 change->has_action = getFile8Bit(file);
2919 change->action_type = getFile8Bit(file);
2920 change->action_mode = getFile8Bit(file);
2921 change->action_arg = getFile16BitBE(file);
2923 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2924 event_bits = getFile8Bit(file);
2925 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2926 if (event_bits & (1 << (j - 32)))
2927 change->has_event[j] = TRUE;
2930 /* mark this custom element as modified */
2931 ei->modified_settings = TRUE;
2933 level->file_has_custom_elements = TRUE;
2938 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2940 struct ElementInfo *ei;
2941 struct ElementGroupInfo *group;
2945 element = getMappedElement(getFile16BitBE(file));
2947 if (!IS_GROUP_ELEMENT(element))
2949 Error(ERR_WARN, "invalid group element number %d", element);
2951 ReadUnusedBytesFromFile(file, chunk_size - 2);
2955 ei = &element_info[element];
2957 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2958 ei->description[i] = getFile8Bit(file);
2959 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2961 group = element_info[element].group;
2963 group->num_elements = getFile8Bit(file);
2965 ei->use_gfx_element = getFile8Bit(file);
2966 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2968 group->choice_mode = getFile8Bit(file);
2970 /* some free bytes for future values and padding */
2971 ReadUnusedBytesFromFile(file, 3);
2973 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2974 group->element[i] = getMappedElement(getFile16BitBE(file));
2976 /* mark this group element as modified */
2977 element_info[element].modified_settings = TRUE;
2979 level->file_has_custom_elements = TRUE;
2984 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2985 int element, int real_element)
2987 int micro_chunk_size = 0;
2988 int conf_type = getFile8Bit(file);
2989 int byte_mask = conf_type & CONF_MASK_BYTES;
2990 boolean element_found = FALSE;
2993 micro_chunk_size += 1;
2995 if (byte_mask == CONF_MASK_MULTI_BYTES)
2997 int num_bytes = getFile16BitBE(file);
2998 byte *buffer = checked_malloc(num_bytes);
3000 ReadBytesFromFile(file, buffer, num_bytes);
3002 for (i = 0; conf[i].data_type != -1; i++)
3004 if (conf[i].element == element &&
3005 conf[i].conf_type == conf_type)
3007 int data_type = conf[i].data_type;
3008 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3009 int max_num_entities = conf[i].max_num_entities;
3011 if (num_entities > max_num_entities)
3014 "truncating number of entities for element %d from %d to %d",
3015 element, num_entities, max_num_entities);
3017 num_entities = max_num_entities;
3020 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3021 data_type == TYPE_CONTENT_LIST))
3023 /* for element and content lists, zero entities are not allowed */
3024 Error(ERR_WARN, "found empty list of entities for element %d",
3027 /* do not set "num_entities" here to prevent reading behind buffer */
3029 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3033 *(int *)(conf[i].num_entities) = num_entities;
3036 element_found = TRUE;
3038 if (data_type == TYPE_STRING)
3040 char *string = (char *)(conf[i].value);
3043 for (j = 0; j < max_num_entities; j++)
3044 string[j] = (j < num_entities ? buffer[j] : '\0');
3046 else if (data_type == TYPE_ELEMENT_LIST)
3048 int *element_array = (int *)(conf[i].value);
3051 for (j = 0; j < num_entities; j++)
3053 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3055 else if (data_type == TYPE_CONTENT_LIST)
3057 struct Content *content= (struct Content *)(conf[i].value);
3060 for (c = 0; c < num_entities; c++)
3061 for (y = 0; y < 3; y++)
3062 for (x = 0; x < 3; x++)
3063 content[c].e[x][y] =
3064 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3067 element_found = FALSE;
3073 checked_free(buffer);
3075 micro_chunk_size += 2 + num_bytes;
3077 else /* constant size configuration data (1, 2 or 4 bytes) */
3079 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3080 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3081 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3083 for (i = 0; conf[i].data_type != -1; i++)
3085 if (conf[i].element == element &&
3086 conf[i].conf_type == conf_type)
3088 int data_type = conf[i].data_type;
3090 if (data_type == TYPE_ELEMENT)
3091 value = getMappedElement(value);
3093 if (data_type == TYPE_BOOLEAN)
3094 *(boolean *)(conf[i].value) = value;
3096 *(int *) (conf[i].value) = value;
3098 element_found = TRUE;
3104 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3109 char *error_conf_chunk_bytes =
3110 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3111 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3112 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3113 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3114 int error_element = real_element;
3116 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3117 error_conf_chunk_bytes, error_conf_chunk_token,
3118 error_element, EL_NAME(error_element));
3121 return micro_chunk_size;
3124 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3126 int real_chunk_size = 0;
3128 li = *level; /* copy level data into temporary buffer */
3130 while (!checkEndOfFile(file))
3132 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3134 if (real_chunk_size >= chunk_size)
3138 *level = li; /* copy temporary buffer back to level data */
3140 return real_chunk_size;
3143 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3145 int real_chunk_size = 0;
3147 li = *level; /* copy level data into temporary buffer */
3149 while (!checkEndOfFile(file))
3151 int element = getMappedElement(getFile16BitBE(file));
3153 real_chunk_size += 2;
3154 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3156 if (real_chunk_size >= chunk_size)
3160 *level = li; /* copy temporary buffer back to level data */
3162 return real_chunk_size;
3165 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3167 int real_chunk_size = 0;
3169 li = *level; /* copy level data into temporary buffer */
3171 while (!checkEndOfFile(file))
3173 int element = getMappedElement(getFile16BitBE(file));
3175 real_chunk_size += 2;
3176 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3178 if (real_chunk_size >= chunk_size)
3182 *level = li; /* copy temporary buffer back to level data */
3184 return real_chunk_size;
3187 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3189 int element = getMappedElement(getFile16BitBE(file));
3190 int envelope_nr = element - EL_ENVELOPE_1;
3191 int real_chunk_size = 2;
3193 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3195 while (!checkEndOfFile(file))
3197 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3200 if (real_chunk_size >= chunk_size)
3204 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3206 return real_chunk_size;
3209 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3211 int element = getMappedElement(getFile16BitBE(file));
3212 int real_chunk_size = 2;
3213 struct ElementInfo *ei = &element_info[element];
3216 xx_ei = *ei; /* copy element data into temporary buffer */
3218 xx_ei.num_change_pages = -1;
3220 while (!checkEndOfFile(file))
3222 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3224 if (xx_ei.num_change_pages != -1)
3227 if (real_chunk_size >= chunk_size)
3233 if (ei->num_change_pages == -1)
3235 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3238 ei->num_change_pages = 1;
3240 setElementChangePages(ei, 1);
3241 setElementChangeInfoToDefaults(ei->change);
3243 return real_chunk_size;
3246 /* initialize number of change pages stored for this custom element */
3247 setElementChangePages(ei, ei->num_change_pages);
3248 for (i = 0; i < ei->num_change_pages; i++)
3249 setElementChangeInfoToDefaults(&ei->change_page[i]);
3251 /* start with reading properties for the first change page */
3252 xx_current_change_page = 0;
3254 while (!checkEndOfFile(file))
3256 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3258 xx_change = *change; /* copy change data into temporary buffer */
3260 resetEventBits(); /* reset bits; change page might have changed */
3262 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3265 *change = xx_change;
3267 setEventFlagsFromEventBits(change);
3269 if (real_chunk_size >= chunk_size)
3273 level->file_has_custom_elements = TRUE;
3275 return real_chunk_size;
3278 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3280 int element = getMappedElement(getFile16BitBE(file));
3281 int real_chunk_size = 2;
3282 struct ElementInfo *ei = &element_info[element];
3283 struct ElementGroupInfo *group = ei->group;
3285 xx_ei = *ei; /* copy element data into temporary buffer */
3286 xx_group = *group; /* copy group data into temporary buffer */
3288 while (!checkEndOfFile(file))
3290 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3293 if (real_chunk_size >= chunk_size)
3300 level->file_has_custom_elements = TRUE;
3302 return real_chunk_size;
3305 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3306 struct LevelFileInfo *level_file_info,
3307 boolean level_info_only)
3309 char *filename = level_file_info->filename;
3310 char cookie[MAX_LINE_LEN];
3311 char chunk_name[CHUNK_ID_LEN + 1];
3315 if (!(file = openFile(filename, MODE_READ)))
3317 level->no_valid_file = TRUE;
3318 level->no_level_file = TRUE;
3320 if (level_info_only)
3323 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3325 if (!setup.editor.use_template_for_new_levels)
3328 /* if level file not found, try to initialize level data from template */
3329 filename = getGlobalLevelTemplateFilename();
3331 if (!(file = openFile(filename, MODE_READ)))
3334 /* default: for empty levels, use level template for custom elements */
3335 level->use_custom_template = TRUE;
3337 level->no_valid_file = FALSE;
3340 getFileChunkBE(file, chunk_name, NULL);
3341 if (strEqual(chunk_name, "RND1"))
3343 getFile32BitBE(file); /* not used */
3345 getFileChunkBE(file, chunk_name, NULL);
3346 if (!strEqual(chunk_name, "CAVE"))
3348 level->no_valid_file = TRUE;
3350 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3357 else /* check for pre-2.0 file format with cookie string */
3359 strcpy(cookie, chunk_name);
3360 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3362 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3363 cookie[strlen(cookie) - 1] = '\0';
3365 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3367 level->no_valid_file = TRUE;
3369 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3376 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3378 level->no_valid_file = TRUE;
3380 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3387 /* pre-2.0 level files have no game version, so use file version here */
3388 level->game_version = level->file_version;
3391 if (level->file_version < FILE_VERSION_1_2)
3393 /* level files from versions before 1.2.0 without chunk structure */
3394 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3395 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3403 int (*loader)(File *, int, struct LevelInfo *);
3407 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3408 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3409 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3410 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3411 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3412 { "INFO", -1, LoadLevel_INFO },
3413 { "BODY", -1, LoadLevel_BODY },
3414 { "CONT", -1, LoadLevel_CONT },
3415 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3416 { "CNT3", -1, LoadLevel_CNT3 },
3417 { "CUS1", -1, LoadLevel_CUS1 },
3418 { "CUS2", -1, LoadLevel_CUS2 },
3419 { "CUS3", -1, LoadLevel_CUS3 },
3420 { "CUS4", -1, LoadLevel_CUS4 },
3421 { "GRP1", -1, LoadLevel_GRP1 },
3422 { "CONF", -1, LoadLevel_CONF },
3423 { "ELEM", -1, LoadLevel_ELEM },
3424 { "NOTE", -1, LoadLevel_NOTE },
3425 { "CUSX", -1, LoadLevel_CUSX },
3426 { "GRPX", -1, LoadLevel_GRPX },
3431 while (getFileChunkBE(file, chunk_name, &chunk_size))
3435 while (chunk_info[i].name != NULL &&
3436 !strEqual(chunk_name, chunk_info[i].name))
3439 if (chunk_info[i].name == NULL)
3441 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3442 chunk_name, filename);
3443 ReadUnusedBytesFromFile(file, chunk_size);
3445 else if (chunk_info[i].size != -1 &&
3446 chunk_info[i].size != chunk_size)
3448 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3449 chunk_size, chunk_name, filename);
3450 ReadUnusedBytesFromFile(file, chunk_size);
3454 /* call function to load this level chunk */
3455 int chunk_size_expected =
3456 (chunk_info[i].loader)(file, chunk_size, level);
3458 /* the size of some chunks cannot be checked before reading other
3459 chunks first (like "HEAD" and "BODY") that contain some header
3460 information, so check them here */
3461 if (chunk_size_expected != chunk_size)
3463 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3464 chunk_size, chunk_name, filename);
3474 /* ------------------------------------------------------------------------- */
3475 /* functions for loading EM level */
3476 /* ------------------------------------------------------------------------- */
3478 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3480 static int ball_xy[8][2] =
3491 struct LevelInfo_EM *level_em = level->native_em_level;
3492 struct LEVEL *lev = level_em->lev;
3493 struct PLAYER **ply = level_em->ply;
3496 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3497 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3499 lev->time_seconds = level->time;
3500 lev->required_initial = level->gems_needed;
3502 lev->emerald_score = level->score[SC_EMERALD];
3503 lev->diamond_score = level->score[SC_DIAMOND];
3504 lev->alien_score = level->score[SC_ROBOT];
3505 lev->tank_score = level->score[SC_SPACESHIP];
3506 lev->bug_score = level->score[SC_BUG];
3507 lev->eater_score = level->score[SC_YAMYAM];
3508 lev->nut_score = level->score[SC_NUT];
3509 lev->dynamite_score = level->score[SC_DYNAMITE];
3510 lev->key_score = level->score[SC_KEY];
3511 lev->exit_score = level->score[SC_TIME_BONUS];
3513 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3514 for (y = 0; y < 3; y++)
3515 for (x = 0; x < 3; x++)
3516 lev->eater_array[i][y * 3 + x] =
3517 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3519 lev->amoeba_time = level->amoeba_speed;
3520 lev->wonderwall_time_initial = level->time_magic_wall;
3521 lev->wheel_time = level->time_wheel;
3523 lev->android_move_time = level->android_move_time;
3524 lev->android_clone_time = level->android_clone_time;
3525 lev->ball_random = level->ball_random;
3526 lev->ball_state_initial = level->ball_state_initial;
3527 lev->ball_time = level->ball_time;
3528 lev->num_ball_arrays = level->num_ball_contents;
3530 lev->lenses_score = level->lenses_score;
3531 lev->magnify_score = level->magnify_score;
3532 lev->slurp_score = level->slurp_score;
3534 lev->lenses_time = level->lenses_time;
3535 lev->magnify_time = level->magnify_time;
3537 lev->wind_direction_initial =
3538 map_direction_RND_to_EM(level->wind_direction_initial);
3539 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3540 lev->wind_time : 0);
3542 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3543 for (j = 0; j < 8; j++)
3544 lev->ball_array[i][j] =
3545 map_element_RND_to_EM(level->
3546 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3548 map_android_clone_elements_RND_to_EM(level);
3550 /* first fill the complete playfield with the default border element */
3551 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3552 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3553 level_em->cave[x][y] = ZBORDER;
3555 if (BorderElement == EL_STEELWALL)
3557 for (y = 0; y < lev->height + 2; y++)
3558 for (x = 0; x < lev->width + 2; x++)
3559 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3562 /* then copy the real level contents from level file into the playfield */
3563 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3565 int new_element = map_element_RND_to_EM(level->field[x][y]);
3566 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3567 int xx = x + 1 + offset;
3568 int yy = y + 1 + offset;
3570 if (level->field[x][y] == EL_AMOEBA_DEAD)
3571 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3573 level_em->cave[xx][yy] = new_element;
3576 for (i = 0; i < MAX_PLAYERS; i++)
3578 ply[i]->x_initial = 0;
3579 ply[i]->y_initial = 0;
3582 /* initialize player positions and delete players from the playfield */
3583 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3585 if (ELEM_IS_PLAYER(level->field[x][y]))
3587 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3588 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3589 int xx = x + 1 + offset;
3590 int yy = y + 1 + offset;
3592 ply[player_nr]->x_initial = xx;
3593 ply[player_nr]->y_initial = yy;
3595 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3599 if (BorderElement == EL_STEELWALL)
3606 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3608 static int ball_xy[8][2] =
3619 struct LevelInfo_EM *level_em = level->native_em_level;
3620 struct LEVEL *lev = level_em->lev;
3621 struct PLAYER **ply = level_em->ply;
3624 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3625 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3627 level->time = lev->time_seconds;
3628 level->gems_needed = lev->required_initial;
3630 sprintf(level->name, "Level %d", level->file_info.nr);
3632 level->score[SC_EMERALD] = lev->emerald_score;
3633 level->score[SC_DIAMOND] = lev->diamond_score;
3634 level->score[SC_ROBOT] = lev->alien_score;
3635 level->score[SC_SPACESHIP] = lev->tank_score;
3636 level->score[SC_BUG] = lev->bug_score;
3637 level->score[SC_YAMYAM] = lev->eater_score;
3638 level->score[SC_NUT] = lev->nut_score;
3639 level->score[SC_DYNAMITE] = lev->dynamite_score;
3640 level->score[SC_KEY] = lev->key_score;
3641 level->score[SC_TIME_BONUS] = lev->exit_score;
3643 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3645 for (i = 0; i < level->num_yamyam_contents; i++)
3646 for (y = 0; y < 3; y++)
3647 for (x = 0; x < 3; x++)
3648 level->yamyam_content[i].e[x][y] =
3649 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3651 level->amoeba_speed = lev->amoeba_time;
3652 level->time_magic_wall = lev->wonderwall_time_initial;
3653 level->time_wheel = lev->wheel_time;
3655 level->android_move_time = lev->android_move_time;
3656 level->android_clone_time = lev->android_clone_time;
3657 level->ball_random = lev->ball_random;
3658 level->ball_state_initial = lev->ball_state_initial;
3659 level->ball_time = lev->ball_time;
3660 level->num_ball_contents = lev->num_ball_arrays;
3662 level->lenses_score = lev->lenses_score;
3663 level->magnify_score = lev->magnify_score;
3664 level->slurp_score = lev->slurp_score;
3666 level->lenses_time = lev->lenses_time;
3667 level->magnify_time = lev->magnify_time;
3669 level->wind_direction_initial =
3670 map_direction_EM_to_RND(lev->wind_direction_initial);
3672 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3673 for (j = 0; j < 8; j++)
3674 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3675 map_element_EM_to_RND(lev->ball_array[i][j]);
3677 map_android_clone_elements_EM_to_RND(level);
3679 /* convert the playfield (some elements need special treatment) */
3680 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3682 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3684 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3685 new_element = EL_AMOEBA_DEAD;
3687 level->field[x][y] = new_element;
3690 for (i = 0; i < MAX_PLAYERS; i++)
3692 /* in case of all players set to the same field, use the first player */
3693 int nr = MAX_PLAYERS - i - 1;
3694 int jx = ply[nr]->x_initial - 1;
3695 int jy = ply[nr]->y_initial - 1;
3697 if (jx != -1 && jy != -1)
3698 level->field[jx][jy] = EL_PLAYER_1 + nr;
3703 /* ------------------------------------------------------------------------- */
3704 /* functions for loading SP level */
3705 /* ------------------------------------------------------------------------- */
3707 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3709 struct LevelInfo_SP *level_sp = level->native_sp_level;
3710 LevelInfoType *header = &level_sp->header;
3713 level_sp->width = level->fieldx;
3714 level_sp->height = level->fieldy;
3716 for (x = 0; x < level->fieldx; x++)
3717 for (y = 0; y < level->fieldy; y++)
3718 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3720 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3722 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3723 header->LevelTitle[i] = level->name[i];
3724 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3726 header->InfotronsNeeded = level->gems_needed;
3728 header->SpecialPortCount = 0;
3730 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3732 boolean gravity_port_found = FALSE;
3733 boolean gravity_port_valid = FALSE;
3734 int gravity_port_flag;
3735 int gravity_port_base_element;
3736 int element = level->field[x][y];
3738 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3739 element <= EL_SP_GRAVITY_ON_PORT_UP)
3741 gravity_port_found = TRUE;
3742 gravity_port_valid = TRUE;
3743 gravity_port_flag = 1;
3744 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3746 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3747 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3749 gravity_port_found = TRUE;
3750 gravity_port_valid = TRUE;
3751 gravity_port_flag = 0;
3752 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3754 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3755 element <= EL_SP_GRAVITY_PORT_UP)
3757 /* change R'n'D style gravity inverting special port to normal port
3758 (there are no gravity inverting ports in native Supaplex engine) */
3760 gravity_port_found = TRUE;
3761 gravity_port_valid = FALSE;
3762 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3765 if (gravity_port_found)
3767 if (gravity_port_valid &&
3768 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3770 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3772 port->PortLocation = (y * level->fieldx + x) * 2;
3773 port->Gravity = gravity_port_flag;
3775 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3777 header->SpecialPortCount++;
3781 /* change special gravity port to normal port */
3783 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3786 level_sp->playfield[x][y] = element - EL_SP_START;
3791 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3793 struct LevelInfo_SP *level_sp = level->native_sp_level;
3794 LevelInfoType *header = &level_sp->header;
3795 boolean num_invalid_elements = 0;
3798 level->fieldx = level_sp->width;
3799 level->fieldy = level_sp->height;
3801 for (x = 0; x < level->fieldx; x++)
3803 for (y = 0; y < level->fieldy; y++)
3805 int element_old = level_sp->playfield[x][y];
3806 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3808 if (element_new == EL_UNKNOWN)
3810 num_invalid_elements++;
3812 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3816 level->field[x][y] = element_new;
3820 if (num_invalid_elements > 0)
3821 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3822 (!options.debug ? " (use '--debug' for more details)" : ""));
3824 for (i = 0; i < MAX_PLAYERS; i++)
3825 level->initial_player_gravity[i] =
3826 (header->InitialGravity == 1 ? TRUE : FALSE);
3828 /* skip leading spaces */
3829 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3830 if (header->LevelTitle[i] != ' ')
3833 /* copy level title */
3834 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3835 level->name[j] = header->LevelTitle[i];
3836 level->name[j] = '\0';
3838 /* cut trailing spaces */
3840 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3841 level->name[j - 1] = '\0';
3843 level->gems_needed = header->InfotronsNeeded;
3845 for (i = 0; i < header->SpecialPortCount; i++)
3847 SpecialPortType *port = &header->SpecialPort[i];
3848 int port_location = port->PortLocation;
3849 int gravity = port->Gravity;
3850 int port_x, port_y, port_element;
3852 port_x = (port_location / 2) % level->fieldx;
3853 port_y = (port_location / 2) / level->fieldx;
3855 if (port_x < 0 || port_x >= level->fieldx ||
3856 port_y < 0 || port_y >= level->fieldy)
3858 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3864 port_element = level->field[port_x][port_y];
3866 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3867 port_element > EL_SP_GRAVITY_PORT_UP)
3869 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3874 /* change previous (wrong) gravity inverting special port to either
3875 gravity enabling special port or gravity disabling special port */
3876 level->field[port_x][port_y] +=
3877 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3878 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3881 /* change special gravity ports without database entries to normal ports */
3882 for (x = 0; x < level->fieldx; x++)
3883 for (y = 0; y < level->fieldy; y++)
3884 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3885 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3886 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3888 level->time = 0; /* no time limit */
3889 level->amoeba_speed = 0;
3890 level->time_magic_wall = 0;
3891 level->time_wheel = 0;
3892 level->amoeba_content = EL_EMPTY;
3895 /* original Supaplex does not use score values -- use default values */
3897 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3898 level->score[i] = 0;
3901 /* there are no yamyams in supaplex levels */
3902 for (i = 0; i < level->num_yamyam_contents; i++)
3903 for (x = 0; x < 3; x++)
3904 for (y = 0; y < 3; y++)
3905 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3908 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3910 struct LevelInfo_SP *level_sp = level->native_sp_level;
3911 struct DemoInfo_SP *demo = &level_sp->demo;
3914 /* always start with reliable default values */
3915 demo->is_available = FALSE;
3918 if (TAPE_IS_EMPTY(tape))
3921 demo->level_nr = tape.level_nr; /* (currently not used) */
3923 level_sp->header.DemoRandomSeed = tape.random_seed;
3927 for (i = 0; i < tape.length; i++)
3929 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3930 int demo_repeat = tape.pos[i].delay;
3931 int demo_entries = (demo_repeat + 15) / 16;
3933 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3935 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3941 for (j = 0; j < demo_repeat / 16; j++)
3942 demo->data[demo->length++] = 0xf0 | demo_action;
3944 if (demo_repeat % 16)
3945 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3948 demo->is_available = TRUE;
3951 static void setTapeInfoToDefaults();
3953 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3955 struct LevelInfo_SP *level_sp = level->native_sp_level;
3956 struct DemoInfo_SP *demo = &level_sp->demo;
3957 char *filename = level->file_info.filename;
3960 /* always start with reliable default values */
3961 setTapeInfoToDefaults();
3963 if (!demo->is_available)
3966 tape.level_nr = demo->level_nr; /* (currently not used) */
3967 tape.random_seed = level_sp->header.DemoRandomSeed;
3969 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3972 tape.pos[tape.counter].delay = 0;
3974 for (i = 0; i < demo->length; i++)
3976 int demo_action = demo->data[i] & 0x0f;
3977 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3978 int tape_action = map_key_SP_to_RND(demo_action);
3979 int tape_repeat = demo_repeat + 1;
3980 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3981 boolean success = 0;
3984 for (j = 0; j < tape_repeat; j++)
3985 success = TapeAddAction(action);
3989 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3996 TapeHaltRecording();
4000 /* ------------------------------------------------------------------------- */
4001 /* functions for loading MM level */
4002 /* ------------------------------------------------------------------------- */
4004 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4006 struct LevelInfo_MM *level_mm = level->native_mm_level;
4009 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4010 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4012 level_mm->time = level->time;
4013 level_mm->kettles_needed = level->gems_needed;
4014 level_mm->auto_count_kettles = level->auto_count_gems;
4016 level_mm->laser_red = level->mm_laser_red;
4017 level_mm->laser_green = level->mm_laser_green;
4018 level_mm->laser_blue = level->mm_laser_blue;
4020 strcpy(level_mm->name, level->name);
4021 strcpy(level_mm->author, level->author);
4023 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4024 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4025 level_mm->score[SC_KEY] = level->score[SC_KEY];
4026 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4027 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4029 level_mm->amoeba_speed = level->amoeba_speed;
4030 level_mm->time_fuse = level->mm_time_fuse;
4031 level_mm->time_bomb = level->mm_time_bomb;
4032 level_mm->time_ball = level->mm_time_ball;
4033 level_mm->time_block = level->mm_time_block;
4035 for (x = 0; x < level->fieldx; x++)
4036 for (y = 0; y < level->fieldy; y++)
4038 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4041 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4043 struct LevelInfo_MM *level_mm = level->native_mm_level;
4046 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4047 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4049 level->time = level_mm->time;
4050 level->gems_needed = level_mm->kettles_needed;
4051 level->auto_count_gems = level_mm->auto_count_kettles;
4053 level->mm_laser_red = level_mm->laser_red;
4054 level->mm_laser_green = level_mm->laser_green;
4055 level->mm_laser_blue = level_mm->laser_blue;
4057 strcpy(level->name, level_mm->name);
4059 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4060 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4061 strcpy(level->author, level_mm->author);
4063 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4064 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4065 level->score[SC_KEY] = level_mm->score[SC_KEY];
4066 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4067 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4069 level->amoeba_speed = level_mm->amoeba_speed;
4070 level->mm_time_fuse = level_mm->time_fuse;
4071 level->mm_time_bomb = level_mm->time_bomb;
4072 level->mm_time_ball = level_mm->time_ball;
4073 level->mm_time_block = level_mm->time_block;
4075 for (x = 0; x < level->fieldx; x++)
4076 for (y = 0; y < level->fieldy; y++)
4077 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4081 /* ------------------------------------------------------------------------- */
4082 /* functions for loading DC level */
4083 /* ------------------------------------------------------------------------- */
4085 #define DC_LEVEL_HEADER_SIZE 344
4087 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4089 static int last_data_encoded;
4093 int diff_hi, diff_lo;
4094 int data_hi, data_lo;
4095 unsigned short data_decoded;
4099 last_data_encoded = 0;
4106 diff = data_encoded - last_data_encoded;
4107 diff_hi = diff & ~0xff;
4108 diff_lo = diff & 0xff;
4112 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4113 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4114 data_hi = data_hi & 0xff00;
4116 data_decoded = data_hi | data_lo;
4118 last_data_encoded = data_encoded;
4120 offset1 = (offset1 + 1) % 31;
4121 offset2 = offset2 & 0xff;
4123 return data_decoded;
4126 int getMappedElement_DC(int element)
4134 /* 0x0117 - 0x036e: (?) */
4137 /* 0x042d - 0x0684: (?) */
4153 element = EL_CRYSTAL;
4156 case 0x0e77: /* quicksand (boulder) */
4157 element = EL_QUICKSAND_FAST_FULL;
4160 case 0x0e99: /* slow quicksand (boulder) */
4161 element = EL_QUICKSAND_FULL;
4165 element = EL_EM_EXIT_OPEN;
4169 element = EL_EM_EXIT_CLOSED;
4173 element = EL_EM_STEEL_EXIT_OPEN;
4177 element = EL_EM_STEEL_EXIT_CLOSED;
4180 case 0x0f4f: /* dynamite (lit 1) */
4181 element = EL_EM_DYNAMITE_ACTIVE;
4184 case 0x0f57: /* dynamite (lit 2) */
4185 element = EL_EM_DYNAMITE_ACTIVE;
4188 case 0x0f5f: /* dynamite (lit 3) */
4189 element = EL_EM_DYNAMITE_ACTIVE;
4192 case 0x0f67: /* dynamite (lit 4) */
4193 element = EL_EM_DYNAMITE_ACTIVE;
4200 element = EL_AMOEBA_WET;
4204 element = EL_AMOEBA_DROP;
4208 element = EL_DC_MAGIC_WALL;
4212 element = EL_SPACESHIP_UP;
4216 element = EL_SPACESHIP_DOWN;
4220 element = EL_SPACESHIP_LEFT;
4224 element = EL_SPACESHIP_RIGHT;
4228 element = EL_BUG_UP;
4232 element = EL_BUG_DOWN;
4236 element = EL_BUG_LEFT;
4240 element = EL_BUG_RIGHT;
4244 element = EL_MOLE_UP;
4248 element = EL_MOLE_DOWN;
4252 element = EL_MOLE_LEFT;
4256 element = EL_MOLE_RIGHT;
4264 element = EL_YAMYAM;
4268 element = EL_SWITCHGATE_OPEN;
4272 element = EL_SWITCHGATE_CLOSED;
4276 element = EL_DC_SWITCHGATE_SWITCH_UP;
4280 element = EL_TIMEGATE_CLOSED;
4283 case 0x144c: /* conveyor belt switch (green) */
4284 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4287 case 0x144f: /* conveyor belt switch (red) */
4288 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4291 case 0x1452: /* conveyor belt switch (blue) */
4292 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4296 element = EL_CONVEYOR_BELT_3_MIDDLE;
4300 element = EL_CONVEYOR_BELT_3_LEFT;
4304 element = EL_CONVEYOR_BELT_3_RIGHT;
4308 element = EL_CONVEYOR_BELT_1_MIDDLE;
4312 element = EL_CONVEYOR_BELT_1_LEFT;
4316 element = EL_CONVEYOR_BELT_1_RIGHT;
4320 element = EL_CONVEYOR_BELT_4_MIDDLE;
4324 element = EL_CONVEYOR_BELT_4_LEFT;
4328 element = EL_CONVEYOR_BELT_4_RIGHT;
4332 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4336 element = EL_EXPANDABLE_WALL_VERTICAL;
4340 element = EL_EXPANDABLE_WALL_ANY;
4343 case 0x14ce: /* growing steel wall (left/right) */
4344 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4347 case 0x14df: /* growing steel wall (up/down) */
4348 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4351 case 0x14e8: /* growing steel wall (up/down/left/right) */
4352 element = EL_EXPANDABLE_STEELWALL_ANY;
4356 element = EL_SHIELD_DEADLY;
4360 element = EL_EXTRA_TIME;
4368 element = EL_EMPTY_SPACE;
4371 case 0x1578: /* quicksand (empty) */
4372 element = EL_QUICKSAND_FAST_EMPTY;
4375 case 0x1579: /* slow quicksand (empty) */
4376 element = EL_QUICKSAND_EMPTY;
4379 /* 0x157c - 0x158b: */
4382 /* 0x1590 - 0x159f: */
4383 /* EL_DC_LANDMINE */
4386 element = EL_EM_DYNAMITE;
4389 case 0x15a1: /* key (red) */
4390 element = EL_EM_KEY_1;
4393 case 0x15a2: /* key (yellow) */
4394 element = EL_EM_KEY_2;
4397 case 0x15a3: /* key (blue) */
4398 element = EL_EM_KEY_4;
4401 case 0x15a4: /* key (green) */
4402 element = EL_EM_KEY_3;
4405 case 0x15a5: /* key (white) */
4406 element = EL_DC_KEY_WHITE;
4410 element = EL_WALL_SLIPPERY;
4417 case 0x15a8: /* wall (not round) */
4421 case 0x15a9: /* (blue) */
4422 element = EL_CHAR_A;
4425 case 0x15aa: /* (blue) */
4426 element = EL_CHAR_B;
4429 case 0x15ab: /* (blue) */
4430 element = EL_CHAR_C;
4433 case 0x15ac: /* (blue) */
4434 element = EL_CHAR_D;
4437 case 0x15ad: /* (blue) */
4438 element = EL_CHAR_E;
4441 case 0x15ae: /* (blue) */
4442 element = EL_CHAR_F;
4445 case 0x15af: /* (blue) */
4446 element = EL_CHAR_G;
4449 case 0x15b0: /* (blue) */
4450 element = EL_CHAR_H;
4453 case 0x15b1: /* (blue) */
4454 element = EL_CHAR_I;
4457 case 0x15b2: /* (blue) */
4458 element = EL_CHAR_J;
4461 case 0x15b3: /* (blue) */
4462 element = EL_CHAR_K;
4465 case 0x15b4: /* (blue) */
4466 element = EL_CHAR_L;
4469 case 0x15b5: /* (blue) */
4470 element = EL_CHAR_M;
4473 case 0x15b6: /* (blue) */
4474 element = EL_CHAR_N;
4477 case 0x15b7: /* (blue) */
4478 element = EL_CHAR_O;
4481 case 0x15b8: /* (blue) */
4482 element = EL_CHAR_P;
4485 case 0x15b9: /* (blue) */
4486 element = EL_CHAR_Q;
4489 case 0x15ba: /* (blue) */
4490 element = EL_CHAR_R;
4493 case 0x15bb: /* (blue) */
4494 element = EL_CHAR_S;
4497 case 0x15bc: /* (blue) */
4498 element = EL_CHAR_T;
4501 case 0x15bd: /* (blue) */
4502 element = EL_CHAR_U;
4505 case 0x15be: /* (blue) */
4506 element = EL_CHAR_V;
4509 case 0x15bf: /* (blue) */
4510 element = EL_CHAR_W;
4513 case 0x15c0: /* (blue) */
4514 element = EL_CHAR_X;
4517 case 0x15c1: /* (blue) */
4518 element = EL_CHAR_Y;
4521 case 0x15c2: /* (blue) */
4522 element = EL_CHAR_Z;
4525 case 0x15c3: /* (blue) */
4526 element = EL_CHAR_AUMLAUT;
4529 case 0x15c4: /* (blue) */
4530 element = EL_CHAR_OUMLAUT;
4533 case 0x15c5: /* (blue) */
4534 element = EL_CHAR_UUMLAUT;
4537 case 0x15c6: /* (blue) */
4538 element = EL_CHAR_0;
4541 case 0x15c7: /* (blue) */
4542 element = EL_CHAR_1;
4545 case 0x15c8: /* (blue) */
4546 element = EL_CHAR_2;
4549 case 0x15c9: /* (blue) */
4550 element = EL_CHAR_3;
4553 case 0x15ca: /* (blue) */
4554 element = EL_CHAR_4;
4557 case 0x15cb: /* (blue) */
4558 element = EL_CHAR_5;
4561 case 0x15cc: /* (blue) */
4562 element = EL_CHAR_6;
4565 case 0x15cd: /* (blue) */
4566 element = EL_CHAR_7;
4569 case 0x15ce: /* (blue) */
4570 element = EL_CHAR_8;
4573 case 0x15cf: /* (blue) */
4574 element = EL_CHAR_9;
4577 case 0x15d0: /* (blue) */
4578 element = EL_CHAR_PERIOD;
4581 case 0x15d1: /* (blue) */
4582 element = EL_CHAR_EXCLAM;
4585 case 0x15d2: /* (blue) */
4586 element = EL_CHAR_COLON;
4589 case 0x15d3: /* (blue) */
4590 element = EL_CHAR_LESS;
4593 case 0x15d4: /* (blue) */
4594 element = EL_CHAR_GREATER;
4597 case 0x15d5: /* (blue) */
4598 element = EL_CHAR_QUESTION;
4601 case 0x15d6: /* (blue) */
4602 element = EL_CHAR_COPYRIGHT;
4605 case 0x15d7: /* (blue) */
4606 element = EL_CHAR_UP;
4609 case 0x15d8: /* (blue) */
4610 element = EL_CHAR_DOWN;
4613 case 0x15d9: /* (blue) */
4614 element = EL_CHAR_BUTTON;
4617 case 0x15da: /* (blue) */
4618 element = EL_CHAR_PLUS;
4621 case 0x15db: /* (blue) */
4622 element = EL_CHAR_MINUS;
4625 case 0x15dc: /* (blue) */
4626 element = EL_CHAR_APOSTROPHE;
4629 case 0x15dd: /* (blue) */
4630 element = EL_CHAR_PARENLEFT;
4633 case 0x15de: /* (blue) */
4634 element = EL_CHAR_PARENRIGHT;
4637 case 0x15df: /* (green) */
4638 element = EL_CHAR_A;
4641 case 0x15e0: /* (green) */
4642 element = EL_CHAR_B;
4645 case 0x15e1: /* (green) */
4646 element = EL_CHAR_C;
4649 case 0x15e2: /* (green) */
4650 element = EL_CHAR_D;
4653 case 0x15e3: /* (green) */
4654 element = EL_CHAR_E;
4657 case 0x15e4: /* (green) */
4658 element = EL_CHAR_F;
4661 case 0x15e5: /* (green) */
4662 element = EL_CHAR_G;
4665 case 0x15e6: /* (green) */
4666 element = EL_CHAR_H;
4669 case 0x15e7: /* (green) */
4670 element = EL_CHAR_I;
4673 case 0x15e8: /* (green) */
4674 element = EL_CHAR_J;
4677 case 0x15e9: /* (green) */
4678 element = EL_CHAR_K;
4681 case 0x15ea: /* (green) */
4682 element = EL_CHAR_L;
4685 case 0x15eb: /* (green) */
4686 element = EL_CHAR_M;
4689 case 0x15ec: /* (green) */
4690 element = EL_CHAR_N;
4693 case 0x15ed: /* (green) */
4694 element = EL_CHAR_O;
4697 case 0x15ee: /* (green) */
4698 element = EL_CHAR_P;
4701 case 0x15ef: /* (green) */
4702 element = EL_CHAR_Q;
4705 case 0x15f0: /* (green) */
4706 element = EL_CHAR_R;
4709 case 0x15f1: /* (green) */
4710 element = EL_CHAR_S;
4713 case 0x15f2: /* (green) */
4714 element = EL_CHAR_T;
4717 case 0x15f3: /* (green) */
4718 element = EL_CHAR_U;
4721 case 0x15f4: /* (green) */
4722 element = EL_CHAR_V;
4725 case 0x15f5: /* (green) */
4726 element = EL_CHAR_W;
4729 case 0x15f6: /* (green) */
4730 element = EL_CHAR_X;
4733 case 0x15f7: /* (green) */
4734 element = EL_CHAR_Y;
4737 case 0x15f8: /* (green) */
4738 element = EL_CHAR_Z;
4741 case 0x15f9: /* (green) */
4742 element = EL_CHAR_AUMLAUT;
4745 case 0x15fa: /* (green) */
4746 element = EL_CHAR_OUMLAUT;
4749 case 0x15fb: /* (green) */
4750 element = EL_CHAR_UUMLAUT;
4753 case 0x15fc: /* (green) */
4754 element = EL_CHAR_0;
4757 case 0x15fd: /* (green) */
4758 element = EL_CHAR_1;
4761 case 0x15fe: /* (green) */
4762 element = EL_CHAR_2;
4765 case 0x15ff: /* (green) */
4766 element = EL_CHAR_3;
4769 case 0x1600: /* (green) */
4770 element = EL_CHAR_4;
4773 case 0x1601: /* (green) */
4774 element = EL_CHAR_5;
4777 case 0x1602: /* (green) */
4778 element = EL_CHAR_6;
4781 case 0x1603: /* (green) */
4782 element = EL_CHAR_7;
4785 case 0x1604: /* (green) */
4786 element = EL_CHAR_8;
4789 case 0x1605: /* (green) */
4790 element = EL_CHAR_9;
4793 case 0x1606: /* (green) */
4794 element = EL_CHAR_PERIOD;
4797 case 0x1607: /* (green) */
4798 element = EL_CHAR_EXCLAM;
4801 case 0x1608: /* (green) */
4802 element = EL_CHAR_COLON;
4805 case 0x1609: /* (green) */
4806 element = EL_CHAR_LESS;
4809 case 0x160a: /* (green) */
4810 element = EL_CHAR_GREATER;
4813 case 0x160b: /* (green) */
4814 element = EL_CHAR_QUESTION;
4817 case 0x160c: /* (green) */
4818 element = EL_CHAR_COPYRIGHT;
4821 case 0x160d: /* (green) */
4822 element = EL_CHAR_UP;
4825 case 0x160e: /* (green) */
4826 element = EL_CHAR_DOWN;
4829 case 0x160f: /* (green) */
4830 element = EL_CHAR_BUTTON;
4833 case 0x1610: /* (green) */
4834 element = EL_CHAR_PLUS;
4837 case 0x1611: /* (green) */
4838 element = EL_CHAR_MINUS;
4841 case 0x1612: /* (green) */
4842 element = EL_CHAR_APOSTROPHE;
4845 case 0x1613: /* (green) */
4846 element = EL_CHAR_PARENLEFT;
4849 case 0x1614: /* (green) */
4850 element = EL_CHAR_PARENRIGHT;
4853 case 0x1615: /* (blue steel) */
4854 element = EL_STEEL_CHAR_A;
4857 case 0x1616: /* (blue steel) */
4858 element = EL_STEEL_CHAR_B;
4861 case 0x1617: /* (blue steel) */
4862 element = EL_STEEL_CHAR_C;
4865 case 0x1618: /* (blue steel) */
4866 element = EL_STEEL_CHAR_D;
4869 case 0x1619: /* (blue steel) */
4870 element = EL_STEEL_CHAR_E;
4873 case 0x161a: /* (blue steel) */
4874 element = EL_STEEL_CHAR_F;
4877 case 0x161b: /* (blue steel) */
4878 element = EL_STEEL_CHAR_G;
4881 case 0x161c: /* (blue steel) */
4882 element = EL_STEEL_CHAR_H;
4885 case 0x161d: /* (blue steel) */
4886 element = EL_STEEL_CHAR_I;
4889 case 0x161e: /* (blue steel) */
4890 element = EL_STEEL_CHAR_J;
4893 case 0x161f: /* (blue steel) */
4894 element = EL_STEEL_CHAR_K;
4897 case 0x1620: /* (blue steel) */
4898 element = EL_STEEL_CHAR_L;
4901 case 0x1621: /* (blue steel) */
4902 element = EL_STEEL_CHAR_M;
4905 case 0x1622: /* (blue steel) */
4906 element = EL_STEEL_CHAR_N;
4909 case 0x1623: /* (blue steel) */
4910 element = EL_STEEL_CHAR_O;
4913 case 0x1624: /* (blue steel) */
4914 element = EL_STEEL_CHAR_P;
4917 case 0x1625: /* (blue steel) */
4918 element = EL_STEEL_CHAR_Q;
4921 case 0x1626: /* (blue steel) */
4922 element = EL_STEEL_CHAR_R;
4925 case 0x1627: /* (blue steel) */
4926 element = EL_STEEL_CHAR_S;
4929 case 0x1628: /* (blue steel) */
4930 element = EL_STEEL_CHAR_T;
4933 case 0x1629: /* (blue steel) */
4934 element = EL_STEEL_CHAR_U;
4937 case 0x162a: /* (blue steel) */
4938 element = EL_STEEL_CHAR_V;
4941 case 0x162b: /* (blue steel) */
4942 element = EL_STEEL_CHAR_W;
4945 case 0x162c: /* (blue steel) */
4946 element = EL_STEEL_CHAR_X;
4949 case 0x162d: /* (blue steel) */
4950 element = EL_STEEL_CHAR_Y;
4953 case 0x162e: /* (blue steel) */
4954 element = EL_STEEL_CHAR_Z;
4957 case 0x162f: /* (blue steel) */
4958 element = EL_STEEL_CHAR_AUMLAUT;
4961 case 0x1630: /* (blue steel) */
4962 element = EL_STEEL_CHAR_OUMLAUT;
4965 case 0x1631: /* (blue steel) */
4966 element = EL_STEEL_CHAR_UUMLAUT;
4969 case 0x1632: /* (blue steel) */
4970 element = EL_STEEL_CHAR_0;
4973 case 0x1633: /* (blue steel) */
4974 element = EL_STEEL_CHAR_1;
4977 case 0x1634: /* (blue steel) */
4978 element = EL_STEEL_CHAR_2;
4981 case 0x1635: /* (blue steel) */
4982 element = EL_STEEL_CHAR_3;
4985 case 0x1636: /* (blue steel) */
4986 element = EL_STEEL_CHAR_4;
4989 case 0x1637: /* (blue steel) */
4990 element = EL_STEEL_CHAR_5;
4993 case 0x1638: /* (blue steel) */
4994 element = EL_STEEL_CHAR_6;
4997 case 0x1639: /* (blue steel) */
4998 element = EL_STEEL_CHAR_7;
5001 case 0x163a: /* (blue steel) */
5002 element = EL_STEEL_CHAR_8;
5005 case 0x163b: /* (blue steel) */
5006 element = EL_STEEL_CHAR_9;
5009 case 0x163c: /* (blue steel) */
5010 element = EL_STEEL_CHAR_PERIOD;
5013 case 0x163d: /* (blue steel) */
5014 element = EL_STEEL_CHAR_EXCLAM;
5017 case 0x163e: /* (blue steel) */
5018 element = EL_STEEL_CHAR_COLON;
5021 case 0x163f: /* (blue steel) */
5022 element = EL_STEEL_CHAR_LESS;
5025 case 0x1640: /* (blue steel) */
5026 element = EL_STEEL_CHAR_GREATER;
5029 case 0x1641: /* (blue steel) */
5030 element = EL_STEEL_CHAR_QUESTION;
5033 case 0x1642: /* (blue steel) */
5034 element = EL_STEEL_CHAR_COPYRIGHT;
5037 case 0x1643: /* (blue steel) */
5038 element = EL_STEEL_CHAR_UP;
5041 case 0x1644: /* (blue steel) */
5042 element = EL_STEEL_CHAR_DOWN;
5045 case 0x1645: /* (blue steel) */
5046 element = EL_STEEL_CHAR_BUTTON;
5049 case 0x1646: /* (blue steel) */
5050 element = EL_STEEL_CHAR_PLUS;
5053 case 0x1647: /* (blue steel) */
5054 element = EL_STEEL_CHAR_MINUS;
5057 case 0x1648: /* (blue steel) */
5058 element = EL_STEEL_CHAR_APOSTROPHE;
5061 case 0x1649: /* (blue steel) */
5062 element = EL_STEEL_CHAR_PARENLEFT;
5065 case 0x164a: /* (blue steel) */
5066 element = EL_STEEL_CHAR_PARENRIGHT;
5069 case 0x164b: /* (green steel) */
5070 element = EL_STEEL_CHAR_A;
5073 case 0x164c: /* (green steel) */
5074 element = EL_STEEL_CHAR_B;
5077 case 0x164d: /* (green steel) */
5078 element = EL_STEEL_CHAR_C;
5081 case 0x164e: /* (green steel) */
5082 element = EL_STEEL_CHAR_D;
5085 case 0x164f: /* (green steel) */
5086 element = EL_STEEL_CHAR_E;
5089 case 0x1650: /* (green steel) */
5090 element = EL_STEEL_CHAR_F;
5093 case 0x1651: /* (green steel) */
5094 element = EL_STEEL_CHAR_G;
5097 case 0x1652: /* (green steel) */
5098 element = EL_STEEL_CHAR_H;
5101 case 0x1653: /* (green steel) */
5102 element = EL_STEEL_CHAR_I;
5105 case 0x1654: /* (green steel) */
5106 element = EL_STEEL_CHAR_J;
5109 case 0x1655: /* (green steel) */
5110 element = EL_STEEL_CHAR_K;
5113 case 0x1656: /* (green steel) */
5114 element = EL_STEEL_CHAR_L;
5117 case 0x1657: /* (green steel) */
5118 element = EL_STEEL_CHAR_M;
5121 case 0x1658: /* (green steel) */
5122 element = EL_STEEL_CHAR_N;
5125 case 0x1659: /* (green steel) */
5126 element = EL_STEEL_CHAR_O;
5129 case 0x165a: /* (green steel) */
5130 element = EL_STEEL_CHAR_P;
5133 case 0x165b: /* (green steel) */
5134 element = EL_STEEL_CHAR_Q;
5137 case 0x165c: /* (green steel) */
5138 element = EL_STEEL_CHAR_R;
5141 case 0x165d: /* (green steel) */
5142 element = EL_STEEL_CHAR_S;
5145 case 0x165e: /* (green steel) */
5146 element = EL_STEEL_CHAR_T;
5149 case 0x165f: /* (green steel) */
5150 element = EL_STEEL_CHAR_U;
5153 case 0x1660: /* (green steel) */
5154 element = EL_STEEL_CHAR_V;
5157 case 0x1661: /* (green steel) */
5158 element = EL_STEEL_CHAR_W;
5161 case 0x1662: /* (green steel) */
5162 element = EL_STEEL_CHAR_X;
5165 case 0x1663: /* (green steel) */
5166 element = EL_STEEL_CHAR_Y;
5169 case 0x1664: /* (green steel) */
5170 element = EL_STEEL_CHAR_Z;
5173 case 0x1665: /* (green steel) */
5174 element = EL_STEEL_CHAR_AUMLAUT;
5177 case 0x1666: /* (green steel) */
5178 element = EL_STEEL_CHAR_OUMLAUT;
5181 case 0x1667: /* (green steel) */
5182 element = EL_STEEL_CHAR_UUMLAUT;
5185 case 0x1668: /* (green steel) */
5186 element = EL_STEEL_CHAR_0;
5189 case 0x1669: /* (green steel) */
5190 element = EL_STEEL_CHAR_1;
5193 case 0x166a: /* (green steel) */
5194 element = EL_STEEL_CHAR_2;
5197 case 0x166b: /* (green steel) */
5198 element = EL_STEEL_CHAR_3;
5201 case 0x166c: /* (green steel) */
5202 element = EL_STEEL_CHAR_4;
5205 case 0x166d: /* (green steel) */
5206 element = EL_STEEL_CHAR_5;
5209 case 0x166e: /* (green steel) */
5210 element = EL_STEEL_CHAR_6;
5213 case 0x166f: /* (green steel) */
5214 element = EL_STEEL_CHAR_7;
5217 case 0x1670: /* (green steel) */
5218 element = EL_STEEL_CHAR_8;
5221 case 0x1671: /* (green steel) */
5222 element = EL_STEEL_CHAR_9;
5225 case 0x1672: /* (green steel) */
5226 element = EL_STEEL_CHAR_PERIOD;
5229 case 0x1673: /* (green steel) */
5230 element = EL_STEEL_CHAR_EXCLAM;
5233 case 0x1674: /* (green steel) */
5234 element = EL_STEEL_CHAR_COLON;
5237 case 0x1675: /* (green steel) */
5238 element = EL_STEEL_CHAR_LESS;
5241 case 0x1676: /* (green steel) */
5242 element = EL_STEEL_CHAR_GREATER;
5245 case 0x1677: /* (green steel) */
5246 element = EL_STEEL_CHAR_QUESTION;
5249 case 0x1678: /* (green steel) */
5250 element = EL_STEEL_CHAR_COPYRIGHT;
5253 case 0x1679: /* (green steel) */
5254 element = EL_STEEL_CHAR_UP;
5257 case 0x167a: /* (green steel) */
5258 element = EL_STEEL_CHAR_DOWN;
5261 case 0x167b: /* (green steel) */
5262 element = EL_STEEL_CHAR_BUTTON;
5265 case 0x167c: /* (green steel) */
5266 element = EL_STEEL_CHAR_PLUS;
5269 case 0x167d: /* (green steel) */
5270 element = EL_STEEL_CHAR_MINUS;
5273 case 0x167e: /* (green steel) */
5274 element = EL_STEEL_CHAR_APOSTROPHE;
5277 case 0x167f: /* (green steel) */
5278 element = EL_STEEL_CHAR_PARENLEFT;
5281 case 0x1680: /* (green steel) */
5282 element = EL_STEEL_CHAR_PARENRIGHT;
5285 case 0x1681: /* gate (red) */
5286 element = EL_EM_GATE_1;
5289 case 0x1682: /* secret gate (red) */
5290 element = EL_GATE_1_GRAY;
5293 case 0x1683: /* gate (yellow) */
5294 element = EL_EM_GATE_2;
5297 case 0x1684: /* secret gate (yellow) */
5298 element = EL_GATE_2_GRAY;
5301 case 0x1685: /* gate (blue) */
5302 element = EL_EM_GATE_4;
5305 case 0x1686: /* secret gate (blue) */
5306 element = EL_GATE_4_GRAY;
5309 case 0x1687: /* gate (green) */
5310 element = EL_EM_GATE_3;
5313 case 0x1688: /* secret gate (green) */
5314 element = EL_GATE_3_GRAY;
5317 case 0x1689: /* gate (white) */
5318 element = EL_DC_GATE_WHITE;
5321 case 0x168a: /* secret gate (white) */
5322 element = EL_DC_GATE_WHITE_GRAY;
5325 case 0x168b: /* secret gate (no key) */
5326 element = EL_DC_GATE_FAKE_GRAY;
5330 element = EL_ROBOT_WHEEL;
5334 element = EL_DC_TIMEGATE_SWITCH;
5338 element = EL_ACID_POOL_BOTTOM;
5342 element = EL_ACID_POOL_TOPLEFT;
5346 element = EL_ACID_POOL_TOPRIGHT;
5350 element = EL_ACID_POOL_BOTTOMLEFT;
5354 element = EL_ACID_POOL_BOTTOMRIGHT;
5358 element = EL_STEELWALL;
5362 element = EL_STEELWALL_SLIPPERY;
5365 case 0x1695: /* steel wall (not round) */
5366 element = EL_STEELWALL;
5369 case 0x1696: /* steel wall (left) */
5370 element = EL_DC_STEELWALL_1_LEFT;
5373 case 0x1697: /* steel wall (bottom) */
5374 element = EL_DC_STEELWALL_1_BOTTOM;
5377 case 0x1698: /* steel wall (right) */
5378 element = EL_DC_STEELWALL_1_RIGHT;
5381 case 0x1699: /* steel wall (top) */
5382 element = EL_DC_STEELWALL_1_TOP;
5385 case 0x169a: /* steel wall (left/bottom) */
5386 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5389 case 0x169b: /* steel wall (right/bottom) */
5390 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5393 case 0x169c: /* steel wall (right/top) */
5394 element = EL_DC_STEELWALL_1_TOPRIGHT;
5397 case 0x169d: /* steel wall (left/top) */
5398 element = EL_DC_STEELWALL_1_TOPLEFT;
5401 case 0x169e: /* steel wall (right/bottom small) */
5402 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5405 case 0x169f: /* steel wall (left/bottom small) */
5406 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5409 case 0x16a0: /* steel wall (right/top small) */
5410 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5413 case 0x16a1: /* steel wall (left/top small) */
5414 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5417 case 0x16a2: /* steel wall (left/right) */
5418 element = EL_DC_STEELWALL_1_VERTICAL;
5421 case 0x16a3: /* steel wall (top/bottom) */
5422 element = EL_DC_STEELWALL_1_HORIZONTAL;
5425 case 0x16a4: /* steel wall 2 (left end) */
5426 element = EL_DC_STEELWALL_2_LEFT;
5429 case 0x16a5: /* steel wall 2 (right end) */
5430 element = EL_DC_STEELWALL_2_RIGHT;
5433 case 0x16a6: /* steel wall 2 (top end) */
5434 element = EL_DC_STEELWALL_2_TOP;
5437 case 0x16a7: /* steel wall 2 (bottom end) */
5438 element = EL_DC_STEELWALL_2_BOTTOM;
5441 case 0x16a8: /* steel wall 2 (left/right) */
5442 element = EL_DC_STEELWALL_2_HORIZONTAL;
5445 case 0x16a9: /* steel wall 2 (up/down) */
5446 element = EL_DC_STEELWALL_2_VERTICAL;
5449 case 0x16aa: /* steel wall 2 (mid) */
5450 element = EL_DC_STEELWALL_2_MIDDLE;
5454 element = EL_SIGN_EXCLAMATION;
5458 element = EL_SIGN_RADIOACTIVITY;
5462 element = EL_SIGN_STOP;
5466 element = EL_SIGN_WHEELCHAIR;
5470 element = EL_SIGN_PARKING;
5474 element = EL_SIGN_NO_ENTRY;
5478 element = EL_SIGN_HEART;
5482 element = EL_SIGN_GIVE_WAY;
5486 element = EL_SIGN_ENTRY_FORBIDDEN;
5490 element = EL_SIGN_EMERGENCY_EXIT;
5494 element = EL_SIGN_YIN_YANG;
5498 element = EL_WALL_EMERALD;
5502 element = EL_WALL_DIAMOND;
5506 element = EL_WALL_PEARL;
5510 element = EL_WALL_CRYSTAL;
5514 element = EL_INVISIBLE_WALL;
5518 element = EL_INVISIBLE_STEELWALL;
5521 /* 0x16bc - 0x16cb: */
5522 /* EL_INVISIBLE_SAND */
5525 element = EL_LIGHT_SWITCH;
5529 element = EL_ENVELOPE_1;
5533 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5534 element = EL_DIAMOND;
5535 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5536 element = EL_EMERALD;
5537 else if (element >= 0x157c && element <= 0x158b)
5539 else if (element >= 0x1590 && element <= 0x159f)
5540 element = EL_DC_LANDMINE;
5541 else if (element >= 0x16bc && element <= 0x16cb)
5542 element = EL_INVISIBLE_SAND;
5545 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5546 element = EL_UNKNOWN;
5551 return getMappedElement(element);
5554 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5557 byte header[DC_LEVEL_HEADER_SIZE];
5559 int envelope_header_pos = 62;
5560 int envelope_content_pos = 94;
5561 int level_name_pos = 251;
5562 int level_author_pos = 292;
5563 int envelope_header_len;
5564 int envelope_content_len;
5566 int level_author_len;
5568 int num_yamyam_contents;
5571 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5573 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5575 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5577 header[i * 2 + 0] = header_word >> 8;
5578 header[i * 2 + 1] = header_word & 0xff;
5581 /* read some values from level header to check level decoding integrity */
5582 fieldx = header[6] | (header[7] << 8);
5583 fieldy = header[8] | (header[9] << 8);
5584 num_yamyam_contents = header[60] | (header[61] << 8);
5586 /* do some simple sanity checks to ensure that level was correctly decoded */
5587 if (fieldx < 1 || fieldx > 256 ||
5588 fieldy < 1 || fieldy > 256 ||
5589 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5591 level->no_valid_file = TRUE;
5593 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5598 /* maximum envelope header size is 31 bytes */
5599 envelope_header_len = header[envelope_header_pos];
5600 /* maximum envelope content size is 110 (156?) bytes */
5601 envelope_content_len = header[envelope_content_pos];
5603 /* maximum level title size is 40 bytes */
5604 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5605 /* maximum level author size is 30 (51?) bytes */
5606 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5610 for (i = 0; i < envelope_header_len; i++)
5611 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5612 level->envelope[0].text[envelope_size++] =
5613 header[envelope_header_pos + 1 + i];
5615 if (envelope_header_len > 0 && envelope_content_len > 0)
5617 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5618 level->envelope[0].text[envelope_size++] = '\n';
5619 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5620 level->envelope[0].text[envelope_size++] = '\n';
5623 for (i = 0; i < envelope_content_len; i++)
5624 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5625 level->envelope[0].text[envelope_size++] =
5626 header[envelope_content_pos + 1 + i];
5628 level->envelope[0].text[envelope_size] = '\0';
5630 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5631 level->envelope[0].ysize = 10;
5632 level->envelope[0].autowrap = TRUE;
5633 level->envelope[0].centered = TRUE;
5635 for (i = 0; i < level_name_len; i++)
5636 level->name[i] = header[level_name_pos + 1 + i];
5637 level->name[level_name_len] = '\0';
5639 for (i = 0; i < level_author_len; i++)
5640 level->author[i] = header[level_author_pos + 1 + i];
5641 level->author[level_author_len] = '\0';
5643 num_yamyam_contents = header[60] | (header[61] << 8);
5644 level->num_yamyam_contents =
5645 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5647 for (i = 0; i < num_yamyam_contents; i++)
5649 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5651 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5652 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5654 if (i < MAX_ELEMENT_CONTENTS)
5655 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5659 fieldx = header[6] | (header[7] << 8);
5660 fieldy = header[8] | (header[9] << 8);
5661 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5662 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5664 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5666 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5667 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5669 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5670 level->field[x][y] = getMappedElement_DC(element_dc);
5673 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5674 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5675 level->field[x][y] = EL_PLAYER_1;
5677 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5678 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5679 level->field[x][y] = EL_PLAYER_2;
5681 level->gems_needed = header[18] | (header[19] << 8);
5683 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5684 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5685 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5686 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5687 level->score[SC_NUT] = header[28] | (header[29] << 8);
5688 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5689 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5690 level->score[SC_BUG] = header[34] | (header[35] << 8);
5691 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5692 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5693 level->score[SC_KEY] = header[40] | (header[41] << 8);
5694 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5696 level->time = header[44] | (header[45] << 8);
5698 level->amoeba_speed = header[46] | (header[47] << 8);
5699 level->time_light = header[48] | (header[49] << 8);
5700 level->time_timegate = header[50] | (header[51] << 8);
5701 level->time_wheel = header[52] | (header[53] << 8);
5702 level->time_magic_wall = header[54] | (header[55] << 8);
5703 level->extra_time = header[56] | (header[57] << 8);
5704 level->shield_normal_time = header[58] | (header[59] << 8);
5706 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5707 can slip down from flat walls, like normal walls and steel walls */
5708 level->em_slippery_gems = TRUE;
5711 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5712 struct LevelFileInfo *level_file_info,
5713 boolean level_info_only)
5715 char *filename = level_file_info->filename;
5717 int num_magic_bytes = 8;
5718 char magic_bytes[num_magic_bytes + 1];
5719 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5721 if (!(file = openFile(filename, MODE_READ)))
5723 level->no_valid_file = TRUE;
5725 if (!level_info_only)
5726 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5731 // fseek(file, 0x0000, SEEK_SET);
5733 if (level_file_info->packed)
5735 /* read "magic bytes" from start of file */
5736 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5737 magic_bytes[0] = '\0';
5739 /* check "magic bytes" for correct file format */
5740 if (!strPrefix(magic_bytes, "DC2"))
5742 level->no_valid_file = TRUE;
5744 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5750 if (strPrefix(magic_bytes, "DC2Win95") ||
5751 strPrefix(magic_bytes, "DC2Win98"))
5753 int position_first_level = 0x00fa;
5754 int extra_bytes = 4;
5757 /* advance file stream to first level inside the level package */
5758 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5760 /* each block of level data is followed by block of non-level data */
5761 num_levels_to_skip *= 2;
5763 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5764 while (num_levels_to_skip >= 0)
5766 /* advance file stream to next level inside the level package */
5767 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5769 level->no_valid_file = TRUE;
5771 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5777 /* skip apparently unused extra bytes following each level */
5778 ReadUnusedBytesFromFile(file, extra_bytes);
5780 /* read size of next level in level package */
5781 skip_bytes = getFile32BitLE(file);
5783 num_levels_to_skip--;
5788 level->no_valid_file = TRUE;
5790 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5797 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5803 /* ------------------------------------------------------------------------- */
5804 /* functions for loading SB level */
5805 /* ------------------------------------------------------------------------- */
5807 int getMappedElement_SB(int element_ascii, boolean use_ces)
5815 sb_element_mapping[] =
5817 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5818 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5819 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5820 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5821 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5822 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5823 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5824 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5831 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5832 if (element_ascii == sb_element_mapping[i].ascii)
5833 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5835 return EL_UNDEFINED;
5838 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5839 struct LevelFileInfo *level_file_info,
5840 boolean level_info_only)
5842 char *filename = level_file_info->filename;
5843 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5844 char last_comment[MAX_LINE_LEN];
5845 char level_name[MAX_LINE_LEN];
5848 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5849 boolean read_continued_line = FALSE;
5850 boolean reading_playfield = FALSE;
5851 boolean got_valid_playfield_line = FALSE;
5852 boolean invalid_playfield_char = FALSE;
5853 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5854 int file_level_nr = 0;
5856 int x = 0, y = 0; /* initialized to make compilers happy */
5858 last_comment[0] = '\0';
5859 level_name[0] = '\0';
5861 if (!(file = openFile(filename, MODE_READ)))
5863 level->no_valid_file = TRUE;
5865 if (!level_info_only)
5866 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5871 while (!checkEndOfFile(file))
5873 /* level successfully read, but next level may follow here */
5874 if (!got_valid_playfield_line && reading_playfield)
5876 /* read playfield from single level file -- skip remaining file */
5877 if (!level_file_info->packed)
5880 if (file_level_nr >= num_levels_to_skip)
5885 last_comment[0] = '\0';
5886 level_name[0] = '\0';
5888 reading_playfield = FALSE;
5891 got_valid_playfield_line = FALSE;
5893 /* read next line of input file */
5894 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5897 /* check if line was completely read and is terminated by line break */
5898 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5901 /* cut trailing line break (this can be newline and/or carriage return) */
5902 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5903 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5906 /* copy raw input line for later use (mainly debugging output) */
5907 strcpy(line_raw, line);
5909 if (read_continued_line)
5911 /* append new line to existing line, if there is enough space */
5912 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5913 strcat(previous_line, line_ptr);
5915 strcpy(line, previous_line); /* copy storage buffer to line */
5917 read_continued_line = FALSE;
5920 /* if the last character is '\', continue at next line */
5921 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5923 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5924 strcpy(previous_line, line); /* copy line to storage buffer */
5926 read_continued_line = TRUE;
5931 /* skip empty lines */
5932 if (line[0] == '\0')
5935 /* extract comment text from comment line */
5938 for (line_ptr = line; *line_ptr; line_ptr++)
5939 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5942 strcpy(last_comment, line_ptr);
5947 /* extract level title text from line containing level title */
5948 if (line[0] == '\'')
5950 strcpy(level_name, &line[1]);
5952 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5953 level_name[strlen(level_name) - 1] = '\0';
5958 /* skip lines containing only spaces (or empty lines) */
5959 for (line_ptr = line; *line_ptr; line_ptr++)
5960 if (*line_ptr != ' ')
5962 if (*line_ptr == '\0')
5965 /* at this point, we have found a line containing part of a playfield */
5967 got_valid_playfield_line = TRUE;
5969 if (!reading_playfield)
5971 reading_playfield = TRUE;
5972 invalid_playfield_char = FALSE;
5974 for (x = 0; x < MAX_LEV_FIELDX; x++)
5975 for (y = 0; y < MAX_LEV_FIELDY; y++)
5976 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5981 /* start with topmost tile row */
5985 /* skip playfield line if larger row than allowed */
5986 if (y >= MAX_LEV_FIELDY)
5989 /* start with leftmost tile column */
5992 /* read playfield elements from line */
5993 for (line_ptr = line; *line_ptr; line_ptr++)
5995 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5997 /* stop parsing playfield line if larger column than allowed */
5998 if (x >= MAX_LEV_FIELDX)
6001 if (mapped_sb_element == EL_UNDEFINED)
6003 invalid_playfield_char = TRUE;
6008 level->field[x][y] = mapped_sb_element;
6010 /* continue with next tile column */
6013 level->fieldx = MAX(x, level->fieldx);
6016 if (invalid_playfield_char)
6018 /* if first playfield line, treat invalid lines as comment lines */
6020 reading_playfield = FALSE;
6025 /* continue with next tile row */
6033 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6034 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6036 if (!reading_playfield)
6038 level->no_valid_file = TRUE;
6040 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6045 if (*level_name != '\0')
6047 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6048 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6050 else if (*last_comment != '\0')
6052 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6053 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6057 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6060 /* set all empty fields beyond the border walls to invisible steel wall */
6061 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6063 if ((x == 0 || x == level->fieldx - 1 ||
6064 y == 0 || y == level->fieldy - 1) &&
6065 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6066 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6067 level->field, level->fieldx, level->fieldy);
6070 /* set special level settings for Sokoban levels */
6073 level->use_step_counter = TRUE;
6075 if (load_xsb_to_ces)
6077 /* special global settings can now be set in level template */
6079 level->use_custom_template = TRUE;
6084 /* ------------------------------------------------------------------------- */
6085 /* functions for handling native levels */
6086 /* ------------------------------------------------------------------------- */
6088 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6089 struct LevelFileInfo *level_file_info,
6090 boolean level_info_only)
6092 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6093 level->no_valid_file = TRUE;
6096 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6097 struct LevelFileInfo *level_file_info,
6098 boolean level_info_only)
6102 /* determine position of requested level inside level package */
6103 if (level_file_info->packed)
6104 pos = level_file_info->nr - leveldir_current->first_level;
6106 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6107 level->no_valid_file = TRUE;
6110 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6111 struct LevelFileInfo *level_file_info,
6112 boolean level_info_only)
6114 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6115 level->no_valid_file = TRUE;
6118 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6120 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6121 CopyNativeLevel_RND_to_EM(level);
6122 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6123 CopyNativeLevel_RND_to_SP(level);
6124 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6125 CopyNativeLevel_RND_to_MM(level);
6128 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6130 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6131 CopyNativeLevel_EM_to_RND(level);
6132 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6133 CopyNativeLevel_SP_to_RND(level);
6134 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6135 CopyNativeLevel_MM_to_RND(level);
6138 void SaveNativeLevel(struct LevelInfo *level)
6140 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6142 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6143 char *filename = getLevelFilenameFromBasename(basename);
6145 CopyNativeLevel_RND_to_SP(level);
6146 CopyNativeTape_RND_to_SP(level);
6148 SaveNativeLevel_SP(filename);
6153 /* ------------------------------------------------------------------------- */
6154 /* functions for loading generic level */
6155 /* ------------------------------------------------------------------------- */
6157 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6158 struct LevelFileInfo *level_file_info,
6159 boolean level_info_only)
6161 /* always start with reliable default values */
6162 setLevelInfoToDefaults(level, level_info_only, TRUE);
6164 switch (level_file_info->type)
6166 case LEVEL_FILE_TYPE_RND:
6167 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6170 case LEVEL_FILE_TYPE_EM:
6171 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6172 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6175 case LEVEL_FILE_TYPE_SP:
6176 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6177 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6180 case LEVEL_FILE_TYPE_MM:
6181 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6182 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6185 case LEVEL_FILE_TYPE_DC:
6186 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6189 case LEVEL_FILE_TYPE_SB:
6190 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6194 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6198 /* if level file is invalid, restore level structure to default values */
6199 if (level->no_valid_file)
6200 setLevelInfoToDefaults(level, level_info_only, FALSE);
6202 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6203 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6205 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6206 CopyNativeLevel_Native_to_RND(level);
6209 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6211 static struct LevelFileInfo level_file_info;
6213 /* always start with reliable default values */
6214 setFileInfoToDefaults(&level_file_info);
6216 level_file_info.nr = 0; /* unknown level number */
6217 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6219 setString(&level_file_info.filename, filename);
6221 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6224 static void LoadLevel_InitVersion(struct LevelInfo *level)
6228 if (leveldir_current == NULL) /* only when dumping level */
6231 /* all engine modifications also valid for levels which use latest engine */
6232 if (level->game_version < VERSION_IDENT(3,2,0,5))
6234 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6235 level->score[SC_TIME_BONUS] /= 10;
6238 if (leveldir_current->latest_engine)
6240 /* ---------- use latest game engine ----------------------------------- */
6242 /* For all levels which are forced to use the latest game engine version
6243 (normally all but user contributed, private and undefined levels), set
6244 the game engine version to the actual version; this allows for actual
6245 corrections in the game engine to take effect for existing, converted
6246 levels (from "classic" or other existing games) to make the emulation
6247 of the corresponding game more accurate, while (hopefully) not breaking
6248 existing levels created from other players. */
6250 level->game_version = GAME_VERSION_ACTUAL;
6252 /* Set special EM style gems behaviour: EM style gems slip down from
6253 normal, steel and growing wall. As this is a more fundamental change,
6254 it seems better to set the default behaviour to "off" (as it is more
6255 natural) and make it configurable in the level editor (as a property
6256 of gem style elements). Already existing converted levels (neither
6257 private nor contributed levels) are changed to the new behaviour. */
6259 if (level->file_version < FILE_VERSION_2_0)
6260 level->em_slippery_gems = TRUE;
6265 /* ---------- use game engine the level was created with ----------------- */
6267 /* For all levels which are not forced to use the latest game engine
6268 version (normally user contributed, private and undefined levels),
6269 use the version of the game engine the levels were created for.
6271 Since 2.0.1, the game engine version is now directly stored
6272 in the level file (chunk "VERS"), so there is no need anymore
6273 to set the game version from the file version (except for old,
6274 pre-2.0 levels, where the game version is still taken from the
6275 file format version used to store the level -- see above). */
6277 /* player was faster than enemies in 1.0.0 and before */
6278 if (level->file_version == FILE_VERSION_1_0)
6279 for (i = 0; i < MAX_PLAYERS; i++)
6280 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6282 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6283 if (level->game_version == VERSION_IDENT(2,0,1,0))
6284 level->em_slippery_gems = TRUE;
6286 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6287 if (level->game_version < VERSION_IDENT(2,2,0,0))
6288 level->use_spring_bug = TRUE;
6290 if (level->game_version < VERSION_IDENT(3,2,0,5))
6292 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6293 level->use_time_orb_bug = TRUE;
6295 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6296 level->block_snap_field = FALSE;
6298 /* extra time score was same value as time left score before 3.2.0-5 */
6299 level->extra_time_score = level->score[SC_TIME_BONUS];
6302 if (level->game_version < VERSION_IDENT(3,2,0,7))
6304 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6305 level->continuous_snapping = FALSE;
6308 /* only few elements were able to actively move into acid before 3.1.0 */
6309 /* trigger settings did not exist before 3.1.0; set to default "any" */
6310 if (level->game_version < VERSION_IDENT(3,1,0,0))
6312 /* correct "can move into acid" settings (all zero in old levels) */
6314 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6315 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6317 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6318 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6319 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6320 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6322 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6323 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6325 /* correct trigger settings (stored as zero == "none" in old levels) */
6327 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6329 int element = EL_CUSTOM_START + i;
6330 struct ElementInfo *ei = &element_info[element];
6332 for (j = 0; j < ei->num_change_pages; j++)
6334 struct ElementChangeInfo *change = &ei->change_page[j];
6336 change->trigger_player = CH_PLAYER_ANY;
6337 change->trigger_page = CH_PAGE_ANY;
6342 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6344 int element = EL_CUSTOM_256;
6345 struct ElementInfo *ei = &element_info[element];
6346 struct ElementChangeInfo *change = &ei->change_page[0];
6348 /* This is needed to fix a problem that was caused by a bugfix in function
6349 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6350 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6351 not replace walkable elements, but instead just placed the player on it,
6352 without placing the Sokoban field under the player). Unfortunately, this
6353 breaks "Snake Bite" style levels when the snake is halfway through a door
6354 that just closes (the snake head is still alive and can be moved in this
6355 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6356 player (without Sokoban element) which then gets killed as designed). */
6358 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6359 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6360 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6361 change->target_element = EL_PLAYER_1;
6364 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6365 if (level->game_version < VERSION_IDENT(3,2,5,0))
6367 /* This is needed to fix a problem that was caused by a bugfix in function
6368 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6369 corrects the behaviour when a custom element changes to another custom
6370 element with a higher element number that has change actions defined.
6371 Normally, only one change per frame is allowed for custom elements.
6372 Therefore, it is checked if a custom element already changed in the
6373 current frame; if it did, subsequent changes are suppressed.
6374 Unfortunately, this is only checked for element changes, but not for
6375 change actions, which are still executed. As the function above loops
6376 through all custom elements from lower to higher, an element change
6377 resulting in a lower CE number won't be checked again, while a target
6378 element with a higher number will also be checked, and potential change
6379 actions will get executed for this CE, too (which is wrong), while
6380 further changes are ignored (which is correct). As this bugfix breaks
6381 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6382 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6383 behaviour for existing levels and tapes that make use of this bug */
6385 level->use_action_after_change_bug = TRUE;
6388 /* not centering level after relocating player was default only in 3.2.3 */
6389 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6390 level->shifted_relocation = TRUE;
6392 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6393 if (level->game_version < VERSION_IDENT(3,2,6,0))
6394 level->em_explodes_by_fire = TRUE;
6397 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6401 /* map elements that have changed in newer versions */
6402 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6403 level->game_version);
6404 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6405 for (x = 0; x < 3; x++)
6406 for (y = 0; y < 3; y++)
6407 level->yamyam_content[i].e[x][y] =
6408 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6409 level->game_version);
6413 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6417 /* map custom element change events that have changed in newer versions
6418 (these following values were accidentally changed in version 3.0.1)
6419 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6420 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6422 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6424 int element = EL_CUSTOM_START + i;
6426 /* order of checking and copying events to be mapped is important */
6427 /* (do not change the start and end value -- they are constant) */
6428 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6430 if (HAS_CHANGE_EVENT(element, j - 2))
6432 SET_CHANGE_EVENT(element, j - 2, FALSE);
6433 SET_CHANGE_EVENT(element, j, TRUE);
6437 /* order of checking and copying events to be mapped is important */
6438 /* (do not change the start and end value -- they are constant) */
6439 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6441 if (HAS_CHANGE_EVENT(element, j - 1))
6443 SET_CHANGE_EVENT(element, j - 1, FALSE);
6444 SET_CHANGE_EVENT(element, j, TRUE);
6450 /* initialize "can_change" field for old levels with only one change page */
6451 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6453 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6455 int element = EL_CUSTOM_START + i;
6457 if (CAN_CHANGE(element))
6458 element_info[element].change->can_change = TRUE;
6462 /* correct custom element values (for old levels without these options) */
6463 if (level->game_version < VERSION_IDENT(3,1,1,0))
6465 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6467 int element = EL_CUSTOM_START + i;
6468 struct ElementInfo *ei = &element_info[element];
6470 if (ei->access_direction == MV_NO_DIRECTION)
6471 ei->access_direction = MV_ALL_DIRECTIONS;
6475 /* correct custom element values (fix invalid values for all versions) */
6478 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6480 int element = EL_CUSTOM_START + i;
6481 struct ElementInfo *ei = &element_info[element];
6483 for (j = 0; j < ei->num_change_pages; j++)
6485 struct ElementChangeInfo *change = &ei->change_page[j];
6487 if (change->trigger_player == CH_PLAYER_NONE)
6488 change->trigger_player = CH_PLAYER_ANY;
6490 if (change->trigger_side == CH_SIDE_NONE)
6491 change->trigger_side = CH_SIDE_ANY;
6496 /* initialize "can_explode" field for old levels which did not store this */
6497 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6498 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6502 int element = EL_CUSTOM_START + i;
6504 if (EXPLODES_1X1_OLD(element))
6505 element_info[element].explosion_type = EXPLODES_1X1;
6507 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6508 EXPLODES_SMASHED(element) ||
6509 EXPLODES_IMPACT(element)));
6513 /* correct previously hard-coded move delay values for maze runner style */
6514 if (level->game_version < VERSION_IDENT(3,1,1,0))
6516 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6518 int element = EL_CUSTOM_START + i;
6520 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6522 /* previously hard-coded and therefore ignored */
6523 element_info[element].move_delay_fixed = 9;
6524 element_info[element].move_delay_random = 0;
6529 /* set some other uninitialized values of custom elements in older levels */
6530 if (level->game_version < VERSION_IDENT(3,1,0,0))
6532 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6534 int element = EL_CUSTOM_START + i;
6536 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6538 element_info[element].explosion_delay = 17;
6539 element_info[element].ignition_delay = 8;
6544 static void LoadLevel_InitElements(struct LevelInfo *level)
6546 LoadLevel_InitStandardElements(level);
6548 if (level->file_has_custom_elements)
6549 LoadLevel_InitCustomElements(level);
6551 /* initialize element properties for level editor etc. */
6552 InitElementPropertiesEngine(level->game_version);
6553 InitElementPropertiesGfxElement();
6556 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6560 /* map elements that have changed in newer versions */
6561 for (y = 0; y < level->fieldy; y++)
6562 for (x = 0; x < level->fieldx; x++)
6563 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6564 level->game_version);
6566 /* clear unused playfield data (nicer if level gets resized in editor) */
6567 for (x = 0; x < MAX_LEV_FIELDX; x++)
6568 for (y = 0; y < MAX_LEV_FIELDY; y++)
6569 if (x >= level->fieldx || y >= level->fieldy)
6570 level->field[x][y] = EL_EMPTY;
6572 /* copy elements to runtime playfield array */
6573 for (x = 0; x < MAX_LEV_FIELDX; x++)
6574 for (y = 0; y < MAX_LEV_FIELDY; y++)
6575 Feld[x][y] = level->field[x][y];
6577 /* initialize level size variables for faster access */
6578 lev_fieldx = level->fieldx;
6579 lev_fieldy = level->fieldy;
6581 /* determine border element for this level */
6582 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6583 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6588 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6590 struct LevelFileInfo *level_file_info = &level->file_info;
6592 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6593 CopyNativeLevel_RND_to_Native(level);
6596 static void LoadLevelTemplate_LoadAndInit()
6598 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6600 LoadLevel_InitVersion(&level_template);
6601 LoadLevel_InitElements(&level_template);
6603 ActivateLevelTemplate();
6606 void LoadLevelTemplate(int nr)
6608 if (!fileExists(getGlobalLevelTemplateFilename()))
6610 Error(ERR_WARN, "no level template found for this level");
6615 setLevelFileInfo(&level_template.file_info, nr);
6617 LoadLevelTemplate_LoadAndInit();
6620 static void LoadLevelTemplateFromNetwork(struct LevelFileInfo *lfi_network_template)
6622 copyLevelFileInfo(lfi_network_template, &level_template.file_info);
6624 LoadLevelTemplate_LoadAndInit();
6627 static void LoadLevel_LoadAndInit(struct LevelFileInfo *lfi_network_template)
6629 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6631 if (level.use_custom_template)
6633 if (lfi_network_template != NULL)
6634 LoadLevelTemplateFromNetwork(lfi_network_template);
6636 LoadLevelTemplate(-1);
6639 LoadLevel_InitVersion(&level);
6640 LoadLevel_InitElements(&level);
6641 LoadLevel_InitPlayfield(&level);
6643 LoadLevel_InitNativeEngines(&level);
6646 void LoadLevel(int nr)
6648 setLevelFileInfo(&level.file_info, nr);
6650 LoadLevel_LoadAndInit(NULL);
6653 void LoadLevelInfoOnly(int nr)
6655 setLevelFileInfo(&level.file_info, nr);
6657 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6660 void LoadLevelFromNetwork(struct LevelFileInfo *lfi_network_level,
6661 struct LevelFileInfo *lfi_network_template)
6663 copyLevelFileInfo(lfi_network_level, &level.file_info);
6665 LoadLevel_LoadAndInit(lfi_network_template);
6668 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6672 chunk_size += putFileVersion(file, level->file_version);
6673 chunk_size += putFileVersion(file, level->game_version);
6678 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6682 chunk_size += putFile16BitBE(file, level->creation_date.year);
6683 chunk_size += putFile8Bit(file, level->creation_date.month);
6684 chunk_size += putFile8Bit(file, level->creation_date.day);
6689 #if ENABLE_HISTORIC_CHUNKS
6690 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6694 putFile8Bit(file, level->fieldx);
6695 putFile8Bit(file, level->fieldy);
6697 putFile16BitBE(file, level->time);
6698 putFile16BitBE(file, level->gems_needed);
6700 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6701 putFile8Bit(file, level->name[i]);
6703 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6704 putFile8Bit(file, level->score[i]);
6706 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6707 for (y = 0; y < 3; y++)
6708 for (x = 0; x < 3; x++)
6709 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6710 level->yamyam_content[i].e[x][y]));
6711 putFile8Bit(file, level->amoeba_speed);
6712 putFile8Bit(file, level->time_magic_wall);
6713 putFile8Bit(file, level->time_wheel);
6714 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6715 level->amoeba_content));
6716 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6717 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6718 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6719 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6721 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6723 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6724 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6725 putFile32BitBE(file, level->can_move_into_acid_bits);
6726 putFile8Bit(file, level->dont_collide_with_bits);
6728 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6729 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6731 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6732 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6733 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6735 putFile8Bit(file, level->game_engine_type);
6737 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6741 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6746 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6747 chunk_size += putFile8Bit(file, level->name[i]);
6752 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6757 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6758 chunk_size += putFile8Bit(file, level->author[i]);
6763 #if ENABLE_HISTORIC_CHUNKS
6764 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6769 for (y = 0; y < level->fieldy; y++)
6770 for (x = 0; x < level->fieldx; x++)
6771 if (level->encoding_16bit_field)
6772 chunk_size += putFile16BitBE(file, level->field[x][y]);
6774 chunk_size += putFile8Bit(file, level->field[x][y]);
6780 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6785 for (y = 0; y < level->fieldy; y++)
6786 for (x = 0; x < level->fieldx; x++)
6787 chunk_size += putFile16BitBE(file, level->field[x][y]);
6792 #if ENABLE_HISTORIC_CHUNKS
6793 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6797 putFile8Bit(file, EL_YAMYAM);
6798 putFile8Bit(file, level->num_yamyam_contents);
6799 putFile8Bit(file, 0);
6800 putFile8Bit(file, 0);
6802 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6803 for (y = 0; y < 3; y++)
6804 for (x = 0; x < 3; x++)
6805 if (level->encoding_16bit_field)
6806 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6808 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6812 #if ENABLE_HISTORIC_CHUNKS
6813 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6816 int num_contents, content_xsize, content_ysize;
6817 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6819 if (element == EL_YAMYAM)
6821 num_contents = level->num_yamyam_contents;
6825 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6826 for (y = 0; y < 3; y++)
6827 for (x = 0; x < 3; x++)
6828 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6830 else if (element == EL_BD_AMOEBA)
6836 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6837 for (y = 0; y < 3; y++)
6838 for (x = 0; x < 3; x++)
6839 content_array[i][x][y] = EL_EMPTY;
6840 content_array[0][0][0] = level->amoeba_content;
6844 /* chunk header already written -- write empty chunk data */
6845 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6847 Error(ERR_WARN, "cannot save content for element '%d'", element);
6851 putFile16BitBE(file, element);
6852 putFile8Bit(file, num_contents);
6853 putFile8Bit(file, content_xsize);
6854 putFile8Bit(file, content_ysize);
6856 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6858 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6859 for (y = 0; y < 3; y++)
6860 for (x = 0; x < 3; x++)
6861 putFile16BitBE(file, content_array[i][x][y]);
6865 #if ENABLE_HISTORIC_CHUNKS
6866 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6868 int envelope_nr = element - EL_ENVELOPE_1;
6869 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6873 chunk_size += putFile16BitBE(file, element);
6874 chunk_size += putFile16BitBE(file, envelope_len);
6875 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6876 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6878 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6879 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6881 for (i = 0; i < envelope_len; i++)
6882 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6888 #if ENABLE_HISTORIC_CHUNKS
6889 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6890 int num_changed_custom_elements)
6894 putFile16BitBE(file, num_changed_custom_elements);
6896 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6898 int element = EL_CUSTOM_START + i;
6900 struct ElementInfo *ei = &element_info[element];
6902 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6904 if (check < num_changed_custom_elements)
6906 putFile16BitBE(file, element);
6907 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6914 if (check != num_changed_custom_elements) /* should not happen */
6915 Error(ERR_WARN, "inconsistent number of custom element properties");
6919 #if ENABLE_HISTORIC_CHUNKS
6920 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6921 int num_changed_custom_elements)
6925 putFile16BitBE(file, num_changed_custom_elements);
6927 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6929 int element = EL_CUSTOM_START + i;
6931 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6933 if (check < num_changed_custom_elements)
6935 putFile16BitBE(file, element);
6936 putFile16BitBE(file, element_info[element].change->target_element);
6943 if (check != num_changed_custom_elements) /* should not happen */
6944 Error(ERR_WARN, "inconsistent number of custom target elements");
6948 #if ENABLE_HISTORIC_CHUNKS
6949 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6950 int num_changed_custom_elements)
6952 int i, j, x, y, check = 0;
6954 putFile16BitBE(file, num_changed_custom_elements);
6956 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6958 int element = EL_CUSTOM_START + i;
6959 struct ElementInfo *ei = &element_info[element];
6961 if (ei->modified_settings)
6963 if (check < num_changed_custom_elements)
6965 putFile16BitBE(file, element);
6967 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6968 putFile8Bit(file, ei->description[j]);
6970 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6972 /* some free bytes for future properties and padding */
6973 WriteUnusedBytesToFile(file, 7);
6975 putFile8Bit(file, ei->use_gfx_element);
6976 putFile16BitBE(file, ei->gfx_element_initial);
6978 putFile8Bit(file, ei->collect_score_initial);
6979 putFile8Bit(file, ei->collect_count_initial);
6981 putFile16BitBE(file, ei->push_delay_fixed);
6982 putFile16BitBE(file, ei->push_delay_random);
6983 putFile16BitBE(file, ei->move_delay_fixed);
6984 putFile16BitBE(file, ei->move_delay_random);
6986 putFile16BitBE(file, ei->move_pattern);
6987 putFile8Bit(file, ei->move_direction_initial);
6988 putFile8Bit(file, ei->move_stepsize);
6990 for (y = 0; y < 3; y++)
6991 for (x = 0; x < 3; x++)
6992 putFile16BitBE(file, ei->content.e[x][y]);
6994 putFile32BitBE(file, ei->change->events);
6996 putFile16BitBE(file, ei->change->target_element);
6998 putFile16BitBE(file, ei->change->delay_fixed);
6999 putFile16BitBE(file, ei->change->delay_random);
7000 putFile16BitBE(file, ei->change->delay_frames);
7002 putFile16BitBE(file, ei->change->initial_trigger_element);
7004 putFile8Bit(file, ei->change->explode);
7005 putFile8Bit(file, ei->change->use_target_content);
7006 putFile8Bit(file, ei->change->only_if_complete);
7007 putFile8Bit(file, ei->change->use_random_replace);
7009 putFile8Bit(file, ei->change->random_percentage);
7010 putFile8Bit(file, ei->change->replace_when);
7012 for (y = 0; y < 3; y++)
7013 for (x = 0; x < 3; x++)
7014 putFile16BitBE(file, ei->change->content.e[x][y]);
7016 putFile8Bit(file, ei->slippery_type);
7018 /* some free bytes for future properties and padding */
7019 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7026 if (check != num_changed_custom_elements) /* should not happen */
7027 Error(ERR_WARN, "inconsistent number of custom element properties");
7031 #if ENABLE_HISTORIC_CHUNKS
7032 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7034 struct ElementInfo *ei = &element_info[element];
7037 /* ---------- custom element base property values (96 bytes) ------------- */
7039 putFile16BitBE(file, element);
7041 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7042 putFile8Bit(file, ei->description[i]);
7044 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7046 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7048 putFile8Bit(file, ei->num_change_pages);
7050 putFile16BitBE(file, ei->ce_value_fixed_initial);
7051 putFile16BitBE(file, ei->ce_value_random_initial);
7052 putFile8Bit(file, ei->use_last_ce_value);
7054 putFile8Bit(file, ei->use_gfx_element);
7055 putFile16BitBE(file, ei->gfx_element_initial);
7057 putFile8Bit(file, ei->collect_score_initial);
7058 putFile8Bit(file, ei->collect_count_initial);
7060 putFile8Bit(file, ei->drop_delay_fixed);
7061 putFile8Bit(file, ei->push_delay_fixed);
7062 putFile8Bit(file, ei->drop_delay_random);
7063 putFile8Bit(file, ei->push_delay_random);
7064 putFile16BitBE(file, ei->move_delay_fixed);
7065 putFile16BitBE(file, ei->move_delay_random);
7067 /* bits 0 - 15 of "move_pattern" ... */
7068 putFile16BitBE(file, ei->move_pattern & 0xffff);
7069 putFile8Bit(file, ei->move_direction_initial);
7070 putFile8Bit(file, ei->move_stepsize);
7072 putFile8Bit(file, ei->slippery_type);
7074 for (y = 0; y < 3; y++)
7075 for (x = 0; x < 3; x++)
7076 putFile16BitBE(file, ei->content.e[x][y]);
7078 putFile16BitBE(file, ei->move_enter_element);
7079 putFile16BitBE(file, ei->move_leave_element);
7080 putFile8Bit(file, ei->move_leave_type);
7082 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7083 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7085 putFile8Bit(file, ei->access_direction);
7087 putFile8Bit(file, ei->explosion_delay);
7088 putFile8Bit(file, ei->ignition_delay);
7089 putFile8Bit(file, ei->explosion_type);
7091 /* some free bytes for future custom property values and padding */
7092 WriteUnusedBytesToFile(file, 1);
7094 /* ---------- change page property values (48 bytes) --------------------- */
7096 for (i = 0; i < ei->num_change_pages; i++)
7098 struct ElementChangeInfo *change = &ei->change_page[i];
7099 unsigned int event_bits;
7101 /* bits 0 - 31 of "has_event[]" ... */
7103 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7104 if (change->has_event[j])
7105 event_bits |= (1 << j);
7106 putFile32BitBE(file, event_bits);
7108 putFile16BitBE(file, change->target_element);
7110 putFile16BitBE(file, change->delay_fixed);
7111 putFile16BitBE(file, change->delay_random);
7112 putFile16BitBE(file, change->delay_frames);
7114 putFile16BitBE(file, change->initial_trigger_element);
7116 putFile8Bit(file, change->explode);
7117 putFile8Bit(file, change->use_target_content);
7118 putFile8Bit(file, change->only_if_complete);
7119 putFile8Bit(file, change->use_random_replace);
7121 putFile8Bit(file, change->random_percentage);
7122 putFile8Bit(file, change->replace_when);
7124 for (y = 0; y < 3; y++)
7125 for (x = 0; x < 3; x++)
7126 putFile16BitBE(file, change->target_content.e[x][y]);
7128 putFile8Bit(file, change->can_change);
7130 putFile8Bit(file, change->trigger_side);
7132 putFile8Bit(file, change->trigger_player);
7133 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7134 log_2(change->trigger_page)));
7136 putFile8Bit(file, change->has_action);
7137 putFile8Bit(file, change->action_type);
7138 putFile8Bit(file, change->action_mode);
7139 putFile16BitBE(file, change->action_arg);
7141 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7143 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7144 if (change->has_event[j])
7145 event_bits |= (1 << (j - 32));
7146 putFile8Bit(file, event_bits);
7151 #if ENABLE_HISTORIC_CHUNKS
7152 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7154 struct ElementInfo *ei = &element_info[element];
7155 struct ElementGroupInfo *group = ei->group;
7158 putFile16BitBE(file, element);
7160 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7161 putFile8Bit(file, ei->description[i]);
7163 putFile8Bit(file, group->num_elements);
7165 putFile8Bit(file, ei->use_gfx_element);
7166 putFile16BitBE(file, ei->gfx_element_initial);
7168 putFile8Bit(file, group->choice_mode);
7170 /* some free bytes for future values and padding */
7171 WriteUnusedBytesToFile(file, 3);
7173 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7174 putFile16BitBE(file, group->element[i]);
7178 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7179 boolean write_element)
7181 int save_type = entry->save_type;
7182 int data_type = entry->data_type;
7183 int conf_type = entry->conf_type;
7184 int byte_mask = conf_type & CONF_MASK_BYTES;
7185 int element = entry->element;
7186 int default_value = entry->default_value;
7188 boolean modified = FALSE;
7190 if (byte_mask != CONF_MASK_MULTI_BYTES)
7192 void *value_ptr = entry->value;
7193 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7196 /* check if any settings have been modified before saving them */
7197 if (value != default_value)
7200 /* do not save if explicitly told or if unmodified default settings */
7201 if ((save_type == SAVE_CONF_NEVER) ||
7202 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7206 num_bytes += putFile16BitBE(file, element);
7208 num_bytes += putFile8Bit(file, conf_type);
7209 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7210 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7211 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7214 else if (data_type == TYPE_STRING)
7216 char *default_string = entry->default_string;
7217 char *string = (char *)(entry->value);
7218 int string_length = strlen(string);
7221 /* check if any settings have been modified before saving them */
7222 if (!strEqual(string, default_string))
7225 /* do not save if explicitly told or if unmodified default settings */
7226 if ((save_type == SAVE_CONF_NEVER) ||
7227 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7231 num_bytes += putFile16BitBE(file, element);
7233 num_bytes += putFile8Bit(file, conf_type);
7234 num_bytes += putFile16BitBE(file, string_length);
7236 for (i = 0; i < string_length; i++)
7237 num_bytes += putFile8Bit(file, string[i]);
7239 else if (data_type == TYPE_ELEMENT_LIST)
7241 int *element_array = (int *)(entry->value);
7242 int num_elements = *(int *)(entry->num_entities);
7245 /* check if any settings have been modified before saving them */
7246 for (i = 0; i < num_elements; i++)
7247 if (element_array[i] != default_value)
7250 /* do not save if explicitly told or if unmodified default settings */
7251 if ((save_type == SAVE_CONF_NEVER) ||
7252 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7256 num_bytes += putFile16BitBE(file, element);
7258 num_bytes += putFile8Bit(file, conf_type);
7259 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7261 for (i = 0; i < num_elements; i++)
7262 num_bytes += putFile16BitBE(file, element_array[i]);
7264 else if (data_type == TYPE_CONTENT_LIST)
7266 struct Content *content = (struct Content *)(entry->value);
7267 int num_contents = *(int *)(entry->num_entities);
7270 /* check if any settings have been modified before saving them */
7271 for (i = 0; i < num_contents; i++)
7272 for (y = 0; y < 3; y++)
7273 for (x = 0; x < 3; x++)
7274 if (content[i].e[x][y] != default_value)
7277 /* do not save if explicitly told or if unmodified default settings */
7278 if ((save_type == SAVE_CONF_NEVER) ||
7279 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7283 num_bytes += putFile16BitBE(file, element);
7285 num_bytes += putFile8Bit(file, conf_type);
7286 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7288 for (i = 0; i < num_contents; i++)
7289 for (y = 0; y < 3; y++)
7290 for (x = 0; x < 3; x++)
7291 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7297 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7302 li = *level; /* copy level data into temporary buffer */
7304 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7305 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7310 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7315 li = *level; /* copy level data into temporary buffer */
7317 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7318 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7323 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7325 int envelope_nr = element - EL_ENVELOPE_1;
7329 chunk_size += putFile16BitBE(file, element);
7331 /* copy envelope data into temporary buffer */
7332 xx_envelope = level->envelope[envelope_nr];
7334 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7335 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7340 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7342 struct ElementInfo *ei = &element_info[element];
7346 chunk_size += putFile16BitBE(file, element);
7348 xx_ei = *ei; /* copy element data into temporary buffer */
7350 /* set default description string for this specific element */
7351 strcpy(xx_default_description, getDefaultElementDescription(ei));
7353 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7354 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7356 for (i = 0; i < ei->num_change_pages; i++)
7358 struct ElementChangeInfo *change = &ei->change_page[i];
7360 xx_current_change_page = i;
7362 xx_change = *change; /* copy change data into temporary buffer */
7365 setEventBitsFromEventFlags(change);
7367 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7368 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7375 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7377 struct ElementInfo *ei = &element_info[element];
7378 struct ElementGroupInfo *group = ei->group;
7382 chunk_size += putFile16BitBE(file, element);
7384 xx_ei = *ei; /* copy element data into temporary buffer */
7385 xx_group = *group; /* copy group data into temporary buffer */
7387 /* set default description string for this specific element */
7388 strcpy(xx_default_description, getDefaultElementDescription(ei));
7390 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7391 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7396 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7397 boolean save_as_template)
7403 if (!(file = fopen(filename, MODE_WRITE)))
7405 Error(ERR_WARN, "cannot save level file '%s'", filename);
7409 level->file_version = FILE_VERSION_ACTUAL;
7410 level->game_version = GAME_VERSION_ACTUAL;
7412 level->creation_date = getCurrentDate();
7414 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7415 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7417 chunk_size = SaveLevel_VERS(NULL, level);
7418 putFileChunkBE(file, "VERS", chunk_size);
7419 SaveLevel_VERS(file, level);
7421 chunk_size = SaveLevel_DATE(NULL, level);
7422 putFileChunkBE(file, "DATE", chunk_size);
7423 SaveLevel_DATE(file, level);
7425 chunk_size = SaveLevel_NAME(NULL, level);
7426 putFileChunkBE(file, "NAME", chunk_size);
7427 SaveLevel_NAME(file, level);
7429 chunk_size = SaveLevel_AUTH(NULL, level);
7430 putFileChunkBE(file, "AUTH", chunk_size);
7431 SaveLevel_AUTH(file, level);
7433 chunk_size = SaveLevel_INFO(NULL, level);
7434 putFileChunkBE(file, "INFO", chunk_size);
7435 SaveLevel_INFO(file, level);
7437 chunk_size = SaveLevel_BODY(NULL, level);
7438 putFileChunkBE(file, "BODY", chunk_size);
7439 SaveLevel_BODY(file, level);
7441 chunk_size = SaveLevel_ELEM(NULL, level);
7442 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7444 putFileChunkBE(file, "ELEM", chunk_size);
7445 SaveLevel_ELEM(file, level);
7448 for (i = 0; i < NUM_ENVELOPES; i++)
7450 int element = EL_ENVELOPE_1 + i;
7452 chunk_size = SaveLevel_NOTE(NULL, level, element);
7453 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7455 putFileChunkBE(file, "NOTE", chunk_size);
7456 SaveLevel_NOTE(file, level, element);
7460 /* if not using template level, check for non-default custom/group elements */
7461 if (!level->use_custom_template || save_as_template)
7463 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7465 int element = EL_CUSTOM_START + i;
7467 chunk_size = SaveLevel_CUSX(NULL, level, element);
7468 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7470 putFileChunkBE(file, "CUSX", chunk_size);
7471 SaveLevel_CUSX(file, level, element);
7475 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7477 int element = EL_GROUP_START + i;
7479 chunk_size = SaveLevel_GRPX(NULL, level, element);
7480 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7482 putFileChunkBE(file, "GRPX", chunk_size);
7483 SaveLevel_GRPX(file, level, element);
7490 SetFilePermissions(filename, PERMS_PRIVATE);
7493 void SaveLevel(int nr)
7495 char *filename = getDefaultLevelFilename(nr);
7497 SaveLevelFromFilename(&level, filename, FALSE);
7500 void SaveLevelTemplate()
7502 char *filename = getLocalLevelTemplateFilename();
7504 SaveLevelFromFilename(&level, filename, TRUE);
7507 boolean SaveLevelChecked(int nr)
7509 char *filename = getDefaultLevelFilename(nr);
7510 boolean new_level = !fileExists(filename);
7511 boolean level_saved = FALSE;
7513 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7518 Request("Level saved!", REQ_CONFIRM);
7526 void DumpLevel(struct LevelInfo *level)
7528 if (level->no_level_file || level->no_valid_file)
7530 Error(ERR_WARN, "cannot dump -- no valid level file found");
7536 Print("Level xxx (file version %08d, game version %08d)\n",
7537 level->file_version, level->game_version);
7540 Print("Level author: '%s'\n", level->author);
7541 Print("Level title: '%s'\n", level->name);
7543 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7545 Print("Level time: %d seconds\n", level->time);
7546 Print("Gems needed: %d\n", level->gems_needed);
7548 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7549 Print("Time for wheel: %d seconds\n", level->time_wheel);
7550 Print("Time for light: %d seconds\n", level->time_light);
7551 Print("Time for timegate: %d seconds\n", level->time_timegate);
7553 Print("Amoeba speed: %d\n", level->amoeba_speed);
7556 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7557 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7558 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7559 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7560 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7566 /* ========================================================================= */
7567 /* tape file functions */
7568 /* ========================================================================= */
7570 static void setTapeInfoToDefaults()
7574 /* always start with reliable default values (empty tape) */
7577 /* default values (also for pre-1.2 tapes) with only the first player */
7578 tape.player_participates[0] = TRUE;
7579 for (i = 1; i < MAX_PLAYERS; i++)
7580 tape.player_participates[i] = FALSE;
7582 /* at least one (default: the first) player participates in every tape */
7583 tape.num_participating_players = 1;
7585 tape.level_nr = level_nr;
7587 tape.changed = FALSE;
7589 tape.recording = FALSE;
7590 tape.playing = FALSE;
7591 tape.pausing = FALSE;
7593 tape.no_valid_file = FALSE;
7596 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7598 tape->file_version = getFileVersion(file);
7599 tape->game_version = getFileVersion(file);
7604 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7608 tape->random_seed = getFile32BitBE(file);
7609 tape->date = getFile32BitBE(file);
7610 tape->length = getFile32BitBE(file);
7612 /* read header fields that are new since version 1.2 */
7613 if (tape->file_version >= FILE_VERSION_1_2)
7615 byte store_participating_players = getFile8Bit(file);
7618 /* since version 1.2, tapes store which players participate in the tape */
7619 tape->num_participating_players = 0;
7620 for (i = 0; i < MAX_PLAYERS; i++)
7622 tape->player_participates[i] = FALSE;
7624 if (store_participating_players & (1 << i))
7626 tape->player_participates[i] = TRUE;
7627 tape->num_participating_players++;
7631 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7633 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7635 engine_version = getFileVersion(file);
7636 if (engine_version > 0)
7637 tape->engine_version = engine_version;
7639 tape->engine_version = tape->game_version;
7645 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7647 int level_identifier_size;
7650 level_identifier_size = getFile16BitBE(file);
7652 tape->level_identifier =
7653 checked_realloc(tape->level_identifier, level_identifier_size);
7655 for (i = 0; i < level_identifier_size; i++)
7656 tape->level_identifier[i] = getFile8Bit(file);
7658 tape->level_nr = getFile16BitBE(file);
7660 chunk_size = 2 + level_identifier_size + 2;
7665 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7669 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7670 int chunk_size_expected = tape_pos_size * tape->length;
7672 if (chunk_size_expected != chunk_size)
7674 ReadUnusedBytesFromFile(file, chunk_size);
7675 return chunk_size_expected;
7678 for (i = 0; i < tape->length; i++)
7680 if (i >= MAX_TAPE_LEN)
7682 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7685 // tape too large; read and ignore remaining tape data from this chunk
7686 for (;i < tape->length; i++)
7687 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7692 if (tape->use_mouse)
7694 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7695 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7696 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7698 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7702 for (j = 0; j < MAX_PLAYERS; j++)
7704 tape->pos[i].action[j] = MV_NONE;
7706 if (tape->player_participates[j])
7707 tape->pos[i].action[j] = getFile8Bit(file);
7711 tape->pos[i].delay = getFile8Bit(file);
7713 if (tape->file_version == FILE_VERSION_1_0)
7715 /* eliminate possible diagonal moves in old tapes */
7716 /* this is only for backward compatibility */
7718 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7719 byte action = tape->pos[i].action[0];
7720 int k, num_moves = 0;
7722 for (k = 0; k<4; k++)
7724 if (action & joy_dir[k])
7726 tape->pos[i + num_moves].action[0] = joy_dir[k];
7728 tape->pos[i + num_moves].delay = 0;
7737 tape->length += num_moves;
7740 else if (tape->file_version < FILE_VERSION_2_0)
7742 /* convert pre-2.0 tapes to new tape format */
7744 if (tape->pos[i].delay > 1)
7747 tape->pos[i + 1] = tape->pos[i];
7748 tape->pos[i + 1].delay = 1;
7751 for (j = 0; j < MAX_PLAYERS; j++)
7752 tape->pos[i].action[j] = MV_NONE;
7753 tape->pos[i].delay--;
7760 if (checkEndOfFile(file))
7764 if (i != tape->length)
7765 chunk_size = tape_pos_size * i;
7770 void LoadTape_SokobanSolution(char *filename)
7773 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7775 if (!(file = openFile(filename, MODE_READ)))
7777 tape.no_valid_file = TRUE;
7782 while (!checkEndOfFile(file))
7784 unsigned char c = getByteFromFile(file);
7786 if (checkEndOfFile(file))
7793 tape.pos[tape.length].action[0] = MV_UP;
7794 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7800 tape.pos[tape.length].action[0] = MV_DOWN;
7801 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7807 tape.pos[tape.length].action[0] = MV_LEFT;
7808 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7814 tape.pos[tape.length].action[0] = MV_RIGHT;
7815 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7823 /* ignore white-space characters */
7827 tape.no_valid_file = TRUE;
7829 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7837 if (tape.no_valid_file)
7840 tape.length_frames = GetTapeLengthFrames();
7841 tape.length_seconds = GetTapeLengthSeconds();
7844 void LoadTapeFromFilename(char *filename)
7846 char cookie[MAX_LINE_LEN];
7847 char chunk_name[CHUNK_ID_LEN + 1];
7851 /* always start with reliable default values */
7852 setTapeInfoToDefaults();
7854 if (strSuffix(filename, ".sln"))
7856 LoadTape_SokobanSolution(filename);
7861 if (!(file = openFile(filename, MODE_READ)))
7863 tape.no_valid_file = TRUE;
7868 getFileChunkBE(file, chunk_name, NULL);
7869 if (strEqual(chunk_name, "RND1"))
7871 getFile32BitBE(file); /* not used */
7873 getFileChunkBE(file, chunk_name, NULL);
7874 if (!strEqual(chunk_name, "TAPE"))
7876 tape.no_valid_file = TRUE;
7878 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7885 else /* check for pre-2.0 file format with cookie string */
7887 strcpy(cookie, chunk_name);
7888 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7890 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7891 cookie[strlen(cookie) - 1] = '\0';
7893 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7895 tape.no_valid_file = TRUE;
7897 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7904 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7906 tape.no_valid_file = TRUE;
7908 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7915 /* pre-2.0 tape files have no game version, so use file version here */
7916 tape.game_version = tape.file_version;
7919 if (tape.file_version < FILE_VERSION_1_2)
7921 /* tape files from versions before 1.2.0 without chunk structure */
7922 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7923 LoadTape_BODY(file, 2 * tape.length, &tape);
7931 int (*loader)(File *, int, struct TapeInfo *);
7935 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7936 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7937 { "INFO", -1, LoadTape_INFO },
7938 { "BODY", -1, LoadTape_BODY },
7942 while (getFileChunkBE(file, chunk_name, &chunk_size))
7946 while (chunk_info[i].name != NULL &&
7947 !strEqual(chunk_name, chunk_info[i].name))
7950 if (chunk_info[i].name == NULL)
7952 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7953 chunk_name, filename);
7954 ReadUnusedBytesFromFile(file, chunk_size);
7956 else if (chunk_info[i].size != -1 &&
7957 chunk_info[i].size != chunk_size)
7959 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7960 chunk_size, chunk_name, filename);
7961 ReadUnusedBytesFromFile(file, chunk_size);
7965 /* call function to load this tape chunk */
7966 int chunk_size_expected =
7967 (chunk_info[i].loader)(file, chunk_size, &tape);
7969 /* the size of some chunks cannot be checked before reading other
7970 chunks first (like "HEAD" and "BODY") that contain some header
7971 information, so check them here */
7972 if (chunk_size_expected != chunk_size)
7974 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7975 chunk_size, chunk_name, filename);
7983 tape.length_frames = GetTapeLengthFrames();
7984 tape.length_seconds = GetTapeLengthSeconds();
7987 printf("::: tape file version: %d\n", tape.file_version);
7988 printf("::: tape game version: %d\n", tape.game_version);
7989 printf("::: tape engine version: %d\n", tape.engine_version);
7993 void LoadTape(int nr)
7995 char *filename = getTapeFilename(nr);
7997 LoadTapeFromFilename(filename);
8000 void LoadSolutionTape(int nr)
8002 char *filename = getSolutionTapeFilename(nr);
8004 LoadTapeFromFilename(filename);
8006 if (TAPE_IS_EMPTY(tape) &&
8007 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8008 level.native_sp_level->demo.is_available)
8009 CopyNativeTape_SP_to_RND(&level);
8012 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8014 putFileVersion(file, tape->file_version);
8015 putFileVersion(file, tape->game_version);
8018 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8021 byte store_participating_players = 0;
8023 /* set bits for participating players for compact storage */
8024 for (i = 0; i < MAX_PLAYERS; i++)
8025 if (tape->player_participates[i])
8026 store_participating_players |= (1 << i);
8028 putFile32BitBE(file, tape->random_seed);
8029 putFile32BitBE(file, tape->date);
8030 putFile32BitBE(file, tape->length);
8032 putFile8Bit(file, store_participating_players);
8034 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8036 /* unused bytes not at the end here for 4-byte alignment of engine_version */
8037 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8039 putFileVersion(file, tape->engine_version);
8042 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8044 int level_identifier_size = strlen(tape->level_identifier) + 1;
8047 putFile16BitBE(file, level_identifier_size);
8049 for (i = 0; i < level_identifier_size; i++)
8050 putFile8Bit(file, tape->level_identifier[i]);
8052 putFile16BitBE(file, tape->level_nr);
8055 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8059 for (i = 0; i < tape->length; i++)
8061 if (tape->use_mouse)
8063 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8064 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8065 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8069 for (j = 0; j < MAX_PLAYERS; j++)
8070 if (tape->player_participates[j])
8071 putFile8Bit(file, tape->pos[i].action[j]);
8074 putFile8Bit(file, tape->pos[i].delay);
8078 void SaveTape(int nr)
8080 char *filename = getTapeFilename(nr);
8082 int num_participating_players = 0;
8084 int info_chunk_size;
8085 int body_chunk_size;
8088 InitTapeDirectory(leveldir_current->subdir);
8090 if (!(file = fopen(filename, MODE_WRITE)))
8092 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8096 tape.file_version = FILE_VERSION_ACTUAL;
8097 tape.game_version = GAME_VERSION_ACTUAL;
8099 /* count number of participating players */
8100 for (i = 0; i < MAX_PLAYERS; i++)
8101 if (tape.player_participates[i])
8102 num_participating_players++;
8104 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8106 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8107 body_chunk_size = tape_pos_size * tape.length;
8109 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8110 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8112 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8113 SaveTape_VERS(file, &tape);
8115 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8116 SaveTape_HEAD(file, &tape);
8118 putFileChunkBE(file, "INFO", info_chunk_size);
8119 SaveTape_INFO(file, &tape);
8121 putFileChunkBE(file, "BODY", body_chunk_size);
8122 SaveTape_BODY(file, &tape);
8126 SetFilePermissions(filename, PERMS_PRIVATE);
8128 tape.changed = FALSE;
8131 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8133 char *filename = getTapeFilename(nr);
8134 boolean new_tape = !fileExists(filename);
8135 boolean tape_saved = FALSE;
8137 if (new_tape || Request(msg_replace, REQ_ASK))
8142 Request(msg_saved, REQ_CONFIRM);
8150 boolean SaveTapeChecked(int nr)
8152 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8155 boolean SaveTapeChecked_LevelSolved(int nr)
8157 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8158 "Level solved! Tape saved!");
8161 void DumpTape(struct TapeInfo *tape)
8163 int tape_frame_counter;
8166 if (tape->no_valid_file)
8168 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8174 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8175 tape->level_nr, tape->file_version, tape->game_version);
8176 Print(" (effective engine version %08d)\n",
8177 tape->engine_version);
8178 Print("Level series identifier: '%s'\n", tape->level_identifier);
8181 tape_frame_counter = 0;
8183 for (i = 0; i < tape->length; i++)
8185 if (i >= MAX_TAPE_LEN)
8190 for (j = 0; j < MAX_PLAYERS; j++)
8192 if (tape->player_participates[j])
8194 int action = tape->pos[i].action[j];
8196 Print("%d:%02x ", j, action);
8197 Print("[%c%c%c%c|%c%c] - ",
8198 (action & JOY_LEFT ? '<' : ' '),
8199 (action & JOY_RIGHT ? '>' : ' '),
8200 (action & JOY_UP ? '^' : ' '),
8201 (action & JOY_DOWN ? 'v' : ' '),
8202 (action & JOY_BUTTON_1 ? '1' : ' '),
8203 (action & JOY_BUTTON_2 ? '2' : ' '));
8207 Print("(%03d) ", tape->pos[i].delay);
8208 Print("[%05d]\n", tape_frame_counter);
8210 tape_frame_counter += tape->pos[i].delay;
8217 /* ========================================================================= */
8218 /* score file functions */
8219 /* ========================================================================= */
8221 void LoadScore(int nr)
8224 char *filename = getScoreFilename(nr);
8225 char cookie[MAX_LINE_LEN];
8226 char line[MAX_LINE_LEN];
8230 /* always start with reliable default values */
8231 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8233 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8234 highscore[i].Score = 0;
8237 if (!(file = fopen(filename, MODE_READ)))
8240 /* check file identifier */
8241 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8243 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8244 cookie[strlen(cookie) - 1] = '\0';
8246 if (!checkCookieString(cookie, SCORE_COOKIE))
8248 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8253 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8255 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8256 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8257 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8260 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8261 line[strlen(line) - 1] = '\0';
8263 for (line_ptr = line; *line_ptr; line_ptr++)
8265 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8267 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8268 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8277 void SaveScore(int nr)
8280 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8281 char *filename = getScoreFilename(nr);
8284 InitScoreDirectory(leveldir_current->subdir);
8286 if (!(file = fopen(filename, MODE_WRITE)))
8288 Error(ERR_WARN, "cannot save score for level %d", nr);
8292 fprintf(file, "%s\n\n", SCORE_COOKIE);
8294 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8295 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8299 SetFilePermissions(filename, permissions);
8303 /* ========================================================================= */
8304 /* setup file functions */
8305 /* ========================================================================= */
8307 #define TOKEN_STR_PLAYER_PREFIX "player_"
8312 SETUP_TOKEN_PLAYER_NAME = 0,
8314 SETUP_TOKEN_SOUND_LOOPS,
8315 SETUP_TOKEN_SOUND_MUSIC,
8316 SETUP_TOKEN_SOUND_SIMPLE,
8318 SETUP_TOKEN_SCROLL_DELAY,
8319 SETUP_TOKEN_SCROLL_DELAY_VALUE,
8320 SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8321 SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8322 SETUP_TOKEN_FADE_SCREENS,
8323 SETUP_TOKEN_AUTORECORD,
8324 SETUP_TOKEN_SHOW_TITLESCREEN,
8325 SETUP_TOKEN_QUICK_DOORS,
8326 SETUP_TOKEN_TEAM_MODE,
8327 SETUP_TOKEN_HANDICAP,
8328 SETUP_TOKEN_SKIP_LEVELS,
8329 SETUP_TOKEN_INCREMENT_LEVELS,
8330 SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8331 SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8332 SETUP_TOKEN_TIME_LIMIT,
8333 SETUP_TOKEN_FULLSCREEN,
8334 SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8335 SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8336 SETUP_TOKEN_SCREEN_RENDERING_MODE,
8337 SETUP_TOKEN_ASK_ON_ESCAPE,
8338 SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8339 SETUP_TOKEN_QUICK_SWITCH,
8340 SETUP_TOKEN_INPUT_ON_FOCUS,
8341 SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8342 SETUP_TOKEN_GAME_FRAME_DELAY,
8343 SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8344 SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8345 SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8346 SETUP_TOKEN_GRAPHICS_SET,
8347 SETUP_TOKEN_SOUNDS_SET,
8348 SETUP_TOKEN_MUSIC_SET,
8349 SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8350 SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8351 SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8352 SETUP_TOKEN_VOLUME_SIMPLE,
8353 SETUP_TOKEN_VOLUME_LOOPS,
8354 SETUP_TOKEN_VOLUME_MUSIC,
8355 SETUP_TOKEN_NETWORK_MODE,
8356 SETUP_TOKEN_NETWORK_PLAYER_NR,
8357 SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8358 SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8359 SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8360 SETUP_TOKEN_TOUCH_TRANSPARENCY,
8361 SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8362 SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8363 SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8364 SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8365 SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8366 SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8368 NUM_GLOBAL_SETUP_TOKENS
8374 SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8376 NUM_AUTO_SETUP_TOKENS
8382 SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8383 SETUP_TOKEN_EDITOR_EL_CUSTOM,
8384 SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8385 SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8386 SETUP_TOKEN_EDITOR_EL_HEADLINES,
8387 SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8389 NUM_EDITOR_SETUP_TOKENS
8392 /* editor cascade setup */
8395 SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8396 SETUP_TOKEN_EDITOR_CASCADE_EM,
8397 SETUP_TOKEN_EDITOR_CASCADE_EMC,
8398 SETUP_TOKEN_EDITOR_CASCADE_RND,
8399 SETUP_TOKEN_EDITOR_CASCADE_SB,
8400 SETUP_TOKEN_EDITOR_CASCADE_SP,
8401 SETUP_TOKEN_EDITOR_CASCADE_DC,
8402 SETUP_TOKEN_EDITOR_CASCADE_DX,
8403 SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8404 SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8405 SETUP_TOKEN_EDITOR_CASCADE_CE,
8406 SETUP_TOKEN_EDITOR_CASCADE_GE,
8407 SETUP_TOKEN_EDITOR_CASCADE_REF,
8408 SETUP_TOKEN_EDITOR_CASCADE_USER,
8409 SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8411 NUM_EDITOR_CASCADE_SETUP_TOKENS
8414 /* shortcut setup */
8417 SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8418 SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8419 SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8420 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8421 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8422 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8423 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8424 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8425 SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8426 SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8427 SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8428 SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8429 SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8430 SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8431 SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8432 SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8433 SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8434 SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8435 SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8436 SETUP_TOKEN_SHORTCUT_SNAP_UP,
8437 SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8439 NUM_SHORTCUT_SETUP_TOKENS
8445 SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8446 SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8447 SETUP_TOKEN_PLAYER_JOY_XLEFT,
8448 SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8449 SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8450 SETUP_TOKEN_PLAYER_JOY_YUPPER,
8451 SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8452 SETUP_TOKEN_PLAYER_JOY_YLOWER,
8453 SETUP_TOKEN_PLAYER_JOY_SNAP,
8454 SETUP_TOKEN_PLAYER_JOY_DROP,
8455 SETUP_TOKEN_PLAYER_KEY_LEFT,
8456 SETUP_TOKEN_PLAYER_KEY_RIGHT,
8457 SETUP_TOKEN_PLAYER_KEY_UP,
8458 SETUP_TOKEN_PLAYER_KEY_DOWN,
8459 SETUP_TOKEN_PLAYER_KEY_SNAP,
8460 SETUP_TOKEN_PLAYER_KEY_DROP,
8462 NUM_PLAYER_SETUP_TOKENS
8468 SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8469 SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8470 SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8472 NUM_SYSTEM_SETUP_TOKENS
8475 /* internal setup */
8478 SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8479 SETUP_TOKEN_INT_PROGRAM_VERSION,
8480 SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8481 SETUP_TOKEN_INT_PROGRAM_EMAIL,
8482 SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8483 SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8484 SETUP_TOKEN_INT_PROGRAM_COMPANY,
8485 SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8486 SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8487 SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8488 SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8489 SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8490 SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8491 SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8492 SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8493 SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8494 SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8495 SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8496 SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8498 NUM_INTERNAL_SETUP_TOKENS
8504 SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8505 SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8506 SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8507 SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8508 SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8509 SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8510 SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8511 SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8512 SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8513 SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8514 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8515 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8516 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8517 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8518 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8519 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8520 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8521 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8522 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8523 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8524 SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8525 SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8526 SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8528 NUM_DEBUG_SETUP_TOKENS
8534 SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8536 NUM_OPTIONS_SETUP_TOKENS
8540 static struct SetupInfo si;
8541 static struct SetupAutoSetupInfo sasi;
8542 static struct SetupEditorInfo sei;
8543 static struct SetupEditorCascadeInfo seci;
8544 static struct SetupShortcutInfo ssi;
8545 static struct SetupInputInfo sii;
8546 static struct SetupSystemInfo syi;
8547 static struct SetupInternalInfo sxi;
8548 static struct SetupDebugInfo sdi;
8549 static struct OptionInfo soi;
8551 static struct TokenInfo global_setup_tokens[] =
8553 { TYPE_STRING, &si.player_name, "player_name" },
8554 { TYPE_SWITCH, &si.sound, "sound" },
8555 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8556 { TYPE_SWITCH, &si.sound_music, "background_music" },
8557 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8558 { TYPE_SWITCH, &si.toons, "toons" },
8559 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8560 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8561 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8562 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8563 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8564 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8565 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8566 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8567 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8568 { TYPE_SWITCH, &si.handicap, "handicap" },
8569 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8570 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8571 { TYPE_SWITCH, &si.auto_play_next_level, "auto_play_next_level" },
8572 { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
8573 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8574 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8575 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8576 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8577 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8578 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8579 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8580 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8581 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8582 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8583 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8584 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8585 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8586 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8587 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8588 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8589 { TYPE_STRING, &si.music_set, "music_set" },
8590 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8591 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8592 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8593 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8594 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8595 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8596 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8597 { TYPE_PLAYER, &si.network_player_nr, "network_player" },
8598 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8599 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8600 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8601 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8602 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8603 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8604 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8605 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8606 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8607 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8610 static struct TokenInfo auto_setup_tokens[] =
8612 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8615 static struct TokenInfo editor_setup_tokens[] =
8617 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8618 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8619 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8620 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8621 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8622 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8625 static struct TokenInfo editor_cascade_setup_tokens[] =
8627 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8628 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8629 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8630 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8631 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8632 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8633 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8634 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8635 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8636 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8637 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8638 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8639 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8640 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8641 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8642 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8643 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8646 static struct TokenInfo shortcut_setup_tokens[] =
8648 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8649 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8650 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8651 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8652 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8653 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8654 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8655 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8656 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8657 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8658 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8659 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8660 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8661 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8662 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8663 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8664 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8665 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8666 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8667 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8668 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8671 static struct TokenInfo player_setup_tokens[] =
8673 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8674 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8675 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8676 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8677 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8678 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8679 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8680 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8681 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8682 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8683 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8684 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8685 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8686 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8687 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8688 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8691 static struct TokenInfo system_setup_tokens[] =
8693 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8694 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8695 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8698 static struct TokenInfo internal_setup_tokens[] =
8700 { TYPE_STRING, &sxi.program_title, "program_title" },
8701 { TYPE_STRING, &sxi.program_version, "program_version" },
8702 { TYPE_STRING, &sxi.program_author, "program_author" },
8703 { TYPE_STRING, &sxi.program_email, "program_email" },
8704 { TYPE_STRING, &sxi.program_website, "program_website" },
8705 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8706 { TYPE_STRING, &sxi.program_company, "program_company" },
8707 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8708 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8709 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8710 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8711 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8712 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8713 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8714 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8715 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8716 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8717 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8718 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8721 static struct TokenInfo debug_setup_tokens[] =
8723 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8724 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8725 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8726 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8727 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8728 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8729 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8730 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8731 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8732 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8733 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8734 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8735 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8736 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8737 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8738 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8739 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8740 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8741 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8742 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8743 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8744 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8745 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8748 static struct TokenInfo options_setup_tokens[] =
8750 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8753 static char *get_corrected_login_name(char *login_name)
8755 /* needed because player name must be a fixed length string */
8756 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8758 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8759 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8761 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8762 if (strchr(login_name_new, ' '))
8763 *strchr(login_name_new, ' ') = '\0';
8765 return login_name_new;
8768 static void setSetupInfoToDefaults(struct SetupInfo *si)
8772 si->player_name = get_corrected_login_name(getLoginName());
8775 si->sound_loops = TRUE;
8776 si->sound_music = TRUE;
8777 si->sound_simple = TRUE;
8779 si->scroll_delay = TRUE;
8780 si->scroll_delay_value = STD_SCROLL_DELAY;
8781 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8782 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8783 si->fade_screens = TRUE;
8784 si->autorecord = TRUE;
8785 si->show_titlescreen = TRUE;
8786 si->quick_doors = FALSE;
8787 si->team_mode = FALSE;
8788 si->handicap = TRUE;
8789 si->skip_levels = TRUE;
8790 si->increment_levels = TRUE;
8791 si->auto_play_next_level = TRUE;
8792 si->skip_scores_after_game = FALSE;
8793 si->time_limit = TRUE;
8794 si->fullscreen = FALSE;
8795 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8796 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8797 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8798 si->ask_on_escape = TRUE;
8799 si->ask_on_escape_editor = TRUE;
8800 si->quick_switch = FALSE;
8801 si->input_on_focus = FALSE;
8802 si->prefer_aga_graphics = TRUE;
8803 si->game_frame_delay = GAME_FRAME_DELAY;
8804 si->sp_show_border_elements = FALSE;
8805 si->small_game_graphics = FALSE;
8806 si->show_snapshot_buttons = FALSE;
8808 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8809 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8810 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8812 si->override_level_graphics = FALSE;
8813 si->override_level_sounds = FALSE;
8814 si->override_level_music = FALSE;
8816 si->volume_simple = 100; /* percent */
8817 si->volume_loops = 100; /* percent */
8818 si->volume_music = 100; /* percent */
8820 si->network_mode = FALSE;
8821 si->network_player_nr = 0; /* first player */
8823 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8824 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8825 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8826 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8827 si->touch.draw_outlined = TRUE;
8828 si->touch.draw_pressed = TRUE;
8830 for (i = 0; i < 2; i++)
8832 char *default_grid_button[6][2] =
8838 { "111222", " vv " },
8839 { "111222", " vv " }
8841 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8842 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8843 int min_xsize = MIN(6, grid_xsize);
8844 int min_ysize = MIN(6, grid_ysize);
8845 int startx = grid_xsize - min_xsize;
8846 int starty = grid_ysize - min_ysize;
8849 // virtual buttons grid can only be set to defaults if video is initialized
8850 // (this will be repeated if virtual buttons are not loaded from setup file)
8851 if (video.initialized)
8853 si->touch.grid_xsize[i] = grid_xsize;
8854 si->touch.grid_ysize[i] = grid_ysize;
8858 si->touch.grid_xsize[i] = -1;
8859 si->touch.grid_ysize[i] = -1;
8862 for (x = 0; x < MAX_GRID_XSIZE; x++)
8863 for (y = 0; y < MAX_GRID_YSIZE; y++)
8864 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8866 for (x = 0; x < min_xsize; x++)
8867 for (y = 0; y < min_ysize; y++)
8868 si->touch.grid_button[i][x][starty + y] =
8869 default_grid_button[y][0][x];
8871 for (x = 0; x < min_xsize; x++)
8872 for (y = 0; y < min_ysize; y++)
8873 si->touch.grid_button[i][startx + x][starty + y] =
8874 default_grid_button[y][1][x];
8877 si->touch.grid_initialized = video.initialized;
8879 si->editor.el_boulderdash = TRUE;
8880 si->editor.el_emerald_mine = TRUE;
8881 si->editor.el_emerald_mine_club = TRUE;
8882 si->editor.el_more = TRUE;
8883 si->editor.el_sokoban = TRUE;
8884 si->editor.el_supaplex = TRUE;
8885 si->editor.el_diamond_caves = TRUE;
8886 si->editor.el_dx_boulderdash = TRUE;
8888 si->editor.el_mirror_magic = TRUE;
8889 si->editor.el_deflektor = TRUE;
8891 si->editor.el_chars = TRUE;
8892 si->editor.el_steel_chars = TRUE;
8894 si->editor.el_classic = TRUE;
8895 si->editor.el_custom = TRUE;
8897 si->editor.el_user_defined = FALSE;
8898 si->editor.el_dynamic = TRUE;
8900 si->editor.el_headlines = TRUE;
8902 si->editor.show_element_token = FALSE;
8904 si->editor.use_template_for_new_levels = TRUE;
8906 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8907 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8908 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8910 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8911 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8912 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8913 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8914 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8916 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8917 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8918 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8919 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8920 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8921 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8923 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8924 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8925 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8927 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8928 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8929 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8930 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8932 for (i = 0; i < MAX_PLAYERS; i++)
8934 si->input[i].use_joystick = FALSE;
8935 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8936 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8937 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8938 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8939 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8940 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8941 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8942 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8943 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8944 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8945 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8946 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8947 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8948 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8949 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8952 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8953 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8954 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8956 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8957 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8958 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8959 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8960 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8961 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8962 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8964 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8966 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8967 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8968 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8970 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8971 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8972 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8974 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8975 si->internal.choose_from_top_leveldir = FALSE;
8976 si->internal.show_scaling_in_title = TRUE;
8978 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8979 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8981 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8982 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8983 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8984 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8985 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8986 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8987 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8988 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8989 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8990 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8992 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8993 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8994 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8995 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8996 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8997 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8998 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8999 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9000 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9001 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9003 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9004 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9006 si->debug.show_frames_per_second = FALSE;
9008 si->options.verbose = FALSE;
9010 #if defined(PLATFORM_ANDROID)
9011 si->fullscreen = TRUE;
9015 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9017 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9020 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9022 si->editor_cascade.el_bd = TRUE;
9023 si->editor_cascade.el_em = TRUE;
9024 si->editor_cascade.el_emc = TRUE;
9025 si->editor_cascade.el_rnd = TRUE;
9026 si->editor_cascade.el_sb = TRUE;
9027 si->editor_cascade.el_sp = TRUE;
9028 si->editor_cascade.el_dc = TRUE;
9029 si->editor_cascade.el_dx = TRUE;
9031 si->editor_cascade.el_mm = TRUE;
9032 si->editor_cascade.el_df = TRUE;
9034 si->editor_cascade.el_chars = FALSE;
9035 si->editor_cascade.el_steel_chars = FALSE;
9036 si->editor_cascade.el_ce = FALSE;
9037 si->editor_cascade.el_ge = FALSE;
9038 si->editor_cascade.el_ref = FALSE;
9039 si->editor_cascade.el_user = FALSE;
9040 si->editor_cascade.el_dynamic = FALSE;
9043 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9045 static char *getHideSetupToken(void *setup_value)
9047 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9049 if (setup_value != NULL)
9050 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9052 return hide_setup_token;
9055 void setHideSetupEntry(void *setup_value)
9057 char *hide_setup_token = getHideSetupToken(setup_value);
9059 if (setup_value != NULL)
9060 setHashEntry(hide_setup_hash, hide_setup_token, "");
9063 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9065 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9066 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9068 setHideSetupEntry(setup_value);
9071 boolean hideSetupEntry(void *setup_value)
9073 char *hide_setup_token = getHideSetupToken(setup_value);
9075 return (setup_value != NULL &&
9076 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9079 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9080 struct TokenInfo *token_info,
9081 int token_nr, char *token_text)
9083 char *token_hide_text = getStringCat2(token_text, ".hide");
9084 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9086 /* set the value of this setup option in the setup option structure */
9087 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9089 /* check if this setup option should be hidden in the setup menu */
9090 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9091 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9094 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9095 struct TokenInfo *token_info,
9098 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9099 token_info[token_nr].text);
9102 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9106 if (!setup_file_hash)
9109 if (hide_setup_hash == NULL)
9110 hide_setup_hash = newSetupFileHash();
9114 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9115 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9118 /* virtual buttons setup */
9119 setup.touch.grid_initialized = TRUE;
9120 for (i = 0; i < 2; i++)
9122 int grid_xsize = setup.touch.grid_xsize[i];
9123 int grid_ysize = setup.touch.grid_ysize[i];
9126 // if virtual buttons are not loaded from setup file, repeat initializing
9127 // virtual buttons grid with default values later when video is initialized
9128 if (grid_xsize == -1 ||
9131 setup.touch.grid_initialized = FALSE;
9136 for (y = 0; y < grid_ysize; y++)
9138 char token_string[MAX_LINE_LEN];
9140 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9142 char *value_string = getHashEntry(setup_file_hash, token_string);
9144 if (value_string == NULL)
9147 for (x = 0; x < grid_xsize; x++)
9149 char c = value_string[x];
9151 setup.touch.grid_button[i][x][y] =
9152 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9159 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9160 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9163 /* shortcut setup */
9164 ssi = setup.shortcut;
9165 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9166 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9167 setup.shortcut = ssi;
9170 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9174 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9176 sii = setup.input[pnr];
9177 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9179 char full_token[100];
9181 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9182 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9185 setup.input[pnr] = sii;
9190 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9191 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9194 /* internal setup */
9195 sxi = setup.internal;
9196 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9197 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9198 setup.internal = sxi;
9202 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9203 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9207 soi = setup.options;
9208 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9209 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9210 setup.options = soi;
9212 setHideRelatedSetupEntries();
9215 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9219 if (!setup_file_hash)
9223 sasi = setup.auto_setup;
9224 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9225 setSetupInfo(auto_setup_tokens, i,
9226 getHashEntry(setup_file_hash,
9227 auto_setup_tokens[i].text));
9228 setup.auto_setup = sasi;
9231 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9235 if (!setup_file_hash)
9238 /* editor cascade setup */
9239 seci = setup.editor_cascade;
9240 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9241 setSetupInfo(editor_cascade_setup_tokens, i,
9242 getHashEntry(setup_file_hash,
9243 editor_cascade_setup_tokens[i].text));
9244 setup.editor_cascade = seci;
9247 void LoadSetupFromFilename(char *filename)
9249 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9251 if (setup_file_hash)
9253 decodeSetupFileHash(setup_file_hash);
9255 freeSetupFileHash(setup_file_hash);
9259 Error(ERR_DEBUG, "using default setup values");
9263 static void LoadSetup_SpecialPostProcessing()
9265 char *player_name_new;
9267 /* needed to work around problems with fixed length strings */
9268 player_name_new = get_corrected_login_name(setup.player_name);
9269 free(setup.player_name);
9270 setup.player_name = player_name_new;
9272 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9273 if (setup.scroll_delay == FALSE)
9275 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9276 setup.scroll_delay = TRUE; /* now always "on" */
9279 /* make sure that scroll delay value stays inside valid range */
9280 setup.scroll_delay_value =
9281 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9288 /* always start with reliable default values */
9289 setSetupInfoToDefaults(&setup);
9291 /* try to load setup values from default setup file */
9292 filename = getDefaultSetupFilename();
9294 if (fileExists(filename))
9295 LoadSetupFromFilename(filename);
9297 /* try to load setup values from user setup file */
9298 filename = getSetupFilename();
9300 LoadSetupFromFilename(filename);
9302 LoadSetup_SpecialPostProcessing();
9305 void LoadSetup_AutoSetup()
9307 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9308 SetupFileHash *setup_file_hash = NULL;
9310 /* always start with reliable default values */
9311 setSetupInfoToDefaults_AutoSetup(&setup);
9313 setup_file_hash = loadSetupFileHash(filename);
9315 if (setup_file_hash)
9317 decodeSetupFileHash_AutoSetup(setup_file_hash);
9319 freeSetupFileHash(setup_file_hash);
9325 void LoadSetup_EditorCascade()
9327 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9328 SetupFileHash *setup_file_hash = NULL;
9330 /* always start with reliable default values */
9331 setSetupInfoToDefaults_EditorCascade(&setup);
9333 setup_file_hash = loadSetupFileHash(filename);
9335 if (setup_file_hash)
9337 decodeSetupFileHash_EditorCascade(setup_file_hash);
9339 freeSetupFileHash(setup_file_hash);
9345 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9348 char mapping_guid[MAX_LINE_LEN];
9349 char *mapping_start, *mapping_end;
9351 // get GUID from game controller mapping line: copy complete line
9352 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9353 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9355 // get GUID from game controller mapping line: cut after GUID part
9356 mapping_start = strchr(mapping_guid, ',');
9357 if (mapping_start != NULL)
9358 *mapping_start = '\0';
9360 // cut newline from game controller mapping line
9361 mapping_end = strchr(mapping_line, '\n');
9362 if (mapping_end != NULL)
9363 *mapping_end = '\0';
9365 // add mapping entry to game controller mappings hash
9366 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9369 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9374 if (!(file = fopen(filename, MODE_READ)))
9376 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9383 char line[MAX_LINE_LEN];
9385 if (!fgets(line, MAX_LINE_LEN, file))
9388 addGameControllerMappingToHash(mappings_hash, line);
9396 char *filename = getSetupFilename();
9400 InitUserDataDirectory();
9402 if (!(file = fopen(filename, MODE_WRITE)))
9404 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9408 fprintFileHeader(file, SETUP_FILENAME);
9412 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9414 /* just to make things nicer :) */
9415 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9416 i == SETUP_TOKEN_GRAPHICS_SET ||
9417 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9418 i == SETUP_TOKEN_NETWORK_MODE ||
9419 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9420 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9421 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9422 fprintf(file, "\n");
9424 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9427 /* virtual buttons setup */
9428 for (i = 0; i < 2; i++)
9430 int grid_xsize = setup.touch.grid_xsize[i];
9431 int grid_ysize = setup.touch.grid_ysize[i];
9434 fprintf(file, "\n");
9436 for (y = 0; y < grid_ysize; y++)
9438 char token_string[MAX_LINE_LEN];
9439 char value_string[MAX_LINE_LEN];
9441 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9443 for (x = 0; x < grid_xsize; x++)
9445 char c = setup.touch.grid_button[i][x][y];
9447 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9450 value_string[grid_xsize] = '\0';
9452 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9458 fprintf(file, "\n");
9459 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9460 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9462 /* shortcut setup */
9463 ssi = setup.shortcut;
9464 fprintf(file, "\n");
9465 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9466 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9469 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9473 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9474 fprintf(file, "\n");
9476 sii = setup.input[pnr];
9477 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9478 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9483 fprintf(file, "\n");
9484 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9485 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9487 /* internal setup */
9488 /* (internal setup values not saved to user setup file) */
9492 fprintf(file, "\n");
9493 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9494 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9497 soi = setup.options;
9498 fprintf(file, "\n");
9499 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9500 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9504 SetFilePermissions(filename, PERMS_PRIVATE);
9507 void SaveSetup_AutoSetup()
9509 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9513 InitUserDataDirectory();
9515 if (!(file = fopen(filename, MODE_WRITE)))
9517 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9522 fprintFileHeader(file, AUTOSETUP_FILENAME);
9524 sasi = setup.auto_setup;
9525 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9526 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9530 SetFilePermissions(filename, PERMS_PRIVATE);
9535 void SaveSetup_EditorCascade()
9537 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9541 InitUserDataDirectory();
9543 if (!(file = fopen(filename, MODE_WRITE)))
9545 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9550 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9552 seci = setup.editor_cascade;
9553 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9554 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9558 SetFilePermissions(filename, PERMS_PRIVATE);
9563 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9568 if (!(file = fopen(filename, MODE_WRITE)))
9570 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9575 BEGIN_HASH_ITERATION(mappings_hash, itr)
9577 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9579 END_HASH_ITERATION(mappings_hash, itr)
9584 void SaveSetup_AddGameControllerMapping(char *mapping)
9586 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9587 SetupFileHash *mappings_hash = newSetupFileHash();
9589 InitUserDataDirectory();
9591 // load existing personal game controller mappings
9592 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9594 // add new mapping to personal game controller mappings
9595 addGameControllerMappingToHash(mappings_hash, mapping);
9597 // save updated personal game controller mappings
9598 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9600 freeSetupFileHash(mappings_hash);
9604 void LoadCustomElementDescriptions()
9606 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9607 SetupFileHash *setup_file_hash;
9610 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9612 if (element_info[i].custom_description != NULL)
9614 free(element_info[i].custom_description);
9615 element_info[i].custom_description = NULL;
9619 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9622 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9624 char *token = getStringCat2(element_info[i].token_name, ".name");
9625 char *value = getHashEntry(setup_file_hash, token);
9628 element_info[i].custom_description = getStringCopy(value);
9633 freeSetupFileHash(setup_file_hash);
9636 static int getElementFromToken(char *token)
9638 char *value = getHashEntry(element_token_hash, token);
9643 Error(ERR_WARN, "unknown element token '%s'", token);
9645 return EL_UNDEFINED;
9648 static int get_token_parameter_value(char *token, char *value_raw)
9652 if (token == NULL || value_raw == NULL)
9653 return ARG_UNDEFINED_VALUE;
9655 suffix = strrchr(token, '.');
9659 if (strEqual(suffix, ".element"))
9660 return getElementFromToken(value_raw);
9662 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9663 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9666 void InitMenuDesignSettings_Static()
9670 /* always start with reliable default values from static default config */
9671 for (i = 0; image_config_vars[i].token != NULL; i++)
9673 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9676 *image_config_vars[i].value =
9677 get_token_parameter_value(image_config_vars[i].token, value);
9681 static void InitMenuDesignSettings_SpecialPreProcessing()
9685 /* the following initializes hierarchical values from static configuration */
9687 /* special case: initialize "ARG_DEFAULT" values in static default config */
9688 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9689 titlescreen_initial_first_default.fade_mode =
9690 title_initial_first_default.fade_mode;
9691 titlescreen_initial_first_default.fade_delay =
9692 title_initial_first_default.fade_delay;
9693 titlescreen_initial_first_default.post_delay =
9694 title_initial_first_default.post_delay;
9695 titlescreen_initial_first_default.auto_delay =
9696 title_initial_first_default.auto_delay;
9697 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9698 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9699 titlescreen_first_default.post_delay = title_first_default.post_delay;
9700 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9701 titlemessage_initial_first_default.fade_mode =
9702 title_initial_first_default.fade_mode;
9703 titlemessage_initial_first_default.fade_delay =
9704 title_initial_first_default.fade_delay;
9705 titlemessage_initial_first_default.post_delay =
9706 title_initial_first_default.post_delay;
9707 titlemessage_initial_first_default.auto_delay =
9708 title_initial_first_default.auto_delay;
9709 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9710 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9711 titlemessage_first_default.post_delay = title_first_default.post_delay;
9712 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9714 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9715 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9716 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9717 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9718 titlescreen_default.fade_mode = title_default.fade_mode;
9719 titlescreen_default.fade_delay = title_default.fade_delay;
9720 titlescreen_default.post_delay = title_default.post_delay;
9721 titlescreen_default.auto_delay = title_default.auto_delay;
9722 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9723 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9724 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9725 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9726 titlemessage_default.fade_mode = title_default.fade_mode;
9727 titlemessage_default.fade_delay = title_default.fade_delay;
9728 titlemessage_default.post_delay = title_default.post_delay;
9729 titlemessage_default.auto_delay = title_default.auto_delay;
9731 /* special case: initialize "ARG_DEFAULT" values in static default config */
9732 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9733 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9735 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9736 titlescreen_first[i] = titlescreen_first_default;
9737 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9738 titlemessage_first[i] = titlemessage_first_default;
9740 titlescreen_initial[i] = titlescreen_initial_default;
9741 titlescreen[i] = titlescreen_default;
9742 titlemessage_initial[i] = titlemessage_initial_default;
9743 titlemessage[i] = titlemessage_default;
9746 /* special case: initialize "ARG_DEFAULT" values in static default config */
9747 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9748 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9750 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9753 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9754 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9755 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9758 /* special case: initialize "ARG_DEFAULT" values in static default config */
9759 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9760 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9762 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9763 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9764 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9766 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9769 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9773 static void InitMenuDesignSettings_SpecialPostProcessing()
9777 struct XY *dst, *src;
9781 { &game.button.save, &game.button.stop },
9782 { &game.button.pause2, &game.button.pause },
9783 { &game.button.load, &game.button.play },
9784 { &game.button.undo, &game.button.stop },
9785 { &game.button.redo, &game.button.play },
9791 /* special case: initialize later added SETUP list size from LEVELS value */
9792 if (menu.list_size[GAME_MODE_SETUP] == -1)
9793 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9795 /* set default position for snapshot buttons to stop/pause/play buttons */
9796 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9797 if ((*game_buttons_xy[i].dst).x == -1 &&
9798 (*game_buttons_xy[i].dst).y == -1)
9799 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9802 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9806 struct XYTileSize *dst, *src;
9809 editor_buttons_xy[] =
9812 &editor.button.element_left, &editor.palette.element_left,
9813 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9816 &editor.button.element_middle, &editor.palette.element_middle,
9817 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9820 &editor.button.element_right, &editor.palette.element_right,
9821 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9828 /* set default position for element buttons to element graphics */
9829 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9831 if ((*editor_buttons_xy[i].dst).x == -1 &&
9832 (*editor_buttons_xy[i].dst).y == -1)
9834 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9836 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9838 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9843 static void LoadMenuDesignSettingsFromFilename(char *filename)
9845 static struct TitleFadingInfo tfi;
9846 static struct TitleMessageInfo tmi;
9847 static struct TokenInfo title_tokens[] =
9849 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9850 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9851 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9852 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9856 static struct TokenInfo titlemessage_tokens[] =
9858 { TYPE_INTEGER, &tmi.x, ".x" },
9859 { TYPE_INTEGER, &tmi.y, ".y" },
9860 { TYPE_INTEGER, &tmi.width, ".width" },
9861 { TYPE_INTEGER, &tmi.height, ".height" },
9862 { TYPE_INTEGER, &tmi.chars, ".chars" },
9863 { TYPE_INTEGER, &tmi.lines, ".lines" },
9864 { TYPE_INTEGER, &tmi.align, ".align" },
9865 { TYPE_INTEGER, &tmi.valign, ".valign" },
9866 { TYPE_INTEGER, &tmi.font, ".font" },
9867 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9868 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9869 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9870 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9871 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9872 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9873 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9874 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9880 struct TitleFadingInfo *info;
9885 /* initialize first titles from "enter screen" definitions, if defined */
9886 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9887 { &title_first_default, "menu.enter_screen.TITLE" },
9889 /* initialize title screens from "next screen" definitions, if defined */
9890 { &title_initial_default, "menu.next_screen.TITLE" },
9891 { &title_default, "menu.next_screen.TITLE" },
9897 struct TitleMessageInfo *array;
9900 titlemessage_arrays[] =
9902 /* initialize first titles from "enter screen" definitions, if defined */
9903 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9904 { titlescreen_first, "menu.enter_screen.TITLE" },
9905 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9906 { titlemessage_first, "menu.enter_screen.TITLE" },
9908 /* initialize titles from "next screen" definitions, if defined */
9909 { titlescreen_initial, "menu.next_screen.TITLE" },
9910 { titlescreen, "menu.next_screen.TITLE" },
9911 { titlemessage_initial, "menu.next_screen.TITLE" },
9912 { titlemessage, "menu.next_screen.TITLE" },
9914 /* overwrite titles with title definitions, if defined */
9915 { titlescreen_initial_first, "[title_initial]" },
9916 { titlescreen_first, "[title]" },
9917 { titlemessage_initial_first, "[title_initial]" },
9918 { titlemessage_first, "[title]" },
9920 { titlescreen_initial, "[title_initial]" },
9921 { titlescreen, "[title]" },
9922 { titlemessage_initial, "[title_initial]" },
9923 { titlemessage, "[title]" },
9925 /* overwrite titles with title screen/message definitions, if defined */
9926 { titlescreen_initial_first, "[titlescreen_initial]" },
9927 { titlescreen_first, "[titlescreen]" },
9928 { titlemessage_initial_first, "[titlemessage_initial]" },
9929 { titlemessage_first, "[titlemessage]" },
9931 { titlescreen_initial, "[titlescreen_initial]" },
9932 { titlescreen, "[titlescreen]" },
9933 { titlemessage_initial, "[titlemessage_initial]" },
9934 { titlemessage, "[titlemessage]" },
9938 SetupFileHash *setup_file_hash;
9941 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9944 /* the following initializes hierarchical values from dynamic configuration */
9946 /* special case: initialize with default values that may be overwritten */
9947 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9948 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9950 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9951 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9952 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9954 if (value_1 != NULL)
9955 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9956 if (value_2 != NULL)
9957 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9958 if (value_3 != NULL)
9959 menu.list_size[i] = get_integer_from_string(value_3);
9962 /* special case: initialize with default values that may be overwritten */
9963 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9964 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9966 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9967 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9969 if (value_1 != NULL)
9970 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9971 if (value_2 != NULL)
9972 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9974 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9976 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9978 if (value_1 != NULL)
9979 menu.list_size_info[i] = get_integer_from_string(value_1);
9983 /* special case: initialize with default values that may be overwritten */
9984 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9985 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9987 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9988 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9990 if (value_1 != NULL)
9991 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9992 if (value_2 != NULL)
9993 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9996 /* special case: initialize with default values that may be overwritten */
9997 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9998 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10000 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
10001 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
10002 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
10003 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
10004 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
10005 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
10006 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
10007 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
10008 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
10010 if (value_1 != NULL)
10011 menu.left_spacing_info[i] = get_integer_from_string(value_1);
10012 if (value_2 != NULL)
10013 menu.right_spacing_info[i] = get_integer_from_string(value_2);
10014 if (value_3 != NULL)
10015 menu.top_spacing_info[i] = get_integer_from_string(value_3);
10016 if (value_4 != NULL)
10017 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
10018 if (value_5 != NULL)
10019 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
10020 if (value_6 != NULL)
10021 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
10022 if (value_7 != NULL)
10023 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
10024 if (value_8 != NULL)
10025 menu.line_spacing_info[i] = get_integer_from_string(value_8);
10026 if (value_9 != NULL)
10027 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
10030 /* special case: initialize with default values that may be overwritten */
10031 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10032 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10034 char *token_1 = "menu.enter_screen.fade_mode";
10035 char *token_2 = "menu.enter_screen.fade_delay";
10036 char *token_3 = "menu.enter_screen.post_delay";
10037 char *token_4 = "menu.leave_screen.fade_mode";
10038 char *token_5 = "menu.leave_screen.fade_delay";
10039 char *token_6 = "menu.leave_screen.post_delay";
10040 char *token_7 = "menu.next_screen.fade_mode";
10041 char *token_8 = "menu.next_screen.fade_delay";
10042 char *token_9 = "menu.next_screen.post_delay";
10043 char *value_1 = getHashEntry(setup_file_hash, token_1);
10044 char *value_2 = getHashEntry(setup_file_hash, token_2);
10045 char *value_3 = getHashEntry(setup_file_hash, token_3);
10046 char *value_4 = getHashEntry(setup_file_hash, token_4);
10047 char *value_5 = getHashEntry(setup_file_hash, token_5);
10048 char *value_6 = getHashEntry(setup_file_hash, token_6);
10049 char *value_7 = getHashEntry(setup_file_hash, token_7);
10050 char *value_8 = getHashEntry(setup_file_hash, token_8);
10051 char *value_9 = getHashEntry(setup_file_hash, token_9);
10053 if (value_1 != NULL)
10054 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10056 if (value_2 != NULL)
10057 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10059 if (value_3 != NULL)
10060 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10062 if (value_4 != NULL)
10063 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10065 if (value_5 != NULL)
10066 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10068 if (value_6 != NULL)
10069 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10071 if (value_7 != NULL)
10072 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10074 if (value_8 != NULL)
10075 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10077 if (value_9 != NULL)
10078 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10082 /* special case: initialize with default values that may be overwritten */
10083 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10084 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10086 char *token_w1 = "viewport.window.width";
10087 char *token_w2 = "viewport.window.height";
10088 char *token_01 = "viewport.playfield.x";
10089 char *token_02 = "viewport.playfield.y";
10090 char *token_03 = "viewport.playfield.width";
10091 char *token_04 = "viewport.playfield.height";
10092 char *token_05 = "viewport.playfield.border_size";
10093 char *token_06 = "viewport.door_1.x";
10094 char *token_07 = "viewport.door_1.y";
10095 char *token_08 = "viewport.door_1.width";
10096 char *token_09 = "viewport.door_1.height";
10097 char *token_10 = "viewport.door_1.border_size";
10098 char *token_11 = "viewport.door_2.x";
10099 char *token_12 = "viewport.door_2.y";
10100 char *token_13 = "viewport.door_2.width";
10101 char *token_14 = "viewport.door_2.height";
10102 char *token_15 = "viewport.door_2.border_size";
10103 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10104 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10105 char *value_01 = getHashEntry(setup_file_hash, token_01);
10106 char *value_02 = getHashEntry(setup_file_hash, token_02);
10107 char *value_03 = getHashEntry(setup_file_hash, token_03);
10108 char *value_04 = getHashEntry(setup_file_hash, token_04);
10109 char *value_05 = getHashEntry(setup_file_hash, token_05);
10110 char *value_06 = getHashEntry(setup_file_hash, token_06);
10111 char *value_07 = getHashEntry(setup_file_hash, token_07);
10112 char *value_08 = getHashEntry(setup_file_hash, token_08);
10113 char *value_09 = getHashEntry(setup_file_hash, token_09);
10114 char *value_10 = getHashEntry(setup_file_hash, token_10);
10115 char *value_11 = getHashEntry(setup_file_hash, token_11);
10116 char *value_12 = getHashEntry(setup_file_hash, token_12);
10117 char *value_13 = getHashEntry(setup_file_hash, token_13);
10118 char *value_14 = getHashEntry(setup_file_hash, token_14);
10119 char *value_15 = getHashEntry(setup_file_hash, token_15);
10121 if (value_w1 != NULL)
10122 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10123 if (value_w2 != NULL)
10124 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10125 if (value_01 != NULL)
10126 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10127 if (value_02 != NULL)
10128 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10129 if (value_03 != NULL)
10130 viewport.playfield[i].width = get_token_parameter_value(token_03,
10132 if (value_04 != NULL)
10133 viewport.playfield[i].height = get_token_parameter_value(token_04,
10135 if (value_05 != NULL)
10136 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10138 if (value_06 != NULL)
10139 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10140 if (value_07 != NULL)
10141 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10142 if (value_08 != NULL)
10143 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10144 if (value_09 != NULL)
10145 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10146 if (value_10 != NULL)
10147 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10149 if (value_11 != NULL)
10150 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10151 if (value_12 != NULL)
10152 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10153 if (value_13 != NULL)
10154 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10155 if (value_14 != NULL)
10156 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10157 if (value_15 != NULL)
10158 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10162 /* special case: initialize with default values that may be overwritten */
10163 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10164 for (i = 0; title_info[i].info != NULL; i++)
10166 struct TitleFadingInfo *info = title_info[i].info;
10167 char *base_token = title_info[i].text;
10169 for (j = 0; title_tokens[j].type != -1; j++)
10171 char *token = getStringCat2(base_token, title_tokens[j].text);
10172 char *value = getHashEntry(setup_file_hash, token);
10176 int parameter_value = get_token_parameter_value(token, value);
10180 *(int *)title_tokens[j].value = (int)parameter_value;
10189 /* special case: initialize with default values that may be overwritten */
10190 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10191 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10193 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10194 char *base_token = titlemessage_arrays[i].text;
10196 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10198 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10199 char *value = getHashEntry(setup_file_hash, token);
10203 int parameter_value = get_token_parameter_value(token, value);
10205 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10209 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10210 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10212 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10222 /* read (and overwrite with) values that may be specified in config file */
10223 for (i = 0; image_config_vars[i].token != NULL; i++)
10225 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10227 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10228 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10229 *image_config_vars[i].value =
10230 get_token_parameter_value(image_config_vars[i].token, value);
10233 freeSetupFileHash(setup_file_hash);
10236 void LoadMenuDesignSettings()
10238 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10240 InitMenuDesignSettings_Static();
10241 InitMenuDesignSettings_SpecialPreProcessing();
10243 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10245 /* first look for special settings configured in level series config */
10246 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10248 if (fileExists(filename_base))
10249 LoadMenuDesignSettingsFromFilename(filename_base);
10252 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10254 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10255 LoadMenuDesignSettingsFromFilename(filename_local);
10257 InitMenuDesignSettings_SpecialPostProcessing();
10260 void LoadMenuDesignSettings_AfterGraphics()
10262 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10265 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10267 char *filename = getEditorSetupFilename();
10268 SetupFileList *setup_file_list, *list;
10269 SetupFileHash *element_hash;
10270 int num_unknown_tokens = 0;
10273 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10276 element_hash = newSetupFileHash();
10278 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10279 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10281 /* determined size may be larger than needed (due to unknown elements) */
10283 for (list = setup_file_list; list != NULL; list = list->next)
10286 /* add space for up to 3 more elements for padding that may be needed */
10287 *num_elements += 3;
10289 /* free memory for old list of elements, if needed */
10290 checked_free(*elements);
10292 /* allocate memory for new list of elements */
10293 *elements = checked_malloc(*num_elements * sizeof(int));
10296 for (list = setup_file_list; list != NULL; list = list->next)
10298 char *value = getHashEntry(element_hash, list->token);
10300 if (value == NULL) /* try to find obsolete token mapping */
10302 char *mapped_token = get_mapped_token(list->token);
10304 if (mapped_token != NULL)
10306 value = getHashEntry(element_hash, mapped_token);
10308 free(mapped_token);
10314 (*elements)[(*num_elements)++] = atoi(value);
10318 if (num_unknown_tokens == 0)
10320 Error(ERR_INFO_LINE, "-");
10321 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10322 Error(ERR_INFO, "- config file: '%s'", filename);
10324 num_unknown_tokens++;
10327 Error(ERR_INFO, "- token: '%s'", list->token);
10331 if (num_unknown_tokens > 0)
10332 Error(ERR_INFO_LINE, "-");
10334 while (*num_elements % 4) /* pad with empty elements, if needed */
10335 (*elements)[(*num_elements)++] = EL_EMPTY;
10337 freeSetupFileList(setup_file_list);
10338 freeSetupFileHash(element_hash);
10341 for (i = 0; i < *num_elements; i++)
10342 printf("editor: element '%s' [%d]\n",
10343 element_info[(*elements)[i]].token_name, (*elements)[i]);
10347 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10350 SetupFileHash *setup_file_hash = NULL;
10351 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10352 char *filename_music, *filename_prefix, *filename_info;
10358 token_to_value_ptr[] =
10360 { "title_header", &tmp_music_file_info.title_header },
10361 { "artist_header", &tmp_music_file_info.artist_header },
10362 { "album_header", &tmp_music_file_info.album_header },
10363 { "year_header", &tmp_music_file_info.year_header },
10365 { "title", &tmp_music_file_info.title },
10366 { "artist", &tmp_music_file_info.artist },
10367 { "album", &tmp_music_file_info.album },
10368 { "year", &tmp_music_file_info.year },
10374 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10375 getCustomMusicFilename(basename));
10377 if (filename_music == NULL)
10380 /* ---------- try to replace file extension ---------- */
10382 filename_prefix = getStringCopy(filename_music);
10383 if (strrchr(filename_prefix, '.') != NULL)
10384 *strrchr(filename_prefix, '.') = '\0';
10385 filename_info = getStringCat2(filename_prefix, ".txt");
10387 if (fileExists(filename_info))
10388 setup_file_hash = loadSetupFileHash(filename_info);
10390 free(filename_prefix);
10391 free(filename_info);
10393 if (setup_file_hash == NULL)
10395 /* ---------- try to add file extension ---------- */
10397 filename_prefix = getStringCopy(filename_music);
10398 filename_info = getStringCat2(filename_prefix, ".txt");
10400 if (fileExists(filename_info))
10401 setup_file_hash = loadSetupFileHash(filename_info);
10403 free(filename_prefix);
10404 free(filename_info);
10407 if (setup_file_hash == NULL)
10410 /* ---------- music file info found ---------- */
10412 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10414 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10416 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10418 *token_to_value_ptr[i].value_ptr =
10419 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10422 tmp_music_file_info.basename = getStringCopy(basename);
10423 tmp_music_file_info.music = music;
10424 tmp_music_file_info.is_sound = is_sound;
10426 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10427 *new_music_file_info = tmp_music_file_info;
10429 return new_music_file_info;
10432 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10434 return get_music_file_info_ext(basename, music, FALSE);
10437 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10439 return get_music_file_info_ext(basename, sound, TRUE);
10442 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10443 char *basename, boolean is_sound)
10445 for (; list != NULL; list = list->next)
10446 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10452 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10454 return music_info_listed_ext(list, basename, FALSE);
10457 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10459 return music_info_listed_ext(list, basename, TRUE);
10462 void LoadMusicInfo()
10464 char *music_directory = getCustomMusicDirectory();
10465 int num_music = getMusicListSize();
10466 int num_music_noconf = 0;
10467 int num_sounds = getSoundListSize();
10469 DirectoryEntry *dir_entry;
10470 struct FileInfo *music, *sound;
10471 struct MusicFileInfo *next, **new;
10474 while (music_file_info != NULL)
10476 next = music_file_info->next;
10478 checked_free(music_file_info->basename);
10480 checked_free(music_file_info->title_header);
10481 checked_free(music_file_info->artist_header);
10482 checked_free(music_file_info->album_header);
10483 checked_free(music_file_info->year_header);
10485 checked_free(music_file_info->title);
10486 checked_free(music_file_info->artist);
10487 checked_free(music_file_info->album);
10488 checked_free(music_file_info->year);
10490 free(music_file_info);
10492 music_file_info = next;
10495 new = &music_file_info;
10497 for (i = 0; i < num_music; i++)
10499 music = getMusicListEntry(i);
10501 if (music->filename == NULL)
10504 if (strEqual(music->filename, UNDEFINED_FILENAME))
10507 /* a configured file may be not recognized as music */
10508 if (!FileIsMusic(music->filename))
10511 if (!music_info_listed(music_file_info, music->filename))
10513 *new = get_music_file_info(music->filename, i);
10516 new = &(*new)->next;
10520 if ((dir = openDirectory(music_directory)) == NULL)
10522 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10526 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10528 char *basename = dir_entry->basename;
10529 boolean music_already_used = FALSE;
10532 /* skip all music files that are configured in music config file */
10533 for (i = 0; i < num_music; i++)
10535 music = getMusicListEntry(i);
10537 if (music->filename == NULL)
10540 if (strEqual(basename, music->filename))
10542 music_already_used = TRUE;
10547 if (music_already_used)
10550 if (!FileIsMusic(dir_entry->filename))
10553 if (!music_info_listed(music_file_info, basename))
10555 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10558 new = &(*new)->next;
10561 num_music_noconf++;
10564 closeDirectory(dir);
10566 for (i = 0; i < num_sounds; i++)
10568 sound = getSoundListEntry(i);
10570 if (sound->filename == NULL)
10573 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10576 /* a configured file may be not recognized as sound */
10577 if (!FileIsSound(sound->filename))
10580 if (!sound_info_listed(music_file_info, sound->filename))
10582 *new = get_sound_file_info(sound->filename, i);
10584 new = &(*new)->next;
10589 void add_helpanim_entry(int element, int action, int direction, int delay,
10590 int *num_list_entries)
10592 struct HelpAnimInfo *new_list_entry;
10593 (*num_list_entries)++;
10596 checked_realloc(helpanim_info,
10597 *num_list_entries * sizeof(struct HelpAnimInfo));
10598 new_list_entry = &helpanim_info[*num_list_entries - 1];
10600 new_list_entry->element = element;
10601 new_list_entry->action = action;
10602 new_list_entry->direction = direction;
10603 new_list_entry->delay = delay;
10606 void print_unknown_token(char *filename, char *token, int token_nr)
10610 Error(ERR_INFO_LINE, "-");
10611 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10612 Error(ERR_INFO, "- config file: '%s'", filename);
10615 Error(ERR_INFO, "- token: '%s'", token);
10618 void print_unknown_token_end(int token_nr)
10621 Error(ERR_INFO_LINE, "-");
10624 void LoadHelpAnimInfo()
10626 char *filename = getHelpAnimFilename();
10627 SetupFileList *setup_file_list = NULL, *list;
10628 SetupFileHash *element_hash, *action_hash, *direction_hash;
10629 int num_list_entries = 0;
10630 int num_unknown_tokens = 0;
10633 if (fileExists(filename))
10634 setup_file_list = loadSetupFileList(filename);
10636 if (setup_file_list == NULL)
10638 /* use reliable default values from static configuration */
10639 SetupFileList *insert_ptr;
10641 insert_ptr = setup_file_list =
10642 newSetupFileList(helpanim_config[0].token,
10643 helpanim_config[0].value);
10645 for (i = 1; helpanim_config[i].token; i++)
10646 insert_ptr = addListEntry(insert_ptr,
10647 helpanim_config[i].token,
10648 helpanim_config[i].value);
10651 element_hash = newSetupFileHash();
10652 action_hash = newSetupFileHash();
10653 direction_hash = newSetupFileHash();
10655 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10656 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10658 for (i = 0; i < NUM_ACTIONS; i++)
10659 setHashEntry(action_hash, element_action_info[i].suffix,
10660 i_to_a(element_action_info[i].value));
10662 /* do not store direction index (bit) here, but direction value! */
10663 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10664 setHashEntry(direction_hash, element_direction_info[i].suffix,
10665 i_to_a(1 << element_direction_info[i].value));
10667 for (list = setup_file_list; list != NULL; list = list->next)
10669 char *element_token, *action_token, *direction_token;
10670 char *element_value, *action_value, *direction_value;
10671 int delay = atoi(list->value);
10673 if (strEqual(list->token, "end"))
10675 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10680 /* first try to break element into element/action/direction parts;
10681 if this does not work, also accept combined "element[.act][.dir]"
10682 elements (like "dynamite.active"), which are unique elements */
10684 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10686 element_value = getHashEntry(element_hash, list->token);
10687 if (element_value != NULL) /* element found */
10688 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10689 &num_list_entries);
10692 /* no further suffixes found -- this is not an element */
10693 print_unknown_token(filename, list->token, num_unknown_tokens++);
10699 /* token has format "<prefix>.<something>" */
10701 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10702 direction_token = action_token; /* ... or direction */
10704 element_token = getStringCopy(list->token);
10705 *strchr(element_token, '.') = '\0';
10707 element_value = getHashEntry(element_hash, element_token);
10709 if (element_value == NULL) /* this is no element */
10711 element_value = getHashEntry(element_hash, list->token);
10712 if (element_value != NULL) /* combined element found */
10713 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10714 &num_list_entries);
10716 print_unknown_token(filename, list->token, num_unknown_tokens++);
10718 free(element_token);
10723 action_value = getHashEntry(action_hash, action_token);
10725 if (action_value != NULL) /* action found */
10727 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10728 &num_list_entries);
10730 free(element_token);
10735 direction_value = getHashEntry(direction_hash, direction_token);
10737 if (direction_value != NULL) /* direction found */
10739 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10740 &num_list_entries);
10742 free(element_token);
10747 if (strchr(action_token + 1, '.') == NULL)
10749 /* no further suffixes found -- this is not an action nor direction */
10751 element_value = getHashEntry(element_hash, list->token);
10752 if (element_value != NULL) /* combined element found */
10753 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10754 &num_list_entries);
10756 print_unknown_token(filename, list->token, num_unknown_tokens++);
10758 free(element_token);
10763 /* token has format "<prefix>.<suffix>.<something>" */
10765 direction_token = strchr(action_token + 1, '.');
10767 action_token = getStringCopy(action_token);
10768 *strchr(action_token + 1, '.') = '\0';
10770 action_value = getHashEntry(action_hash, action_token);
10772 if (action_value == NULL) /* this is no action */
10774 element_value = getHashEntry(element_hash, list->token);
10775 if (element_value != NULL) /* combined element found */
10776 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10777 &num_list_entries);
10779 print_unknown_token(filename, list->token, num_unknown_tokens++);
10781 free(element_token);
10782 free(action_token);
10787 direction_value = getHashEntry(direction_hash, direction_token);
10789 if (direction_value != NULL) /* direction found */
10791 add_helpanim_entry(atoi(element_value), atoi(action_value),
10792 atoi(direction_value), delay, &num_list_entries);
10794 free(element_token);
10795 free(action_token);
10800 /* this is no direction */
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);
10813 print_unknown_token_end(num_unknown_tokens);
10815 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10816 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10818 freeSetupFileList(setup_file_list);
10819 freeSetupFileHash(element_hash);
10820 freeSetupFileHash(action_hash);
10821 freeSetupFileHash(direction_hash);
10824 for (i = 0; i < num_list_entries; i++)
10825 printf("::: '%s': %d, %d, %d => %d\n",
10826 EL_NAME(helpanim_info[i].element),
10827 helpanim_info[i].element,
10828 helpanim_info[i].action,
10829 helpanim_info[i].direction,
10830 helpanim_info[i].delay);
10834 void LoadHelpTextInfo()
10836 char *filename = getHelpTextFilename();
10839 if (helptext_info != NULL)
10841 freeSetupFileHash(helptext_info);
10842 helptext_info = NULL;
10845 if (fileExists(filename))
10846 helptext_info = loadSetupFileHash(filename);
10848 if (helptext_info == NULL)
10850 /* use reliable default values from static configuration */
10851 helptext_info = newSetupFileHash();
10853 for (i = 0; helptext_config[i].token; i++)
10854 setHashEntry(helptext_info,
10855 helptext_config[i].token,
10856 helptext_config[i].value);
10860 BEGIN_HASH_ITERATION(helptext_info, itr)
10862 printf("::: '%s' => '%s'\n",
10863 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10865 END_HASH_ITERATION(hash, itr)
10870 /* ------------------------------------------------------------------------- */
10871 /* convert levels */
10872 /* ------------------------------------------------------------------------- */
10874 #define MAX_NUM_CONVERT_LEVELS 1000
10876 void ConvertLevels()
10878 static LevelDirTree *convert_leveldir = NULL;
10879 static int convert_level_nr = -1;
10880 static int num_levels_handled = 0;
10881 static int num_levels_converted = 0;
10882 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10885 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10886 global.convert_leveldir);
10888 if (convert_leveldir == NULL)
10889 Error(ERR_EXIT, "no such level identifier: '%s'",
10890 global.convert_leveldir);
10892 leveldir_current = convert_leveldir;
10894 if (global.convert_level_nr != -1)
10896 convert_leveldir->first_level = global.convert_level_nr;
10897 convert_leveldir->last_level = global.convert_level_nr;
10900 convert_level_nr = convert_leveldir->first_level;
10902 PrintLine("=", 79);
10903 Print("Converting levels\n");
10904 PrintLine("-", 79);
10905 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10906 Print("Level series name: '%s'\n", convert_leveldir->name);
10907 Print("Level series author: '%s'\n", convert_leveldir->author);
10908 Print("Number of levels: %d\n", convert_leveldir->levels);
10909 PrintLine("=", 79);
10912 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10913 levels_failed[i] = FALSE;
10915 while (convert_level_nr <= convert_leveldir->last_level)
10917 char *level_filename;
10920 level_nr = convert_level_nr++;
10922 Print("Level %03d: ", level_nr);
10924 LoadLevel(level_nr);
10925 if (level.no_level_file || level.no_valid_file)
10927 Print("(no level)\n");
10931 Print("converting level ... ");
10933 level_filename = getDefaultLevelFilename(level_nr);
10934 new_level = !fileExists(level_filename);
10938 SaveLevel(level_nr);
10940 num_levels_converted++;
10942 Print("converted.\n");
10946 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10947 levels_failed[level_nr] = TRUE;
10949 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10952 num_levels_handled++;
10956 PrintLine("=", 79);
10957 Print("Number of levels handled: %d\n", num_levels_handled);
10958 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10959 (num_levels_handled ?
10960 num_levels_converted * 100 / num_levels_handled : 0));
10961 PrintLine("-", 79);
10962 Print("Summary (for automatic parsing by scripts):\n");
10963 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10964 convert_leveldir->identifier, num_levels_converted,
10965 num_levels_handled,
10966 (num_levels_handled ?
10967 num_levels_converted * 100 / num_levels_handled : 0));
10969 if (num_levels_handled != num_levels_converted)
10971 Print(", FAILED:");
10972 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10973 if (levels_failed[i])
10978 PrintLine("=", 79);
10980 CloseAllAndExit(0);
10984 /* ------------------------------------------------------------------------- */
10985 /* create and save images for use in level sketches (raw BMP format) */
10986 /* ------------------------------------------------------------------------- */
10988 void CreateLevelSketchImages()
10990 #if defined(TARGET_SDL)
10995 InitElementPropertiesGfxElement();
10997 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10998 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11000 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11002 Bitmap *src_bitmap;
11004 int element = getMappedElement(i);
11005 int graphic = el2edimg(element);
11006 char basename1[16];
11007 char basename2[16];
11011 sprintf(basename1, "%03d.bmp", i);
11012 sprintf(basename2, "%03ds.bmp", i);
11014 filename1 = getPath2(global.create_images_dir, basename1);
11015 filename2 = getPath2(global.create_images_dir, basename2);
11017 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
11018 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
11021 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11022 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11024 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
11025 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
11027 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11028 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11034 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11037 FreeBitmap(bitmap1);
11038 FreeBitmap(bitmap2);
11043 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11045 CloseAllAndExit(0);
11050 /* ------------------------------------------------------------------------- */
11051 /* create and save images for custom and group elements (raw BMP format) */
11052 /* ------------------------------------------------------------------------- */
11054 void CreateCustomElementImages(char *directory)
11056 #if defined(TARGET_SDL)
11057 char *src_basename = "RocksCE-template.ilbm";
11058 char *dst_basename = "RocksCE.bmp";
11059 char *src_filename = getPath2(directory, src_basename);
11060 char *dst_filename = getPath2(directory, dst_basename);
11061 Bitmap *src_bitmap;
11063 int yoffset_ce = 0;
11064 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11067 SDLInitVideoDisplay();
11069 ReCreateBitmap(&backbuffer, video.width, video.height);
11071 src_bitmap = LoadImage(src_filename);
11073 bitmap = CreateBitmap(TILEX * 16 * 2,
11074 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11077 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11084 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11085 TILEX * x, TILEY * y + yoffset_ce);
11087 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11089 TILEX * x + TILEX * 16,
11090 TILEY * y + yoffset_ce);
11092 for (j = 2; j >= 0; j--)
11096 BlitBitmap(src_bitmap, bitmap,
11097 TILEX + c * 7, 0, 6, 10,
11098 TILEX * x + 6 + j * 7,
11099 TILEY * y + 11 + yoffset_ce);
11101 BlitBitmap(src_bitmap, bitmap,
11102 TILEX + c * 8, TILEY, 6, 10,
11103 TILEX * 16 + TILEX * x + 6 + j * 8,
11104 TILEY * y + 10 + yoffset_ce);
11110 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11117 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11118 TILEX * x, TILEY * y + yoffset_ge);
11120 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11122 TILEX * x + TILEX * 16,
11123 TILEY * y + yoffset_ge);
11125 for (j = 1; j >= 0; j--)
11129 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11130 TILEX * x + 6 + j * 10,
11131 TILEY * y + 11 + yoffset_ge);
11133 BlitBitmap(src_bitmap, bitmap,
11134 TILEX + c * 8, TILEY + 12, 6, 10,
11135 TILEX * 16 + TILEX * x + 10 + j * 8,
11136 TILEY * y + 10 + yoffset_ge);
11142 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11143 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11145 FreeBitmap(bitmap);
11147 CloseAllAndExit(0);