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 /* ------------------------------------------------------------------------- */
2291 /* functions for loading R'n'D level */
2292 /* ------------------------------------------------------------------------- */
2294 int getMappedElement(int element)
2296 /* remap some (historic, now obsolete) elements */
2300 case EL_PLAYER_OBSOLETE:
2301 element = EL_PLAYER_1;
2304 case EL_KEY_OBSOLETE:
2308 case EL_EM_KEY_1_FILE_OBSOLETE:
2309 element = EL_EM_KEY_1;
2312 case EL_EM_KEY_2_FILE_OBSOLETE:
2313 element = EL_EM_KEY_2;
2316 case EL_EM_KEY_3_FILE_OBSOLETE:
2317 element = EL_EM_KEY_3;
2320 case EL_EM_KEY_4_FILE_OBSOLETE:
2321 element = EL_EM_KEY_4;
2324 case EL_ENVELOPE_OBSOLETE:
2325 element = EL_ENVELOPE_1;
2333 if (element >= NUM_FILE_ELEMENTS)
2335 Error(ERR_WARN, "invalid level element %d", element);
2337 element = EL_UNKNOWN;
2345 int getMappedElementByVersion(int element, int game_version)
2347 /* remap some elements due to certain game version */
2349 if (game_version <= VERSION_IDENT(2,2,0,0))
2351 /* map game font elements */
2352 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2353 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2354 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2355 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2358 if (game_version < VERSION_IDENT(3,0,0,0))
2360 /* map Supaplex gravity tube elements */
2361 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2362 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2363 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2364 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2371 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2373 level->file_version = getFileVersion(file);
2374 level->game_version = getFileVersion(file);
2379 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2381 level->creation_date.year = getFile16BitBE(file);
2382 level->creation_date.month = getFile8Bit(file);
2383 level->creation_date.day = getFile8Bit(file);
2385 level->creation_date.src = DATE_SRC_LEVELFILE;
2390 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2392 int initial_player_stepsize;
2393 int initial_player_gravity;
2396 level->fieldx = getFile8Bit(file);
2397 level->fieldy = getFile8Bit(file);
2399 level->time = getFile16BitBE(file);
2400 level->gems_needed = getFile16BitBE(file);
2402 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2403 level->name[i] = getFile8Bit(file);
2404 level->name[MAX_LEVEL_NAME_LEN] = 0;
2406 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2407 level->score[i] = getFile8Bit(file);
2409 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2410 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2411 for (y = 0; y < 3; y++)
2412 for (x = 0; x < 3; x++)
2413 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2415 level->amoeba_speed = getFile8Bit(file);
2416 level->time_magic_wall = getFile8Bit(file);
2417 level->time_wheel = getFile8Bit(file);
2418 level->amoeba_content = getMappedElement(getFile8Bit(file));
2420 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2423 for (i = 0; i < MAX_PLAYERS; i++)
2424 level->initial_player_stepsize[i] = initial_player_stepsize;
2426 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2428 for (i = 0; i < MAX_PLAYERS; i++)
2429 level->initial_player_gravity[i] = initial_player_gravity;
2431 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2432 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2434 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2436 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2437 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2438 level->can_move_into_acid_bits = getFile32BitBE(file);
2439 level->dont_collide_with_bits = getFile8Bit(file);
2441 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2442 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2444 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2445 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2446 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2448 level->game_engine_type = getFile8Bit(file);
2450 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2455 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2459 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2460 level->name[i] = getFile8Bit(file);
2461 level->name[MAX_LEVEL_NAME_LEN] = 0;
2466 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2470 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2471 level->author[i] = getFile8Bit(file);
2472 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2477 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2480 int chunk_size_expected = level->fieldx * level->fieldy;
2482 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2483 stored with 16-bit encoding (and should be twice as big then).
2484 Even worse, playfield data was stored 16-bit when only yamyam content
2485 contained 16-bit elements and vice versa. */
2487 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2488 chunk_size_expected *= 2;
2490 if (chunk_size_expected != chunk_size)
2492 ReadUnusedBytesFromFile(file, chunk_size);
2493 return chunk_size_expected;
2496 for (y = 0; y < level->fieldy; y++)
2497 for (x = 0; x < level->fieldx; x++)
2498 level->field[x][y] =
2499 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2504 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2507 int header_size = 4;
2508 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2509 int chunk_size_expected = header_size + content_size;
2511 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2512 stored with 16-bit encoding (and should be twice as big then).
2513 Even worse, playfield data was stored 16-bit when only yamyam content
2514 contained 16-bit elements and vice versa. */
2516 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2517 chunk_size_expected += content_size;
2519 if (chunk_size_expected != chunk_size)
2521 ReadUnusedBytesFromFile(file, chunk_size);
2522 return chunk_size_expected;
2526 level->num_yamyam_contents = getFile8Bit(file);
2530 /* correct invalid number of content fields -- should never happen */
2531 if (level->num_yamyam_contents < 1 ||
2532 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2533 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2535 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2536 for (y = 0; y < 3; y++)
2537 for (x = 0; x < 3; x++)
2538 level->yamyam_content[i].e[x][y] =
2539 getMappedElement(level->encoding_16bit_field ?
2540 getFile16BitBE(file) : getFile8Bit(file));
2544 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2549 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2551 element = getMappedElement(getFile16BitBE(file));
2552 num_contents = getFile8Bit(file);
2554 getFile8Bit(file); /* content x size (unused) */
2555 getFile8Bit(file); /* content y size (unused) */
2557 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2559 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2560 for (y = 0; y < 3; y++)
2561 for (x = 0; x < 3; x++)
2562 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2564 /* correct invalid number of content fields -- should never happen */
2565 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2566 num_contents = STD_ELEMENT_CONTENTS;
2568 if (element == EL_YAMYAM)
2570 level->num_yamyam_contents = num_contents;
2572 for (i = 0; i < num_contents; i++)
2573 for (y = 0; y < 3; y++)
2574 for (x = 0; x < 3; x++)
2575 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2577 else if (element == EL_BD_AMOEBA)
2579 level->amoeba_content = content_array[0][0][0];
2583 Error(ERR_WARN, "cannot load content for element '%d'", element);
2589 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2595 int chunk_size_expected;
2597 element = getMappedElement(getFile16BitBE(file));
2598 if (!IS_ENVELOPE(element))
2599 element = EL_ENVELOPE_1;
2601 envelope_nr = element - EL_ENVELOPE_1;
2603 envelope_len = getFile16BitBE(file);
2605 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2606 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2608 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2610 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2611 if (chunk_size_expected != chunk_size)
2613 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2614 return chunk_size_expected;
2617 for (i = 0; i < envelope_len; i++)
2618 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2623 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2625 int num_changed_custom_elements = getFile16BitBE(file);
2626 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2629 if (chunk_size_expected != chunk_size)
2631 ReadUnusedBytesFromFile(file, chunk_size - 2);
2632 return chunk_size_expected;
2635 for (i = 0; i < num_changed_custom_elements; i++)
2637 int element = getMappedElement(getFile16BitBE(file));
2638 int properties = getFile32BitBE(file);
2640 if (IS_CUSTOM_ELEMENT(element))
2641 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2643 Error(ERR_WARN, "invalid custom element number %d", element);
2645 /* older game versions that wrote level files with CUS1 chunks used
2646 different default push delay values (not yet stored in level file) */
2647 element_info[element].push_delay_fixed = 2;
2648 element_info[element].push_delay_random = 8;
2651 level->file_has_custom_elements = TRUE;
2656 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2658 int num_changed_custom_elements = getFile16BitBE(file);
2659 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2662 if (chunk_size_expected != chunk_size)
2664 ReadUnusedBytesFromFile(file, chunk_size - 2);
2665 return chunk_size_expected;
2668 for (i = 0; i < num_changed_custom_elements; i++)
2670 int element = getMappedElement(getFile16BitBE(file));
2671 int custom_target_element = getMappedElement(getFile16BitBE(file));
2673 if (IS_CUSTOM_ELEMENT(element))
2674 element_info[element].change->target_element = custom_target_element;
2676 Error(ERR_WARN, "invalid custom element number %d", element);
2679 level->file_has_custom_elements = TRUE;
2684 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2686 int num_changed_custom_elements = getFile16BitBE(file);
2687 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2690 if (chunk_size_expected != chunk_size)
2692 ReadUnusedBytesFromFile(file, chunk_size - 2);
2693 return chunk_size_expected;
2696 for (i = 0; i < num_changed_custom_elements; i++)
2698 int element = getMappedElement(getFile16BitBE(file));
2699 struct ElementInfo *ei = &element_info[element];
2700 unsigned int event_bits;
2702 if (!IS_CUSTOM_ELEMENT(element))
2704 Error(ERR_WARN, "invalid custom element number %d", element);
2706 element = EL_INTERNAL_DUMMY;
2709 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2710 ei->description[j] = getFile8Bit(file);
2711 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2713 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2715 /* some free bytes for future properties and padding */
2716 ReadUnusedBytesFromFile(file, 7);
2718 ei->use_gfx_element = getFile8Bit(file);
2719 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2721 ei->collect_score_initial = getFile8Bit(file);
2722 ei->collect_count_initial = getFile8Bit(file);
2724 ei->push_delay_fixed = getFile16BitBE(file);
2725 ei->push_delay_random = getFile16BitBE(file);
2726 ei->move_delay_fixed = getFile16BitBE(file);
2727 ei->move_delay_random = getFile16BitBE(file);
2729 ei->move_pattern = getFile16BitBE(file);
2730 ei->move_direction_initial = getFile8Bit(file);
2731 ei->move_stepsize = getFile8Bit(file);
2733 for (y = 0; y < 3; y++)
2734 for (x = 0; x < 3; x++)
2735 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2737 event_bits = getFile32BitBE(file);
2738 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2739 if (event_bits & (1 << j))
2740 ei->change->has_event[j] = TRUE;
2742 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2744 ei->change->delay_fixed = getFile16BitBE(file);
2745 ei->change->delay_random = getFile16BitBE(file);
2746 ei->change->delay_frames = getFile16BitBE(file);
2748 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2750 ei->change->explode = getFile8Bit(file);
2751 ei->change->use_target_content = getFile8Bit(file);
2752 ei->change->only_if_complete = getFile8Bit(file);
2753 ei->change->use_random_replace = getFile8Bit(file);
2755 ei->change->random_percentage = getFile8Bit(file);
2756 ei->change->replace_when = getFile8Bit(file);
2758 for (y = 0; y < 3; y++)
2759 for (x = 0; x < 3; x++)
2760 ei->change->target_content.e[x][y] =
2761 getMappedElement(getFile16BitBE(file));
2763 ei->slippery_type = getFile8Bit(file);
2765 /* some free bytes for future properties and padding */
2766 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2768 /* mark that this custom element has been modified */
2769 ei->modified_settings = TRUE;
2772 level->file_has_custom_elements = TRUE;
2777 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2779 struct ElementInfo *ei;
2780 int chunk_size_expected;
2784 /* ---------- custom element base property values (96 bytes) ------------- */
2786 element = getMappedElement(getFile16BitBE(file));
2788 if (!IS_CUSTOM_ELEMENT(element))
2790 Error(ERR_WARN, "invalid custom element number %d", element);
2792 ReadUnusedBytesFromFile(file, chunk_size - 2);
2796 ei = &element_info[element];
2798 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2799 ei->description[i] = getFile8Bit(file);
2800 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2802 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2804 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2806 ei->num_change_pages = getFile8Bit(file);
2808 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2809 if (chunk_size_expected != chunk_size)
2811 ReadUnusedBytesFromFile(file, chunk_size - 43);
2812 return chunk_size_expected;
2815 ei->ce_value_fixed_initial = getFile16BitBE(file);
2816 ei->ce_value_random_initial = getFile16BitBE(file);
2817 ei->use_last_ce_value = getFile8Bit(file);
2819 ei->use_gfx_element = getFile8Bit(file);
2820 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2822 ei->collect_score_initial = getFile8Bit(file);
2823 ei->collect_count_initial = getFile8Bit(file);
2825 ei->drop_delay_fixed = getFile8Bit(file);
2826 ei->push_delay_fixed = getFile8Bit(file);
2827 ei->drop_delay_random = getFile8Bit(file);
2828 ei->push_delay_random = getFile8Bit(file);
2829 ei->move_delay_fixed = getFile16BitBE(file);
2830 ei->move_delay_random = getFile16BitBE(file);
2832 /* bits 0 - 15 of "move_pattern" ... */
2833 ei->move_pattern = getFile16BitBE(file);
2834 ei->move_direction_initial = getFile8Bit(file);
2835 ei->move_stepsize = getFile8Bit(file);
2837 ei->slippery_type = getFile8Bit(file);
2839 for (y = 0; y < 3; y++)
2840 for (x = 0; x < 3; x++)
2841 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2843 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2844 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2845 ei->move_leave_type = getFile8Bit(file);
2847 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2848 ei->move_pattern |= (getFile16BitBE(file) << 16);
2850 ei->access_direction = getFile8Bit(file);
2852 ei->explosion_delay = getFile8Bit(file);
2853 ei->ignition_delay = getFile8Bit(file);
2854 ei->explosion_type = getFile8Bit(file);
2856 /* some free bytes for future custom property values and padding */
2857 ReadUnusedBytesFromFile(file, 1);
2859 /* ---------- change page property values (48 bytes) --------------------- */
2861 setElementChangePages(ei, ei->num_change_pages);
2863 for (i = 0; i < ei->num_change_pages; i++)
2865 struct ElementChangeInfo *change = &ei->change_page[i];
2866 unsigned int event_bits;
2868 /* always start with reliable default values */
2869 setElementChangeInfoToDefaults(change);
2871 /* bits 0 - 31 of "has_event[]" ... */
2872 event_bits = getFile32BitBE(file);
2873 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2874 if (event_bits & (1 << j))
2875 change->has_event[j] = TRUE;
2877 change->target_element = getMappedElement(getFile16BitBE(file));
2879 change->delay_fixed = getFile16BitBE(file);
2880 change->delay_random = getFile16BitBE(file);
2881 change->delay_frames = getFile16BitBE(file);
2883 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2885 change->explode = getFile8Bit(file);
2886 change->use_target_content = getFile8Bit(file);
2887 change->only_if_complete = getFile8Bit(file);
2888 change->use_random_replace = getFile8Bit(file);
2890 change->random_percentage = getFile8Bit(file);
2891 change->replace_when = getFile8Bit(file);
2893 for (y = 0; y < 3; y++)
2894 for (x = 0; x < 3; x++)
2895 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2897 change->can_change = getFile8Bit(file);
2899 change->trigger_side = getFile8Bit(file);
2901 change->trigger_player = getFile8Bit(file);
2902 change->trigger_page = getFile8Bit(file);
2904 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2905 CH_PAGE_ANY : (1 << change->trigger_page));
2907 change->has_action = getFile8Bit(file);
2908 change->action_type = getFile8Bit(file);
2909 change->action_mode = getFile8Bit(file);
2910 change->action_arg = getFile16BitBE(file);
2912 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2913 event_bits = getFile8Bit(file);
2914 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2915 if (event_bits & (1 << (j - 32)))
2916 change->has_event[j] = TRUE;
2919 /* mark this custom element as modified */
2920 ei->modified_settings = TRUE;
2922 level->file_has_custom_elements = TRUE;
2927 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2929 struct ElementInfo *ei;
2930 struct ElementGroupInfo *group;
2934 element = getMappedElement(getFile16BitBE(file));
2936 if (!IS_GROUP_ELEMENT(element))
2938 Error(ERR_WARN, "invalid group element number %d", element);
2940 ReadUnusedBytesFromFile(file, chunk_size - 2);
2944 ei = &element_info[element];
2946 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2947 ei->description[i] = getFile8Bit(file);
2948 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2950 group = element_info[element].group;
2952 group->num_elements = getFile8Bit(file);
2954 ei->use_gfx_element = getFile8Bit(file);
2955 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2957 group->choice_mode = getFile8Bit(file);
2959 /* some free bytes for future values and padding */
2960 ReadUnusedBytesFromFile(file, 3);
2962 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2963 group->element[i] = getMappedElement(getFile16BitBE(file));
2965 /* mark this group element as modified */
2966 element_info[element].modified_settings = TRUE;
2968 level->file_has_custom_elements = TRUE;
2973 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2974 int element, int real_element)
2976 int micro_chunk_size = 0;
2977 int conf_type = getFile8Bit(file);
2978 int byte_mask = conf_type & CONF_MASK_BYTES;
2979 boolean element_found = FALSE;
2982 micro_chunk_size += 1;
2984 if (byte_mask == CONF_MASK_MULTI_BYTES)
2986 int num_bytes = getFile16BitBE(file);
2987 byte *buffer = checked_malloc(num_bytes);
2989 ReadBytesFromFile(file, buffer, num_bytes);
2991 for (i = 0; conf[i].data_type != -1; i++)
2993 if (conf[i].element == element &&
2994 conf[i].conf_type == conf_type)
2996 int data_type = conf[i].data_type;
2997 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2998 int max_num_entities = conf[i].max_num_entities;
3000 if (num_entities > max_num_entities)
3003 "truncating number of entities for element %d from %d to %d",
3004 element, num_entities, max_num_entities);
3006 num_entities = max_num_entities;
3009 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3010 data_type == TYPE_CONTENT_LIST))
3012 /* for element and content lists, zero entities are not allowed */
3013 Error(ERR_WARN, "found empty list of entities for element %d",
3016 /* do not set "num_entities" here to prevent reading behind buffer */
3018 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3022 *(int *)(conf[i].num_entities) = num_entities;
3025 element_found = TRUE;
3027 if (data_type == TYPE_STRING)
3029 char *string = (char *)(conf[i].value);
3032 for (j = 0; j < max_num_entities; j++)
3033 string[j] = (j < num_entities ? buffer[j] : '\0');
3035 else if (data_type == TYPE_ELEMENT_LIST)
3037 int *element_array = (int *)(conf[i].value);
3040 for (j = 0; j < num_entities; j++)
3042 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3044 else if (data_type == TYPE_CONTENT_LIST)
3046 struct Content *content= (struct Content *)(conf[i].value);
3049 for (c = 0; c < num_entities; c++)
3050 for (y = 0; y < 3; y++)
3051 for (x = 0; x < 3; x++)
3052 content[c].e[x][y] =
3053 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3056 element_found = FALSE;
3062 checked_free(buffer);
3064 micro_chunk_size += 2 + num_bytes;
3066 else /* constant size configuration data (1, 2 or 4 bytes) */
3068 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3069 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3070 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3072 for (i = 0; conf[i].data_type != -1; i++)
3074 if (conf[i].element == element &&
3075 conf[i].conf_type == conf_type)
3077 int data_type = conf[i].data_type;
3079 if (data_type == TYPE_ELEMENT)
3080 value = getMappedElement(value);
3082 if (data_type == TYPE_BOOLEAN)
3083 *(boolean *)(conf[i].value) = value;
3085 *(int *) (conf[i].value) = value;
3087 element_found = TRUE;
3093 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3098 char *error_conf_chunk_bytes =
3099 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3100 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3101 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3102 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3103 int error_element = real_element;
3105 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3106 error_conf_chunk_bytes, error_conf_chunk_token,
3107 error_element, EL_NAME(error_element));
3110 return micro_chunk_size;
3113 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3115 int real_chunk_size = 0;
3117 li = *level; /* copy level data into temporary buffer */
3119 while (!checkEndOfFile(file))
3121 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3123 if (real_chunk_size >= chunk_size)
3127 *level = li; /* copy temporary buffer back to level data */
3129 return real_chunk_size;
3132 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3134 int real_chunk_size = 0;
3136 li = *level; /* copy level data into temporary buffer */
3138 while (!checkEndOfFile(file))
3140 int element = getMappedElement(getFile16BitBE(file));
3142 real_chunk_size += 2;
3143 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3145 if (real_chunk_size >= chunk_size)
3149 *level = li; /* copy temporary buffer back to level data */
3151 return real_chunk_size;
3154 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3156 int real_chunk_size = 0;
3158 li = *level; /* copy level data into temporary buffer */
3160 while (!checkEndOfFile(file))
3162 int element = getMappedElement(getFile16BitBE(file));
3164 real_chunk_size += 2;
3165 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3167 if (real_chunk_size >= chunk_size)
3171 *level = li; /* copy temporary buffer back to level data */
3173 return real_chunk_size;
3176 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3178 int element = getMappedElement(getFile16BitBE(file));
3179 int envelope_nr = element - EL_ENVELOPE_1;
3180 int real_chunk_size = 2;
3182 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3184 while (!checkEndOfFile(file))
3186 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3189 if (real_chunk_size >= chunk_size)
3193 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3195 return real_chunk_size;
3198 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3200 int element = getMappedElement(getFile16BitBE(file));
3201 int real_chunk_size = 2;
3202 struct ElementInfo *ei = &element_info[element];
3205 xx_ei = *ei; /* copy element data into temporary buffer */
3207 xx_ei.num_change_pages = -1;
3209 while (!checkEndOfFile(file))
3211 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3213 if (xx_ei.num_change_pages != -1)
3216 if (real_chunk_size >= chunk_size)
3222 if (ei->num_change_pages == -1)
3224 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3227 ei->num_change_pages = 1;
3229 setElementChangePages(ei, 1);
3230 setElementChangeInfoToDefaults(ei->change);
3232 return real_chunk_size;
3235 /* initialize number of change pages stored for this custom element */
3236 setElementChangePages(ei, ei->num_change_pages);
3237 for (i = 0; i < ei->num_change_pages; i++)
3238 setElementChangeInfoToDefaults(&ei->change_page[i]);
3240 /* start with reading properties for the first change page */
3241 xx_current_change_page = 0;
3243 while (!checkEndOfFile(file))
3245 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3247 xx_change = *change; /* copy change data into temporary buffer */
3249 resetEventBits(); /* reset bits; change page might have changed */
3251 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3254 *change = xx_change;
3256 setEventFlagsFromEventBits(change);
3258 if (real_chunk_size >= chunk_size)
3262 level->file_has_custom_elements = TRUE;
3264 return real_chunk_size;
3267 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3269 int element = getMappedElement(getFile16BitBE(file));
3270 int real_chunk_size = 2;
3271 struct ElementInfo *ei = &element_info[element];
3272 struct ElementGroupInfo *group = ei->group;
3274 xx_ei = *ei; /* copy element data into temporary buffer */
3275 xx_group = *group; /* copy group data into temporary buffer */
3277 while (!checkEndOfFile(file))
3279 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3282 if (real_chunk_size >= chunk_size)
3289 level->file_has_custom_elements = TRUE;
3291 return real_chunk_size;
3294 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3295 struct LevelFileInfo *level_file_info,
3296 boolean level_info_only)
3298 char *filename = level_file_info->filename;
3299 char cookie[MAX_LINE_LEN];
3300 char chunk_name[CHUNK_ID_LEN + 1];
3304 if (!(file = openFile(filename, MODE_READ)))
3306 level->no_valid_file = TRUE;
3307 level->no_level_file = TRUE;
3309 if (level_info_only)
3312 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3314 if (!setup.editor.use_template_for_new_levels)
3317 /* if level file not found, try to initialize level data from template */
3318 filename = getGlobalLevelTemplateFilename();
3320 if (!(file = openFile(filename, MODE_READ)))
3323 /* default: for empty levels, use level template for custom elements */
3324 level->use_custom_template = TRUE;
3326 level->no_valid_file = FALSE;
3329 getFileChunkBE(file, chunk_name, NULL);
3330 if (strEqual(chunk_name, "RND1"))
3332 getFile32BitBE(file); /* not used */
3334 getFileChunkBE(file, chunk_name, NULL);
3335 if (!strEqual(chunk_name, "CAVE"))
3337 level->no_valid_file = TRUE;
3339 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3346 else /* check for pre-2.0 file format with cookie string */
3348 strcpy(cookie, chunk_name);
3349 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3351 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3352 cookie[strlen(cookie) - 1] = '\0';
3354 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3356 level->no_valid_file = TRUE;
3358 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3365 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3367 level->no_valid_file = TRUE;
3369 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3376 /* pre-2.0 level files have no game version, so use file version here */
3377 level->game_version = level->file_version;
3380 if (level->file_version < FILE_VERSION_1_2)
3382 /* level files from versions before 1.2.0 without chunk structure */
3383 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3384 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3392 int (*loader)(File *, int, struct LevelInfo *);
3396 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3397 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3398 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3399 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3400 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3401 { "INFO", -1, LoadLevel_INFO },
3402 { "BODY", -1, LoadLevel_BODY },
3403 { "CONT", -1, LoadLevel_CONT },
3404 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3405 { "CNT3", -1, LoadLevel_CNT3 },
3406 { "CUS1", -1, LoadLevel_CUS1 },
3407 { "CUS2", -1, LoadLevel_CUS2 },
3408 { "CUS3", -1, LoadLevel_CUS3 },
3409 { "CUS4", -1, LoadLevel_CUS4 },
3410 { "GRP1", -1, LoadLevel_GRP1 },
3411 { "CONF", -1, LoadLevel_CONF },
3412 { "ELEM", -1, LoadLevel_ELEM },
3413 { "NOTE", -1, LoadLevel_NOTE },
3414 { "CUSX", -1, LoadLevel_CUSX },
3415 { "GRPX", -1, LoadLevel_GRPX },
3420 while (getFileChunkBE(file, chunk_name, &chunk_size))
3424 while (chunk_info[i].name != NULL &&
3425 !strEqual(chunk_name, chunk_info[i].name))
3428 if (chunk_info[i].name == NULL)
3430 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3431 chunk_name, filename);
3432 ReadUnusedBytesFromFile(file, chunk_size);
3434 else if (chunk_info[i].size != -1 &&
3435 chunk_info[i].size != chunk_size)
3437 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3438 chunk_size, chunk_name, filename);
3439 ReadUnusedBytesFromFile(file, chunk_size);
3443 /* call function to load this level chunk */
3444 int chunk_size_expected =
3445 (chunk_info[i].loader)(file, chunk_size, level);
3447 /* the size of some chunks cannot be checked before reading other
3448 chunks first (like "HEAD" and "BODY") that contain some header
3449 information, so check them here */
3450 if (chunk_size_expected != chunk_size)
3452 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3453 chunk_size, chunk_name, filename);
3463 /* ------------------------------------------------------------------------- */
3464 /* functions for loading EM level */
3465 /* ------------------------------------------------------------------------- */
3467 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3469 static int ball_xy[8][2] =
3480 struct LevelInfo_EM *level_em = level->native_em_level;
3481 struct LEVEL *lev = level_em->lev;
3482 struct PLAYER **ply = level_em->ply;
3485 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3486 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3488 lev->time_seconds = level->time;
3489 lev->required_initial = level->gems_needed;
3491 lev->emerald_score = level->score[SC_EMERALD];
3492 lev->diamond_score = level->score[SC_DIAMOND];
3493 lev->alien_score = level->score[SC_ROBOT];
3494 lev->tank_score = level->score[SC_SPACESHIP];
3495 lev->bug_score = level->score[SC_BUG];
3496 lev->eater_score = level->score[SC_YAMYAM];
3497 lev->nut_score = level->score[SC_NUT];
3498 lev->dynamite_score = level->score[SC_DYNAMITE];
3499 lev->key_score = level->score[SC_KEY];
3500 lev->exit_score = level->score[SC_TIME_BONUS];
3502 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3503 for (y = 0; y < 3; y++)
3504 for (x = 0; x < 3; x++)
3505 lev->eater_array[i][y * 3 + x] =
3506 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3508 lev->amoeba_time = level->amoeba_speed;
3509 lev->wonderwall_time_initial = level->time_magic_wall;
3510 lev->wheel_time = level->time_wheel;
3512 lev->android_move_time = level->android_move_time;
3513 lev->android_clone_time = level->android_clone_time;
3514 lev->ball_random = level->ball_random;
3515 lev->ball_state_initial = level->ball_state_initial;
3516 lev->ball_time = level->ball_time;
3517 lev->num_ball_arrays = level->num_ball_contents;
3519 lev->lenses_score = level->lenses_score;
3520 lev->magnify_score = level->magnify_score;
3521 lev->slurp_score = level->slurp_score;
3523 lev->lenses_time = level->lenses_time;
3524 lev->magnify_time = level->magnify_time;
3526 lev->wind_direction_initial =
3527 map_direction_RND_to_EM(level->wind_direction_initial);
3528 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3529 lev->wind_time : 0);
3531 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3532 for (j = 0; j < 8; j++)
3533 lev->ball_array[i][j] =
3534 map_element_RND_to_EM(level->
3535 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3537 map_android_clone_elements_RND_to_EM(level);
3539 /* first fill the complete playfield with the default border element */
3540 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3541 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3542 level_em->cave[x][y] = ZBORDER;
3544 if (BorderElement == EL_STEELWALL)
3546 for (y = 0; y < lev->height + 2; y++)
3547 for (x = 0; x < lev->width + 2; x++)
3548 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3551 /* then copy the real level contents from level file into the playfield */
3552 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3554 int new_element = map_element_RND_to_EM(level->field[x][y]);
3555 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3556 int xx = x + 1 + offset;
3557 int yy = y + 1 + offset;
3559 if (level->field[x][y] == EL_AMOEBA_DEAD)
3560 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3562 level_em->cave[xx][yy] = new_element;
3565 for (i = 0; i < MAX_PLAYERS; i++)
3567 ply[i]->x_initial = 0;
3568 ply[i]->y_initial = 0;
3571 /* initialize player positions and delete players from the playfield */
3572 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3574 if (ELEM_IS_PLAYER(level->field[x][y]))
3576 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3577 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3578 int xx = x + 1 + offset;
3579 int yy = y + 1 + offset;
3581 ply[player_nr]->x_initial = xx;
3582 ply[player_nr]->y_initial = yy;
3584 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3588 if (BorderElement == EL_STEELWALL)
3595 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3597 static int ball_xy[8][2] =
3608 struct LevelInfo_EM *level_em = level->native_em_level;
3609 struct LEVEL *lev = level_em->lev;
3610 struct PLAYER **ply = level_em->ply;
3613 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3614 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3616 level->time = lev->time_seconds;
3617 level->gems_needed = lev->required_initial;
3619 sprintf(level->name, "Level %d", level->file_info.nr);
3621 level->score[SC_EMERALD] = lev->emerald_score;
3622 level->score[SC_DIAMOND] = lev->diamond_score;
3623 level->score[SC_ROBOT] = lev->alien_score;
3624 level->score[SC_SPACESHIP] = lev->tank_score;
3625 level->score[SC_BUG] = lev->bug_score;
3626 level->score[SC_YAMYAM] = lev->eater_score;
3627 level->score[SC_NUT] = lev->nut_score;
3628 level->score[SC_DYNAMITE] = lev->dynamite_score;
3629 level->score[SC_KEY] = lev->key_score;
3630 level->score[SC_TIME_BONUS] = lev->exit_score;
3632 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3634 for (i = 0; i < level->num_yamyam_contents; i++)
3635 for (y = 0; y < 3; y++)
3636 for (x = 0; x < 3; x++)
3637 level->yamyam_content[i].e[x][y] =
3638 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3640 level->amoeba_speed = lev->amoeba_time;
3641 level->time_magic_wall = lev->wonderwall_time_initial;
3642 level->time_wheel = lev->wheel_time;
3644 level->android_move_time = lev->android_move_time;
3645 level->android_clone_time = lev->android_clone_time;
3646 level->ball_random = lev->ball_random;
3647 level->ball_state_initial = lev->ball_state_initial;
3648 level->ball_time = lev->ball_time;
3649 level->num_ball_contents = lev->num_ball_arrays;
3651 level->lenses_score = lev->lenses_score;
3652 level->magnify_score = lev->magnify_score;
3653 level->slurp_score = lev->slurp_score;
3655 level->lenses_time = lev->lenses_time;
3656 level->magnify_time = lev->magnify_time;
3658 level->wind_direction_initial =
3659 map_direction_EM_to_RND(lev->wind_direction_initial);
3661 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3662 for (j = 0; j < 8; j++)
3663 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3664 map_element_EM_to_RND(lev->ball_array[i][j]);
3666 map_android_clone_elements_EM_to_RND(level);
3668 /* convert the playfield (some elements need special treatment) */
3669 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3671 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3673 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3674 new_element = EL_AMOEBA_DEAD;
3676 level->field[x][y] = new_element;
3679 for (i = 0; i < MAX_PLAYERS; i++)
3681 /* in case of all players set to the same field, use the first player */
3682 int nr = MAX_PLAYERS - i - 1;
3683 int jx = ply[nr]->x_initial - 1;
3684 int jy = ply[nr]->y_initial - 1;
3686 if (jx != -1 && jy != -1)
3687 level->field[jx][jy] = EL_PLAYER_1 + nr;
3692 /* ------------------------------------------------------------------------- */
3693 /* functions for loading SP level */
3694 /* ------------------------------------------------------------------------- */
3696 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3698 struct LevelInfo_SP *level_sp = level->native_sp_level;
3699 LevelInfoType *header = &level_sp->header;
3702 level_sp->width = level->fieldx;
3703 level_sp->height = level->fieldy;
3705 for (x = 0; x < level->fieldx; x++)
3706 for (y = 0; y < level->fieldy; y++)
3707 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3709 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3711 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3712 header->LevelTitle[i] = level->name[i];
3713 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3715 header->InfotronsNeeded = level->gems_needed;
3717 header->SpecialPortCount = 0;
3719 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3721 boolean gravity_port_found = FALSE;
3722 boolean gravity_port_valid = FALSE;
3723 int gravity_port_flag;
3724 int gravity_port_base_element;
3725 int element = level->field[x][y];
3727 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3728 element <= EL_SP_GRAVITY_ON_PORT_UP)
3730 gravity_port_found = TRUE;
3731 gravity_port_valid = TRUE;
3732 gravity_port_flag = 1;
3733 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3735 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3736 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3738 gravity_port_found = TRUE;
3739 gravity_port_valid = TRUE;
3740 gravity_port_flag = 0;
3741 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3743 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3744 element <= EL_SP_GRAVITY_PORT_UP)
3746 /* change R'n'D style gravity inverting special port to normal port
3747 (there are no gravity inverting ports in native Supaplex engine) */
3749 gravity_port_found = TRUE;
3750 gravity_port_valid = FALSE;
3751 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3754 if (gravity_port_found)
3756 if (gravity_port_valid &&
3757 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3759 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3761 port->PortLocation = (y * level->fieldx + x) * 2;
3762 port->Gravity = gravity_port_flag;
3764 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3766 header->SpecialPortCount++;
3770 /* change special gravity port to normal port */
3772 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3775 level_sp->playfield[x][y] = element - EL_SP_START;
3780 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3782 struct LevelInfo_SP *level_sp = level->native_sp_level;
3783 LevelInfoType *header = &level_sp->header;
3784 boolean num_invalid_elements = 0;
3787 level->fieldx = level_sp->width;
3788 level->fieldy = level_sp->height;
3790 for (x = 0; x < level->fieldx; x++)
3792 for (y = 0; y < level->fieldy; y++)
3794 int element_old = level_sp->playfield[x][y];
3795 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3797 if (element_new == EL_UNKNOWN)
3799 num_invalid_elements++;
3801 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3805 level->field[x][y] = element_new;
3809 if (num_invalid_elements > 0)
3810 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3811 (!options.debug ? " (use '--debug' for more details)" : ""));
3813 for (i = 0; i < MAX_PLAYERS; i++)
3814 level->initial_player_gravity[i] =
3815 (header->InitialGravity == 1 ? TRUE : FALSE);
3817 /* skip leading spaces */
3818 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3819 if (header->LevelTitle[i] != ' ')
3822 /* copy level title */
3823 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3824 level->name[j] = header->LevelTitle[i];
3825 level->name[j] = '\0';
3827 /* cut trailing spaces */
3829 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3830 level->name[j - 1] = '\0';
3832 level->gems_needed = header->InfotronsNeeded;
3834 for (i = 0; i < header->SpecialPortCount; i++)
3836 SpecialPortType *port = &header->SpecialPort[i];
3837 int port_location = port->PortLocation;
3838 int gravity = port->Gravity;
3839 int port_x, port_y, port_element;
3841 port_x = (port_location / 2) % level->fieldx;
3842 port_y = (port_location / 2) / level->fieldx;
3844 if (port_x < 0 || port_x >= level->fieldx ||
3845 port_y < 0 || port_y >= level->fieldy)
3847 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3853 port_element = level->field[port_x][port_y];
3855 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3856 port_element > EL_SP_GRAVITY_PORT_UP)
3858 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3863 /* change previous (wrong) gravity inverting special port to either
3864 gravity enabling special port or gravity disabling special port */
3865 level->field[port_x][port_y] +=
3866 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3867 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3870 /* change special gravity ports without database entries to normal ports */
3871 for (x = 0; x < level->fieldx; x++)
3872 for (y = 0; y < level->fieldy; y++)
3873 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3874 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3875 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3877 level->time = 0; /* no time limit */
3878 level->amoeba_speed = 0;
3879 level->time_magic_wall = 0;
3880 level->time_wheel = 0;
3881 level->amoeba_content = EL_EMPTY;
3884 /* original Supaplex does not use score values -- use default values */
3886 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3887 level->score[i] = 0;
3890 /* there are no yamyams in supaplex levels */
3891 for (i = 0; i < level->num_yamyam_contents; i++)
3892 for (x = 0; x < 3; x++)
3893 for (y = 0; y < 3; y++)
3894 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3897 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3899 struct LevelInfo_SP *level_sp = level->native_sp_level;
3900 struct DemoInfo_SP *demo = &level_sp->demo;
3903 /* always start with reliable default values */
3904 demo->is_available = FALSE;
3907 if (TAPE_IS_EMPTY(tape))
3910 demo->level_nr = tape.level_nr; /* (currently not used) */
3912 level_sp->header.DemoRandomSeed = tape.random_seed;
3916 for (i = 0; i < tape.length; i++)
3918 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3919 int demo_repeat = tape.pos[i].delay;
3920 int demo_entries = (demo_repeat + 15) / 16;
3922 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3924 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3930 for (j = 0; j < demo_repeat / 16; j++)
3931 demo->data[demo->length++] = 0xf0 | demo_action;
3933 if (demo_repeat % 16)
3934 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3937 demo->is_available = TRUE;
3940 static void setTapeInfoToDefaults();
3942 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3944 struct LevelInfo_SP *level_sp = level->native_sp_level;
3945 struct DemoInfo_SP *demo = &level_sp->demo;
3946 char *filename = level->file_info.filename;
3949 /* always start with reliable default values */
3950 setTapeInfoToDefaults();
3952 if (!demo->is_available)
3955 tape.level_nr = demo->level_nr; /* (currently not used) */
3956 tape.random_seed = level_sp->header.DemoRandomSeed;
3958 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3961 tape.pos[tape.counter].delay = 0;
3963 for (i = 0; i < demo->length; i++)
3965 int demo_action = demo->data[i] & 0x0f;
3966 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3967 int tape_action = map_key_SP_to_RND(demo_action);
3968 int tape_repeat = demo_repeat + 1;
3969 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3970 boolean success = 0;
3973 for (j = 0; j < tape_repeat; j++)
3974 success = TapeAddAction(action);
3978 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3985 TapeHaltRecording();
3989 /* ------------------------------------------------------------------------- */
3990 /* functions for loading MM level */
3991 /* ------------------------------------------------------------------------- */
3993 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3995 struct LevelInfo_MM *level_mm = level->native_mm_level;
3998 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3999 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4001 level_mm->time = level->time;
4002 level_mm->kettles_needed = level->gems_needed;
4003 level_mm->auto_count_kettles = level->auto_count_gems;
4005 level_mm->laser_red = level->mm_laser_red;
4006 level_mm->laser_green = level->mm_laser_green;
4007 level_mm->laser_blue = level->mm_laser_blue;
4009 strcpy(level_mm->name, level->name);
4010 strcpy(level_mm->author, level->author);
4012 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4013 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4014 level_mm->score[SC_KEY] = level->score[SC_KEY];
4015 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4016 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4018 level_mm->amoeba_speed = level->amoeba_speed;
4019 level_mm->time_fuse = level->mm_time_fuse;
4020 level_mm->time_bomb = level->mm_time_bomb;
4021 level_mm->time_ball = level->mm_time_ball;
4022 level_mm->time_block = level->mm_time_block;
4024 for (x = 0; x < level->fieldx; x++)
4025 for (y = 0; y < level->fieldy; y++)
4027 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4030 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4032 struct LevelInfo_MM *level_mm = level->native_mm_level;
4035 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4036 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4038 level->time = level_mm->time;
4039 level->gems_needed = level_mm->kettles_needed;
4040 level->auto_count_gems = level_mm->auto_count_kettles;
4042 level->mm_laser_red = level_mm->laser_red;
4043 level->mm_laser_green = level_mm->laser_green;
4044 level->mm_laser_blue = level_mm->laser_blue;
4046 strcpy(level->name, level_mm->name);
4048 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4049 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4050 strcpy(level->author, level_mm->author);
4052 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4053 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4054 level->score[SC_KEY] = level_mm->score[SC_KEY];
4055 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4056 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4058 level->amoeba_speed = level_mm->amoeba_speed;
4059 level->mm_time_fuse = level_mm->time_fuse;
4060 level->mm_time_bomb = level_mm->time_bomb;
4061 level->mm_time_ball = level_mm->time_ball;
4062 level->mm_time_block = level_mm->time_block;
4064 for (x = 0; x < level->fieldx; x++)
4065 for (y = 0; y < level->fieldy; y++)
4066 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4070 /* ------------------------------------------------------------------------- */
4071 /* functions for loading DC level */
4072 /* ------------------------------------------------------------------------- */
4074 #define DC_LEVEL_HEADER_SIZE 344
4076 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4078 static int last_data_encoded;
4082 int diff_hi, diff_lo;
4083 int data_hi, data_lo;
4084 unsigned short data_decoded;
4088 last_data_encoded = 0;
4095 diff = data_encoded - last_data_encoded;
4096 diff_hi = diff & ~0xff;
4097 diff_lo = diff & 0xff;
4101 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4102 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4103 data_hi = data_hi & 0xff00;
4105 data_decoded = data_hi | data_lo;
4107 last_data_encoded = data_encoded;
4109 offset1 = (offset1 + 1) % 31;
4110 offset2 = offset2 & 0xff;
4112 return data_decoded;
4115 int getMappedElement_DC(int element)
4123 /* 0x0117 - 0x036e: (?) */
4126 /* 0x042d - 0x0684: (?) */
4142 element = EL_CRYSTAL;
4145 case 0x0e77: /* quicksand (boulder) */
4146 element = EL_QUICKSAND_FAST_FULL;
4149 case 0x0e99: /* slow quicksand (boulder) */
4150 element = EL_QUICKSAND_FULL;
4154 element = EL_EM_EXIT_OPEN;
4158 element = EL_EM_EXIT_CLOSED;
4162 element = EL_EM_STEEL_EXIT_OPEN;
4166 element = EL_EM_STEEL_EXIT_CLOSED;
4169 case 0x0f4f: /* dynamite (lit 1) */
4170 element = EL_EM_DYNAMITE_ACTIVE;
4173 case 0x0f57: /* dynamite (lit 2) */
4174 element = EL_EM_DYNAMITE_ACTIVE;
4177 case 0x0f5f: /* dynamite (lit 3) */
4178 element = EL_EM_DYNAMITE_ACTIVE;
4181 case 0x0f67: /* dynamite (lit 4) */
4182 element = EL_EM_DYNAMITE_ACTIVE;
4189 element = EL_AMOEBA_WET;
4193 element = EL_AMOEBA_DROP;
4197 element = EL_DC_MAGIC_WALL;
4201 element = EL_SPACESHIP_UP;
4205 element = EL_SPACESHIP_DOWN;
4209 element = EL_SPACESHIP_LEFT;
4213 element = EL_SPACESHIP_RIGHT;
4217 element = EL_BUG_UP;
4221 element = EL_BUG_DOWN;
4225 element = EL_BUG_LEFT;
4229 element = EL_BUG_RIGHT;
4233 element = EL_MOLE_UP;
4237 element = EL_MOLE_DOWN;
4241 element = EL_MOLE_LEFT;
4245 element = EL_MOLE_RIGHT;
4253 element = EL_YAMYAM;
4257 element = EL_SWITCHGATE_OPEN;
4261 element = EL_SWITCHGATE_CLOSED;
4265 element = EL_DC_SWITCHGATE_SWITCH_UP;
4269 element = EL_TIMEGATE_CLOSED;
4272 case 0x144c: /* conveyor belt switch (green) */
4273 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4276 case 0x144f: /* conveyor belt switch (red) */
4277 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4280 case 0x1452: /* conveyor belt switch (blue) */
4281 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4285 element = EL_CONVEYOR_BELT_3_MIDDLE;
4289 element = EL_CONVEYOR_BELT_3_LEFT;
4293 element = EL_CONVEYOR_BELT_3_RIGHT;
4297 element = EL_CONVEYOR_BELT_1_MIDDLE;
4301 element = EL_CONVEYOR_BELT_1_LEFT;
4305 element = EL_CONVEYOR_BELT_1_RIGHT;
4309 element = EL_CONVEYOR_BELT_4_MIDDLE;
4313 element = EL_CONVEYOR_BELT_4_LEFT;
4317 element = EL_CONVEYOR_BELT_4_RIGHT;
4321 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4325 element = EL_EXPANDABLE_WALL_VERTICAL;
4329 element = EL_EXPANDABLE_WALL_ANY;
4332 case 0x14ce: /* growing steel wall (left/right) */
4333 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4336 case 0x14df: /* growing steel wall (up/down) */
4337 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4340 case 0x14e8: /* growing steel wall (up/down/left/right) */
4341 element = EL_EXPANDABLE_STEELWALL_ANY;
4345 element = EL_SHIELD_DEADLY;
4349 element = EL_EXTRA_TIME;
4357 element = EL_EMPTY_SPACE;
4360 case 0x1578: /* quicksand (empty) */
4361 element = EL_QUICKSAND_FAST_EMPTY;
4364 case 0x1579: /* slow quicksand (empty) */
4365 element = EL_QUICKSAND_EMPTY;
4368 /* 0x157c - 0x158b: */
4371 /* 0x1590 - 0x159f: */
4372 /* EL_DC_LANDMINE */
4375 element = EL_EM_DYNAMITE;
4378 case 0x15a1: /* key (red) */
4379 element = EL_EM_KEY_1;
4382 case 0x15a2: /* key (yellow) */
4383 element = EL_EM_KEY_2;
4386 case 0x15a3: /* key (blue) */
4387 element = EL_EM_KEY_4;
4390 case 0x15a4: /* key (green) */
4391 element = EL_EM_KEY_3;
4394 case 0x15a5: /* key (white) */
4395 element = EL_DC_KEY_WHITE;
4399 element = EL_WALL_SLIPPERY;
4406 case 0x15a8: /* wall (not round) */
4410 case 0x15a9: /* (blue) */
4411 element = EL_CHAR_A;
4414 case 0x15aa: /* (blue) */
4415 element = EL_CHAR_B;
4418 case 0x15ab: /* (blue) */
4419 element = EL_CHAR_C;
4422 case 0x15ac: /* (blue) */
4423 element = EL_CHAR_D;
4426 case 0x15ad: /* (blue) */
4427 element = EL_CHAR_E;
4430 case 0x15ae: /* (blue) */
4431 element = EL_CHAR_F;
4434 case 0x15af: /* (blue) */
4435 element = EL_CHAR_G;
4438 case 0x15b0: /* (blue) */
4439 element = EL_CHAR_H;
4442 case 0x15b1: /* (blue) */
4443 element = EL_CHAR_I;
4446 case 0x15b2: /* (blue) */
4447 element = EL_CHAR_J;
4450 case 0x15b3: /* (blue) */
4451 element = EL_CHAR_K;
4454 case 0x15b4: /* (blue) */
4455 element = EL_CHAR_L;
4458 case 0x15b5: /* (blue) */
4459 element = EL_CHAR_M;
4462 case 0x15b6: /* (blue) */
4463 element = EL_CHAR_N;
4466 case 0x15b7: /* (blue) */
4467 element = EL_CHAR_O;
4470 case 0x15b8: /* (blue) */
4471 element = EL_CHAR_P;
4474 case 0x15b9: /* (blue) */
4475 element = EL_CHAR_Q;
4478 case 0x15ba: /* (blue) */
4479 element = EL_CHAR_R;
4482 case 0x15bb: /* (blue) */
4483 element = EL_CHAR_S;
4486 case 0x15bc: /* (blue) */
4487 element = EL_CHAR_T;
4490 case 0x15bd: /* (blue) */
4491 element = EL_CHAR_U;
4494 case 0x15be: /* (blue) */
4495 element = EL_CHAR_V;
4498 case 0x15bf: /* (blue) */
4499 element = EL_CHAR_W;
4502 case 0x15c0: /* (blue) */
4503 element = EL_CHAR_X;
4506 case 0x15c1: /* (blue) */
4507 element = EL_CHAR_Y;
4510 case 0x15c2: /* (blue) */
4511 element = EL_CHAR_Z;
4514 case 0x15c3: /* (blue) */
4515 element = EL_CHAR_AUMLAUT;
4518 case 0x15c4: /* (blue) */
4519 element = EL_CHAR_OUMLAUT;
4522 case 0x15c5: /* (blue) */
4523 element = EL_CHAR_UUMLAUT;
4526 case 0x15c6: /* (blue) */
4527 element = EL_CHAR_0;
4530 case 0x15c7: /* (blue) */
4531 element = EL_CHAR_1;
4534 case 0x15c8: /* (blue) */
4535 element = EL_CHAR_2;
4538 case 0x15c9: /* (blue) */
4539 element = EL_CHAR_3;
4542 case 0x15ca: /* (blue) */
4543 element = EL_CHAR_4;
4546 case 0x15cb: /* (blue) */
4547 element = EL_CHAR_5;
4550 case 0x15cc: /* (blue) */
4551 element = EL_CHAR_6;
4554 case 0x15cd: /* (blue) */
4555 element = EL_CHAR_7;
4558 case 0x15ce: /* (blue) */
4559 element = EL_CHAR_8;
4562 case 0x15cf: /* (blue) */
4563 element = EL_CHAR_9;
4566 case 0x15d0: /* (blue) */
4567 element = EL_CHAR_PERIOD;
4570 case 0x15d1: /* (blue) */
4571 element = EL_CHAR_EXCLAM;
4574 case 0x15d2: /* (blue) */
4575 element = EL_CHAR_COLON;
4578 case 0x15d3: /* (blue) */
4579 element = EL_CHAR_LESS;
4582 case 0x15d4: /* (blue) */
4583 element = EL_CHAR_GREATER;
4586 case 0x15d5: /* (blue) */
4587 element = EL_CHAR_QUESTION;
4590 case 0x15d6: /* (blue) */
4591 element = EL_CHAR_COPYRIGHT;
4594 case 0x15d7: /* (blue) */
4595 element = EL_CHAR_UP;
4598 case 0x15d8: /* (blue) */
4599 element = EL_CHAR_DOWN;
4602 case 0x15d9: /* (blue) */
4603 element = EL_CHAR_BUTTON;
4606 case 0x15da: /* (blue) */
4607 element = EL_CHAR_PLUS;
4610 case 0x15db: /* (blue) */
4611 element = EL_CHAR_MINUS;
4614 case 0x15dc: /* (blue) */
4615 element = EL_CHAR_APOSTROPHE;
4618 case 0x15dd: /* (blue) */
4619 element = EL_CHAR_PARENLEFT;
4622 case 0x15de: /* (blue) */
4623 element = EL_CHAR_PARENRIGHT;
4626 case 0x15df: /* (green) */
4627 element = EL_CHAR_A;
4630 case 0x15e0: /* (green) */
4631 element = EL_CHAR_B;
4634 case 0x15e1: /* (green) */
4635 element = EL_CHAR_C;
4638 case 0x15e2: /* (green) */
4639 element = EL_CHAR_D;
4642 case 0x15e3: /* (green) */
4643 element = EL_CHAR_E;
4646 case 0x15e4: /* (green) */
4647 element = EL_CHAR_F;
4650 case 0x15e5: /* (green) */
4651 element = EL_CHAR_G;
4654 case 0x15e6: /* (green) */
4655 element = EL_CHAR_H;
4658 case 0x15e7: /* (green) */
4659 element = EL_CHAR_I;
4662 case 0x15e8: /* (green) */
4663 element = EL_CHAR_J;
4666 case 0x15e9: /* (green) */
4667 element = EL_CHAR_K;
4670 case 0x15ea: /* (green) */
4671 element = EL_CHAR_L;
4674 case 0x15eb: /* (green) */
4675 element = EL_CHAR_M;
4678 case 0x15ec: /* (green) */
4679 element = EL_CHAR_N;
4682 case 0x15ed: /* (green) */
4683 element = EL_CHAR_O;
4686 case 0x15ee: /* (green) */
4687 element = EL_CHAR_P;
4690 case 0x15ef: /* (green) */
4691 element = EL_CHAR_Q;
4694 case 0x15f0: /* (green) */
4695 element = EL_CHAR_R;
4698 case 0x15f1: /* (green) */
4699 element = EL_CHAR_S;
4702 case 0x15f2: /* (green) */
4703 element = EL_CHAR_T;
4706 case 0x15f3: /* (green) */
4707 element = EL_CHAR_U;
4710 case 0x15f4: /* (green) */
4711 element = EL_CHAR_V;
4714 case 0x15f5: /* (green) */
4715 element = EL_CHAR_W;
4718 case 0x15f6: /* (green) */
4719 element = EL_CHAR_X;
4722 case 0x15f7: /* (green) */
4723 element = EL_CHAR_Y;
4726 case 0x15f8: /* (green) */
4727 element = EL_CHAR_Z;
4730 case 0x15f9: /* (green) */
4731 element = EL_CHAR_AUMLAUT;
4734 case 0x15fa: /* (green) */
4735 element = EL_CHAR_OUMLAUT;
4738 case 0x15fb: /* (green) */
4739 element = EL_CHAR_UUMLAUT;
4742 case 0x15fc: /* (green) */
4743 element = EL_CHAR_0;
4746 case 0x15fd: /* (green) */
4747 element = EL_CHAR_1;
4750 case 0x15fe: /* (green) */
4751 element = EL_CHAR_2;
4754 case 0x15ff: /* (green) */
4755 element = EL_CHAR_3;
4758 case 0x1600: /* (green) */
4759 element = EL_CHAR_4;
4762 case 0x1601: /* (green) */
4763 element = EL_CHAR_5;
4766 case 0x1602: /* (green) */
4767 element = EL_CHAR_6;
4770 case 0x1603: /* (green) */
4771 element = EL_CHAR_7;
4774 case 0x1604: /* (green) */
4775 element = EL_CHAR_8;
4778 case 0x1605: /* (green) */
4779 element = EL_CHAR_9;
4782 case 0x1606: /* (green) */
4783 element = EL_CHAR_PERIOD;
4786 case 0x1607: /* (green) */
4787 element = EL_CHAR_EXCLAM;
4790 case 0x1608: /* (green) */
4791 element = EL_CHAR_COLON;
4794 case 0x1609: /* (green) */
4795 element = EL_CHAR_LESS;
4798 case 0x160a: /* (green) */
4799 element = EL_CHAR_GREATER;
4802 case 0x160b: /* (green) */
4803 element = EL_CHAR_QUESTION;
4806 case 0x160c: /* (green) */
4807 element = EL_CHAR_COPYRIGHT;
4810 case 0x160d: /* (green) */
4811 element = EL_CHAR_UP;
4814 case 0x160e: /* (green) */
4815 element = EL_CHAR_DOWN;
4818 case 0x160f: /* (green) */
4819 element = EL_CHAR_BUTTON;
4822 case 0x1610: /* (green) */
4823 element = EL_CHAR_PLUS;
4826 case 0x1611: /* (green) */
4827 element = EL_CHAR_MINUS;
4830 case 0x1612: /* (green) */
4831 element = EL_CHAR_APOSTROPHE;
4834 case 0x1613: /* (green) */
4835 element = EL_CHAR_PARENLEFT;
4838 case 0x1614: /* (green) */
4839 element = EL_CHAR_PARENRIGHT;
4842 case 0x1615: /* (blue steel) */
4843 element = EL_STEEL_CHAR_A;
4846 case 0x1616: /* (blue steel) */
4847 element = EL_STEEL_CHAR_B;
4850 case 0x1617: /* (blue steel) */
4851 element = EL_STEEL_CHAR_C;
4854 case 0x1618: /* (blue steel) */
4855 element = EL_STEEL_CHAR_D;
4858 case 0x1619: /* (blue steel) */
4859 element = EL_STEEL_CHAR_E;
4862 case 0x161a: /* (blue steel) */
4863 element = EL_STEEL_CHAR_F;
4866 case 0x161b: /* (blue steel) */
4867 element = EL_STEEL_CHAR_G;
4870 case 0x161c: /* (blue steel) */
4871 element = EL_STEEL_CHAR_H;
4874 case 0x161d: /* (blue steel) */
4875 element = EL_STEEL_CHAR_I;
4878 case 0x161e: /* (blue steel) */
4879 element = EL_STEEL_CHAR_J;
4882 case 0x161f: /* (blue steel) */
4883 element = EL_STEEL_CHAR_K;
4886 case 0x1620: /* (blue steel) */
4887 element = EL_STEEL_CHAR_L;
4890 case 0x1621: /* (blue steel) */
4891 element = EL_STEEL_CHAR_M;
4894 case 0x1622: /* (blue steel) */
4895 element = EL_STEEL_CHAR_N;
4898 case 0x1623: /* (blue steel) */
4899 element = EL_STEEL_CHAR_O;
4902 case 0x1624: /* (blue steel) */
4903 element = EL_STEEL_CHAR_P;
4906 case 0x1625: /* (blue steel) */
4907 element = EL_STEEL_CHAR_Q;
4910 case 0x1626: /* (blue steel) */
4911 element = EL_STEEL_CHAR_R;
4914 case 0x1627: /* (blue steel) */
4915 element = EL_STEEL_CHAR_S;
4918 case 0x1628: /* (blue steel) */
4919 element = EL_STEEL_CHAR_T;
4922 case 0x1629: /* (blue steel) */
4923 element = EL_STEEL_CHAR_U;
4926 case 0x162a: /* (blue steel) */
4927 element = EL_STEEL_CHAR_V;
4930 case 0x162b: /* (blue steel) */
4931 element = EL_STEEL_CHAR_W;
4934 case 0x162c: /* (blue steel) */
4935 element = EL_STEEL_CHAR_X;
4938 case 0x162d: /* (blue steel) */
4939 element = EL_STEEL_CHAR_Y;
4942 case 0x162e: /* (blue steel) */
4943 element = EL_STEEL_CHAR_Z;
4946 case 0x162f: /* (blue steel) */
4947 element = EL_STEEL_CHAR_AUMLAUT;
4950 case 0x1630: /* (blue steel) */
4951 element = EL_STEEL_CHAR_OUMLAUT;
4954 case 0x1631: /* (blue steel) */
4955 element = EL_STEEL_CHAR_UUMLAUT;
4958 case 0x1632: /* (blue steel) */
4959 element = EL_STEEL_CHAR_0;
4962 case 0x1633: /* (blue steel) */
4963 element = EL_STEEL_CHAR_1;
4966 case 0x1634: /* (blue steel) */
4967 element = EL_STEEL_CHAR_2;
4970 case 0x1635: /* (blue steel) */
4971 element = EL_STEEL_CHAR_3;
4974 case 0x1636: /* (blue steel) */
4975 element = EL_STEEL_CHAR_4;
4978 case 0x1637: /* (blue steel) */
4979 element = EL_STEEL_CHAR_5;
4982 case 0x1638: /* (blue steel) */
4983 element = EL_STEEL_CHAR_6;
4986 case 0x1639: /* (blue steel) */
4987 element = EL_STEEL_CHAR_7;
4990 case 0x163a: /* (blue steel) */
4991 element = EL_STEEL_CHAR_8;
4994 case 0x163b: /* (blue steel) */
4995 element = EL_STEEL_CHAR_9;
4998 case 0x163c: /* (blue steel) */
4999 element = EL_STEEL_CHAR_PERIOD;
5002 case 0x163d: /* (blue steel) */
5003 element = EL_STEEL_CHAR_EXCLAM;
5006 case 0x163e: /* (blue steel) */
5007 element = EL_STEEL_CHAR_COLON;
5010 case 0x163f: /* (blue steel) */
5011 element = EL_STEEL_CHAR_LESS;
5014 case 0x1640: /* (blue steel) */
5015 element = EL_STEEL_CHAR_GREATER;
5018 case 0x1641: /* (blue steel) */
5019 element = EL_STEEL_CHAR_QUESTION;
5022 case 0x1642: /* (blue steel) */
5023 element = EL_STEEL_CHAR_COPYRIGHT;
5026 case 0x1643: /* (blue steel) */
5027 element = EL_STEEL_CHAR_UP;
5030 case 0x1644: /* (blue steel) */
5031 element = EL_STEEL_CHAR_DOWN;
5034 case 0x1645: /* (blue steel) */
5035 element = EL_STEEL_CHAR_BUTTON;
5038 case 0x1646: /* (blue steel) */
5039 element = EL_STEEL_CHAR_PLUS;
5042 case 0x1647: /* (blue steel) */
5043 element = EL_STEEL_CHAR_MINUS;
5046 case 0x1648: /* (blue steel) */
5047 element = EL_STEEL_CHAR_APOSTROPHE;
5050 case 0x1649: /* (blue steel) */
5051 element = EL_STEEL_CHAR_PARENLEFT;
5054 case 0x164a: /* (blue steel) */
5055 element = EL_STEEL_CHAR_PARENRIGHT;
5058 case 0x164b: /* (green steel) */
5059 element = EL_STEEL_CHAR_A;
5062 case 0x164c: /* (green steel) */
5063 element = EL_STEEL_CHAR_B;
5066 case 0x164d: /* (green steel) */
5067 element = EL_STEEL_CHAR_C;
5070 case 0x164e: /* (green steel) */
5071 element = EL_STEEL_CHAR_D;
5074 case 0x164f: /* (green steel) */
5075 element = EL_STEEL_CHAR_E;
5078 case 0x1650: /* (green steel) */
5079 element = EL_STEEL_CHAR_F;
5082 case 0x1651: /* (green steel) */
5083 element = EL_STEEL_CHAR_G;
5086 case 0x1652: /* (green steel) */
5087 element = EL_STEEL_CHAR_H;
5090 case 0x1653: /* (green steel) */
5091 element = EL_STEEL_CHAR_I;
5094 case 0x1654: /* (green steel) */
5095 element = EL_STEEL_CHAR_J;
5098 case 0x1655: /* (green steel) */
5099 element = EL_STEEL_CHAR_K;
5102 case 0x1656: /* (green steel) */
5103 element = EL_STEEL_CHAR_L;
5106 case 0x1657: /* (green steel) */
5107 element = EL_STEEL_CHAR_M;
5110 case 0x1658: /* (green steel) */
5111 element = EL_STEEL_CHAR_N;
5114 case 0x1659: /* (green steel) */
5115 element = EL_STEEL_CHAR_O;
5118 case 0x165a: /* (green steel) */
5119 element = EL_STEEL_CHAR_P;
5122 case 0x165b: /* (green steel) */
5123 element = EL_STEEL_CHAR_Q;
5126 case 0x165c: /* (green steel) */
5127 element = EL_STEEL_CHAR_R;
5130 case 0x165d: /* (green steel) */
5131 element = EL_STEEL_CHAR_S;
5134 case 0x165e: /* (green steel) */
5135 element = EL_STEEL_CHAR_T;
5138 case 0x165f: /* (green steel) */
5139 element = EL_STEEL_CHAR_U;
5142 case 0x1660: /* (green steel) */
5143 element = EL_STEEL_CHAR_V;
5146 case 0x1661: /* (green steel) */
5147 element = EL_STEEL_CHAR_W;
5150 case 0x1662: /* (green steel) */
5151 element = EL_STEEL_CHAR_X;
5154 case 0x1663: /* (green steel) */
5155 element = EL_STEEL_CHAR_Y;
5158 case 0x1664: /* (green steel) */
5159 element = EL_STEEL_CHAR_Z;
5162 case 0x1665: /* (green steel) */
5163 element = EL_STEEL_CHAR_AUMLAUT;
5166 case 0x1666: /* (green steel) */
5167 element = EL_STEEL_CHAR_OUMLAUT;
5170 case 0x1667: /* (green steel) */
5171 element = EL_STEEL_CHAR_UUMLAUT;
5174 case 0x1668: /* (green steel) */
5175 element = EL_STEEL_CHAR_0;
5178 case 0x1669: /* (green steel) */
5179 element = EL_STEEL_CHAR_1;
5182 case 0x166a: /* (green steel) */
5183 element = EL_STEEL_CHAR_2;
5186 case 0x166b: /* (green steel) */
5187 element = EL_STEEL_CHAR_3;
5190 case 0x166c: /* (green steel) */
5191 element = EL_STEEL_CHAR_4;
5194 case 0x166d: /* (green steel) */
5195 element = EL_STEEL_CHAR_5;
5198 case 0x166e: /* (green steel) */
5199 element = EL_STEEL_CHAR_6;
5202 case 0x166f: /* (green steel) */
5203 element = EL_STEEL_CHAR_7;
5206 case 0x1670: /* (green steel) */
5207 element = EL_STEEL_CHAR_8;
5210 case 0x1671: /* (green steel) */
5211 element = EL_STEEL_CHAR_9;
5214 case 0x1672: /* (green steel) */
5215 element = EL_STEEL_CHAR_PERIOD;
5218 case 0x1673: /* (green steel) */
5219 element = EL_STEEL_CHAR_EXCLAM;
5222 case 0x1674: /* (green steel) */
5223 element = EL_STEEL_CHAR_COLON;
5226 case 0x1675: /* (green steel) */
5227 element = EL_STEEL_CHAR_LESS;
5230 case 0x1676: /* (green steel) */
5231 element = EL_STEEL_CHAR_GREATER;
5234 case 0x1677: /* (green steel) */
5235 element = EL_STEEL_CHAR_QUESTION;
5238 case 0x1678: /* (green steel) */
5239 element = EL_STEEL_CHAR_COPYRIGHT;
5242 case 0x1679: /* (green steel) */
5243 element = EL_STEEL_CHAR_UP;
5246 case 0x167a: /* (green steel) */
5247 element = EL_STEEL_CHAR_DOWN;
5250 case 0x167b: /* (green steel) */
5251 element = EL_STEEL_CHAR_BUTTON;
5254 case 0x167c: /* (green steel) */
5255 element = EL_STEEL_CHAR_PLUS;
5258 case 0x167d: /* (green steel) */
5259 element = EL_STEEL_CHAR_MINUS;
5262 case 0x167e: /* (green steel) */
5263 element = EL_STEEL_CHAR_APOSTROPHE;
5266 case 0x167f: /* (green steel) */
5267 element = EL_STEEL_CHAR_PARENLEFT;
5270 case 0x1680: /* (green steel) */
5271 element = EL_STEEL_CHAR_PARENRIGHT;
5274 case 0x1681: /* gate (red) */
5275 element = EL_EM_GATE_1;
5278 case 0x1682: /* secret gate (red) */
5279 element = EL_GATE_1_GRAY;
5282 case 0x1683: /* gate (yellow) */
5283 element = EL_EM_GATE_2;
5286 case 0x1684: /* secret gate (yellow) */
5287 element = EL_GATE_2_GRAY;
5290 case 0x1685: /* gate (blue) */
5291 element = EL_EM_GATE_4;
5294 case 0x1686: /* secret gate (blue) */
5295 element = EL_GATE_4_GRAY;
5298 case 0x1687: /* gate (green) */
5299 element = EL_EM_GATE_3;
5302 case 0x1688: /* secret gate (green) */
5303 element = EL_GATE_3_GRAY;
5306 case 0x1689: /* gate (white) */
5307 element = EL_DC_GATE_WHITE;
5310 case 0x168a: /* secret gate (white) */
5311 element = EL_DC_GATE_WHITE_GRAY;
5314 case 0x168b: /* secret gate (no key) */
5315 element = EL_DC_GATE_FAKE_GRAY;
5319 element = EL_ROBOT_WHEEL;
5323 element = EL_DC_TIMEGATE_SWITCH;
5327 element = EL_ACID_POOL_BOTTOM;
5331 element = EL_ACID_POOL_TOPLEFT;
5335 element = EL_ACID_POOL_TOPRIGHT;
5339 element = EL_ACID_POOL_BOTTOMLEFT;
5343 element = EL_ACID_POOL_BOTTOMRIGHT;
5347 element = EL_STEELWALL;
5351 element = EL_STEELWALL_SLIPPERY;
5354 case 0x1695: /* steel wall (not round) */
5355 element = EL_STEELWALL;
5358 case 0x1696: /* steel wall (left) */
5359 element = EL_DC_STEELWALL_1_LEFT;
5362 case 0x1697: /* steel wall (bottom) */
5363 element = EL_DC_STEELWALL_1_BOTTOM;
5366 case 0x1698: /* steel wall (right) */
5367 element = EL_DC_STEELWALL_1_RIGHT;
5370 case 0x1699: /* steel wall (top) */
5371 element = EL_DC_STEELWALL_1_TOP;
5374 case 0x169a: /* steel wall (left/bottom) */
5375 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5378 case 0x169b: /* steel wall (right/bottom) */
5379 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5382 case 0x169c: /* steel wall (right/top) */
5383 element = EL_DC_STEELWALL_1_TOPRIGHT;
5386 case 0x169d: /* steel wall (left/top) */
5387 element = EL_DC_STEELWALL_1_TOPLEFT;
5390 case 0x169e: /* steel wall (right/bottom small) */
5391 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5394 case 0x169f: /* steel wall (left/bottom small) */
5395 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5398 case 0x16a0: /* steel wall (right/top small) */
5399 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5402 case 0x16a1: /* steel wall (left/top small) */
5403 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5406 case 0x16a2: /* steel wall (left/right) */
5407 element = EL_DC_STEELWALL_1_VERTICAL;
5410 case 0x16a3: /* steel wall (top/bottom) */
5411 element = EL_DC_STEELWALL_1_HORIZONTAL;
5414 case 0x16a4: /* steel wall 2 (left end) */
5415 element = EL_DC_STEELWALL_2_LEFT;
5418 case 0x16a5: /* steel wall 2 (right end) */
5419 element = EL_DC_STEELWALL_2_RIGHT;
5422 case 0x16a6: /* steel wall 2 (top end) */
5423 element = EL_DC_STEELWALL_2_TOP;
5426 case 0x16a7: /* steel wall 2 (bottom end) */
5427 element = EL_DC_STEELWALL_2_BOTTOM;
5430 case 0x16a8: /* steel wall 2 (left/right) */
5431 element = EL_DC_STEELWALL_2_HORIZONTAL;
5434 case 0x16a9: /* steel wall 2 (up/down) */
5435 element = EL_DC_STEELWALL_2_VERTICAL;
5438 case 0x16aa: /* steel wall 2 (mid) */
5439 element = EL_DC_STEELWALL_2_MIDDLE;
5443 element = EL_SIGN_EXCLAMATION;
5447 element = EL_SIGN_RADIOACTIVITY;
5451 element = EL_SIGN_STOP;
5455 element = EL_SIGN_WHEELCHAIR;
5459 element = EL_SIGN_PARKING;
5463 element = EL_SIGN_NO_ENTRY;
5467 element = EL_SIGN_HEART;
5471 element = EL_SIGN_GIVE_WAY;
5475 element = EL_SIGN_ENTRY_FORBIDDEN;
5479 element = EL_SIGN_EMERGENCY_EXIT;
5483 element = EL_SIGN_YIN_YANG;
5487 element = EL_WALL_EMERALD;
5491 element = EL_WALL_DIAMOND;
5495 element = EL_WALL_PEARL;
5499 element = EL_WALL_CRYSTAL;
5503 element = EL_INVISIBLE_WALL;
5507 element = EL_INVISIBLE_STEELWALL;
5510 /* 0x16bc - 0x16cb: */
5511 /* EL_INVISIBLE_SAND */
5514 element = EL_LIGHT_SWITCH;
5518 element = EL_ENVELOPE_1;
5522 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5523 element = EL_DIAMOND;
5524 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5525 element = EL_EMERALD;
5526 else if (element >= 0x157c && element <= 0x158b)
5528 else if (element >= 0x1590 && element <= 0x159f)
5529 element = EL_DC_LANDMINE;
5530 else if (element >= 0x16bc && element <= 0x16cb)
5531 element = EL_INVISIBLE_SAND;
5534 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5535 element = EL_UNKNOWN;
5540 return getMappedElement(element);
5543 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5546 byte header[DC_LEVEL_HEADER_SIZE];
5548 int envelope_header_pos = 62;
5549 int envelope_content_pos = 94;
5550 int level_name_pos = 251;
5551 int level_author_pos = 292;
5552 int envelope_header_len;
5553 int envelope_content_len;
5555 int level_author_len;
5557 int num_yamyam_contents;
5560 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5562 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5564 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5566 header[i * 2 + 0] = header_word >> 8;
5567 header[i * 2 + 1] = header_word & 0xff;
5570 /* read some values from level header to check level decoding integrity */
5571 fieldx = header[6] | (header[7] << 8);
5572 fieldy = header[8] | (header[9] << 8);
5573 num_yamyam_contents = header[60] | (header[61] << 8);
5575 /* do some simple sanity checks to ensure that level was correctly decoded */
5576 if (fieldx < 1 || fieldx > 256 ||
5577 fieldy < 1 || fieldy > 256 ||
5578 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5580 level->no_valid_file = TRUE;
5582 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5587 /* maximum envelope header size is 31 bytes */
5588 envelope_header_len = header[envelope_header_pos];
5589 /* maximum envelope content size is 110 (156?) bytes */
5590 envelope_content_len = header[envelope_content_pos];
5592 /* maximum level title size is 40 bytes */
5593 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5594 /* maximum level author size is 30 (51?) bytes */
5595 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5599 for (i = 0; i < envelope_header_len; i++)
5600 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5601 level->envelope[0].text[envelope_size++] =
5602 header[envelope_header_pos + 1 + i];
5604 if (envelope_header_len > 0 && envelope_content_len > 0)
5606 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5607 level->envelope[0].text[envelope_size++] = '\n';
5608 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5609 level->envelope[0].text[envelope_size++] = '\n';
5612 for (i = 0; i < envelope_content_len; i++)
5613 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5614 level->envelope[0].text[envelope_size++] =
5615 header[envelope_content_pos + 1 + i];
5617 level->envelope[0].text[envelope_size] = '\0';
5619 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5620 level->envelope[0].ysize = 10;
5621 level->envelope[0].autowrap = TRUE;
5622 level->envelope[0].centered = TRUE;
5624 for (i = 0; i < level_name_len; i++)
5625 level->name[i] = header[level_name_pos + 1 + i];
5626 level->name[level_name_len] = '\0';
5628 for (i = 0; i < level_author_len; i++)
5629 level->author[i] = header[level_author_pos + 1 + i];
5630 level->author[level_author_len] = '\0';
5632 num_yamyam_contents = header[60] | (header[61] << 8);
5633 level->num_yamyam_contents =
5634 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5636 for (i = 0; i < num_yamyam_contents; i++)
5638 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5640 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5641 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5643 if (i < MAX_ELEMENT_CONTENTS)
5644 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5648 fieldx = header[6] | (header[7] << 8);
5649 fieldy = header[8] | (header[9] << 8);
5650 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5651 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5653 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5655 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5656 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5658 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5659 level->field[x][y] = getMappedElement_DC(element_dc);
5662 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5663 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5664 level->field[x][y] = EL_PLAYER_1;
5666 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5667 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5668 level->field[x][y] = EL_PLAYER_2;
5670 level->gems_needed = header[18] | (header[19] << 8);
5672 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5673 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5674 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5675 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5676 level->score[SC_NUT] = header[28] | (header[29] << 8);
5677 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5678 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5679 level->score[SC_BUG] = header[34] | (header[35] << 8);
5680 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5681 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5682 level->score[SC_KEY] = header[40] | (header[41] << 8);
5683 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5685 level->time = header[44] | (header[45] << 8);
5687 level->amoeba_speed = header[46] | (header[47] << 8);
5688 level->time_light = header[48] | (header[49] << 8);
5689 level->time_timegate = header[50] | (header[51] << 8);
5690 level->time_wheel = header[52] | (header[53] << 8);
5691 level->time_magic_wall = header[54] | (header[55] << 8);
5692 level->extra_time = header[56] | (header[57] << 8);
5693 level->shield_normal_time = header[58] | (header[59] << 8);
5695 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5696 can slip down from flat walls, like normal walls and steel walls */
5697 level->em_slippery_gems = TRUE;
5700 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5701 struct LevelFileInfo *level_file_info,
5702 boolean level_info_only)
5704 char *filename = level_file_info->filename;
5706 int num_magic_bytes = 8;
5707 char magic_bytes[num_magic_bytes + 1];
5708 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5710 if (!(file = openFile(filename, MODE_READ)))
5712 level->no_valid_file = TRUE;
5714 if (!level_info_only)
5715 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5720 // fseek(file, 0x0000, SEEK_SET);
5722 if (level_file_info->packed)
5724 /* read "magic bytes" from start of file */
5725 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5726 magic_bytes[0] = '\0';
5728 /* check "magic bytes" for correct file format */
5729 if (!strPrefix(magic_bytes, "DC2"))
5731 level->no_valid_file = TRUE;
5733 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5739 if (strPrefix(magic_bytes, "DC2Win95") ||
5740 strPrefix(magic_bytes, "DC2Win98"))
5742 int position_first_level = 0x00fa;
5743 int extra_bytes = 4;
5746 /* advance file stream to first level inside the level package */
5747 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5749 /* each block of level data is followed by block of non-level data */
5750 num_levels_to_skip *= 2;
5752 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5753 while (num_levels_to_skip >= 0)
5755 /* advance file stream to next level inside the level package */
5756 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5758 level->no_valid_file = TRUE;
5760 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5766 /* skip apparently unused extra bytes following each level */
5767 ReadUnusedBytesFromFile(file, extra_bytes);
5769 /* read size of next level in level package */
5770 skip_bytes = getFile32BitLE(file);
5772 num_levels_to_skip--;
5777 level->no_valid_file = TRUE;
5779 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5786 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5792 /* ------------------------------------------------------------------------- */
5793 /* functions for loading SB level */
5794 /* ------------------------------------------------------------------------- */
5796 int getMappedElement_SB(int element_ascii, boolean use_ces)
5804 sb_element_mapping[] =
5806 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5807 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5808 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5809 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5810 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5811 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5812 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5813 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5820 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5821 if (element_ascii == sb_element_mapping[i].ascii)
5822 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5824 return EL_UNDEFINED;
5827 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5828 struct LevelFileInfo *level_file_info,
5829 boolean level_info_only)
5831 char *filename = level_file_info->filename;
5832 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5833 char last_comment[MAX_LINE_LEN];
5834 char level_name[MAX_LINE_LEN];
5837 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5838 boolean read_continued_line = FALSE;
5839 boolean reading_playfield = FALSE;
5840 boolean got_valid_playfield_line = FALSE;
5841 boolean invalid_playfield_char = FALSE;
5842 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5843 int file_level_nr = 0;
5845 int x = 0, y = 0; /* initialized to make compilers happy */
5847 last_comment[0] = '\0';
5848 level_name[0] = '\0';
5850 if (!(file = openFile(filename, MODE_READ)))
5852 level->no_valid_file = TRUE;
5854 if (!level_info_only)
5855 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5860 while (!checkEndOfFile(file))
5862 /* level successfully read, but next level may follow here */
5863 if (!got_valid_playfield_line && reading_playfield)
5865 /* read playfield from single level file -- skip remaining file */
5866 if (!level_file_info->packed)
5869 if (file_level_nr >= num_levels_to_skip)
5874 last_comment[0] = '\0';
5875 level_name[0] = '\0';
5877 reading_playfield = FALSE;
5880 got_valid_playfield_line = FALSE;
5882 /* read next line of input file */
5883 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5886 /* check if line was completely read and is terminated by line break */
5887 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5890 /* cut trailing line break (this can be newline and/or carriage return) */
5891 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5892 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5895 /* copy raw input line for later use (mainly debugging output) */
5896 strcpy(line_raw, line);
5898 if (read_continued_line)
5900 /* append new line to existing line, if there is enough space */
5901 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5902 strcat(previous_line, line_ptr);
5904 strcpy(line, previous_line); /* copy storage buffer to line */
5906 read_continued_line = FALSE;
5909 /* if the last character is '\', continue at next line */
5910 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5912 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5913 strcpy(previous_line, line); /* copy line to storage buffer */
5915 read_continued_line = TRUE;
5920 /* skip empty lines */
5921 if (line[0] == '\0')
5924 /* extract comment text from comment line */
5927 for (line_ptr = line; *line_ptr; line_ptr++)
5928 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5931 strcpy(last_comment, line_ptr);
5936 /* extract level title text from line containing level title */
5937 if (line[0] == '\'')
5939 strcpy(level_name, &line[1]);
5941 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5942 level_name[strlen(level_name) - 1] = '\0';
5947 /* skip lines containing only spaces (or empty lines) */
5948 for (line_ptr = line; *line_ptr; line_ptr++)
5949 if (*line_ptr != ' ')
5951 if (*line_ptr == '\0')
5954 /* at this point, we have found a line containing part of a playfield */
5956 got_valid_playfield_line = TRUE;
5958 if (!reading_playfield)
5960 reading_playfield = TRUE;
5961 invalid_playfield_char = FALSE;
5963 for (x = 0; x < MAX_LEV_FIELDX; x++)
5964 for (y = 0; y < MAX_LEV_FIELDY; y++)
5965 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5970 /* start with topmost tile row */
5974 /* skip playfield line if larger row than allowed */
5975 if (y >= MAX_LEV_FIELDY)
5978 /* start with leftmost tile column */
5981 /* read playfield elements from line */
5982 for (line_ptr = line; *line_ptr; line_ptr++)
5984 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5986 /* stop parsing playfield line if larger column than allowed */
5987 if (x >= MAX_LEV_FIELDX)
5990 if (mapped_sb_element == EL_UNDEFINED)
5992 invalid_playfield_char = TRUE;
5997 level->field[x][y] = mapped_sb_element;
5999 /* continue with next tile column */
6002 level->fieldx = MAX(x, level->fieldx);
6005 if (invalid_playfield_char)
6007 /* if first playfield line, treat invalid lines as comment lines */
6009 reading_playfield = FALSE;
6014 /* continue with next tile row */
6022 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6023 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6025 if (!reading_playfield)
6027 level->no_valid_file = TRUE;
6029 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6034 if (*level_name != '\0')
6036 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6037 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6039 else if (*last_comment != '\0')
6041 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6042 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6046 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6049 /* set all empty fields beyond the border walls to invisible steel wall */
6050 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6052 if ((x == 0 || x == level->fieldx - 1 ||
6053 y == 0 || y == level->fieldy - 1) &&
6054 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6055 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6056 level->field, level->fieldx, level->fieldy);
6059 /* set special level settings for Sokoban levels */
6062 level->use_step_counter = TRUE;
6064 if (load_xsb_to_ces)
6066 /* special global settings can now be set in level template */
6068 level->use_custom_template = TRUE;
6073 /* ------------------------------------------------------------------------- */
6074 /* functions for handling native levels */
6075 /* ------------------------------------------------------------------------- */
6077 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6078 struct LevelFileInfo *level_file_info,
6079 boolean level_info_only)
6081 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6082 level->no_valid_file = TRUE;
6085 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6086 struct LevelFileInfo *level_file_info,
6087 boolean level_info_only)
6091 /* determine position of requested level inside level package */
6092 if (level_file_info->packed)
6093 pos = level_file_info->nr - leveldir_current->first_level;
6095 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6096 level->no_valid_file = TRUE;
6099 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6100 struct LevelFileInfo *level_file_info,
6101 boolean level_info_only)
6103 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6104 level->no_valid_file = TRUE;
6107 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6109 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6110 CopyNativeLevel_RND_to_EM(level);
6111 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6112 CopyNativeLevel_RND_to_SP(level);
6113 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6114 CopyNativeLevel_RND_to_MM(level);
6117 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6119 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6120 CopyNativeLevel_EM_to_RND(level);
6121 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6122 CopyNativeLevel_SP_to_RND(level);
6123 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6124 CopyNativeLevel_MM_to_RND(level);
6127 void SaveNativeLevel(struct LevelInfo *level)
6129 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6131 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6132 char *filename = getLevelFilenameFromBasename(basename);
6134 CopyNativeLevel_RND_to_SP(level);
6135 CopyNativeTape_RND_to_SP(level);
6137 SaveNativeLevel_SP(filename);
6142 /* ------------------------------------------------------------------------- */
6143 /* functions for loading generic level */
6144 /* ------------------------------------------------------------------------- */
6146 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6147 struct LevelFileInfo *level_file_info,
6148 boolean level_info_only)
6150 /* always start with reliable default values */
6151 setLevelInfoToDefaults(level, level_info_only, TRUE);
6153 switch (level_file_info->type)
6155 case LEVEL_FILE_TYPE_RND:
6156 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6159 case LEVEL_FILE_TYPE_EM:
6160 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6161 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6164 case LEVEL_FILE_TYPE_SP:
6165 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6166 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6169 case LEVEL_FILE_TYPE_MM:
6170 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6171 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6174 case LEVEL_FILE_TYPE_DC:
6175 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6178 case LEVEL_FILE_TYPE_SB:
6179 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6183 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6187 /* if level file is invalid, restore level structure to default values */
6188 if (level->no_valid_file)
6189 setLevelInfoToDefaults(level, level_info_only, FALSE);
6191 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6192 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6194 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6195 CopyNativeLevel_Native_to_RND(level);
6198 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6200 static struct LevelFileInfo level_file_info;
6202 /* always start with reliable default values */
6203 setFileInfoToDefaults(&level_file_info);
6205 level_file_info.nr = 0; /* unknown level number */
6206 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6208 setString(&level_file_info.filename, filename);
6210 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6213 static void LoadLevel_InitVersion(struct LevelInfo *level)
6217 if (leveldir_current == NULL) /* only when dumping level */
6220 /* all engine modifications also valid for levels which use latest engine */
6221 if (level->game_version < VERSION_IDENT(3,2,0,5))
6223 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6224 level->score[SC_TIME_BONUS] /= 10;
6227 if (leveldir_current->latest_engine)
6229 /* ---------- use latest game engine ----------------------------------- */
6231 /* For all levels which are forced to use the latest game engine version
6232 (normally all but user contributed, private and undefined levels), set
6233 the game engine version to the actual version; this allows for actual
6234 corrections in the game engine to take effect for existing, converted
6235 levels (from "classic" or other existing games) to make the emulation
6236 of the corresponding game more accurate, while (hopefully) not breaking
6237 existing levels created from other players. */
6239 level->game_version = GAME_VERSION_ACTUAL;
6241 /* Set special EM style gems behaviour: EM style gems slip down from
6242 normal, steel and growing wall. As this is a more fundamental change,
6243 it seems better to set the default behaviour to "off" (as it is more
6244 natural) and make it configurable in the level editor (as a property
6245 of gem style elements). Already existing converted levels (neither
6246 private nor contributed levels) are changed to the new behaviour. */
6248 if (level->file_version < FILE_VERSION_2_0)
6249 level->em_slippery_gems = TRUE;
6254 /* ---------- use game engine the level was created with ----------------- */
6256 /* For all levels which are not forced to use the latest game engine
6257 version (normally user contributed, private and undefined levels),
6258 use the version of the game engine the levels were created for.
6260 Since 2.0.1, the game engine version is now directly stored
6261 in the level file (chunk "VERS"), so there is no need anymore
6262 to set the game version from the file version (except for old,
6263 pre-2.0 levels, where the game version is still taken from the
6264 file format version used to store the level -- see above). */
6266 /* player was faster than enemies in 1.0.0 and before */
6267 if (level->file_version == FILE_VERSION_1_0)
6268 for (i = 0; i < MAX_PLAYERS; i++)
6269 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6271 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6272 if (level->game_version == VERSION_IDENT(2,0,1,0))
6273 level->em_slippery_gems = TRUE;
6275 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6276 if (level->game_version < VERSION_IDENT(2,2,0,0))
6277 level->use_spring_bug = TRUE;
6279 if (level->game_version < VERSION_IDENT(3,2,0,5))
6281 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6282 level->use_time_orb_bug = TRUE;
6284 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6285 level->block_snap_field = FALSE;
6287 /* extra time score was same value as time left score before 3.2.0-5 */
6288 level->extra_time_score = level->score[SC_TIME_BONUS];
6291 if (level->game_version < VERSION_IDENT(3,2,0,7))
6293 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6294 level->continuous_snapping = FALSE;
6297 /* only few elements were able to actively move into acid before 3.1.0 */
6298 /* trigger settings did not exist before 3.1.0; set to default "any" */
6299 if (level->game_version < VERSION_IDENT(3,1,0,0))
6301 /* correct "can move into acid" settings (all zero in old levels) */
6303 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6304 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6306 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6307 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6308 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6309 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6311 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6312 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6314 /* correct trigger settings (stored as zero == "none" in old levels) */
6316 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6318 int element = EL_CUSTOM_START + i;
6319 struct ElementInfo *ei = &element_info[element];
6321 for (j = 0; j < ei->num_change_pages; j++)
6323 struct ElementChangeInfo *change = &ei->change_page[j];
6325 change->trigger_player = CH_PLAYER_ANY;
6326 change->trigger_page = CH_PAGE_ANY;
6331 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6333 int element = EL_CUSTOM_256;
6334 struct ElementInfo *ei = &element_info[element];
6335 struct ElementChangeInfo *change = &ei->change_page[0];
6337 /* This is needed to fix a problem that was caused by a bugfix in function
6338 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6339 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6340 not replace walkable elements, but instead just placed the player on it,
6341 without placing the Sokoban field under the player). Unfortunately, this
6342 breaks "Snake Bite" style levels when the snake is halfway through a door
6343 that just closes (the snake head is still alive and can be moved in this
6344 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6345 player (without Sokoban element) which then gets killed as designed). */
6347 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6348 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6349 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6350 change->target_element = EL_PLAYER_1;
6353 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6354 if (level->game_version < VERSION_IDENT(3,2,5,0))
6356 /* This is needed to fix a problem that was caused by a bugfix in function
6357 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6358 corrects the behaviour when a custom element changes to another custom
6359 element with a higher element number that has change actions defined.
6360 Normally, only one change per frame is allowed for custom elements.
6361 Therefore, it is checked if a custom element already changed in the
6362 current frame; if it did, subsequent changes are suppressed.
6363 Unfortunately, this is only checked for element changes, but not for
6364 change actions, which are still executed. As the function above loops
6365 through all custom elements from lower to higher, an element change
6366 resulting in a lower CE number won't be checked again, while a target
6367 element with a higher number will also be checked, and potential change
6368 actions will get executed for this CE, too (which is wrong), while
6369 further changes are ignored (which is correct). As this bugfix breaks
6370 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6371 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6372 behaviour for existing levels and tapes that make use of this bug */
6374 level->use_action_after_change_bug = TRUE;
6377 /* not centering level after relocating player was default only in 3.2.3 */
6378 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6379 level->shifted_relocation = TRUE;
6381 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6382 if (level->game_version < VERSION_IDENT(3,2,6,0))
6383 level->em_explodes_by_fire = TRUE;
6386 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6390 /* map elements that have changed in newer versions */
6391 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6392 level->game_version);
6393 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6394 for (x = 0; x < 3; x++)
6395 for (y = 0; y < 3; y++)
6396 level->yamyam_content[i].e[x][y] =
6397 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6398 level->game_version);
6402 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6406 /* map custom element change events that have changed in newer versions
6407 (these following values were accidentally changed in version 3.0.1)
6408 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6409 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6411 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6413 int element = EL_CUSTOM_START + i;
6415 /* order of checking and copying events to be mapped is important */
6416 /* (do not change the start and end value -- they are constant) */
6417 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6419 if (HAS_CHANGE_EVENT(element, j - 2))
6421 SET_CHANGE_EVENT(element, j - 2, FALSE);
6422 SET_CHANGE_EVENT(element, j, TRUE);
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_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6430 if (HAS_CHANGE_EVENT(element, j - 1))
6432 SET_CHANGE_EVENT(element, j - 1, FALSE);
6433 SET_CHANGE_EVENT(element, j, TRUE);
6439 /* initialize "can_change" field for old levels with only one change page */
6440 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6442 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6444 int element = EL_CUSTOM_START + i;
6446 if (CAN_CHANGE(element))
6447 element_info[element].change->can_change = TRUE;
6451 /* correct custom element values (for old levels without these options) */
6452 if (level->game_version < VERSION_IDENT(3,1,1,0))
6454 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6456 int element = EL_CUSTOM_START + i;
6457 struct ElementInfo *ei = &element_info[element];
6459 if (ei->access_direction == MV_NO_DIRECTION)
6460 ei->access_direction = MV_ALL_DIRECTIONS;
6464 /* correct custom element values (fix invalid values for all versions) */
6467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6469 int element = EL_CUSTOM_START + i;
6470 struct ElementInfo *ei = &element_info[element];
6472 for (j = 0; j < ei->num_change_pages; j++)
6474 struct ElementChangeInfo *change = &ei->change_page[j];
6476 if (change->trigger_player == CH_PLAYER_NONE)
6477 change->trigger_player = CH_PLAYER_ANY;
6479 if (change->trigger_side == CH_SIDE_NONE)
6480 change->trigger_side = CH_SIDE_ANY;
6485 /* initialize "can_explode" field for old levels which did not store this */
6486 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6487 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6489 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6491 int element = EL_CUSTOM_START + i;
6493 if (EXPLODES_1X1_OLD(element))
6494 element_info[element].explosion_type = EXPLODES_1X1;
6496 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6497 EXPLODES_SMASHED(element) ||
6498 EXPLODES_IMPACT(element)));
6502 /* correct previously hard-coded move delay values for maze runner style */
6503 if (level->game_version < VERSION_IDENT(3,1,1,0))
6505 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6507 int element = EL_CUSTOM_START + i;
6509 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6511 /* previously hard-coded and therefore ignored */
6512 element_info[element].move_delay_fixed = 9;
6513 element_info[element].move_delay_random = 0;
6518 /* set some other uninitialized values of custom elements in older levels */
6519 if (level->game_version < VERSION_IDENT(3,1,0,0))
6521 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6523 int element = EL_CUSTOM_START + i;
6525 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6527 element_info[element].explosion_delay = 17;
6528 element_info[element].ignition_delay = 8;
6533 static void LoadLevel_InitElements(struct LevelInfo *level)
6535 LoadLevel_InitStandardElements(level);
6537 if (level->file_has_custom_elements)
6538 LoadLevel_InitCustomElements(level);
6540 /* initialize element properties for level editor etc. */
6541 InitElementPropertiesEngine(level->game_version);
6542 InitElementPropertiesGfxElement();
6545 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6549 /* map elements that have changed in newer versions */
6550 for (y = 0; y < level->fieldy; y++)
6551 for (x = 0; x < level->fieldx; x++)
6552 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6553 level->game_version);
6555 /* clear unused playfield data (nicer if level gets resized in editor) */
6556 for (x = 0; x < MAX_LEV_FIELDX; x++)
6557 for (y = 0; y < MAX_LEV_FIELDY; y++)
6558 if (x >= level->fieldx || y >= level->fieldy)
6559 level->field[x][y] = EL_EMPTY;
6561 /* copy elements to runtime playfield array */
6562 for (x = 0; x < MAX_LEV_FIELDX; x++)
6563 for (y = 0; y < MAX_LEV_FIELDY; y++)
6564 Feld[x][y] = level->field[x][y];
6566 /* initialize level size variables for faster access */
6567 lev_fieldx = level->fieldx;
6568 lev_fieldy = level->fieldy;
6570 /* determine border element for this level */
6571 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6572 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6577 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6579 struct LevelFileInfo *level_file_info = &level->file_info;
6581 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6582 CopyNativeLevel_RND_to_Native(level);
6585 void LoadLevelTemplate(int nr)
6587 if (!fileExists(getGlobalLevelTemplateFilename()))
6589 Error(ERR_WARN, "no level template found for this level");
6594 setLevelFileInfo(&level_template.file_info, nr);
6596 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6598 LoadLevel_InitVersion(&level_template);
6599 LoadLevel_InitElements(&level_template);
6601 ActivateLevelTemplate();
6604 void LoadLevel(int nr)
6606 setLevelFileInfo(&level.file_info, nr);
6608 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6610 if (level.use_custom_template)
6611 LoadLevelTemplate(-1);
6613 LoadLevel_InitVersion(&level);
6614 LoadLevel_InitElements(&level);
6615 LoadLevel_InitPlayfield(&level);
6617 LoadLevel_InitNativeEngines(&level);
6620 void LoadLevelInfoOnly(int nr)
6622 setLevelFileInfo(&level.file_info, nr);
6624 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6627 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6631 chunk_size += putFileVersion(file, level->file_version);
6632 chunk_size += putFileVersion(file, level->game_version);
6637 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6641 chunk_size += putFile16BitBE(file, level->creation_date.year);
6642 chunk_size += putFile8Bit(file, level->creation_date.month);
6643 chunk_size += putFile8Bit(file, level->creation_date.day);
6648 #if ENABLE_HISTORIC_CHUNKS
6649 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6653 putFile8Bit(file, level->fieldx);
6654 putFile8Bit(file, level->fieldy);
6656 putFile16BitBE(file, level->time);
6657 putFile16BitBE(file, level->gems_needed);
6659 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6660 putFile8Bit(file, level->name[i]);
6662 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6663 putFile8Bit(file, level->score[i]);
6665 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6666 for (y = 0; y < 3; y++)
6667 for (x = 0; x < 3; x++)
6668 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6669 level->yamyam_content[i].e[x][y]));
6670 putFile8Bit(file, level->amoeba_speed);
6671 putFile8Bit(file, level->time_magic_wall);
6672 putFile8Bit(file, level->time_wheel);
6673 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6674 level->amoeba_content));
6675 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6676 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6677 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6678 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6680 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6682 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6683 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6684 putFile32BitBE(file, level->can_move_into_acid_bits);
6685 putFile8Bit(file, level->dont_collide_with_bits);
6687 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6688 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6690 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6691 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6692 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6694 putFile8Bit(file, level->game_engine_type);
6696 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6700 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6705 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6706 chunk_size += putFile8Bit(file, level->name[i]);
6711 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6716 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6717 chunk_size += putFile8Bit(file, level->author[i]);
6722 #if ENABLE_HISTORIC_CHUNKS
6723 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6728 for (y = 0; y < level->fieldy; y++)
6729 for (x = 0; x < level->fieldx; x++)
6730 if (level->encoding_16bit_field)
6731 chunk_size += putFile16BitBE(file, level->field[x][y]);
6733 chunk_size += putFile8Bit(file, level->field[x][y]);
6739 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6744 for (y = 0; y < level->fieldy; y++)
6745 for (x = 0; x < level->fieldx; x++)
6746 chunk_size += putFile16BitBE(file, level->field[x][y]);
6751 #if ENABLE_HISTORIC_CHUNKS
6752 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6756 putFile8Bit(file, EL_YAMYAM);
6757 putFile8Bit(file, level->num_yamyam_contents);
6758 putFile8Bit(file, 0);
6759 putFile8Bit(file, 0);
6761 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6762 for (y = 0; y < 3; y++)
6763 for (x = 0; x < 3; x++)
6764 if (level->encoding_16bit_field)
6765 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6767 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6771 #if ENABLE_HISTORIC_CHUNKS
6772 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6775 int num_contents, content_xsize, content_ysize;
6776 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6778 if (element == EL_YAMYAM)
6780 num_contents = level->num_yamyam_contents;
6784 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6785 for (y = 0; y < 3; y++)
6786 for (x = 0; x < 3; x++)
6787 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6789 else if (element == EL_BD_AMOEBA)
6795 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6796 for (y = 0; y < 3; y++)
6797 for (x = 0; x < 3; x++)
6798 content_array[i][x][y] = EL_EMPTY;
6799 content_array[0][0][0] = level->amoeba_content;
6803 /* chunk header already written -- write empty chunk data */
6804 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6806 Error(ERR_WARN, "cannot save content for element '%d'", element);
6810 putFile16BitBE(file, element);
6811 putFile8Bit(file, num_contents);
6812 putFile8Bit(file, content_xsize);
6813 putFile8Bit(file, content_ysize);
6815 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6817 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6818 for (y = 0; y < 3; y++)
6819 for (x = 0; x < 3; x++)
6820 putFile16BitBE(file, content_array[i][x][y]);
6824 #if ENABLE_HISTORIC_CHUNKS
6825 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6827 int envelope_nr = element - EL_ENVELOPE_1;
6828 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6832 chunk_size += putFile16BitBE(file, element);
6833 chunk_size += putFile16BitBE(file, envelope_len);
6834 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6835 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6837 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6838 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6840 for (i = 0; i < envelope_len; i++)
6841 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6847 #if ENABLE_HISTORIC_CHUNKS
6848 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6849 int num_changed_custom_elements)
6853 putFile16BitBE(file, num_changed_custom_elements);
6855 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6857 int element = EL_CUSTOM_START + i;
6859 struct ElementInfo *ei = &element_info[element];
6861 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6863 if (check < num_changed_custom_elements)
6865 putFile16BitBE(file, element);
6866 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6873 if (check != num_changed_custom_elements) /* should not happen */
6874 Error(ERR_WARN, "inconsistent number of custom element properties");
6878 #if ENABLE_HISTORIC_CHUNKS
6879 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6880 int num_changed_custom_elements)
6884 putFile16BitBE(file, num_changed_custom_elements);
6886 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6888 int element = EL_CUSTOM_START + i;
6890 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6892 if (check < num_changed_custom_elements)
6894 putFile16BitBE(file, element);
6895 putFile16BitBE(file, element_info[element].change->target_element);
6902 if (check != num_changed_custom_elements) /* should not happen */
6903 Error(ERR_WARN, "inconsistent number of custom target elements");
6907 #if ENABLE_HISTORIC_CHUNKS
6908 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6909 int num_changed_custom_elements)
6911 int i, j, x, y, check = 0;
6913 putFile16BitBE(file, num_changed_custom_elements);
6915 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6917 int element = EL_CUSTOM_START + i;
6918 struct ElementInfo *ei = &element_info[element];
6920 if (ei->modified_settings)
6922 if (check < num_changed_custom_elements)
6924 putFile16BitBE(file, element);
6926 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6927 putFile8Bit(file, ei->description[j]);
6929 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6931 /* some free bytes for future properties and padding */
6932 WriteUnusedBytesToFile(file, 7);
6934 putFile8Bit(file, ei->use_gfx_element);
6935 putFile16BitBE(file, ei->gfx_element_initial);
6937 putFile8Bit(file, ei->collect_score_initial);
6938 putFile8Bit(file, ei->collect_count_initial);
6940 putFile16BitBE(file, ei->push_delay_fixed);
6941 putFile16BitBE(file, ei->push_delay_random);
6942 putFile16BitBE(file, ei->move_delay_fixed);
6943 putFile16BitBE(file, ei->move_delay_random);
6945 putFile16BitBE(file, ei->move_pattern);
6946 putFile8Bit(file, ei->move_direction_initial);
6947 putFile8Bit(file, ei->move_stepsize);
6949 for (y = 0; y < 3; y++)
6950 for (x = 0; x < 3; x++)
6951 putFile16BitBE(file, ei->content.e[x][y]);
6953 putFile32BitBE(file, ei->change->events);
6955 putFile16BitBE(file, ei->change->target_element);
6957 putFile16BitBE(file, ei->change->delay_fixed);
6958 putFile16BitBE(file, ei->change->delay_random);
6959 putFile16BitBE(file, ei->change->delay_frames);
6961 putFile16BitBE(file, ei->change->initial_trigger_element);
6963 putFile8Bit(file, ei->change->explode);
6964 putFile8Bit(file, ei->change->use_target_content);
6965 putFile8Bit(file, ei->change->only_if_complete);
6966 putFile8Bit(file, ei->change->use_random_replace);
6968 putFile8Bit(file, ei->change->random_percentage);
6969 putFile8Bit(file, ei->change->replace_when);
6971 for (y = 0; y < 3; y++)
6972 for (x = 0; x < 3; x++)
6973 putFile16BitBE(file, ei->change->content.e[x][y]);
6975 putFile8Bit(file, ei->slippery_type);
6977 /* some free bytes for future properties and padding */
6978 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6985 if (check != num_changed_custom_elements) /* should not happen */
6986 Error(ERR_WARN, "inconsistent number of custom element properties");
6990 #if ENABLE_HISTORIC_CHUNKS
6991 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6993 struct ElementInfo *ei = &element_info[element];
6996 /* ---------- custom element base property values (96 bytes) ------------- */
6998 putFile16BitBE(file, element);
7000 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7001 putFile8Bit(file, ei->description[i]);
7003 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7005 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7007 putFile8Bit(file, ei->num_change_pages);
7009 putFile16BitBE(file, ei->ce_value_fixed_initial);
7010 putFile16BitBE(file, ei->ce_value_random_initial);
7011 putFile8Bit(file, ei->use_last_ce_value);
7013 putFile8Bit(file, ei->use_gfx_element);
7014 putFile16BitBE(file, ei->gfx_element_initial);
7016 putFile8Bit(file, ei->collect_score_initial);
7017 putFile8Bit(file, ei->collect_count_initial);
7019 putFile8Bit(file, ei->drop_delay_fixed);
7020 putFile8Bit(file, ei->push_delay_fixed);
7021 putFile8Bit(file, ei->drop_delay_random);
7022 putFile8Bit(file, ei->push_delay_random);
7023 putFile16BitBE(file, ei->move_delay_fixed);
7024 putFile16BitBE(file, ei->move_delay_random);
7026 /* bits 0 - 15 of "move_pattern" ... */
7027 putFile16BitBE(file, ei->move_pattern & 0xffff);
7028 putFile8Bit(file, ei->move_direction_initial);
7029 putFile8Bit(file, ei->move_stepsize);
7031 putFile8Bit(file, ei->slippery_type);
7033 for (y = 0; y < 3; y++)
7034 for (x = 0; x < 3; x++)
7035 putFile16BitBE(file, ei->content.e[x][y]);
7037 putFile16BitBE(file, ei->move_enter_element);
7038 putFile16BitBE(file, ei->move_leave_element);
7039 putFile8Bit(file, ei->move_leave_type);
7041 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7042 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7044 putFile8Bit(file, ei->access_direction);
7046 putFile8Bit(file, ei->explosion_delay);
7047 putFile8Bit(file, ei->ignition_delay);
7048 putFile8Bit(file, ei->explosion_type);
7050 /* some free bytes for future custom property values and padding */
7051 WriteUnusedBytesToFile(file, 1);
7053 /* ---------- change page property values (48 bytes) --------------------- */
7055 for (i = 0; i < ei->num_change_pages; i++)
7057 struct ElementChangeInfo *change = &ei->change_page[i];
7058 unsigned int event_bits;
7060 /* bits 0 - 31 of "has_event[]" ... */
7062 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7063 if (change->has_event[j])
7064 event_bits |= (1 << j);
7065 putFile32BitBE(file, event_bits);
7067 putFile16BitBE(file, change->target_element);
7069 putFile16BitBE(file, change->delay_fixed);
7070 putFile16BitBE(file, change->delay_random);
7071 putFile16BitBE(file, change->delay_frames);
7073 putFile16BitBE(file, change->initial_trigger_element);
7075 putFile8Bit(file, change->explode);
7076 putFile8Bit(file, change->use_target_content);
7077 putFile8Bit(file, change->only_if_complete);
7078 putFile8Bit(file, change->use_random_replace);
7080 putFile8Bit(file, change->random_percentage);
7081 putFile8Bit(file, change->replace_when);
7083 for (y = 0; y < 3; y++)
7084 for (x = 0; x < 3; x++)
7085 putFile16BitBE(file, change->target_content.e[x][y]);
7087 putFile8Bit(file, change->can_change);
7089 putFile8Bit(file, change->trigger_side);
7091 putFile8Bit(file, change->trigger_player);
7092 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7093 log_2(change->trigger_page)));
7095 putFile8Bit(file, change->has_action);
7096 putFile8Bit(file, change->action_type);
7097 putFile8Bit(file, change->action_mode);
7098 putFile16BitBE(file, change->action_arg);
7100 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7102 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7103 if (change->has_event[j])
7104 event_bits |= (1 << (j - 32));
7105 putFile8Bit(file, event_bits);
7110 #if ENABLE_HISTORIC_CHUNKS
7111 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7113 struct ElementInfo *ei = &element_info[element];
7114 struct ElementGroupInfo *group = ei->group;
7117 putFile16BitBE(file, element);
7119 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7120 putFile8Bit(file, ei->description[i]);
7122 putFile8Bit(file, group->num_elements);
7124 putFile8Bit(file, ei->use_gfx_element);
7125 putFile16BitBE(file, ei->gfx_element_initial);
7127 putFile8Bit(file, group->choice_mode);
7129 /* some free bytes for future values and padding */
7130 WriteUnusedBytesToFile(file, 3);
7132 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7133 putFile16BitBE(file, group->element[i]);
7137 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7138 boolean write_element)
7140 int save_type = entry->save_type;
7141 int data_type = entry->data_type;
7142 int conf_type = entry->conf_type;
7143 int byte_mask = conf_type & CONF_MASK_BYTES;
7144 int element = entry->element;
7145 int default_value = entry->default_value;
7147 boolean modified = FALSE;
7149 if (byte_mask != CONF_MASK_MULTI_BYTES)
7151 void *value_ptr = entry->value;
7152 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7155 /* check if any settings have been modified before saving them */
7156 if (value != default_value)
7159 /* do not save if explicitly told or if unmodified default settings */
7160 if ((save_type == SAVE_CONF_NEVER) ||
7161 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7165 num_bytes += putFile16BitBE(file, element);
7167 num_bytes += putFile8Bit(file, conf_type);
7168 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7169 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7170 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7173 else if (data_type == TYPE_STRING)
7175 char *default_string = entry->default_string;
7176 char *string = (char *)(entry->value);
7177 int string_length = strlen(string);
7180 /* check if any settings have been modified before saving them */
7181 if (!strEqual(string, default_string))
7184 /* do not save if explicitly told or if unmodified default settings */
7185 if ((save_type == SAVE_CONF_NEVER) ||
7186 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7190 num_bytes += putFile16BitBE(file, element);
7192 num_bytes += putFile8Bit(file, conf_type);
7193 num_bytes += putFile16BitBE(file, string_length);
7195 for (i = 0; i < string_length; i++)
7196 num_bytes += putFile8Bit(file, string[i]);
7198 else if (data_type == TYPE_ELEMENT_LIST)
7200 int *element_array = (int *)(entry->value);
7201 int num_elements = *(int *)(entry->num_entities);
7204 /* check if any settings have been modified before saving them */
7205 for (i = 0; i < num_elements; i++)
7206 if (element_array[i] != default_value)
7209 /* do not save if explicitly told or if unmodified default settings */
7210 if ((save_type == SAVE_CONF_NEVER) ||
7211 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7215 num_bytes += putFile16BitBE(file, element);
7217 num_bytes += putFile8Bit(file, conf_type);
7218 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7220 for (i = 0; i < num_elements; i++)
7221 num_bytes += putFile16BitBE(file, element_array[i]);
7223 else if (data_type == TYPE_CONTENT_LIST)
7225 struct Content *content = (struct Content *)(entry->value);
7226 int num_contents = *(int *)(entry->num_entities);
7229 /* check if any settings have been modified before saving them */
7230 for (i = 0; i < num_contents; i++)
7231 for (y = 0; y < 3; y++)
7232 for (x = 0; x < 3; x++)
7233 if (content[i].e[x][y] != default_value)
7236 /* do not save if explicitly told or if unmodified default settings */
7237 if ((save_type == SAVE_CONF_NEVER) ||
7238 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7242 num_bytes += putFile16BitBE(file, element);
7244 num_bytes += putFile8Bit(file, conf_type);
7245 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7247 for (i = 0; i < num_contents; i++)
7248 for (y = 0; y < 3; y++)
7249 for (x = 0; x < 3; x++)
7250 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7256 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7261 li = *level; /* copy level data into temporary buffer */
7263 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7264 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7269 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7274 li = *level; /* copy level data into temporary buffer */
7276 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7277 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7282 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7284 int envelope_nr = element - EL_ENVELOPE_1;
7288 chunk_size += putFile16BitBE(file, element);
7290 /* copy envelope data into temporary buffer */
7291 xx_envelope = level->envelope[envelope_nr];
7293 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7294 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7299 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7301 struct ElementInfo *ei = &element_info[element];
7305 chunk_size += putFile16BitBE(file, element);
7307 xx_ei = *ei; /* copy element data into temporary buffer */
7309 /* set default description string for this specific element */
7310 strcpy(xx_default_description, getDefaultElementDescription(ei));
7312 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7313 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7315 for (i = 0; i < ei->num_change_pages; i++)
7317 struct ElementChangeInfo *change = &ei->change_page[i];
7319 xx_current_change_page = i;
7321 xx_change = *change; /* copy change data into temporary buffer */
7324 setEventBitsFromEventFlags(change);
7326 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7327 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7334 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7336 struct ElementInfo *ei = &element_info[element];
7337 struct ElementGroupInfo *group = ei->group;
7341 chunk_size += putFile16BitBE(file, element);
7343 xx_ei = *ei; /* copy element data into temporary buffer */
7344 xx_group = *group; /* copy group data into temporary buffer */
7346 /* set default description string for this specific element */
7347 strcpy(xx_default_description, getDefaultElementDescription(ei));
7349 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7350 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7355 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7356 boolean save_as_template)
7362 if (!(file = fopen(filename, MODE_WRITE)))
7364 Error(ERR_WARN, "cannot save level file '%s'", filename);
7368 level->file_version = FILE_VERSION_ACTUAL;
7369 level->game_version = GAME_VERSION_ACTUAL;
7371 level->creation_date = getCurrentDate();
7373 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7374 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7376 chunk_size = SaveLevel_VERS(NULL, level);
7377 putFileChunkBE(file, "VERS", chunk_size);
7378 SaveLevel_VERS(file, level);
7380 chunk_size = SaveLevel_DATE(NULL, level);
7381 putFileChunkBE(file, "DATE", chunk_size);
7382 SaveLevel_DATE(file, level);
7384 chunk_size = SaveLevel_NAME(NULL, level);
7385 putFileChunkBE(file, "NAME", chunk_size);
7386 SaveLevel_NAME(file, level);
7388 chunk_size = SaveLevel_AUTH(NULL, level);
7389 putFileChunkBE(file, "AUTH", chunk_size);
7390 SaveLevel_AUTH(file, level);
7392 chunk_size = SaveLevel_INFO(NULL, level);
7393 putFileChunkBE(file, "INFO", chunk_size);
7394 SaveLevel_INFO(file, level);
7396 chunk_size = SaveLevel_BODY(NULL, level);
7397 putFileChunkBE(file, "BODY", chunk_size);
7398 SaveLevel_BODY(file, level);
7400 chunk_size = SaveLevel_ELEM(NULL, level);
7401 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7403 putFileChunkBE(file, "ELEM", chunk_size);
7404 SaveLevel_ELEM(file, level);
7407 for (i = 0; i < NUM_ENVELOPES; i++)
7409 int element = EL_ENVELOPE_1 + i;
7411 chunk_size = SaveLevel_NOTE(NULL, level, element);
7412 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7414 putFileChunkBE(file, "NOTE", chunk_size);
7415 SaveLevel_NOTE(file, level, element);
7419 /* if not using template level, check for non-default custom/group elements */
7420 if (!level->use_custom_template || save_as_template)
7422 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7424 int element = EL_CUSTOM_START + i;
7426 chunk_size = SaveLevel_CUSX(NULL, level, element);
7427 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7429 putFileChunkBE(file, "CUSX", chunk_size);
7430 SaveLevel_CUSX(file, level, element);
7434 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7436 int element = EL_GROUP_START + i;
7438 chunk_size = SaveLevel_GRPX(NULL, level, element);
7439 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7441 putFileChunkBE(file, "GRPX", chunk_size);
7442 SaveLevel_GRPX(file, level, element);
7449 SetFilePermissions(filename, PERMS_PRIVATE);
7452 void SaveLevel(int nr)
7454 char *filename = getDefaultLevelFilename(nr);
7456 SaveLevelFromFilename(&level, filename, FALSE);
7459 void SaveLevelTemplate()
7461 char *filename = getLocalLevelTemplateFilename();
7463 SaveLevelFromFilename(&level, filename, TRUE);
7466 boolean SaveLevelChecked(int nr)
7468 char *filename = getDefaultLevelFilename(nr);
7469 boolean new_level = !fileExists(filename);
7470 boolean level_saved = FALSE;
7472 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7477 Request("Level saved!", REQ_CONFIRM);
7485 void DumpLevel(struct LevelInfo *level)
7487 if (level->no_level_file || level->no_valid_file)
7489 Error(ERR_WARN, "cannot dump -- no valid level file found");
7495 Print("Level xxx (file version %08d, game version %08d)\n",
7496 level->file_version, level->game_version);
7499 Print("Level author: '%s'\n", level->author);
7500 Print("Level title: '%s'\n", level->name);
7502 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7504 Print("Level time: %d seconds\n", level->time);
7505 Print("Gems needed: %d\n", level->gems_needed);
7507 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7508 Print("Time for wheel: %d seconds\n", level->time_wheel);
7509 Print("Time for light: %d seconds\n", level->time_light);
7510 Print("Time for timegate: %d seconds\n", level->time_timegate);
7512 Print("Amoeba speed: %d\n", level->amoeba_speed);
7515 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7516 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7517 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7518 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7519 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7525 /* ========================================================================= */
7526 /* tape file functions */
7527 /* ========================================================================= */
7529 static void setTapeInfoToDefaults()
7533 /* always start with reliable default values (empty tape) */
7536 /* default values (also for pre-1.2 tapes) with only the first player */
7537 tape.player_participates[0] = TRUE;
7538 for (i = 1; i < MAX_PLAYERS; i++)
7539 tape.player_participates[i] = FALSE;
7541 /* at least one (default: the first) player participates in every tape */
7542 tape.num_participating_players = 1;
7544 tape.level_nr = level_nr;
7546 tape.changed = FALSE;
7548 tape.recording = FALSE;
7549 tape.playing = FALSE;
7550 tape.pausing = FALSE;
7552 tape.no_valid_file = FALSE;
7555 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7557 tape->file_version = getFileVersion(file);
7558 tape->game_version = getFileVersion(file);
7563 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7567 tape->random_seed = getFile32BitBE(file);
7568 tape->date = getFile32BitBE(file);
7569 tape->length = getFile32BitBE(file);
7571 /* read header fields that are new since version 1.2 */
7572 if (tape->file_version >= FILE_VERSION_1_2)
7574 byte store_participating_players = getFile8Bit(file);
7577 /* since version 1.2, tapes store which players participate in the tape */
7578 tape->num_participating_players = 0;
7579 for (i = 0; i < MAX_PLAYERS; i++)
7581 tape->player_participates[i] = FALSE;
7583 if (store_participating_players & (1 << i))
7585 tape->player_participates[i] = TRUE;
7586 tape->num_participating_players++;
7590 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7592 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7594 engine_version = getFileVersion(file);
7595 if (engine_version > 0)
7596 tape->engine_version = engine_version;
7598 tape->engine_version = tape->game_version;
7604 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7606 int level_identifier_size;
7609 level_identifier_size = getFile16BitBE(file);
7611 tape->level_identifier =
7612 checked_realloc(tape->level_identifier, level_identifier_size);
7614 for (i = 0; i < level_identifier_size; i++)
7615 tape->level_identifier[i] = getFile8Bit(file);
7617 tape->level_nr = getFile16BitBE(file);
7619 chunk_size = 2 + level_identifier_size + 2;
7624 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7628 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7629 int chunk_size_expected = tape_pos_size * tape->length;
7631 if (chunk_size_expected != chunk_size)
7633 ReadUnusedBytesFromFile(file, chunk_size);
7634 return chunk_size_expected;
7637 for (i = 0; i < tape->length; i++)
7639 if (i >= MAX_TAPE_LEN)
7641 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7644 // tape too large; read and ignore remaining tape data from this chunk
7645 for (;i < tape->length; i++)
7646 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7651 if (tape->use_mouse)
7653 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7654 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7655 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7657 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7661 for (j = 0; j < MAX_PLAYERS; j++)
7663 tape->pos[i].action[j] = MV_NONE;
7665 if (tape->player_participates[j])
7666 tape->pos[i].action[j] = getFile8Bit(file);
7670 tape->pos[i].delay = getFile8Bit(file);
7672 if (tape->file_version == FILE_VERSION_1_0)
7674 /* eliminate possible diagonal moves in old tapes */
7675 /* this is only for backward compatibility */
7677 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7678 byte action = tape->pos[i].action[0];
7679 int k, num_moves = 0;
7681 for (k = 0; k<4; k++)
7683 if (action & joy_dir[k])
7685 tape->pos[i + num_moves].action[0] = joy_dir[k];
7687 tape->pos[i + num_moves].delay = 0;
7696 tape->length += num_moves;
7699 else if (tape->file_version < FILE_VERSION_2_0)
7701 /* convert pre-2.0 tapes to new tape format */
7703 if (tape->pos[i].delay > 1)
7706 tape->pos[i + 1] = tape->pos[i];
7707 tape->pos[i + 1].delay = 1;
7710 for (j = 0; j < MAX_PLAYERS; j++)
7711 tape->pos[i].action[j] = MV_NONE;
7712 tape->pos[i].delay--;
7719 if (checkEndOfFile(file))
7723 if (i != tape->length)
7724 chunk_size = tape_pos_size * i;
7729 void LoadTape_SokobanSolution(char *filename)
7732 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7734 if (!(file = openFile(filename, MODE_READ)))
7736 tape.no_valid_file = TRUE;
7741 while (!checkEndOfFile(file))
7743 unsigned char c = getByteFromFile(file);
7745 if (checkEndOfFile(file))
7752 tape.pos[tape.length].action[0] = MV_UP;
7753 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7759 tape.pos[tape.length].action[0] = MV_DOWN;
7760 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7766 tape.pos[tape.length].action[0] = MV_LEFT;
7767 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7773 tape.pos[tape.length].action[0] = MV_RIGHT;
7774 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7782 /* ignore white-space characters */
7786 tape.no_valid_file = TRUE;
7788 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7796 if (tape.no_valid_file)
7799 tape.length_frames = GetTapeLengthFrames();
7800 tape.length_seconds = GetTapeLengthSeconds();
7803 void LoadTapeFromFilename(char *filename)
7805 char cookie[MAX_LINE_LEN];
7806 char chunk_name[CHUNK_ID_LEN + 1];
7810 /* always start with reliable default values */
7811 setTapeInfoToDefaults();
7813 if (strSuffix(filename, ".sln"))
7815 LoadTape_SokobanSolution(filename);
7820 if (!(file = openFile(filename, MODE_READ)))
7822 tape.no_valid_file = TRUE;
7827 getFileChunkBE(file, chunk_name, NULL);
7828 if (strEqual(chunk_name, "RND1"))
7830 getFile32BitBE(file); /* not used */
7832 getFileChunkBE(file, chunk_name, NULL);
7833 if (!strEqual(chunk_name, "TAPE"))
7835 tape.no_valid_file = TRUE;
7837 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7844 else /* check for pre-2.0 file format with cookie string */
7846 strcpy(cookie, chunk_name);
7847 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7849 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7850 cookie[strlen(cookie) - 1] = '\0';
7852 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7854 tape.no_valid_file = TRUE;
7856 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7863 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7865 tape.no_valid_file = TRUE;
7867 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7874 /* pre-2.0 tape files have no game version, so use file version here */
7875 tape.game_version = tape.file_version;
7878 if (tape.file_version < FILE_VERSION_1_2)
7880 /* tape files from versions before 1.2.0 without chunk structure */
7881 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7882 LoadTape_BODY(file, 2 * tape.length, &tape);
7890 int (*loader)(File *, int, struct TapeInfo *);
7894 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7895 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7896 { "INFO", -1, LoadTape_INFO },
7897 { "BODY", -1, LoadTape_BODY },
7901 while (getFileChunkBE(file, chunk_name, &chunk_size))
7905 while (chunk_info[i].name != NULL &&
7906 !strEqual(chunk_name, chunk_info[i].name))
7909 if (chunk_info[i].name == NULL)
7911 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7912 chunk_name, filename);
7913 ReadUnusedBytesFromFile(file, chunk_size);
7915 else if (chunk_info[i].size != -1 &&
7916 chunk_info[i].size != chunk_size)
7918 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7919 chunk_size, chunk_name, filename);
7920 ReadUnusedBytesFromFile(file, chunk_size);
7924 /* call function to load this tape chunk */
7925 int chunk_size_expected =
7926 (chunk_info[i].loader)(file, chunk_size, &tape);
7928 /* the size of some chunks cannot be checked before reading other
7929 chunks first (like "HEAD" and "BODY") that contain some header
7930 information, so check them here */
7931 if (chunk_size_expected != chunk_size)
7933 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7934 chunk_size, chunk_name, filename);
7942 tape.length_frames = GetTapeLengthFrames();
7943 tape.length_seconds = GetTapeLengthSeconds();
7946 printf("::: tape file version: %d\n", tape.file_version);
7947 printf("::: tape game version: %d\n", tape.game_version);
7948 printf("::: tape engine version: %d\n", tape.engine_version);
7952 void LoadTape(int nr)
7954 char *filename = getTapeFilename(nr);
7956 LoadTapeFromFilename(filename);
7959 void LoadSolutionTape(int nr)
7961 char *filename = getSolutionTapeFilename(nr);
7963 LoadTapeFromFilename(filename);
7965 if (TAPE_IS_EMPTY(tape) &&
7966 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7967 level.native_sp_level->demo.is_available)
7968 CopyNativeTape_SP_to_RND(&level);
7971 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7973 putFileVersion(file, tape->file_version);
7974 putFileVersion(file, tape->game_version);
7977 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7980 byte store_participating_players = 0;
7982 /* set bits for participating players for compact storage */
7983 for (i = 0; i < MAX_PLAYERS; i++)
7984 if (tape->player_participates[i])
7985 store_participating_players |= (1 << i);
7987 putFile32BitBE(file, tape->random_seed);
7988 putFile32BitBE(file, tape->date);
7989 putFile32BitBE(file, tape->length);
7991 putFile8Bit(file, store_participating_players);
7993 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7995 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7996 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7998 putFileVersion(file, tape->engine_version);
8001 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8003 int level_identifier_size = strlen(tape->level_identifier) + 1;
8006 putFile16BitBE(file, level_identifier_size);
8008 for (i = 0; i < level_identifier_size; i++)
8009 putFile8Bit(file, tape->level_identifier[i]);
8011 putFile16BitBE(file, tape->level_nr);
8014 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8018 for (i = 0; i < tape->length; i++)
8020 if (tape->use_mouse)
8022 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8023 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8024 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8028 for (j = 0; j < MAX_PLAYERS; j++)
8029 if (tape->player_participates[j])
8030 putFile8Bit(file, tape->pos[i].action[j]);
8033 putFile8Bit(file, tape->pos[i].delay);
8037 void SaveTape(int nr)
8039 char *filename = getTapeFilename(nr);
8041 int num_participating_players = 0;
8043 int info_chunk_size;
8044 int body_chunk_size;
8047 InitTapeDirectory(leveldir_current->subdir);
8049 if (!(file = fopen(filename, MODE_WRITE)))
8051 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8055 tape.file_version = FILE_VERSION_ACTUAL;
8056 tape.game_version = GAME_VERSION_ACTUAL;
8058 /* count number of participating players */
8059 for (i = 0; i < MAX_PLAYERS; i++)
8060 if (tape.player_participates[i])
8061 num_participating_players++;
8063 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8065 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8066 body_chunk_size = tape_pos_size * tape.length;
8068 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8069 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8071 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8072 SaveTape_VERS(file, &tape);
8074 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8075 SaveTape_HEAD(file, &tape);
8077 putFileChunkBE(file, "INFO", info_chunk_size);
8078 SaveTape_INFO(file, &tape);
8080 putFileChunkBE(file, "BODY", body_chunk_size);
8081 SaveTape_BODY(file, &tape);
8085 SetFilePermissions(filename, PERMS_PRIVATE);
8087 tape.changed = FALSE;
8090 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8092 char *filename = getTapeFilename(nr);
8093 boolean new_tape = !fileExists(filename);
8094 boolean tape_saved = FALSE;
8096 if (new_tape || Request(msg_replace, REQ_ASK))
8101 Request(msg_saved, REQ_CONFIRM);
8109 boolean SaveTapeChecked(int nr)
8111 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8114 boolean SaveTapeChecked_LevelSolved(int nr)
8116 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8117 "Level solved! Tape saved!");
8120 void DumpTape(struct TapeInfo *tape)
8122 int tape_frame_counter;
8125 if (tape->no_valid_file)
8127 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8133 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8134 tape->level_nr, tape->file_version, tape->game_version);
8135 Print(" (effective engine version %08d)\n",
8136 tape->engine_version);
8137 Print("Level series identifier: '%s'\n", tape->level_identifier);
8140 tape_frame_counter = 0;
8142 for (i = 0; i < tape->length; i++)
8144 if (i >= MAX_TAPE_LEN)
8149 for (j = 0; j < MAX_PLAYERS; j++)
8151 if (tape->player_participates[j])
8153 int action = tape->pos[i].action[j];
8155 Print("%d:%02x ", j, action);
8156 Print("[%c%c%c%c|%c%c] - ",
8157 (action & JOY_LEFT ? '<' : ' '),
8158 (action & JOY_RIGHT ? '>' : ' '),
8159 (action & JOY_UP ? '^' : ' '),
8160 (action & JOY_DOWN ? 'v' : ' '),
8161 (action & JOY_BUTTON_1 ? '1' : ' '),
8162 (action & JOY_BUTTON_2 ? '2' : ' '));
8166 Print("(%03d) ", tape->pos[i].delay);
8167 Print("[%05d]\n", tape_frame_counter);
8169 tape_frame_counter += tape->pos[i].delay;
8176 /* ========================================================================= */
8177 /* score file functions */
8178 /* ========================================================================= */
8180 void LoadScore(int nr)
8183 char *filename = getScoreFilename(nr);
8184 char cookie[MAX_LINE_LEN];
8185 char line[MAX_LINE_LEN];
8189 /* always start with reliable default values */
8190 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8192 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8193 highscore[i].Score = 0;
8196 if (!(file = fopen(filename, MODE_READ)))
8199 /* check file identifier */
8200 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8202 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8203 cookie[strlen(cookie) - 1] = '\0';
8205 if (!checkCookieString(cookie, SCORE_COOKIE))
8207 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8212 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8214 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8215 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8216 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8219 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8220 line[strlen(line) - 1] = '\0';
8222 for (line_ptr = line; *line_ptr; line_ptr++)
8224 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8226 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8227 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8236 void SaveScore(int nr)
8239 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8240 char *filename = getScoreFilename(nr);
8243 InitScoreDirectory(leveldir_current->subdir);
8245 if (!(file = fopen(filename, MODE_WRITE)))
8247 Error(ERR_WARN, "cannot save score for level %d", nr);
8251 fprintf(file, "%s\n\n", SCORE_COOKIE);
8253 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8254 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8258 SetFilePermissions(filename, permissions);
8262 /* ========================================================================= */
8263 /* setup file functions */
8264 /* ========================================================================= */
8266 #define TOKEN_STR_PLAYER_PREFIX "player_"
8271 SETUP_TOKEN_PLAYER_NAME = 0,
8273 SETUP_TOKEN_SOUND_LOOPS,
8274 SETUP_TOKEN_SOUND_MUSIC,
8275 SETUP_TOKEN_SOUND_SIMPLE,
8277 SETUP_TOKEN_SCROLL_DELAY,
8278 SETUP_TOKEN_SCROLL_DELAY_VALUE,
8279 SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8280 SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8281 SETUP_TOKEN_FADE_SCREENS,
8282 SETUP_TOKEN_AUTORECORD,
8283 SETUP_TOKEN_SHOW_TITLESCREEN,
8284 SETUP_TOKEN_QUICK_DOORS,
8285 SETUP_TOKEN_TEAM_MODE,
8286 SETUP_TOKEN_HANDICAP,
8287 SETUP_TOKEN_SKIP_LEVELS,
8288 SETUP_TOKEN_INCREMENT_LEVELS,
8289 SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8290 SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8291 SETUP_TOKEN_TIME_LIMIT,
8292 SETUP_TOKEN_FULLSCREEN,
8293 SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8294 SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8295 SETUP_TOKEN_SCREEN_RENDERING_MODE,
8296 SETUP_TOKEN_ASK_ON_ESCAPE,
8297 SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8298 SETUP_TOKEN_QUICK_SWITCH,
8299 SETUP_TOKEN_INPUT_ON_FOCUS,
8300 SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8301 SETUP_TOKEN_GAME_FRAME_DELAY,
8302 SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8303 SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8304 SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8305 SETUP_TOKEN_GRAPHICS_SET,
8306 SETUP_TOKEN_SOUNDS_SET,
8307 SETUP_TOKEN_MUSIC_SET,
8308 SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8309 SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8310 SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8311 SETUP_TOKEN_VOLUME_SIMPLE,
8312 SETUP_TOKEN_VOLUME_LOOPS,
8313 SETUP_TOKEN_VOLUME_MUSIC,
8314 SETUP_TOKEN_NETWORK_MODE,
8315 SETUP_TOKEN_NETWORK_PLAYER_NR,
8316 SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8317 SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8318 SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8319 SETUP_TOKEN_TOUCH_TRANSPARENCY,
8320 SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8321 SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8322 SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8323 SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8324 SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8325 SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8327 NUM_GLOBAL_SETUP_TOKENS
8333 SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8335 NUM_AUTO_SETUP_TOKENS
8341 SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8342 SETUP_TOKEN_EDITOR_EL_CUSTOM,
8343 SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8344 SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8345 SETUP_TOKEN_EDITOR_EL_HEADLINES,
8346 SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8348 NUM_EDITOR_SETUP_TOKENS
8351 /* editor cascade setup */
8354 SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8355 SETUP_TOKEN_EDITOR_CASCADE_EM,
8356 SETUP_TOKEN_EDITOR_CASCADE_EMC,
8357 SETUP_TOKEN_EDITOR_CASCADE_RND,
8358 SETUP_TOKEN_EDITOR_CASCADE_SB,
8359 SETUP_TOKEN_EDITOR_CASCADE_SP,
8360 SETUP_TOKEN_EDITOR_CASCADE_DC,
8361 SETUP_TOKEN_EDITOR_CASCADE_DX,
8362 SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8363 SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8364 SETUP_TOKEN_EDITOR_CASCADE_CE,
8365 SETUP_TOKEN_EDITOR_CASCADE_GE,
8366 SETUP_TOKEN_EDITOR_CASCADE_REF,
8367 SETUP_TOKEN_EDITOR_CASCADE_USER,
8368 SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8370 NUM_EDITOR_CASCADE_SETUP_TOKENS
8373 /* shortcut setup */
8376 SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8377 SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8378 SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8379 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8380 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8381 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8382 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8383 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8384 SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8385 SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8386 SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8387 SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8388 SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8389 SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8390 SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8391 SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8392 SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8393 SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8394 SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8395 SETUP_TOKEN_SHORTCUT_SNAP_UP,
8396 SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8398 NUM_SHORTCUT_SETUP_TOKENS
8404 SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8405 SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8406 SETUP_TOKEN_PLAYER_JOY_XLEFT,
8407 SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8408 SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8409 SETUP_TOKEN_PLAYER_JOY_YUPPER,
8410 SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8411 SETUP_TOKEN_PLAYER_JOY_YLOWER,
8412 SETUP_TOKEN_PLAYER_JOY_SNAP,
8413 SETUP_TOKEN_PLAYER_JOY_DROP,
8414 SETUP_TOKEN_PLAYER_KEY_LEFT,
8415 SETUP_TOKEN_PLAYER_KEY_RIGHT,
8416 SETUP_TOKEN_PLAYER_KEY_UP,
8417 SETUP_TOKEN_PLAYER_KEY_DOWN,
8418 SETUP_TOKEN_PLAYER_KEY_SNAP,
8419 SETUP_TOKEN_PLAYER_KEY_DROP,
8421 NUM_PLAYER_SETUP_TOKENS
8427 SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8428 SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8429 SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8431 NUM_SYSTEM_SETUP_TOKENS
8434 /* internal setup */
8437 SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8438 SETUP_TOKEN_INT_PROGRAM_VERSION,
8439 SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8440 SETUP_TOKEN_INT_PROGRAM_EMAIL,
8441 SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8442 SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8443 SETUP_TOKEN_INT_PROGRAM_COMPANY,
8444 SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8445 SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8446 SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8447 SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8448 SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8449 SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8450 SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8451 SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8452 SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8453 SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8454 SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8455 SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8457 NUM_INTERNAL_SETUP_TOKENS
8463 SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8464 SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8465 SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8466 SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8467 SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8468 SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8469 SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8470 SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8471 SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8472 SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8473 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8474 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8475 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8476 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8477 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8478 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8479 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8480 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8481 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8482 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8483 SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8484 SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8485 SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8487 NUM_DEBUG_SETUP_TOKENS
8493 SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8495 NUM_OPTIONS_SETUP_TOKENS
8499 static struct SetupInfo si;
8500 static struct SetupAutoSetupInfo sasi;
8501 static struct SetupEditorInfo sei;
8502 static struct SetupEditorCascadeInfo seci;
8503 static struct SetupShortcutInfo ssi;
8504 static struct SetupInputInfo sii;
8505 static struct SetupSystemInfo syi;
8506 static struct SetupInternalInfo sxi;
8507 static struct SetupDebugInfo sdi;
8508 static struct OptionInfo soi;
8510 static struct TokenInfo global_setup_tokens[] =
8512 { TYPE_STRING, &si.player_name, "player_name" },
8513 { TYPE_SWITCH, &si.sound, "sound" },
8514 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8515 { TYPE_SWITCH, &si.sound_music, "background_music" },
8516 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8517 { TYPE_SWITCH, &si.toons, "toons" },
8518 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8519 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8520 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8521 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8522 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8523 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8524 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8525 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8526 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8527 { TYPE_SWITCH, &si.handicap, "handicap" },
8528 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8529 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8530 { TYPE_SWITCH, &si.auto_play_next_level, "auto_play_next_level" },
8531 { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
8532 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8533 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8534 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8535 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8536 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8537 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8538 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8539 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8540 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8541 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8542 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8543 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8544 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8545 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8546 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8547 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8548 { TYPE_STRING, &si.music_set, "music_set" },
8549 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8550 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8551 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8552 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8553 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8554 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8555 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8556 { TYPE_PLAYER, &si.network_player_nr, "network_player" },
8557 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8558 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8559 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8560 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8561 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8562 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8563 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8564 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8565 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8566 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8569 static struct TokenInfo auto_setup_tokens[] =
8571 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8574 static struct TokenInfo editor_setup_tokens[] =
8576 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8577 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8578 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8579 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8580 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8581 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8584 static struct TokenInfo editor_cascade_setup_tokens[] =
8586 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8587 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8588 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8589 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8590 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8591 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8592 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8593 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8594 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8595 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8596 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8597 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8598 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8599 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8600 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8601 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8602 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8605 static struct TokenInfo shortcut_setup_tokens[] =
8607 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8608 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8609 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8610 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8611 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8612 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8613 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8614 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8615 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8616 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8617 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8618 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8619 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8620 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8621 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8622 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8623 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8624 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8625 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8626 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8627 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8630 static struct TokenInfo player_setup_tokens[] =
8632 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8633 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8634 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8635 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8636 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8637 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8638 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8639 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8640 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8641 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8642 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8643 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8644 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8645 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8646 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8647 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8650 static struct TokenInfo system_setup_tokens[] =
8652 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8653 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8654 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8657 static struct TokenInfo internal_setup_tokens[] =
8659 { TYPE_STRING, &sxi.program_title, "program_title" },
8660 { TYPE_STRING, &sxi.program_version, "program_version" },
8661 { TYPE_STRING, &sxi.program_author, "program_author" },
8662 { TYPE_STRING, &sxi.program_email, "program_email" },
8663 { TYPE_STRING, &sxi.program_website, "program_website" },
8664 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8665 { TYPE_STRING, &sxi.program_company, "program_company" },
8666 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8667 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8668 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8669 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8670 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8671 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8672 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8673 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8674 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8675 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8676 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8677 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8680 static struct TokenInfo debug_setup_tokens[] =
8682 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8683 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8684 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8685 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8686 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8687 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8688 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8689 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8690 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8691 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8692 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8693 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8694 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8695 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8696 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8697 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8698 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8699 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8700 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8701 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8702 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8703 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8704 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8707 static struct TokenInfo options_setup_tokens[] =
8709 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8712 static char *get_corrected_login_name(char *login_name)
8714 /* needed because player name must be a fixed length string */
8715 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8717 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8718 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8720 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8721 if (strchr(login_name_new, ' '))
8722 *strchr(login_name_new, ' ') = '\0';
8724 return login_name_new;
8727 static void setSetupInfoToDefaults(struct SetupInfo *si)
8731 si->player_name = get_corrected_login_name(getLoginName());
8734 si->sound_loops = TRUE;
8735 si->sound_music = TRUE;
8736 si->sound_simple = TRUE;
8738 si->scroll_delay = TRUE;
8739 si->scroll_delay_value = STD_SCROLL_DELAY;
8740 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8741 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8742 si->fade_screens = TRUE;
8743 si->autorecord = TRUE;
8744 si->show_titlescreen = TRUE;
8745 si->quick_doors = FALSE;
8746 si->team_mode = FALSE;
8747 si->handicap = TRUE;
8748 si->skip_levels = TRUE;
8749 si->increment_levels = TRUE;
8750 si->auto_play_next_level = TRUE;
8751 si->skip_scores_after_game = FALSE;
8752 si->time_limit = TRUE;
8753 si->fullscreen = FALSE;
8754 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8755 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8756 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8757 si->ask_on_escape = TRUE;
8758 si->ask_on_escape_editor = TRUE;
8759 si->quick_switch = FALSE;
8760 si->input_on_focus = FALSE;
8761 si->prefer_aga_graphics = TRUE;
8762 si->game_frame_delay = GAME_FRAME_DELAY;
8763 si->sp_show_border_elements = FALSE;
8764 si->small_game_graphics = FALSE;
8765 si->show_snapshot_buttons = FALSE;
8767 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8768 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8769 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8771 si->override_level_graphics = FALSE;
8772 si->override_level_sounds = FALSE;
8773 si->override_level_music = FALSE;
8775 si->volume_simple = 100; /* percent */
8776 si->volume_loops = 100; /* percent */
8777 si->volume_music = 100; /* percent */
8779 si->network_mode = FALSE;
8780 si->network_player_nr = 0; /* first player */
8782 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8783 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8784 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8785 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8786 si->touch.draw_outlined = TRUE;
8787 si->touch.draw_pressed = TRUE;
8789 for (i = 0; i < 2; i++)
8791 char *default_grid_button[6][2] =
8797 { "111222", " vv " },
8798 { "111222", " vv " }
8800 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8801 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8802 int min_xsize = MIN(6, grid_xsize);
8803 int min_ysize = MIN(6, grid_ysize);
8804 int startx = grid_xsize - min_xsize;
8805 int starty = grid_ysize - min_ysize;
8808 // virtual buttons grid can only be set to defaults if video is initialized
8809 // (this will be repeated if virtual buttons are not loaded from setup file)
8810 if (video.initialized)
8812 si->touch.grid_xsize[i] = grid_xsize;
8813 si->touch.grid_ysize[i] = grid_ysize;
8817 si->touch.grid_xsize[i] = -1;
8818 si->touch.grid_ysize[i] = -1;
8821 for (x = 0; x < MAX_GRID_XSIZE; x++)
8822 for (y = 0; y < MAX_GRID_YSIZE; y++)
8823 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8825 for (x = 0; x < min_xsize; x++)
8826 for (y = 0; y < min_ysize; y++)
8827 si->touch.grid_button[i][x][starty + y] =
8828 default_grid_button[y][0][x];
8830 for (x = 0; x < min_xsize; x++)
8831 for (y = 0; y < min_ysize; y++)
8832 si->touch.grid_button[i][startx + x][starty + y] =
8833 default_grid_button[y][1][x];
8836 si->touch.grid_initialized = video.initialized;
8838 si->editor.el_boulderdash = TRUE;
8839 si->editor.el_emerald_mine = TRUE;
8840 si->editor.el_emerald_mine_club = TRUE;
8841 si->editor.el_more = TRUE;
8842 si->editor.el_sokoban = TRUE;
8843 si->editor.el_supaplex = TRUE;
8844 si->editor.el_diamond_caves = TRUE;
8845 si->editor.el_dx_boulderdash = TRUE;
8847 si->editor.el_mirror_magic = TRUE;
8848 si->editor.el_deflektor = TRUE;
8850 si->editor.el_chars = TRUE;
8851 si->editor.el_steel_chars = TRUE;
8853 si->editor.el_classic = TRUE;
8854 si->editor.el_custom = TRUE;
8856 si->editor.el_user_defined = FALSE;
8857 si->editor.el_dynamic = TRUE;
8859 si->editor.el_headlines = TRUE;
8861 si->editor.show_element_token = FALSE;
8863 si->editor.use_template_for_new_levels = TRUE;
8865 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8866 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8867 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8869 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8870 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8871 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8872 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8873 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8875 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8876 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8877 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8878 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8879 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8880 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8882 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8883 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8884 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8886 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8887 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8888 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8889 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8891 for (i = 0; i < MAX_PLAYERS; i++)
8893 si->input[i].use_joystick = FALSE;
8894 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8895 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8896 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8897 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8898 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8899 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8900 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8901 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8902 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8903 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8904 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8905 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8906 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8907 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8908 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8911 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8912 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8913 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8915 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8916 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8917 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8918 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8919 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8920 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8921 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8923 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8925 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8926 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8927 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8929 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8930 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8931 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8933 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8934 si->internal.choose_from_top_leveldir = FALSE;
8935 si->internal.show_scaling_in_title = TRUE;
8937 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8938 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8940 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8941 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8942 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8943 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8944 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8945 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8946 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8947 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8948 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8949 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8951 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8952 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8953 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8954 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8955 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8956 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8957 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8958 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8959 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8960 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8962 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8963 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8965 si->debug.show_frames_per_second = FALSE;
8967 si->options.verbose = FALSE;
8969 #if defined(PLATFORM_ANDROID)
8970 si->fullscreen = TRUE;
8974 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8976 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8979 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8981 si->editor_cascade.el_bd = TRUE;
8982 si->editor_cascade.el_em = TRUE;
8983 si->editor_cascade.el_emc = TRUE;
8984 si->editor_cascade.el_rnd = TRUE;
8985 si->editor_cascade.el_sb = TRUE;
8986 si->editor_cascade.el_sp = TRUE;
8987 si->editor_cascade.el_dc = TRUE;
8988 si->editor_cascade.el_dx = TRUE;
8990 si->editor_cascade.el_mm = TRUE;
8991 si->editor_cascade.el_df = TRUE;
8993 si->editor_cascade.el_chars = FALSE;
8994 si->editor_cascade.el_steel_chars = FALSE;
8995 si->editor_cascade.el_ce = FALSE;
8996 si->editor_cascade.el_ge = FALSE;
8997 si->editor_cascade.el_ref = FALSE;
8998 si->editor_cascade.el_user = FALSE;
8999 si->editor_cascade.el_dynamic = FALSE;
9002 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9004 static char *getHideSetupToken(void *setup_value)
9006 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9008 if (setup_value != NULL)
9009 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9011 return hide_setup_token;
9014 void setHideSetupEntry(void *setup_value)
9016 char *hide_setup_token = getHideSetupToken(setup_value);
9018 if (setup_value != NULL)
9019 setHashEntry(hide_setup_hash, hide_setup_token, "");
9022 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9024 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9025 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9027 setHideSetupEntry(setup_value);
9030 boolean hideSetupEntry(void *setup_value)
9032 char *hide_setup_token = getHideSetupToken(setup_value);
9034 return (setup_value != NULL &&
9035 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9038 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9039 struct TokenInfo *token_info,
9040 int token_nr, char *token_text)
9042 char *token_hide_text = getStringCat2(token_text, ".hide");
9043 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9045 /* set the value of this setup option in the setup option structure */
9046 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9048 /* check if this setup option should be hidden in the setup menu */
9049 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9050 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9053 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9054 struct TokenInfo *token_info,
9057 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9058 token_info[token_nr].text);
9061 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9065 if (!setup_file_hash)
9068 if (hide_setup_hash == NULL)
9069 hide_setup_hash = newSetupFileHash();
9073 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9074 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9077 /* virtual buttons setup */
9078 setup.touch.grid_initialized = TRUE;
9079 for (i = 0; i < 2; i++)
9081 int grid_xsize = setup.touch.grid_xsize[i];
9082 int grid_ysize = setup.touch.grid_ysize[i];
9085 // if virtual buttons are not loaded from setup file, repeat initializing
9086 // virtual buttons grid with default values later when video is initialized
9087 if (grid_xsize == -1 ||
9090 setup.touch.grid_initialized = FALSE;
9095 for (y = 0; y < grid_ysize; y++)
9097 char token_string[MAX_LINE_LEN];
9099 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9101 char *value_string = getHashEntry(setup_file_hash, token_string);
9103 if (value_string == NULL)
9106 for (x = 0; x < grid_xsize; x++)
9108 char c = value_string[x];
9110 setup.touch.grid_button[i][x][y] =
9111 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9118 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9119 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9122 /* shortcut setup */
9123 ssi = setup.shortcut;
9124 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9125 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9126 setup.shortcut = ssi;
9129 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9133 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9135 sii = setup.input[pnr];
9136 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9138 char full_token[100];
9140 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9141 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9144 setup.input[pnr] = sii;
9149 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9150 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9153 /* internal setup */
9154 sxi = setup.internal;
9155 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9156 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9157 setup.internal = sxi;
9161 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9162 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9166 soi = setup.options;
9167 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9168 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9169 setup.options = soi;
9171 setHideRelatedSetupEntries();
9174 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9178 if (!setup_file_hash)
9182 sasi = setup.auto_setup;
9183 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9184 setSetupInfo(auto_setup_tokens, i,
9185 getHashEntry(setup_file_hash,
9186 auto_setup_tokens[i].text));
9187 setup.auto_setup = sasi;
9190 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9194 if (!setup_file_hash)
9197 /* editor cascade setup */
9198 seci = setup.editor_cascade;
9199 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9200 setSetupInfo(editor_cascade_setup_tokens, i,
9201 getHashEntry(setup_file_hash,
9202 editor_cascade_setup_tokens[i].text));
9203 setup.editor_cascade = seci;
9206 void LoadSetupFromFilename(char *filename)
9208 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9210 if (setup_file_hash)
9212 decodeSetupFileHash(setup_file_hash);
9214 freeSetupFileHash(setup_file_hash);
9218 Error(ERR_DEBUG, "using default setup values");
9222 static void LoadSetup_SpecialPostProcessing()
9224 char *player_name_new;
9226 /* needed to work around problems with fixed length strings */
9227 player_name_new = get_corrected_login_name(setup.player_name);
9228 free(setup.player_name);
9229 setup.player_name = player_name_new;
9231 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9232 if (setup.scroll_delay == FALSE)
9234 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9235 setup.scroll_delay = TRUE; /* now always "on" */
9238 /* make sure that scroll delay value stays inside valid range */
9239 setup.scroll_delay_value =
9240 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9247 /* always start with reliable default values */
9248 setSetupInfoToDefaults(&setup);
9250 /* try to load setup values from default setup file */
9251 filename = getDefaultSetupFilename();
9253 if (fileExists(filename))
9254 LoadSetupFromFilename(filename);
9256 /* try to load setup values from user setup file */
9257 filename = getSetupFilename();
9259 LoadSetupFromFilename(filename);
9261 LoadSetup_SpecialPostProcessing();
9264 void LoadSetup_AutoSetup()
9266 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9267 SetupFileHash *setup_file_hash = NULL;
9269 /* always start with reliable default values */
9270 setSetupInfoToDefaults_AutoSetup(&setup);
9272 setup_file_hash = loadSetupFileHash(filename);
9274 if (setup_file_hash)
9276 decodeSetupFileHash_AutoSetup(setup_file_hash);
9278 freeSetupFileHash(setup_file_hash);
9284 void LoadSetup_EditorCascade()
9286 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9287 SetupFileHash *setup_file_hash = NULL;
9289 /* always start with reliable default values */
9290 setSetupInfoToDefaults_EditorCascade(&setup);
9292 setup_file_hash = loadSetupFileHash(filename);
9294 if (setup_file_hash)
9296 decodeSetupFileHash_EditorCascade(setup_file_hash);
9298 freeSetupFileHash(setup_file_hash);
9304 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9307 char mapping_guid[MAX_LINE_LEN];
9308 char *mapping_start, *mapping_end;
9310 // get GUID from game controller mapping line: copy complete line
9311 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9312 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9314 // get GUID from game controller mapping line: cut after GUID part
9315 mapping_start = strchr(mapping_guid, ',');
9316 if (mapping_start != NULL)
9317 *mapping_start = '\0';
9319 // cut newline from game controller mapping line
9320 mapping_end = strchr(mapping_line, '\n');
9321 if (mapping_end != NULL)
9322 *mapping_end = '\0';
9324 // add mapping entry to game controller mappings hash
9325 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9328 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9333 if (!(file = fopen(filename, MODE_READ)))
9335 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9342 char line[MAX_LINE_LEN];
9344 if (!fgets(line, MAX_LINE_LEN, file))
9347 addGameControllerMappingToHash(mappings_hash, line);
9355 char *filename = getSetupFilename();
9359 InitUserDataDirectory();
9361 if (!(file = fopen(filename, MODE_WRITE)))
9363 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9367 fprintFileHeader(file, SETUP_FILENAME);
9371 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9373 /* just to make things nicer :) */
9374 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9375 i == SETUP_TOKEN_GRAPHICS_SET ||
9376 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9377 i == SETUP_TOKEN_NETWORK_MODE ||
9378 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9379 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9380 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9381 fprintf(file, "\n");
9383 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9386 /* virtual buttons setup */
9387 for (i = 0; i < 2; i++)
9389 int grid_xsize = setup.touch.grid_xsize[i];
9390 int grid_ysize = setup.touch.grid_ysize[i];
9393 fprintf(file, "\n");
9395 for (y = 0; y < grid_ysize; y++)
9397 char token_string[MAX_LINE_LEN];
9398 char value_string[MAX_LINE_LEN];
9400 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9402 for (x = 0; x < grid_xsize; x++)
9404 char c = setup.touch.grid_button[i][x][y];
9406 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9409 value_string[grid_xsize] = '\0';
9411 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9417 fprintf(file, "\n");
9418 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9419 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9421 /* shortcut setup */
9422 ssi = setup.shortcut;
9423 fprintf(file, "\n");
9424 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9425 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9428 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9432 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9433 fprintf(file, "\n");
9435 sii = setup.input[pnr];
9436 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9437 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9442 fprintf(file, "\n");
9443 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9444 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9446 /* internal setup */
9447 /* (internal setup values not saved to user setup file) */
9451 fprintf(file, "\n");
9452 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9453 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9456 soi = setup.options;
9457 fprintf(file, "\n");
9458 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9459 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9463 SetFilePermissions(filename, PERMS_PRIVATE);
9466 void SaveSetup_AutoSetup()
9468 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9472 InitUserDataDirectory();
9474 if (!(file = fopen(filename, MODE_WRITE)))
9476 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9481 fprintFileHeader(file, AUTOSETUP_FILENAME);
9483 sasi = setup.auto_setup;
9484 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9485 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9489 SetFilePermissions(filename, PERMS_PRIVATE);
9494 void SaveSetup_EditorCascade()
9496 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9500 InitUserDataDirectory();
9502 if (!(file = fopen(filename, MODE_WRITE)))
9504 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9509 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9511 seci = setup.editor_cascade;
9512 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9513 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9517 SetFilePermissions(filename, PERMS_PRIVATE);
9522 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9527 if (!(file = fopen(filename, MODE_WRITE)))
9529 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9534 BEGIN_HASH_ITERATION(mappings_hash, itr)
9536 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9538 END_HASH_ITERATION(mappings_hash, itr)
9543 void SaveSetup_AddGameControllerMapping(char *mapping)
9545 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9546 SetupFileHash *mappings_hash = newSetupFileHash();
9548 InitUserDataDirectory();
9550 // load existing personal game controller mappings
9551 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9553 // add new mapping to personal game controller mappings
9554 addGameControllerMappingToHash(mappings_hash, mapping);
9556 // save updated personal game controller mappings
9557 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9559 freeSetupFileHash(mappings_hash);
9563 void LoadCustomElementDescriptions()
9565 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9566 SetupFileHash *setup_file_hash;
9569 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9571 if (element_info[i].custom_description != NULL)
9573 free(element_info[i].custom_description);
9574 element_info[i].custom_description = NULL;
9578 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9581 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9583 char *token = getStringCat2(element_info[i].token_name, ".name");
9584 char *value = getHashEntry(setup_file_hash, token);
9587 element_info[i].custom_description = getStringCopy(value);
9592 freeSetupFileHash(setup_file_hash);
9595 static int getElementFromToken(char *token)
9597 char *value = getHashEntry(element_token_hash, token);
9602 Error(ERR_WARN, "unknown element token '%s'", token);
9604 return EL_UNDEFINED;
9607 static int get_token_parameter_value(char *token, char *value_raw)
9611 if (token == NULL || value_raw == NULL)
9612 return ARG_UNDEFINED_VALUE;
9614 suffix = strrchr(token, '.');
9618 if (strEqual(suffix, ".element"))
9619 return getElementFromToken(value_raw);
9621 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9622 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9625 void InitMenuDesignSettings_Static()
9629 /* always start with reliable default values from static default config */
9630 for (i = 0; image_config_vars[i].token != NULL; i++)
9632 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9635 *image_config_vars[i].value =
9636 get_token_parameter_value(image_config_vars[i].token, value);
9640 static void InitMenuDesignSettings_SpecialPreProcessing()
9644 /* the following initializes hierarchical values from static configuration */
9646 /* special case: initialize "ARG_DEFAULT" values in static default config */
9647 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9648 titlescreen_initial_first_default.fade_mode =
9649 title_initial_first_default.fade_mode;
9650 titlescreen_initial_first_default.fade_delay =
9651 title_initial_first_default.fade_delay;
9652 titlescreen_initial_first_default.post_delay =
9653 title_initial_first_default.post_delay;
9654 titlescreen_initial_first_default.auto_delay =
9655 title_initial_first_default.auto_delay;
9656 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9657 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9658 titlescreen_first_default.post_delay = title_first_default.post_delay;
9659 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9660 titlemessage_initial_first_default.fade_mode =
9661 title_initial_first_default.fade_mode;
9662 titlemessage_initial_first_default.fade_delay =
9663 title_initial_first_default.fade_delay;
9664 titlemessage_initial_first_default.post_delay =
9665 title_initial_first_default.post_delay;
9666 titlemessage_initial_first_default.auto_delay =
9667 title_initial_first_default.auto_delay;
9668 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9669 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9670 titlemessage_first_default.post_delay = title_first_default.post_delay;
9671 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9673 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9674 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9675 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9676 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9677 titlescreen_default.fade_mode = title_default.fade_mode;
9678 titlescreen_default.fade_delay = title_default.fade_delay;
9679 titlescreen_default.post_delay = title_default.post_delay;
9680 titlescreen_default.auto_delay = title_default.auto_delay;
9681 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9682 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9683 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9684 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9685 titlemessage_default.fade_mode = title_default.fade_mode;
9686 titlemessage_default.fade_delay = title_default.fade_delay;
9687 titlemessage_default.post_delay = title_default.post_delay;
9688 titlemessage_default.auto_delay = title_default.auto_delay;
9690 /* special case: initialize "ARG_DEFAULT" values in static default config */
9691 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9692 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9694 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9695 titlescreen_first[i] = titlescreen_first_default;
9696 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9697 titlemessage_first[i] = titlemessage_first_default;
9699 titlescreen_initial[i] = titlescreen_initial_default;
9700 titlescreen[i] = titlescreen_default;
9701 titlemessage_initial[i] = titlemessage_initial_default;
9702 titlemessage[i] = titlemessage_default;
9705 /* special case: initialize "ARG_DEFAULT" values in static default config */
9706 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9707 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9709 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9712 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9713 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9714 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9717 /* special case: initialize "ARG_DEFAULT" values in static default config */
9718 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9719 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9721 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9722 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9723 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9725 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9728 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9732 static void InitMenuDesignSettings_SpecialPostProcessing()
9736 struct XY *dst, *src;
9740 { &game.button.save, &game.button.stop },
9741 { &game.button.pause2, &game.button.pause },
9742 { &game.button.load, &game.button.play },
9743 { &game.button.undo, &game.button.stop },
9744 { &game.button.redo, &game.button.play },
9750 /* special case: initialize later added SETUP list size from LEVELS value */
9751 if (menu.list_size[GAME_MODE_SETUP] == -1)
9752 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9754 /* set default position for snapshot buttons to stop/pause/play buttons */
9755 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9756 if ((*game_buttons_xy[i].dst).x == -1 &&
9757 (*game_buttons_xy[i].dst).y == -1)
9758 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9761 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9765 struct XYTileSize *dst, *src;
9768 editor_buttons_xy[] =
9771 &editor.button.element_left, &editor.palette.element_left,
9772 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9775 &editor.button.element_middle, &editor.palette.element_middle,
9776 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9779 &editor.button.element_right, &editor.palette.element_right,
9780 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9787 /* set default position for element buttons to element graphics */
9788 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9790 if ((*editor_buttons_xy[i].dst).x == -1 &&
9791 (*editor_buttons_xy[i].dst).y == -1)
9793 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9795 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9797 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9802 static void LoadMenuDesignSettingsFromFilename(char *filename)
9804 static struct TitleFadingInfo tfi;
9805 static struct TitleMessageInfo tmi;
9806 static struct TokenInfo title_tokens[] =
9808 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9809 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9810 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9811 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9815 static struct TokenInfo titlemessage_tokens[] =
9817 { TYPE_INTEGER, &tmi.x, ".x" },
9818 { TYPE_INTEGER, &tmi.y, ".y" },
9819 { TYPE_INTEGER, &tmi.width, ".width" },
9820 { TYPE_INTEGER, &tmi.height, ".height" },
9821 { TYPE_INTEGER, &tmi.chars, ".chars" },
9822 { TYPE_INTEGER, &tmi.lines, ".lines" },
9823 { TYPE_INTEGER, &tmi.align, ".align" },
9824 { TYPE_INTEGER, &tmi.valign, ".valign" },
9825 { TYPE_INTEGER, &tmi.font, ".font" },
9826 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9827 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9828 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9829 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9830 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9831 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9832 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9833 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9839 struct TitleFadingInfo *info;
9844 /* initialize first titles from "enter screen" definitions, if defined */
9845 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9846 { &title_first_default, "menu.enter_screen.TITLE" },
9848 /* initialize title screens from "next screen" definitions, if defined */
9849 { &title_initial_default, "menu.next_screen.TITLE" },
9850 { &title_default, "menu.next_screen.TITLE" },
9856 struct TitleMessageInfo *array;
9859 titlemessage_arrays[] =
9861 /* initialize first titles from "enter screen" definitions, if defined */
9862 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9863 { titlescreen_first, "menu.enter_screen.TITLE" },
9864 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9865 { titlemessage_first, "menu.enter_screen.TITLE" },
9867 /* initialize titles from "next screen" definitions, if defined */
9868 { titlescreen_initial, "menu.next_screen.TITLE" },
9869 { titlescreen, "menu.next_screen.TITLE" },
9870 { titlemessage_initial, "menu.next_screen.TITLE" },
9871 { titlemessage, "menu.next_screen.TITLE" },
9873 /* overwrite titles with title definitions, if defined */
9874 { titlescreen_initial_first, "[title_initial]" },
9875 { titlescreen_first, "[title]" },
9876 { titlemessage_initial_first, "[title_initial]" },
9877 { titlemessage_first, "[title]" },
9879 { titlescreen_initial, "[title_initial]" },
9880 { titlescreen, "[title]" },
9881 { titlemessage_initial, "[title_initial]" },
9882 { titlemessage, "[title]" },
9884 /* overwrite titles with title screen/message definitions, if defined */
9885 { titlescreen_initial_first, "[titlescreen_initial]" },
9886 { titlescreen_first, "[titlescreen]" },
9887 { titlemessage_initial_first, "[titlemessage_initial]" },
9888 { titlemessage_first, "[titlemessage]" },
9890 { titlescreen_initial, "[titlescreen_initial]" },
9891 { titlescreen, "[titlescreen]" },
9892 { titlemessage_initial, "[titlemessage_initial]" },
9893 { titlemessage, "[titlemessage]" },
9897 SetupFileHash *setup_file_hash;
9900 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9903 /* the following initializes hierarchical values from dynamic configuration */
9905 /* special case: initialize with default values that may be overwritten */
9906 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9907 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9909 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9910 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9911 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9913 if (value_1 != NULL)
9914 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9915 if (value_2 != NULL)
9916 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9917 if (value_3 != NULL)
9918 menu.list_size[i] = get_integer_from_string(value_3);
9921 /* special case: initialize with default values that may be overwritten */
9922 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9923 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9925 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9926 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9928 if (value_1 != NULL)
9929 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9930 if (value_2 != NULL)
9931 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9933 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9935 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9937 if (value_1 != NULL)
9938 menu.list_size_info[i] = get_integer_from_string(value_1);
9942 /* special case: initialize with default values that may be overwritten */
9943 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9944 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9946 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9947 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9949 if (value_1 != NULL)
9950 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9951 if (value_2 != NULL)
9952 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9955 /* special case: initialize with default values that may be overwritten */
9956 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9957 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9959 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
9960 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
9961 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
9962 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
9963 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
9964 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
9965 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
9966 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
9967 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
9969 if (value_1 != NULL)
9970 menu.left_spacing_info[i] = get_integer_from_string(value_1);
9971 if (value_2 != NULL)
9972 menu.right_spacing_info[i] = get_integer_from_string(value_2);
9973 if (value_3 != NULL)
9974 menu.top_spacing_info[i] = get_integer_from_string(value_3);
9975 if (value_4 != NULL)
9976 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
9977 if (value_5 != NULL)
9978 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
9979 if (value_6 != NULL)
9980 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
9981 if (value_7 != NULL)
9982 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
9983 if (value_8 != NULL)
9984 menu.line_spacing_info[i] = get_integer_from_string(value_8);
9985 if (value_9 != NULL)
9986 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
9989 /* special case: initialize with default values that may be overwritten */
9990 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9991 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9993 char *token_1 = "menu.enter_screen.fade_mode";
9994 char *token_2 = "menu.enter_screen.fade_delay";
9995 char *token_3 = "menu.enter_screen.post_delay";
9996 char *token_4 = "menu.leave_screen.fade_mode";
9997 char *token_5 = "menu.leave_screen.fade_delay";
9998 char *token_6 = "menu.leave_screen.post_delay";
9999 char *token_7 = "menu.next_screen.fade_mode";
10000 char *token_8 = "menu.next_screen.fade_delay";
10001 char *token_9 = "menu.next_screen.post_delay";
10002 char *value_1 = getHashEntry(setup_file_hash, token_1);
10003 char *value_2 = getHashEntry(setup_file_hash, token_2);
10004 char *value_3 = getHashEntry(setup_file_hash, token_3);
10005 char *value_4 = getHashEntry(setup_file_hash, token_4);
10006 char *value_5 = getHashEntry(setup_file_hash, token_5);
10007 char *value_6 = getHashEntry(setup_file_hash, token_6);
10008 char *value_7 = getHashEntry(setup_file_hash, token_7);
10009 char *value_8 = getHashEntry(setup_file_hash, token_8);
10010 char *value_9 = getHashEntry(setup_file_hash, token_9);
10012 if (value_1 != NULL)
10013 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10015 if (value_2 != NULL)
10016 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10018 if (value_3 != NULL)
10019 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10021 if (value_4 != NULL)
10022 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10024 if (value_5 != NULL)
10025 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10027 if (value_6 != NULL)
10028 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10030 if (value_7 != NULL)
10031 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10033 if (value_8 != NULL)
10034 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10036 if (value_9 != NULL)
10037 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10041 /* special case: initialize with default values that may be overwritten */
10042 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10043 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10045 char *token_w1 = "viewport.window.width";
10046 char *token_w2 = "viewport.window.height";
10047 char *token_01 = "viewport.playfield.x";
10048 char *token_02 = "viewport.playfield.y";
10049 char *token_03 = "viewport.playfield.width";
10050 char *token_04 = "viewport.playfield.height";
10051 char *token_05 = "viewport.playfield.border_size";
10052 char *token_06 = "viewport.door_1.x";
10053 char *token_07 = "viewport.door_1.y";
10054 char *token_08 = "viewport.door_1.width";
10055 char *token_09 = "viewport.door_1.height";
10056 char *token_10 = "viewport.door_1.border_size";
10057 char *token_11 = "viewport.door_2.x";
10058 char *token_12 = "viewport.door_2.y";
10059 char *token_13 = "viewport.door_2.width";
10060 char *token_14 = "viewport.door_2.height";
10061 char *token_15 = "viewport.door_2.border_size";
10062 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10063 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10064 char *value_01 = getHashEntry(setup_file_hash, token_01);
10065 char *value_02 = getHashEntry(setup_file_hash, token_02);
10066 char *value_03 = getHashEntry(setup_file_hash, token_03);
10067 char *value_04 = getHashEntry(setup_file_hash, token_04);
10068 char *value_05 = getHashEntry(setup_file_hash, token_05);
10069 char *value_06 = getHashEntry(setup_file_hash, token_06);
10070 char *value_07 = getHashEntry(setup_file_hash, token_07);
10071 char *value_08 = getHashEntry(setup_file_hash, token_08);
10072 char *value_09 = getHashEntry(setup_file_hash, token_09);
10073 char *value_10 = getHashEntry(setup_file_hash, token_10);
10074 char *value_11 = getHashEntry(setup_file_hash, token_11);
10075 char *value_12 = getHashEntry(setup_file_hash, token_12);
10076 char *value_13 = getHashEntry(setup_file_hash, token_13);
10077 char *value_14 = getHashEntry(setup_file_hash, token_14);
10078 char *value_15 = getHashEntry(setup_file_hash, token_15);
10080 if (value_w1 != NULL)
10081 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10082 if (value_w2 != NULL)
10083 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10084 if (value_01 != NULL)
10085 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10086 if (value_02 != NULL)
10087 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10088 if (value_03 != NULL)
10089 viewport.playfield[i].width = get_token_parameter_value(token_03,
10091 if (value_04 != NULL)
10092 viewport.playfield[i].height = get_token_parameter_value(token_04,
10094 if (value_05 != NULL)
10095 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10097 if (value_06 != NULL)
10098 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10099 if (value_07 != NULL)
10100 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10101 if (value_08 != NULL)
10102 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10103 if (value_09 != NULL)
10104 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10105 if (value_10 != NULL)
10106 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10108 if (value_11 != NULL)
10109 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10110 if (value_12 != NULL)
10111 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10112 if (value_13 != NULL)
10113 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10114 if (value_14 != NULL)
10115 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10116 if (value_15 != NULL)
10117 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10121 /* special case: initialize with default values that may be overwritten */
10122 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10123 for (i = 0; title_info[i].info != NULL; i++)
10125 struct TitleFadingInfo *info = title_info[i].info;
10126 char *base_token = title_info[i].text;
10128 for (j = 0; title_tokens[j].type != -1; j++)
10130 char *token = getStringCat2(base_token, title_tokens[j].text);
10131 char *value = getHashEntry(setup_file_hash, token);
10135 int parameter_value = get_token_parameter_value(token, value);
10139 *(int *)title_tokens[j].value = (int)parameter_value;
10148 /* special case: initialize with default values that may be overwritten */
10149 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10150 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10152 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10153 char *base_token = titlemessage_arrays[i].text;
10155 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10157 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10158 char *value = getHashEntry(setup_file_hash, token);
10162 int parameter_value = get_token_parameter_value(token, value);
10164 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10168 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10169 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10171 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10181 /* read (and overwrite with) values that may be specified in config file */
10182 for (i = 0; image_config_vars[i].token != NULL; i++)
10184 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10186 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10187 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10188 *image_config_vars[i].value =
10189 get_token_parameter_value(image_config_vars[i].token, value);
10192 freeSetupFileHash(setup_file_hash);
10195 void LoadMenuDesignSettings()
10197 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10199 InitMenuDesignSettings_Static();
10200 InitMenuDesignSettings_SpecialPreProcessing();
10202 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10204 /* first look for special settings configured in level series config */
10205 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10207 if (fileExists(filename_base))
10208 LoadMenuDesignSettingsFromFilename(filename_base);
10211 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10213 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10214 LoadMenuDesignSettingsFromFilename(filename_local);
10216 InitMenuDesignSettings_SpecialPostProcessing();
10219 void LoadMenuDesignSettings_AfterGraphics()
10221 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10224 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10226 char *filename = getEditorSetupFilename();
10227 SetupFileList *setup_file_list, *list;
10228 SetupFileHash *element_hash;
10229 int num_unknown_tokens = 0;
10232 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10235 element_hash = newSetupFileHash();
10237 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10238 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10240 /* determined size may be larger than needed (due to unknown elements) */
10242 for (list = setup_file_list; list != NULL; list = list->next)
10245 /* add space for up to 3 more elements for padding that may be needed */
10246 *num_elements += 3;
10248 /* free memory for old list of elements, if needed */
10249 checked_free(*elements);
10251 /* allocate memory for new list of elements */
10252 *elements = checked_malloc(*num_elements * sizeof(int));
10255 for (list = setup_file_list; list != NULL; list = list->next)
10257 char *value = getHashEntry(element_hash, list->token);
10259 if (value == NULL) /* try to find obsolete token mapping */
10261 char *mapped_token = get_mapped_token(list->token);
10263 if (mapped_token != NULL)
10265 value = getHashEntry(element_hash, mapped_token);
10267 free(mapped_token);
10273 (*elements)[(*num_elements)++] = atoi(value);
10277 if (num_unknown_tokens == 0)
10279 Error(ERR_INFO_LINE, "-");
10280 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10281 Error(ERR_INFO, "- config file: '%s'", filename);
10283 num_unknown_tokens++;
10286 Error(ERR_INFO, "- token: '%s'", list->token);
10290 if (num_unknown_tokens > 0)
10291 Error(ERR_INFO_LINE, "-");
10293 while (*num_elements % 4) /* pad with empty elements, if needed */
10294 (*elements)[(*num_elements)++] = EL_EMPTY;
10296 freeSetupFileList(setup_file_list);
10297 freeSetupFileHash(element_hash);
10300 for (i = 0; i < *num_elements; i++)
10301 printf("editor: element '%s' [%d]\n",
10302 element_info[(*elements)[i]].token_name, (*elements)[i]);
10306 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10309 SetupFileHash *setup_file_hash = NULL;
10310 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10311 char *filename_music, *filename_prefix, *filename_info;
10317 token_to_value_ptr[] =
10319 { "title_header", &tmp_music_file_info.title_header },
10320 { "artist_header", &tmp_music_file_info.artist_header },
10321 { "album_header", &tmp_music_file_info.album_header },
10322 { "year_header", &tmp_music_file_info.year_header },
10324 { "title", &tmp_music_file_info.title },
10325 { "artist", &tmp_music_file_info.artist },
10326 { "album", &tmp_music_file_info.album },
10327 { "year", &tmp_music_file_info.year },
10333 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10334 getCustomMusicFilename(basename));
10336 if (filename_music == NULL)
10339 /* ---------- try to replace file extension ---------- */
10341 filename_prefix = getStringCopy(filename_music);
10342 if (strrchr(filename_prefix, '.') != NULL)
10343 *strrchr(filename_prefix, '.') = '\0';
10344 filename_info = getStringCat2(filename_prefix, ".txt");
10346 if (fileExists(filename_info))
10347 setup_file_hash = loadSetupFileHash(filename_info);
10349 free(filename_prefix);
10350 free(filename_info);
10352 if (setup_file_hash == NULL)
10354 /* ---------- try to add file extension ---------- */
10356 filename_prefix = getStringCopy(filename_music);
10357 filename_info = getStringCat2(filename_prefix, ".txt");
10359 if (fileExists(filename_info))
10360 setup_file_hash = loadSetupFileHash(filename_info);
10362 free(filename_prefix);
10363 free(filename_info);
10366 if (setup_file_hash == NULL)
10369 /* ---------- music file info found ---------- */
10371 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10373 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10375 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10377 *token_to_value_ptr[i].value_ptr =
10378 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10381 tmp_music_file_info.basename = getStringCopy(basename);
10382 tmp_music_file_info.music = music;
10383 tmp_music_file_info.is_sound = is_sound;
10385 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10386 *new_music_file_info = tmp_music_file_info;
10388 return new_music_file_info;
10391 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10393 return get_music_file_info_ext(basename, music, FALSE);
10396 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10398 return get_music_file_info_ext(basename, sound, TRUE);
10401 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10402 char *basename, boolean is_sound)
10404 for (; list != NULL; list = list->next)
10405 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10411 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10413 return music_info_listed_ext(list, basename, FALSE);
10416 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10418 return music_info_listed_ext(list, basename, TRUE);
10421 void LoadMusicInfo()
10423 char *music_directory = getCustomMusicDirectory();
10424 int num_music = getMusicListSize();
10425 int num_music_noconf = 0;
10426 int num_sounds = getSoundListSize();
10428 DirectoryEntry *dir_entry;
10429 struct FileInfo *music, *sound;
10430 struct MusicFileInfo *next, **new;
10433 while (music_file_info != NULL)
10435 next = music_file_info->next;
10437 checked_free(music_file_info->basename);
10439 checked_free(music_file_info->title_header);
10440 checked_free(music_file_info->artist_header);
10441 checked_free(music_file_info->album_header);
10442 checked_free(music_file_info->year_header);
10444 checked_free(music_file_info->title);
10445 checked_free(music_file_info->artist);
10446 checked_free(music_file_info->album);
10447 checked_free(music_file_info->year);
10449 free(music_file_info);
10451 music_file_info = next;
10454 new = &music_file_info;
10456 for (i = 0; i < num_music; i++)
10458 music = getMusicListEntry(i);
10460 if (music->filename == NULL)
10463 if (strEqual(music->filename, UNDEFINED_FILENAME))
10466 /* a configured file may be not recognized as music */
10467 if (!FileIsMusic(music->filename))
10470 if (!music_info_listed(music_file_info, music->filename))
10472 *new = get_music_file_info(music->filename, i);
10475 new = &(*new)->next;
10479 if ((dir = openDirectory(music_directory)) == NULL)
10481 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10485 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10487 char *basename = dir_entry->basename;
10488 boolean music_already_used = FALSE;
10491 /* skip all music files that are configured in music config file */
10492 for (i = 0; i < num_music; i++)
10494 music = getMusicListEntry(i);
10496 if (music->filename == NULL)
10499 if (strEqual(basename, music->filename))
10501 music_already_used = TRUE;
10506 if (music_already_used)
10509 if (!FileIsMusic(dir_entry->filename))
10512 if (!music_info_listed(music_file_info, basename))
10514 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10517 new = &(*new)->next;
10520 num_music_noconf++;
10523 closeDirectory(dir);
10525 for (i = 0; i < num_sounds; i++)
10527 sound = getSoundListEntry(i);
10529 if (sound->filename == NULL)
10532 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10535 /* a configured file may be not recognized as sound */
10536 if (!FileIsSound(sound->filename))
10539 if (!sound_info_listed(music_file_info, sound->filename))
10541 *new = get_sound_file_info(sound->filename, i);
10543 new = &(*new)->next;
10548 void add_helpanim_entry(int element, int action, int direction, int delay,
10549 int *num_list_entries)
10551 struct HelpAnimInfo *new_list_entry;
10552 (*num_list_entries)++;
10555 checked_realloc(helpanim_info,
10556 *num_list_entries * sizeof(struct HelpAnimInfo));
10557 new_list_entry = &helpanim_info[*num_list_entries - 1];
10559 new_list_entry->element = element;
10560 new_list_entry->action = action;
10561 new_list_entry->direction = direction;
10562 new_list_entry->delay = delay;
10565 void print_unknown_token(char *filename, char *token, int token_nr)
10569 Error(ERR_INFO_LINE, "-");
10570 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10571 Error(ERR_INFO, "- config file: '%s'", filename);
10574 Error(ERR_INFO, "- token: '%s'", token);
10577 void print_unknown_token_end(int token_nr)
10580 Error(ERR_INFO_LINE, "-");
10583 void LoadHelpAnimInfo()
10585 char *filename = getHelpAnimFilename();
10586 SetupFileList *setup_file_list = NULL, *list;
10587 SetupFileHash *element_hash, *action_hash, *direction_hash;
10588 int num_list_entries = 0;
10589 int num_unknown_tokens = 0;
10592 if (fileExists(filename))
10593 setup_file_list = loadSetupFileList(filename);
10595 if (setup_file_list == NULL)
10597 /* use reliable default values from static configuration */
10598 SetupFileList *insert_ptr;
10600 insert_ptr = setup_file_list =
10601 newSetupFileList(helpanim_config[0].token,
10602 helpanim_config[0].value);
10604 for (i = 1; helpanim_config[i].token; i++)
10605 insert_ptr = addListEntry(insert_ptr,
10606 helpanim_config[i].token,
10607 helpanim_config[i].value);
10610 element_hash = newSetupFileHash();
10611 action_hash = newSetupFileHash();
10612 direction_hash = newSetupFileHash();
10614 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10615 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10617 for (i = 0; i < NUM_ACTIONS; i++)
10618 setHashEntry(action_hash, element_action_info[i].suffix,
10619 i_to_a(element_action_info[i].value));
10621 /* do not store direction index (bit) here, but direction value! */
10622 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10623 setHashEntry(direction_hash, element_direction_info[i].suffix,
10624 i_to_a(1 << element_direction_info[i].value));
10626 for (list = setup_file_list; list != NULL; list = list->next)
10628 char *element_token, *action_token, *direction_token;
10629 char *element_value, *action_value, *direction_value;
10630 int delay = atoi(list->value);
10632 if (strEqual(list->token, "end"))
10634 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10639 /* first try to break element into element/action/direction parts;
10640 if this does not work, also accept combined "element[.act][.dir]"
10641 elements (like "dynamite.active"), which are unique elements */
10643 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10645 element_value = getHashEntry(element_hash, list->token);
10646 if (element_value != NULL) /* element found */
10647 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10648 &num_list_entries);
10651 /* no further suffixes found -- this is not an element */
10652 print_unknown_token(filename, list->token, num_unknown_tokens++);
10658 /* token has format "<prefix>.<something>" */
10660 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10661 direction_token = action_token; /* ... or direction */
10663 element_token = getStringCopy(list->token);
10664 *strchr(element_token, '.') = '\0';
10666 element_value = getHashEntry(element_hash, element_token);
10668 if (element_value == NULL) /* this is no element */
10670 element_value = getHashEntry(element_hash, list->token);
10671 if (element_value != NULL) /* combined element found */
10672 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10673 &num_list_entries);
10675 print_unknown_token(filename, list->token, num_unknown_tokens++);
10677 free(element_token);
10682 action_value = getHashEntry(action_hash, action_token);
10684 if (action_value != NULL) /* action found */
10686 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10687 &num_list_entries);
10689 free(element_token);
10694 direction_value = getHashEntry(direction_hash, direction_token);
10696 if (direction_value != NULL) /* direction found */
10698 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10699 &num_list_entries);
10701 free(element_token);
10706 if (strchr(action_token + 1, '.') == NULL)
10708 /* no further suffixes found -- this is not an action nor direction */
10710 element_value = getHashEntry(element_hash, list->token);
10711 if (element_value != NULL) /* combined element found */
10712 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10713 &num_list_entries);
10715 print_unknown_token(filename, list->token, num_unknown_tokens++);
10717 free(element_token);
10722 /* token has format "<prefix>.<suffix>.<something>" */
10724 direction_token = strchr(action_token + 1, '.');
10726 action_token = getStringCopy(action_token);
10727 *strchr(action_token + 1, '.') = '\0';
10729 action_value = getHashEntry(action_hash, action_token);
10731 if (action_value == NULL) /* this is no action */
10733 element_value = getHashEntry(element_hash, list->token);
10734 if (element_value != NULL) /* combined element found */
10735 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10736 &num_list_entries);
10738 print_unknown_token(filename, list->token, num_unknown_tokens++);
10740 free(element_token);
10741 free(action_token);
10746 direction_value = getHashEntry(direction_hash, direction_token);
10748 if (direction_value != NULL) /* direction found */
10750 add_helpanim_entry(atoi(element_value), atoi(action_value),
10751 atoi(direction_value), delay, &num_list_entries);
10753 free(element_token);
10754 free(action_token);
10759 /* this is no direction */
10761 element_value = getHashEntry(element_hash, list->token);
10762 if (element_value != NULL) /* combined element found */
10763 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10764 &num_list_entries);
10766 print_unknown_token(filename, list->token, num_unknown_tokens++);
10768 free(element_token);
10769 free(action_token);
10772 print_unknown_token_end(num_unknown_tokens);
10774 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10775 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10777 freeSetupFileList(setup_file_list);
10778 freeSetupFileHash(element_hash);
10779 freeSetupFileHash(action_hash);
10780 freeSetupFileHash(direction_hash);
10783 for (i = 0; i < num_list_entries; i++)
10784 printf("::: '%s': %d, %d, %d => %d\n",
10785 EL_NAME(helpanim_info[i].element),
10786 helpanim_info[i].element,
10787 helpanim_info[i].action,
10788 helpanim_info[i].direction,
10789 helpanim_info[i].delay);
10793 void LoadHelpTextInfo()
10795 char *filename = getHelpTextFilename();
10798 if (helptext_info != NULL)
10800 freeSetupFileHash(helptext_info);
10801 helptext_info = NULL;
10804 if (fileExists(filename))
10805 helptext_info = loadSetupFileHash(filename);
10807 if (helptext_info == NULL)
10809 /* use reliable default values from static configuration */
10810 helptext_info = newSetupFileHash();
10812 for (i = 0; helptext_config[i].token; i++)
10813 setHashEntry(helptext_info,
10814 helptext_config[i].token,
10815 helptext_config[i].value);
10819 BEGIN_HASH_ITERATION(helptext_info, itr)
10821 printf("::: '%s' => '%s'\n",
10822 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10824 END_HASH_ITERATION(hash, itr)
10829 /* ------------------------------------------------------------------------- */
10830 /* convert levels */
10831 /* ------------------------------------------------------------------------- */
10833 #define MAX_NUM_CONVERT_LEVELS 1000
10835 void ConvertLevels()
10837 static LevelDirTree *convert_leveldir = NULL;
10838 static int convert_level_nr = -1;
10839 static int num_levels_handled = 0;
10840 static int num_levels_converted = 0;
10841 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10844 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10845 global.convert_leveldir);
10847 if (convert_leveldir == NULL)
10848 Error(ERR_EXIT, "no such level identifier: '%s'",
10849 global.convert_leveldir);
10851 leveldir_current = convert_leveldir;
10853 if (global.convert_level_nr != -1)
10855 convert_leveldir->first_level = global.convert_level_nr;
10856 convert_leveldir->last_level = global.convert_level_nr;
10859 convert_level_nr = convert_leveldir->first_level;
10861 PrintLine("=", 79);
10862 Print("Converting levels\n");
10863 PrintLine("-", 79);
10864 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10865 Print("Level series name: '%s'\n", convert_leveldir->name);
10866 Print("Level series author: '%s'\n", convert_leveldir->author);
10867 Print("Number of levels: %d\n", convert_leveldir->levels);
10868 PrintLine("=", 79);
10871 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10872 levels_failed[i] = FALSE;
10874 while (convert_level_nr <= convert_leveldir->last_level)
10876 char *level_filename;
10879 level_nr = convert_level_nr++;
10881 Print("Level %03d: ", level_nr);
10883 LoadLevel(level_nr);
10884 if (level.no_level_file || level.no_valid_file)
10886 Print("(no level)\n");
10890 Print("converting level ... ");
10892 level_filename = getDefaultLevelFilename(level_nr);
10893 new_level = !fileExists(level_filename);
10897 SaveLevel(level_nr);
10899 num_levels_converted++;
10901 Print("converted.\n");
10905 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10906 levels_failed[level_nr] = TRUE;
10908 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10911 num_levels_handled++;
10915 PrintLine("=", 79);
10916 Print("Number of levels handled: %d\n", num_levels_handled);
10917 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10918 (num_levels_handled ?
10919 num_levels_converted * 100 / num_levels_handled : 0));
10920 PrintLine("-", 79);
10921 Print("Summary (for automatic parsing by scripts):\n");
10922 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10923 convert_leveldir->identifier, num_levels_converted,
10924 num_levels_handled,
10925 (num_levels_handled ?
10926 num_levels_converted * 100 / num_levels_handled : 0));
10928 if (num_levels_handled != num_levels_converted)
10930 Print(", FAILED:");
10931 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10932 if (levels_failed[i])
10937 PrintLine("=", 79);
10939 CloseAllAndExit(0);
10943 /* ------------------------------------------------------------------------- */
10944 /* create and save images for use in level sketches (raw BMP format) */
10945 /* ------------------------------------------------------------------------- */
10947 void CreateLevelSketchImages()
10949 #if defined(TARGET_SDL)
10954 InitElementPropertiesGfxElement();
10956 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10957 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10959 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10961 Bitmap *src_bitmap;
10963 int element = getMappedElement(i);
10964 int graphic = el2edimg(element);
10965 char basename1[16];
10966 char basename2[16];
10970 sprintf(basename1, "%03d.bmp", i);
10971 sprintf(basename2, "%03ds.bmp", i);
10973 filename1 = getPath2(global.create_images_dir, basename1);
10974 filename2 = getPath2(global.create_images_dir, basename2);
10976 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10977 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10980 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10981 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10983 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10984 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10986 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10987 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10993 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10996 FreeBitmap(bitmap1);
10997 FreeBitmap(bitmap2);
11002 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11004 CloseAllAndExit(0);
11009 /* ------------------------------------------------------------------------- */
11010 /* create and save images for custom and group elements (raw BMP format) */
11011 /* ------------------------------------------------------------------------- */
11013 void CreateCustomElementImages(char *directory)
11015 #if defined(TARGET_SDL)
11016 char *src_basename = "RocksCE-template.ilbm";
11017 char *dst_basename = "RocksCE.bmp";
11018 char *src_filename = getPath2(directory, src_basename);
11019 char *dst_filename = getPath2(directory, dst_basename);
11020 Bitmap *src_bitmap;
11022 int yoffset_ce = 0;
11023 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11026 SDLInitVideoDisplay();
11028 ReCreateBitmap(&backbuffer, video.width, video.height);
11030 src_bitmap = LoadImage(src_filename);
11032 bitmap = CreateBitmap(TILEX * 16 * 2,
11033 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11036 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11043 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11044 TILEX * x, TILEY * y + yoffset_ce);
11046 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11048 TILEX * x + TILEX * 16,
11049 TILEY * y + yoffset_ce);
11051 for (j = 2; j >= 0; j--)
11055 BlitBitmap(src_bitmap, bitmap,
11056 TILEX + c * 7, 0, 6, 10,
11057 TILEX * x + 6 + j * 7,
11058 TILEY * y + 11 + yoffset_ce);
11060 BlitBitmap(src_bitmap, bitmap,
11061 TILEX + c * 8, TILEY, 6, 10,
11062 TILEX * 16 + TILEX * x + 6 + j * 8,
11063 TILEY * y + 10 + yoffset_ce);
11069 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11076 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11077 TILEX * x, TILEY * y + yoffset_ge);
11079 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11081 TILEX * x + TILEX * 16,
11082 TILEY * y + yoffset_ge);
11084 for (j = 1; j >= 0; j--)
11088 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11089 TILEX * x + 6 + j * 10,
11090 TILEY * y + 11 + yoffset_ge);
11092 BlitBitmap(src_bitmap, bitmap,
11093 TILEX + c * 8, TILEY + 12, 6, 10,
11094 TILEX * 16 + TILEX * x + 10 + j * 8,
11095 TILEY * y + 10 + yoffset_ge);
11101 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11102 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11104 FreeBitmap(bitmap);
11106 CloseAllAndExit(0);