1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://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 1 // unused tape header bytes
62 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
64 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
66 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
67 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
68 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
70 // file identifier strings
71 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
72 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
73 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75 // values for deciding when (not) to save configuration data
76 #define SAVE_CONF_NEVER 0
77 #define SAVE_CONF_ALWAYS 1
78 #define SAVE_CONF_WHEN_CHANGED -1
80 // values for chunks using micro chunks
81 #define CONF_MASK_1_BYTE 0x00
82 #define CONF_MASK_2_BYTE 0x40
83 #define CONF_MASK_4_BYTE 0x80
84 #define CONF_MASK_MULTI_BYTES 0xc0
86 #define CONF_MASK_BYTES 0xc0
87 #define CONF_MASK_TOKEN 0x3f
89 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
90 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
91 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
92 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
94 // these definitions are just for convenience of use and readability
95 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
96 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
97 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
98 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
100 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
101 (x) == CONF_MASK_2_BYTE ? 2 : \
102 (x) == CONF_MASK_4_BYTE ? 4 : 0)
104 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
105 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
106 #define CONF_ELEMENT_NUM_BYTES (2)
108 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
109 (t) == TYPE_ELEMENT_LIST ? \
110 CONF_ELEMENT_NUM_BYTES : \
111 (t) == TYPE_CONTENT || \
112 (t) == TYPE_CONTENT_LIST ? \
113 CONF_CONTENT_NUM_BYTES : 1)
115 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
116 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
117 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
121 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
122 CONF_ELEMENT_NUM_BYTES)
123 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
124 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126 // temporary variables used to store pointers to structure members
127 static struct LevelInfo li;
128 static struct ElementInfo xx_ei, yy_ei;
129 static struct ElementChangeInfo xx_change;
130 static struct ElementGroupInfo xx_group;
131 static struct EnvelopeInfo xx_envelope;
132 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
133 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
134 static int xx_num_contents;
135 static int xx_current_change_page;
136 static char xx_default_string_empty[1] = "";
137 static int xx_string_length_unused;
139 struct LevelFileConfigInfo
141 int element; // element for which data is to be stored
142 int save_type; // save data always, never or when changed
143 int data_type; // data type (used internally, not stored)
144 int conf_type; // micro chunk identifier (stored in file)
147 void *value; // variable that holds the data to be stored
148 int default_value; // initial default value for this variable
151 void *value_copy; // variable that holds the data to be copied
152 void *num_entities; // number of entities for multi-byte data
153 int default_num_entities; // default number of entities for this data
154 int max_num_entities; // maximal number of entities for this data
155 char *default_string; // optional default string for string data
158 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 // ---------- values not related to single elements -------------------------
163 -1, SAVE_CONF_ALWAYS,
164 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
165 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
186 -1, SAVE_CONF_ALWAYS,
187 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
193 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
200 &li.use_step_counter, FALSE
205 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
206 &li.wind_direction_initial, MV_NONE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
212 &li.em_slippery_gems, FALSE
217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
218 &li.use_custom_template, FALSE
223 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
224 &li.can_move_into_acid_bits, ~0 // default: everything can
229 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
230 &li.dont_collide_with_bits, ~0 // default: always deadly
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
236 &li.em_explodes_by_fire, FALSE
241 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
242 &li.score[SC_TIME_BONUS], 1
247 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
248 &li.auto_exit_sokoban, FALSE
253 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
254 &li.auto_count_gems, FALSE
259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
260 &li.solved_by_one_player, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
266 &li.time_score_base, 1
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
272 &li.rate_time_over_score, FALSE
282 static struct LevelFileConfigInfo chunk_config_ELEM[] =
284 // (these values are the same for each player)
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
288 &li.block_last_field, FALSE // default case for EM levels
292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
293 &li.sp_block_last_field, TRUE // default case for SP levels
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
298 &li.instant_relocation, FALSE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
303 &li.can_pass_to_walkable, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
308 &li.block_snap_field, TRUE
312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
313 &li.continuous_snapping, TRUE
317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
318 &li.shifted_relocation, FALSE
322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
323 &li.lazy_relocation, FALSE
327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
328 &li.finish_dig_collect, TRUE
331 // (these values are different for each player)
334 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
335 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
339 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
340 &li.initial_player_gravity[0], FALSE
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
345 &li.use_start_element[0], FALSE
349 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
350 &li.start_element[0], EL_PLAYER_1
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
355 &li.use_artwork_element[0], FALSE
359 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
360 &li.artwork_element[0], EL_PLAYER_1
364 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
365 &li.use_explosion_element[0], FALSE
369 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
370 &li.explosion_element[0], EL_PLAYER_1
374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
375 &li.use_initial_inventory[0], FALSE
379 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
380 &li.initial_inventory_size[0], 1
384 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
385 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
386 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
391 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
392 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
396 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
397 &li.initial_player_gravity[1], FALSE
401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
402 &li.use_start_element[1], FALSE
406 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
407 &li.start_element[1], EL_PLAYER_2
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
412 &li.use_artwork_element[1], FALSE
416 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
417 &li.artwork_element[1], EL_PLAYER_2
421 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
422 &li.use_explosion_element[1], FALSE
426 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
427 &li.explosion_element[1], EL_PLAYER_2
431 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
432 &li.use_initial_inventory[1], FALSE
436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
437 &li.initial_inventory_size[1], 1
441 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
442 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
443 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
448 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
449 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
453 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
454 &li.initial_player_gravity[2], FALSE
458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
459 &li.use_start_element[2], FALSE
463 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
464 &li.start_element[2], EL_PLAYER_3
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
469 &li.use_artwork_element[2], FALSE
473 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
474 &li.artwork_element[2], EL_PLAYER_3
478 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
479 &li.use_explosion_element[2], FALSE
483 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
484 &li.explosion_element[2], EL_PLAYER_3
488 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
489 &li.use_initial_inventory[2], FALSE
493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
494 &li.initial_inventory_size[2], 1
498 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
499 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
500 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
505 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
506 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
510 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
511 &li.initial_player_gravity[3], FALSE
515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
516 &li.use_start_element[3], FALSE
520 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
521 &li.start_element[3], EL_PLAYER_4
525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
526 &li.use_artwork_element[3], FALSE
530 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
531 &li.artwork_element[3], EL_PLAYER_4
535 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
536 &li.use_explosion_element[3], FALSE
540 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
541 &li.explosion_element[3], EL_PLAYER_4
545 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
546 &li.use_initial_inventory[3], FALSE
550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
551 &li.initial_inventory_size[3], 1
555 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
556 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
557 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
562 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
563 &li.score[SC_EMERALD], 10
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_DIAMOND], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_BUG], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_SPACESHIP], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_PACMAN], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_NUT], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_DYNAMITE], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_KEY], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_PEARL], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_CRYSTAL], 10
622 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
623 &li.amoeba_content, EL_DIAMOND
627 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
632 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
633 &li.grow_into_diggable, TRUE
638 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
639 &li.yamyam_content, EL_ROCK, NULL,
640 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_YAMYAM], 10
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_ROBOT], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.time_magic_wall, 10
673 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
674 &li.game_of_life[0], 2
678 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
679 &li.game_of_life[1], 3
683 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
684 &li.game_of_life[2], 3
688 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
689 &li.game_of_life[3], 3
693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
694 &li.use_life_bugs, FALSE
699 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
704 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
709 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
714 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
719 EL_TIMEGATE_SWITCH, -1,
720 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
721 &li.time_timegate, 10
725 EL_LIGHT_SWITCH_ACTIVE, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
731 EL_SHIELD_NORMAL, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
733 &li.shield_normal_time, 10
736 EL_SHIELD_NORMAL, -1,
737 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
738 &li.score[SC_SHIELD], 10
742 EL_SHIELD_DEADLY, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
744 &li.shield_deadly_time, 10
747 EL_SHIELD_DEADLY, -1,
748 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
749 &li.score[SC_SHIELD], 10
754 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
759 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
760 &li.extra_time_score, 10
764 EL_TIME_ORB_FULL, -1,
765 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 &li.time_orb_time, 10
769 EL_TIME_ORB_FULL, -1,
770 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
771 &li.use_time_orb_bug, FALSE
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_spring_bug, FALSE
782 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
783 &li.android_move_time, 10
787 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
788 &li.android_clone_time, 10
791 EL_EMC_ANDROID, SAVE_CONF_NEVER,
792 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
793 &li.android_clone_element[0], EL_EMPTY, NULL,
794 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
805 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
810 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
815 EL_EMC_MAGNIFIER, -1,
816 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
817 &li.magnify_score, 10
820 EL_EMC_MAGNIFIER, -1,
821 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
826 EL_EMC_MAGIC_BALL, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
831 EL_EMC_MAGIC_BALL, -1,
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
833 &li.ball_random, FALSE
836 EL_EMC_MAGIC_BALL, -1,
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
838 &li.ball_active_initial, FALSE
841 EL_EMC_MAGIC_BALL, -1,
842 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
843 &li.ball_content, EL_EMPTY, NULL,
844 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
848 EL_SOKOBAN_FIELD_EMPTY, -1,
849 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
850 &li.sb_fields_needed, TRUE
854 EL_SOKOBAN_OBJECT, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_objects_needed, TRUE
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.mm_laser_red, FALSE
866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
867 &li.mm_laser_green, FALSE
871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
872 &li.mm_laser_blue, TRUE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
878 &li.df_laser_red, TRUE
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
883 &li.df_laser_green, TRUE
887 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
888 &li.df_laser_blue, FALSE
892 EL_MM_FUSE_ACTIVE, -1,
893 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
907 EL_MM_STEEL_BLOCK, -1,
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
909 &li.mm_time_block, 75
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 &li.score[SC_ELEM_BONUS], 10
917 // ---------- unused values -------------------------------------------------
920 EL_UNKNOWN, SAVE_CONF_NEVER,
921 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
922 &li.score[SC_UNKNOWN_15], 10
932 static struct LevelFileConfigInfo chunk_config_NOTE[] =
936 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
937 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
941 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
942 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
948 &xx_envelope.autowrap, FALSE
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
953 &xx_envelope.centered, FALSE
958 TYPE_STRING, CONF_VALUE_BYTES(1),
959 &xx_envelope.text, -1, NULL,
960 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
961 &xx_default_string_empty[0]
971 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
975 TYPE_STRING, CONF_VALUE_BYTES(1),
976 &xx_ei.description[0], -1,
977 &yy_ei.description[0],
978 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
979 &xx_default_description[0]
984 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
985 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
986 &yy_ei.properties[EP_BITFIELD_BASE_NR]
988 #if ENABLE_RESERVED_CODE
989 // (reserved for later use)
992 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
993 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
994 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1000 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1001 &xx_ei.use_gfx_element, FALSE,
1002 &yy_ei.use_gfx_element
1006 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1007 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1008 &yy_ei.gfx_element_initial
1013 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1014 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1015 &yy_ei.access_direction
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1021 &xx_ei.collect_score_initial, 10,
1022 &yy_ei.collect_score_initial
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1027 &xx_ei.collect_count_initial, 1,
1028 &yy_ei.collect_count_initial
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1034 &xx_ei.ce_value_fixed_initial, 0,
1035 &yy_ei.ce_value_fixed_initial
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1040 &xx_ei.ce_value_random_initial, 0,
1041 &yy_ei.ce_value_random_initial
1045 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1046 &xx_ei.use_last_ce_value, FALSE,
1047 &yy_ei.use_last_ce_value
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1053 &xx_ei.push_delay_fixed, 8,
1054 &yy_ei.push_delay_fixed
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1059 &xx_ei.push_delay_random, 8,
1060 &yy_ei.push_delay_random
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1065 &xx_ei.drop_delay_fixed, 0,
1066 &yy_ei.drop_delay_fixed
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1071 &xx_ei.drop_delay_random, 0,
1072 &yy_ei.drop_delay_random
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1077 &xx_ei.move_delay_fixed, 0,
1078 &yy_ei.move_delay_fixed
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1083 &xx_ei.move_delay_random, 0,
1084 &yy_ei.move_delay_random
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1089 &xx_ei.step_delay_fixed, 0,
1090 &yy_ei.step_delay_fixed
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1095 &xx_ei.step_delay_random, 0,
1096 &yy_ei.step_delay_random
1101 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1102 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1107 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1108 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1109 &yy_ei.move_direction_initial
1113 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1114 &xx_ei.move_stepsize, TILEX / 8,
1115 &yy_ei.move_stepsize
1120 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1121 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1122 &yy_ei.move_enter_element
1126 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1127 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1128 &yy_ei.move_leave_element
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1133 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1134 &yy_ei.move_leave_type
1139 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1140 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1141 &yy_ei.slippery_type
1146 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1147 &xx_ei.explosion_type, EXPLODES_3X3,
1148 &yy_ei.explosion_type
1152 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1153 &xx_ei.explosion_delay, 16,
1154 &yy_ei.explosion_delay
1158 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1159 &xx_ei.ignition_delay, 8,
1160 &yy_ei.ignition_delay
1165 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1166 &xx_ei.content, EL_EMPTY_SPACE,
1168 &xx_num_contents, 1, 1
1171 // ---------- "num_change_pages" must be the last entry ---------------------
1174 -1, SAVE_CONF_ALWAYS,
1175 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1176 &xx_ei.num_change_pages, 1,
1177 &yy_ei.num_change_pages
1188 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1190 // ---------- "current_change_page" must be the first entry -----------------
1193 -1, SAVE_CONF_ALWAYS,
1194 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1195 &xx_current_change_page, -1
1198 // ---------- (the remaining entries can be in any order) -------------------
1202 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1203 &xx_change.can_change, FALSE
1208 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1209 &xx_event_bits[0], 0
1213 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1214 &xx_event_bits[1], 0
1219 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1220 &xx_change.trigger_player, CH_PLAYER_ANY
1224 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1225 &xx_change.trigger_side, CH_SIDE_ANY
1229 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1230 &xx_change.trigger_page, CH_PAGE_ANY
1235 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1236 &xx_change.target_element, EL_EMPTY_SPACE
1241 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1242 &xx_change.delay_fixed, 0
1246 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1247 &xx_change.delay_random, 0
1251 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1252 &xx_change.delay_frames, FRAMES_PER_SECOND
1257 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1258 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1263 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1264 &xx_change.explode, FALSE
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1269 &xx_change.use_target_content, FALSE
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1274 &xx_change.only_if_complete, FALSE
1278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1279 &xx_change.use_random_replace, FALSE
1283 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1284 &xx_change.random_percentage, 100
1288 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1289 &xx_change.replace_when, CP_WHEN_EMPTY
1294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1295 &xx_change.has_action, FALSE
1299 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1300 &xx_change.action_type, CA_NO_ACTION
1304 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1305 &xx_change.action_mode, CA_MODE_UNDEFINED
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1310 &xx_change.action_arg, CA_ARG_UNDEFINED
1315 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1316 &xx_change.action_element, EL_EMPTY_SPACE
1321 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1322 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1323 &xx_num_contents, 1, 1
1333 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1337 TYPE_STRING, CONF_VALUE_BYTES(1),
1338 &xx_ei.description[0], -1, NULL,
1339 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1340 &xx_default_description[0]
1345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1346 &xx_ei.use_gfx_element, FALSE
1350 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1351 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1356 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1357 &xx_group.choice_mode, ANIM_RANDOM
1362 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1363 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1364 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1374 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1379 &li.block_snap_field, TRUE
1383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1384 &li.continuous_snapping, TRUE
1388 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1389 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1394 &li.use_start_element[0], FALSE
1398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1399 &li.start_element[0], EL_PLAYER_1
1403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1404 &li.use_artwork_element[0], FALSE
1408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1409 &li.artwork_element[0], EL_PLAYER_1
1413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1414 &li.use_explosion_element[0], FALSE
1418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1419 &li.explosion_element[0], EL_PLAYER_1
1434 filetype_id_list[] =
1436 { LEVEL_FILE_TYPE_RND, "RND" },
1437 { LEVEL_FILE_TYPE_BD, "BD" },
1438 { LEVEL_FILE_TYPE_EM, "EM" },
1439 { LEVEL_FILE_TYPE_SP, "SP" },
1440 { LEVEL_FILE_TYPE_DX, "DX" },
1441 { LEVEL_FILE_TYPE_SB, "SB" },
1442 { LEVEL_FILE_TYPE_DC, "DC" },
1443 { LEVEL_FILE_TYPE_MM, "MM" },
1444 { LEVEL_FILE_TYPE_MM, "DF" },
1449 // ============================================================================
1450 // level file functions
1451 // ============================================================================
1453 static boolean check_special_flags(char *flag)
1455 if (strEqual(options.special_flags, flag) ||
1456 strEqual(leveldir_current->special_flags, flag))
1462 static struct DateInfo getCurrentDate(void)
1464 time_t epoch_seconds = time(NULL);
1465 struct tm *now = localtime(&epoch_seconds);
1466 struct DateInfo date;
1468 date.year = now->tm_year + 1900;
1469 date.month = now->tm_mon + 1;
1470 date.day = now->tm_mday;
1472 date.src = DATE_SRC_CLOCK;
1477 static void resetEventFlags(struct ElementChangeInfo *change)
1481 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1482 change->has_event[i] = FALSE;
1485 static void resetEventBits(void)
1489 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1490 xx_event_bits[i] = 0;
1493 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1497 /* important: only change event flag if corresponding event bit is set
1498 (this is because all xx_event_bits[] values are loaded separately,
1499 and all xx_event_bits[] values are set back to zero before loading
1500 another value xx_event_bits[x] (each value representing 32 flags)) */
1502 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1503 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1504 change->has_event[i] = TRUE;
1507 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1511 /* in contrast to the above function setEventFlagsFromEventBits(), it
1512 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1513 depending on the corresponding change->has_event[i] values here, as
1514 all xx_event_bits[] values are reset in resetEventBits() before */
1516 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1517 if (change->has_event[i])
1518 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1521 static char *getDefaultElementDescription(struct ElementInfo *ei)
1523 static char description[MAX_ELEMENT_NAME_LEN + 1];
1524 char *default_description = (ei->custom_description != NULL ?
1525 ei->custom_description :
1526 ei->editor_description);
1529 // always start with reliable default values
1530 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1531 description[i] = '\0';
1533 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1534 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1536 return &description[0];
1539 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1541 char *default_description = getDefaultElementDescription(ei);
1544 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1545 ei->description[i] = default_description[i];
1548 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1552 for (i = 0; conf[i].data_type != -1; i++)
1554 int default_value = conf[i].default_value;
1555 int data_type = conf[i].data_type;
1556 int conf_type = conf[i].conf_type;
1557 int byte_mask = conf_type & CONF_MASK_BYTES;
1559 if (byte_mask == CONF_MASK_MULTI_BYTES)
1561 int default_num_entities = conf[i].default_num_entities;
1562 int max_num_entities = conf[i].max_num_entities;
1564 *(int *)(conf[i].num_entities) = default_num_entities;
1566 if (data_type == TYPE_STRING)
1568 char *default_string = conf[i].default_string;
1569 char *string = (char *)(conf[i].value);
1571 strncpy(string, default_string, max_num_entities);
1573 else if (data_type == TYPE_ELEMENT_LIST)
1575 int *element_array = (int *)(conf[i].value);
1578 for (j = 0; j < max_num_entities; j++)
1579 element_array[j] = default_value;
1581 else if (data_type == TYPE_CONTENT_LIST)
1583 struct Content *content = (struct Content *)(conf[i].value);
1586 for (c = 0; c < max_num_entities; c++)
1587 for (y = 0; y < 3; y++)
1588 for (x = 0; x < 3; x++)
1589 content[c].e[x][y] = default_value;
1592 else // constant size configuration data (1, 2 or 4 bytes)
1594 if (data_type == TYPE_BOOLEAN)
1595 *(boolean *)(conf[i].value) = default_value;
1597 *(int *) (conf[i].value) = default_value;
1602 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1606 for (i = 0; conf[i].data_type != -1; i++)
1608 int data_type = conf[i].data_type;
1609 int conf_type = conf[i].conf_type;
1610 int byte_mask = conf_type & CONF_MASK_BYTES;
1612 if (byte_mask == CONF_MASK_MULTI_BYTES)
1614 int max_num_entities = conf[i].max_num_entities;
1616 if (data_type == TYPE_STRING)
1618 char *string = (char *)(conf[i].value);
1619 char *string_copy = (char *)(conf[i].value_copy);
1621 strncpy(string_copy, string, max_num_entities);
1623 else if (data_type == TYPE_ELEMENT_LIST)
1625 int *element_array = (int *)(conf[i].value);
1626 int *element_array_copy = (int *)(conf[i].value_copy);
1629 for (j = 0; j < max_num_entities; j++)
1630 element_array_copy[j] = element_array[j];
1632 else if (data_type == TYPE_CONTENT_LIST)
1634 struct Content *content = (struct Content *)(conf[i].value);
1635 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1638 for (c = 0; c < max_num_entities; c++)
1639 for (y = 0; y < 3; y++)
1640 for (x = 0; x < 3; x++)
1641 content_copy[c].e[x][y] = content[c].e[x][y];
1644 else // constant size configuration data (1, 2 or 4 bytes)
1646 if (data_type == TYPE_BOOLEAN)
1647 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1649 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1654 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1658 xx_ei = *ei_from; // copy element data into temporary buffer
1659 yy_ei = *ei_to; // copy element data into temporary buffer
1661 copyConfigFromConfigList(chunk_config_CUSX_base);
1666 // ---------- reinitialize and copy change pages ----------
1668 ei_to->num_change_pages = ei_from->num_change_pages;
1669 ei_to->current_change_page = ei_from->current_change_page;
1671 setElementChangePages(ei_to, ei_to->num_change_pages);
1673 for (i = 0; i < ei_to->num_change_pages; i++)
1674 ei_to->change_page[i] = ei_from->change_page[i];
1676 // ---------- copy group element info ----------
1677 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1678 *ei_to->group = *ei_from->group;
1680 // mark this custom element as modified
1681 ei_to->modified_settings = TRUE;
1684 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1686 int change_page_size = sizeof(struct ElementChangeInfo);
1688 ei->num_change_pages = MAX(1, change_pages);
1691 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1693 if (ei->current_change_page >= ei->num_change_pages)
1694 ei->current_change_page = ei->num_change_pages - 1;
1696 ei->change = &ei->change_page[ei->current_change_page];
1699 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1701 xx_change = *change; // copy change data into temporary buffer
1703 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1705 *change = xx_change;
1707 resetEventFlags(change);
1709 change->direct_action = 0;
1710 change->other_action = 0;
1712 change->pre_change_function = NULL;
1713 change->change_function = NULL;
1714 change->post_change_function = NULL;
1717 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1721 li = *level; // copy level data into temporary buffer
1722 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1723 *level = li; // copy temporary buffer back to level data
1725 setLevelInfoToDefaults_EM();
1726 setLevelInfoToDefaults_SP();
1727 setLevelInfoToDefaults_MM();
1729 level->native_em_level = &native_em_level;
1730 level->native_sp_level = &native_sp_level;
1731 level->native_mm_level = &native_mm_level;
1733 level->file_version = FILE_VERSION_ACTUAL;
1734 level->game_version = GAME_VERSION_ACTUAL;
1736 level->creation_date = getCurrentDate();
1738 level->encoding_16bit_field = TRUE;
1739 level->encoding_16bit_yamyam = TRUE;
1740 level->encoding_16bit_amoeba = TRUE;
1742 // clear level name and level author string buffers
1743 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1744 level->name[i] = '\0';
1745 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1746 level->author[i] = '\0';
1748 // set level name and level author to default values
1749 strcpy(level->name, NAMELESS_LEVEL_NAME);
1750 strcpy(level->author, ANONYMOUS_NAME);
1752 // set level playfield to playable default level with player and exit
1753 for (x = 0; x < MAX_LEV_FIELDX; x++)
1754 for (y = 0; y < MAX_LEV_FIELDY; y++)
1755 level->field[x][y] = EL_SAND;
1757 level->field[0][0] = EL_PLAYER_1;
1758 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1760 BorderElement = EL_STEELWALL;
1762 // detect custom elements when loading them
1763 level->file_has_custom_elements = FALSE;
1765 // set all bug compatibility flags to "false" => do not emulate this bug
1766 level->use_action_after_change_bug = FALSE;
1768 if (leveldir_current)
1770 // try to determine better author name than 'anonymous'
1771 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1773 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1774 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1778 switch (LEVELCLASS(leveldir_current))
1780 case LEVELCLASS_TUTORIAL:
1781 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1784 case LEVELCLASS_CONTRIB:
1785 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1786 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1789 case LEVELCLASS_PRIVATE:
1790 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1791 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1795 // keep default value
1802 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1804 static boolean clipboard_elements_initialized = FALSE;
1807 InitElementPropertiesStatic();
1809 li = *level; // copy level data into temporary buffer
1810 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1811 *level = li; // copy temporary buffer back to level data
1813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1816 struct ElementInfo *ei = &element_info[element];
1818 // never initialize clipboard elements after the very first time
1819 // (to be able to use clipboard elements between several levels)
1820 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1823 if (IS_ENVELOPE(element))
1825 int envelope_nr = element - EL_ENVELOPE_1;
1827 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1829 level->envelope[envelope_nr] = xx_envelope;
1832 if (IS_CUSTOM_ELEMENT(element) ||
1833 IS_GROUP_ELEMENT(element) ||
1834 IS_INTERNAL_ELEMENT(element))
1836 xx_ei = *ei; // copy element data into temporary buffer
1838 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1843 setElementChangePages(ei, 1);
1844 setElementChangeInfoToDefaults(ei->change);
1846 if (IS_CUSTOM_ELEMENT(element) ||
1847 IS_GROUP_ELEMENT(element) ||
1848 IS_INTERNAL_ELEMENT(element))
1850 setElementDescriptionToDefault(ei);
1852 ei->modified_settings = FALSE;
1855 if (IS_CUSTOM_ELEMENT(element) ||
1856 IS_INTERNAL_ELEMENT(element))
1858 // internal values used in level editor
1860 ei->access_type = 0;
1861 ei->access_layer = 0;
1862 ei->access_protected = 0;
1863 ei->walk_to_action = 0;
1864 ei->smash_targets = 0;
1867 ei->can_explode_by_fire = FALSE;
1868 ei->can_explode_smashed = FALSE;
1869 ei->can_explode_impact = FALSE;
1871 ei->current_change_page = 0;
1874 if (IS_GROUP_ELEMENT(element) ||
1875 IS_INTERNAL_ELEMENT(element))
1877 struct ElementGroupInfo *group;
1879 // initialize memory for list of elements in group
1880 if (ei->group == NULL)
1881 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1885 xx_group = *group; // copy group data into temporary buffer
1887 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1893 clipboard_elements_initialized = TRUE;
1896 static void setLevelInfoToDefaults(struct LevelInfo *level,
1897 boolean level_info_only,
1898 boolean reset_file_status)
1900 setLevelInfoToDefaults_Level(level);
1902 if (!level_info_only)
1903 setLevelInfoToDefaults_Elements(level);
1905 if (reset_file_status)
1907 level->no_valid_file = FALSE;
1908 level->no_level_file = FALSE;
1911 level->changed = FALSE;
1914 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1916 level_file_info->nr = 0;
1917 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1918 level_file_info->packed = FALSE;
1920 setString(&level_file_info->basename, NULL);
1921 setString(&level_file_info->filename, NULL);
1924 int getMappedElement_SB(int, boolean);
1926 static void ActivateLevelTemplate(void)
1930 if (check_special_flags("load_xsb_to_ces"))
1932 // fill smaller playfields with padding "beyond border wall" elements
1933 if (level.fieldx < level_template.fieldx ||
1934 level.fieldy < level_template.fieldy)
1936 short field[level.fieldx][level.fieldy];
1937 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1938 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1939 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1940 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1942 // copy old playfield (which is smaller than the visible area)
1943 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1944 field[x][y] = level.field[x][y];
1946 // fill new, larger playfield with "beyond border wall" elements
1947 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1948 level.field[x][y] = getMappedElement_SB('_', TRUE);
1950 // copy the old playfield to the middle of the new playfield
1951 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1952 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1954 level.fieldx = new_fieldx;
1955 level.fieldy = new_fieldy;
1959 // Currently there is no special action needed to activate the template
1960 // data, because 'element_info' property settings overwrite the original
1961 // level data, while all other variables do not change.
1963 // Exception: 'from_level_template' elements in the original level playfield
1964 // are overwritten with the corresponding elements at the same position in
1965 // playfield from the level template.
1967 for (x = 0; x < level.fieldx; x++)
1968 for (y = 0; y < level.fieldy; y++)
1969 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1970 level.field[x][y] = level_template.field[x][y];
1972 if (check_special_flags("load_xsb_to_ces"))
1974 struct LevelInfo level_backup = level;
1976 // overwrite all individual level settings from template level settings
1977 level = level_template;
1979 // restore level file info
1980 level.file_info = level_backup.file_info;
1982 // restore playfield size
1983 level.fieldx = level_backup.fieldx;
1984 level.fieldy = level_backup.fieldy;
1986 // restore playfield content
1987 for (x = 0; x < level.fieldx; x++)
1988 for (y = 0; y < level.fieldy; y++)
1989 level.field[x][y] = level_backup.field[x][y];
1991 // restore name and author from individual level
1992 strcpy(level.name, level_backup.name);
1993 strcpy(level.author, level_backup.author);
1995 // restore flag "use_custom_template"
1996 level.use_custom_template = level_backup.use_custom_template;
2000 static char *getLevelFilenameFromBasename(char *basename)
2002 static char *filename = NULL;
2004 checked_free(filename);
2006 filename = getPath2(getCurrentLevelDir(), basename);
2011 static int getFileTypeFromBasename(char *basename)
2013 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2015 static char *filename = NULL;
2016 struct stat file_status;
2018 // ---------- try to determine file type from filename ----------
2020 // check for typical filename of a Supaplex level package file
2021 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2022 return LEVEL_FILE_TYPE_SP;
2024 // check for typical filename of a Diamond Caves II level package file
2025 if (strSuffixLower(basename, ".dc") ||
2026 strSuffixLower(basename, ".dc2"))
2027 return LEVEL_FILE_TYPE_DC;
2029 // check for typical filename of a Sokoban level package file
2030 if (strSuffixLower(basename, ".xsb") &&
2031 strchr(basename, '%') == NULL)
2032 return LEVEL_FILE_TYPE_SB;
2034 // ---------- try to determine file type from filesize ----------
2036 checked_free(filename);
2037 filename = getPath2(getCurrentLevelDir(), basename);
2039 if (stat(filename, &file_status) == 0)
2041 // check for typical filesize of a Supaplex level package file
2042 if (file_status.st_size == 170496)
2043 return LEVEL_FILE_TYPE_SP;
2046 return LEVEL_FILE_TYPE_UNKNOWN;
2049 static int getFileTypeFromMagicBytes(char *filename, int type)
2053 if ((file = openFile(filename, MODE_READ)))
2055 char chunk_name[CHUNK_ID_LEN + 1];
2057 getFileChunkBE(file, chunk_name, NULL);
2059 if (strEqual(chunk_name, "MMII") ||
2060 strEqual(chunk_name, "MIRR"))
2061 type = LEVEL_FILE_TYPE_MM;
2069 static boolean checkForPackageFromBasename(char *basename)
2071 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2072 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2074 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2077 static char *getSingleLevelBasenameExt(int nr, char *extension)
2079 static char basename[MAX_FILENAME_LEN];
2082 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2084 sprintf(basename, "%03d.%s", nr, extension);
2089 static char *getSingleLevelBasename(int nr)
2091 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2094 static char *getPackedLevelBasename(int type)
2096 static char basename[MAX_FILENAME_LEN];
2097 char *directory = getCurrentLevelDir();
2099 DirectoryEntry *dir_entry;
2101 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2103 if ((dir = openDirectory(directory)) == NULL)
2105 Warn("cannot read current level directory '%s'", directory);
2110 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2112 char *entry_basename = dir_entry->basename;
2113 int entry_type = getFileTypeFromBasename(entry_basename);
2115 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2117 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2120 strcpy(basename, entry_basename);
2127 closeDirectory(dir);
2132 static char *getSingleLevelFilename(int nr)
2134 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2137 #if ENABLE_UNUSED_CODE
2138 static char *getPackedLevelFilename(int type)
2140 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2144 char *getDefaultLevelFilename(int nr)
2146 return getSingleLevelFilename(nr);
2149 #if ENABLE_UNUSED_CODE
2150 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2154 lfi->packed = FALSE;
2156 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2157 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2161 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2162 int type, char *format, ...)
2164 static char basename[MAX_FILENAME_LEN];
2167 va_start(ap, format);
2168 vsprintf(basename, format, ap);
2172 lfi->packed = FALSE;
2174 setString(&lfi->basename, basename);
2175 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2178 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2184 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2185 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2188 static int getFiletypeFromID(char *filetype_id)
2190 char *filetype_id_lower;
2191 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2194 if (filetype_id == NULL)
2195 return LEVEL_FILE_TYPE_UNKNOWN;
2197 filetype_id_lower = getStringToLower(filetype_id);
2199 for (i = 0; filetype_id_list[i].id != NULL; i++)
2201 char *id_lower = getStringToLower(filetype_id_list[i].id);
2203 if (strEqual(filetype_id_lower, id_lower))
2204 filetype = filetype_id_list[i].filetype;
2208 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2212 free(filetype_id_lower);
2217 char *getLocalLevelTemplateFilename(void)
2219 return getDefaultLevelFilename(-1);
2222 char *getGlobalLevelTemplateFilename(void)
2224 // global variable "leveldir_current" must be modified in the loop below
2225 LevelDirTree *leveldir_current_last = leveldir_current;
2226 char *filename = NULL;
2228 // check for template level in path from current to topmost tree node
2230 while (leveldir_current != NULL)
2232 filename = getDefaultLevelFilename(-1);
2234 if (fileExists(filename))
2237 leveldir_current = leveldir_current->node_parent;
2240 // restore global variable "leveldir_current" modified in above loop
2241 leveldir_current = leveldir_current_last;
2246 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2250 // special case: level number is negative => check for level template file
2253 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2254 getSingleLevelBasename(-1));
2256 // replace local level template filename with global template filename
2257 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2259 // no fallback if template file not existing
2263 // special case: check for file name/pattern specified in "levelinfo.conf"
2264 if (leveldir_current->level_filename != NULL)
2266 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2268 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2269 leveldir_current->level_filename, nr);
2271 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2273 if (fileExists(lfi->filename))
2276 else if (leveldir_current->level_filetype != NULL)
2278 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2280 // check for specified native level file with standard file name
2281 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2282 "%03d.%s", nr, LEVELFILE_EXTENSION);
2283 if (fileExists(lfi->filename))
2287 // check for native Rocks'n'Diamonds level file
2288 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2289 "%03d.%s", nr, LEVELFILE_EXTENSION);
2290 if (fileExists(lfi->filename))
2293 // check for Emerald Mine level file (V1)
2294 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2295 'a' + (nr / 10) % 26, '0' + nr % 10);
2296 if (fileExists(lfi->filename))
2298 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2299 'A' + (nr / 10) % 26, '0' + nr % 10);
2300 if (fileExists(lfi->filename))
2303 // check for Emerald Mine level file (V2 to V5)
2304 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2305 if (fileExists(lfi->filename))
2308 // check for Emerald Mine level file (V6 / single mode)
2309 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2310 if (fileExists(lfi->filename))
2312 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2313 if (fileExists(lfi->filename))
2316 // check for Emerald Mine level file (V6 / teamwork mode)
2317 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2318 if (fileExists(lfi->filename))
2320 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2321 if (fileExists(lfi->filename))
2324 // check for various packed level file formats
2325 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2326 if (fileExists(lfi->filename))
2329 // no known level file found -- use default values (and fail later)
2330 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2331 "%03d.%s", nr, LEVELFILE_EXTENSION);
2334 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2336 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2337 lfi->type = getFileTypeFromBasename(lfi->basename);
2339 if (lfi->type == LEVEL_FILE_TYPE_RND)
2340 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2343 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2345 // always start with reliable default values
2346 setFileInfoToDefaults(level_file_info);
2348 level_file_info->nr = nr; // set requested level number
2350 determineLevelFileInfo_Filename(level_file_info);
2351 determineLevelFileInfo_Filetype(level_file_info);
2354 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2355 struct LevelFileInfo *lfi_to)
2357 lfi_to->nr = lfi_from->nr;
2358 lfi_to->type = lfi_from->type;
2359 lfi_to->packed = lfi_from->packed;
2361 setString(&lfi_to->basename, lfi_from->basename);
2362 setString(&lfi_to->filename, lfi_from->filename);
2365 // ----------------------------------------------------------------------------
2366 // functions for loading R'n'D level
2367 // ----------------------------------------------------------------------------
2369 int getMappedElement(int element)
2371 // remap some (historic, now obsolete) elements
2375 case EL_PLAYER_OBSOLETE:
2376 element = EL_PLAYER_1;
2379 case EL_KEY_OBSOLETE:
2383 case EL_EM_KEY_1_FILE_OBSOLETE:
2384 element = EL_EM_KEY_1;
2387 case EL_EM_KEY_2_FILE_OBSOLETE:
2388 element = EL_EM_KEY_2;
2391 case EL_EM_KEY_3_FILE_OBSOLETE:
2392 element = EL_EM_KEY_3;
2395 case EL_EM_KEY_4_FILE_OBSOLETE:
2396 element = EL_EM_KEY_4;
2399 case EL_ENVELOPE_OBSOLETE:
2400 element = EL_ENVELOPE_1;
2408 if (element >= NUM_FILE_ELEMENTS)
2410 Warn("invalid level element %d", element);
2412 element = EL_UNKNOWN;
2420 static int getMappedElementByVersion(int element, int game_version)
2422 // remap some elements due to certain game version
2424 if (game_version <= VERSION_IDENT(2,2,0,0))
2426 // map game font elements
2427 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2428 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2429 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2430 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2433 if (game_version < VERSION_IDENT(3,0,0,0))
2435 // map Supaplex gravity tube elements
2436 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2437 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2438 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2439 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2446 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2448 level->file_version = getFileVersion(file);
2449 level->game_version = getFileVersion(file);
2454 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2456 level->creation_date.year = getFile16BitBE(file);
2457 level->creation_date.month = getFile8Bit(file);
2458 level->creation_date.day = getFile8Bit(file);
2460 level->creation_date.src = DATE_SRC_LEVELFILE;
2465 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2467 int initial_player_stepsize;
2468 int initial_player_gravity;
2471 level->fieldx = getFile8Bit(file);
2472 level->fieldy = getFile8Bit(file);
2474 level->time = getFile16BitBE(file);
2475 level->gems_needed = getFile16BitBE(file);
2477 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2478 level->name[i] = getFile8Bit(file);
2479 level->name[MAX_LEVEL_NAME_LEN] = 0;
2481 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2482 level->score[i] = getFile8Bit(file);
2484 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2485 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2486 for (y = 0; y < 3; y++)
2487 for (x = 0; x < 3; x++)
2488 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2490 level->amoeba_speed = getFile8Bit(file);
2491 level->time_magic_wall = getFile8Bit(file);
2492 level->time_wheel = getFile8Bit(file);
2493 level->amoeba_content = getMappedElement(getFile8Bit(file));
2495 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2498 for (i = 0; i < MAX_PLAYERS; i++)
2499 level->initial_player_stepsize[i] = initial_player_stepsize;
2501 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2503 for (i = 0; i < MAX_PLAYERS; i++)
2504 level->initial_player_gravity[i] = initial_player_gravity;
2506 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2507 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2509 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2511 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2512 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2513 level->can_move_into_acid_bits = getFile32BitBE(file);
2514 level->dont_collide_with_bits = getFile8Bit(file);
2516 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2517 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2519 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2520 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2521 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2523 level->game_engine_type = getFile8Bit(file);
2525 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2530 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2534 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2535 level->name[i] = getFile8Bit(file);
2536 level->name[MAX_LEVEL_NAME_LEN] = 0;
2541 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2545 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2546 level->author[i] = getFile8Bit(file);
2547 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2552 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2555 int chunk_size_expected = level->fieldx * level->fieldy;
2557 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2558 stored with 16-bit encoding (and should be twice as big then).
2559 Even worse, playfield data was stored 16-bit when only yamyam content
2560 contained 16-bit elements and vice versa. */
2562 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2563 chunk_size_expected *= 2;
2565 if (chunk_size_expected != chunk_size)
2567 ReadUnusedBytesFromFile(file, chunk_size);
2568 return chunk_size_expected;
2571 for (y = 0; y < level->fieldy; y++)
2572 for (x = 0; x < level->fieldx; x++)
2573 level->field[x][y] =
2574 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2579 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2582 int header_size = 4;
2583 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2584 int chunk_size_expected = header_size + content_size;
2586 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2587 stored with 16-bit encoding (and should be twice as big then).
2588 Even worse, playfield data was stored 16-bit when only yamyam content
2589 contained 16-bit elements and vice versa. */
2591 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2592 chunk_size_expected += content_size;
2594 if (chunk_size_expected != chunk_size)
2596 ReadUnusedBytesFromFile(file, chunk_size);
2597 return chunk_size_expected;
2601 level->num_yamyam_contents = getFile8Bit(file);
2605 // correct invalid number of content fields -- should never happen
2606 if (level->num_yamyam_contents < 1 ||
2607 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2608 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2610 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2611 for (y = 0; y < 3; y++)
2612 for (x = 0; x < 3; x++)
2613 level->yamyam_content[i].e[x][y] =
2614 getMappedElement(level->encoding_16bit_field ?
2615 getFile16BitBE(file) : getFile8Bit(file));
2619 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2624 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2626 element = getMappedElement(getFile16BitBE(file));
2627 num_contents = getFile8Bit(file);
2629 getFile8Bit(file); // content x size (unused)
2630 getFile8Bit(file); // content y size (unused)
2632 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2634 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2635 for (y = 0; y < 3; y++)
2636 for (x = 0; x < 3; x++)
2637 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2639 // correct invalid number of content fields -- should never happen
2640 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2641 num_contents = STD_ELEMENT_CONTENTS;
2643 if (element == EL_YAMYAM)
2645 level->num_yamyam_contents = num_contents;
2647 for (i = 0; i < num_contents; i++)
2648 for (y = 0; y < 3; y++)
2649 for (x = 0; x < 3; x++)
2650 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2652 else if (element == EL_BD_AMOEBA)
2654 level->amoeba_content = content_array[0][0][0];
2658 Warn("cannot load content for element '%d'", element);
2664 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2670 int chunk_size_expected;
2672 element = getMappedElement(getFile16BitBE(file));
2673 if (!IS_ENVELOPE(element))
2674 element = EL_ENVELOPE_1;
2676 envelope_nr = element - EL_ENVELOPE_1;
2678 envelope_len = getFile16BitBE(file);
2680 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2681 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2683 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2685 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2686 if (chunk_size_expected != chunk_size)
2688 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2689 return chunk_size_expected;
2692 for (i = 0; i < envelope_len; i++)
2693 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2698 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2700 int num_changed_custom_elements = getFile16BitBE(file);
2701 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2704 if (chunk_size_expected != chunk_size)
2706 ReadUnusedBytesFromFile(file, chunk_size - 2);
2707 return chunk_size_expected;
2710 for (i = 0; i < num_changed_custom_elements; i++)
2712 int element = getMappedElement(getFile16BitBE(file));
2713 int properties = getFile32BitBE(file);
2715 if (IS_CUSTOM_ELEMENT(element))
2716 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2718 Warn("invalid custom element number %d", element);
2720 // older game versions that wrote level files with CUS1 chunks used
2721 // different default push delay values (not yet stored in level file)
2722 element_info[element].push_delay_fixed = 2;
2723 element_info[element].push_delay_random = 8;
2726 level->file_has_custom_elements = TRUE;
2731 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2733 int num_changed_custom_elements = getFile16BitBE(file);
2734 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2737 if (chunk_size_expected != chunk_size)
2739 ReadUnusedBytesFromFile(file, chunk_size - 2);
2740 return chunk_size_expected;
2743 for (i = 0; i < num_changed_custom_elements; i++)
2745 int element = getMappedElement(getFile16BitBE(file));
2746 int custom_target_element = getMappedElement(getFile16BitBE(file));
2748 if (IS_CUSTOM_ELEMENT(element))
2749 element_info[element].change->target_element = custom_target_element;
2751 Warn("invalid custom element number %d", element);
2754 level->file_has_custom_elements = TRUE;
2759 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2761 int num_changed_custom_elements = getFile16BitBE(file);
2762 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2765 if (chunk_size_expected != chunk_size)
2767 ReadUnusedBytesFromFile(file, chunk_size - 2);
2768 return chunk_size_expected;
2771 for (i = 0; i < num_changed_custom_elements; i++)
2773 int element = getMappedElement(getFile16BitBE(file));
2774 struct ElementInfo *ei = &element_info[element];
2775 unsigned int event_bits;
2777 if (!IS_CUSTOM_ELEMENT(element))
2779 Warn("invalid custom element number %d", element);
2781 element = EL_INTERNAL_DUMMY;
2784 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2785 ei->description[j] = getFile8Bit(file);
2786 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2788 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2790 // some free bytes for future properties and padding
2791 ReadUnusedBytesFromFile(file, 7);
2793 ei->use_gfx_element = getFile8Bit(file);
2794 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2796 ei->collect_score_initial = getFile8Bit(file);
2797 ei->collect_count_initial = getFile8Bit(file);
2799 ei->push_delay_fixed = getFile16BitBE(file);
2800 ei->push_delay_random = getFile16BitBE(file);
2801 ei->move_delay_fixed = getFile16BitBE(file);
2802 ei->move_delay_random = getFile16BitBE(file);
2804 ei->move_pattern = getFile16BitBE(file);
2805 ei->move_direction_initial = getFile8Bit(file);
2806 ei->move_stepsize = getFile8Bit(file);
2808 for (y = 0; y < 3; y++)
2809 for (x = 0; x < 3; x++)
2810 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2812 event_bits = getFile32BitBE(file);
2813 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2814 if (event_bits & (1 << j))
2815 ei->change->has_event[j] = TRUE;
2817 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2819 ei->change->delay_fixed = getFile16BitBE(file);
2820 ei->change->delay_random = getFile16BitBE(file);
2821 ei->change->delay_frames = getFile16BitBE(file);
2823 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2825 ei->change->explode = getFile8Bit(file);
2826 ei->change->use_target_content = getFile8Bit(file);
2827 ei->change->only_if_complete = getFile8Bit(file);
2828 ei->change->use_random_replace = getFile8Bit(file);
2830 ei->change->random_percentage = getFile8Bit(file);
2831 ei->change->replace_when = getFile8Bit(file);
2833 for (y = 0; y < 3; y++)
2834 for (x = 0; x < 3; x++)
2835 ei->change->target_content.e[x][y] =
2836 getMappedElement(getFile16BitBE(file));
2838 ei->slippery_type = getFile8Bit(file);
2840 // some free bytes for future properties and padding
2841 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2843 // mark that this custom element has been modified
2844 ei->modified_settings = TRUE;
2847 level->file_has_custom_elements = TRUE;
2852 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2854 struct ElementInfo *ei;
2855 int chunk_size_expected;
2859 // ---------- custom element base property values (96 bytes) ----------------
2861 element = getMappedElement(getFile16BitBE(file));
2863 if (!IS_CUSTOM_ELEMENT(element))
2865 Warn("invalid custom element number %d", element);
2867 ReadUnusedBytesFromFile(file, chunk_size - 2);
2872 ei = &element_info[element];
2874 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2875 ei->description[i] = getFile8Bit(file);
2876 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2878 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2880 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2882 ei->num_change_pages = getFile8Bit(file);
2884 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2885 if (chunk_size_expected != chunk_size)
2887 ReadUnusedBytesFromFile(file, chunk_size - 43);
2888 return chunk_size_expected;
2891 ei->ce_value_fixed_initial = getFile16BitBE(file);
2892 ei->ce_value_random_initial = getFile16BitBE(file);
2893 ei->use_last_ce_value = getFile8Bit(file);
2895 ei->use_gfx_element = getFile8Bit(file);
2896 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2898 ei->collect_score_initial = getFile8Bit(file);
2899 ei->collect_count_initial = getFile8Bit(file);
2901 ei->drop_delay_fixed = getFile8Bit(file);
2902 ei->push_delay_fixed = getFile8Bit(file);
2903 ei->drop_delay_random = getFile8Bit(file);
2904 ei->push_delay_random = getFile8Bit(file);
2905 ei->move_delay_fixed = getFile16BitBE(file);
2906 ei->move_delay_random = getFile16BitBE(file);
2908 // bits 0 - 15 of "move_pattern" ...
2909 ei->move_pattern = getFile16BitBE(file);
2910 ei->move_direction_initial = getFile8Bit(file);
2911 ei->move_stepsize = getFile8Bit(file);
2913 ei->slippery_type = getFile8Bit(file);
2915 for (y = 0; y < 3; y++)
2916 for (x = 0; x < 3; x++)
2917 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2919 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2920 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2921 ei->move_leave_type = getFile8Bit(file);
2923 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2924 ei->move_pattern |= (getFile16BitBE(file) << 16);
2926 ei->access_direction = getFile8Bit(file);
2928 ei->explosion_delay = getFile8Bit(file);
2929 ei->ignition_delay = getFile8Bit(file);
2930 ei->explosion_type = getFile8Bit(file);
2932 // some free bytes for future custom property values and padding
2933 ReadUnusedBytesFromFile(file, 1);
2935 // ---------- change page property values (48 bytes) ------------------------
2937 setElementChangePages(ei, ei->num_change_pages);
2939 for (i = 0; i < ei->num_change_pages; i++)
2941 struct ElementChangeInfo *change = &ei->change_page[i];
2942 unsigned int event_bits;
2944 // always start with reliable default values
2945 setElementChangeInfoToDefaults(change);
2947 // bits 0 - 31 of "has_event[]" ...
2948 event_bits = getFile32BitBE(file);
2949 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2950 if (event_bits & (1 << j))
2951 change->has_event[j] = TRUE;
2953 change->target_element = getMappedElement(getFile16BitBE(file));
2955 change->delay_fixed = getFile16BitBE(file);
2956 change->delay_random = getFile16BitBE(file);
2957 change->delay_frames = getFile16BitBE(file);
2959 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2961 change->explode = getFile8Bit(file);
2962 change->use_target_content = getFile8Bit(file);
2963 change->only_if_complete = getFile8Bit(file);
2964 change->use_random_replace = getFile8Bit(file);
2966 change->random_percentage = getFile8Bit(file);
2967 change->replace_when = getFile8Bit(file);
2969 for (y = 0; y < 3; y++)
2970 for (x = 0; x < 3; x++)
2971 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2973 change->can_change = getFile8Bit(file);
2975 change->trigger_side = getFile8Bit(file);
2977 change->trigger_player = getFile8Bit(file);
2978 change->trigger_page = getFile8Bit(file);
2980 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2981 CH_PAGE_ANY : (1 << change->trigger_page));
2983 change->has_action = getFile8Bit(file);
2984 change->action_type = getFile8Bit(file);
2985 change->action_mode = getFile8Bit(file);
2986 change->action_arg = getFile16BitBE(file);
2988 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2989 event_bits = getFile8Bit(file);
2990 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2991 if (event_bits & (1 << (j - 32)))
2992 change->has_event[j] = TRUE;
2995 // mark this custom element as modified
2996 ei->modified_settings = TRUE;
2998 level->file_has_custom_elements = TRUE;
3003 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3005 struct ElementInfo *ei;
3006 struct ElementGroupInfo *group;
3010 element = getMappedElement(getFile16BitBE(file));
3012 if (!IS_GROUP_ELEMENT(element))
3014 Warn("invalid group element number %d", element);
3016 ReadUnusedBytesFromFile(file, chunk_size - 2);
3021 ei = &element_info[element];
3023 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3024 ei->description[i] = getFile8Bit(file);
3025 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3027 group = element_info[element].group;
3029 group->num_elements = getFile8Bit(file);
3031 ei->use_gfx_element = getFile8Bit(file);
3032 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3034 group->choice_mode = getFile8Bit(file);
3036 // some free bytes for future values and padding
3037 ReadUnusedBytesFromFile(file, 3);
3039 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3040 group->element[i] = getMappedElement(getFile16BitBE(file));
3042 // mark this group element as modified
3043 element_info[element].modified_settings = TRUE;
3045 level->file_has_custom_elements = TRUE;
3050 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3051 int element, int real_element)
3053 int micro_chunk_size = 0;
3054 int conf_type = getFile8Bit(file);
3055 int byte_mask = conf_type & CONF_MASK_BYTES;
3056 boolean element_found = FALSE;
3059 micro_chunk_size += 1;
3061 if (byte_mask == CONF_MASK_MULTI_BYTES)
3063 int num_bytes = getFile16BitBE(file);
3064 byte *buffer = checked_malloc(num_bytes);
3066 ReadBytesFromFile(file, buffer, num_bytes);
3068 for (i = 0; conf[i].data_type != -1; i++)
3070 if (conf[i].element == element &&
3071 conf[i].conf_type == conf_type)
3073 int data_type = conf[i].data_type;
3074 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3075 int max_num_entities = conf[i].max_num_entities;
3077 if (num_entities > max_num_entities)
3079 Warn("truncating number of entities for element %d from %d to %d",
3080 element, num_entities, max_num_entities);
3082 num_entities = max_num_entities;
3085 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3086 data_type == TYPE_CONTENT_LIST))
3088 // for element and content lists, zero entities are not allowed
3089 Warn("found empty list of entities for element %d", element);
3091 // do not set "num_entities" here to prevent reading behind buffer
3093 *(int *)(conf[i].num_entities) = 1; // at least one is required
3097 *(int *)(conf[i].num_entities) = num_entities;
3100 element_found = TRUE;
3102 if (data_type == TYPE_STRING)
3104 char *string = (char *)(conf[i].value);
3107 for (j = 0; j < max_num_entities; j++)
3108 string[j] = (j < num_entities ? buffer[j] : '\0');
3110 else if (data_type == TYPE_ELEMENT_LIST)
3112 int *element_array = (int *)(conf[i].value);
3115 for (j = 0; j < num_entities; j++)
3117 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3119 else if (data_type == TYPE_CONTENT_LIST)
3121 struct Content *content= (struct Content *)(conf[i].value);
3124 for (c = 0; c < num_entities; c++)
3125 for (y = 0; y < 3; y++)
3126 for (x = 0; x < 3; x++)
3127 content[c].e[x][y] =
3128 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3131 element_found = FALSE;
3137 checked_free(buffer);
3139 micro_chunk_size += 2 + num_bytes;
3141 else // constant size configuration data (1, 2 or 4 bytes)
3143 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3144 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3145 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3147 for (i = 0; conf[i].data_type != -1; i++)
3149 if (conf[i].element == element &&
3150 conf[i].conf_type == conf_type)
3152 int data_type = conf[i].data_type;
3154 if (data_type == TYPE_ELEMENT)
3155 value = getMappedElement(value);
3157 if (data_type == TYPE_BOOLEAN)
3158 *(boolean *)(conf[i].value) = value;
3160 *(int *) (conf[i].value) = value;
3162 element_found = TRUE;
3168 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3173 char *error_conf_chunk_bytes =
3174 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3175 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3176 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3177 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3178 int error_element = real_element;
3180 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3181 error_conf_chunk_bytes, error_conf_chunk_token,
3182 error_element, EL_NAME(error_element));
3185 return micro_chunk_size;
3188 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3190 int real_chunk_size = 0;
3192 li = *level; // copy level data into temporary buffer
3194 while (!checkEndOfFile(file))
3196 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3198 if (real_chunk_size >= chunk_size)
3202 *level = li; // copy temporary buffer back to level data
3204 return real_chunk_size;
3207 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3209 int real_chunk_size = 0;
3211 li = *level; // copy level data into temporary buffer
3213 while (!checkEndOfFile(file))
3215 int element = getMappedElement(getFile16BitBE(file));
3217 real_chunk_size += 2;
3218 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3220 if (real_chunk_size >= chunk_size)
3224 *level = li; // copy temporary buffer back to level data
3226 return real_chunk_size;
3229 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3231 int real_chunk_size = 0;
3233 li = *level; // copy level data into temporary buffer
3235 while (!checkEndOfFile(file))
3237 int element = getMappedElement(getFile16BitBE(file));
3239 real_chunk_size += 2;
3240 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3242 if (real_chunk_size >= chunk_size)
3246 *level = li; // copy temporary buffer back to level data
3248 return real_chunk_size;
3251 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3253 int element = getMappedElement(getFile16BitBE(file));
3254 int envelope_nr = element - EL_ENVELOPE_1;
3255 int real_chunk_size = 2;
3257 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3259 while (!checkEndOfFile(file))
3261 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3264 if (real_chunk_size >= chunk_size)
3268 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3270 return real_chunk_size;
3273 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3275 int element = getMappedElement(getFile16BitBE(file));
3276 int real_chunk_size = 2;
3277 struct ElementInfo *ei = &element_info[element];
3280 xx_ei = *ei; // copy element data into temporary buffer
3282 xx_ei.num_change_pages = -1;
3284 while (!checkEndOfFile(file))
3286 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3288 if (xx_ei.num_change_pages != -1)
3291 if (real_chunk_size >= chunk_size)
3297 if (ei->num_change_pages == -1)
3299 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3302 ei->num_change_pages = 1;
3304 setElementChangePages(ei, 1);
3305 setElementChangeInfoToDefaults(ei->change);
3307 return real_chunk_size;
3310 // initialize number of change pages stored for this custom element
3311 setElementChangePages(ei, ei->num_change_pages);
3312 for (i = 0; i < ei->num_change_pages; i++)
3313 setElementChangeInfoToDefaults(&ei->change_page[i]);
3315 // start with reading properties for the first change page
3316 xx_current_change_page = 0;
3318 while (!checkEndOfFile(file))
3320 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3322 xx_change = *change; // copy change data into temporary buffer
3324 resetEventBits(); // reset bits; change page might have changed
3326 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3329 *change = xx_change;
3331 setEventFlagsFromEventBits(change);
3333 if (real_chunk_size >= chunk_size)
3337 level->file_has_custom_elements = TRUE;
3339 return real_chunk_size;
3342 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3344 int element = getMappedElement(getFile16BitBE(file));
3345 int real_chunk_size = 2;
3346 struct ElementInfo *ei = &element_info[element];
3347 struct ElementGroupInfo *group = ei->group;
3349 xx_ei = *ei; // copy element data into temporary buffer
3350 xx_group = *group; // copy group data into temporary buffer
3352 while (!checkEndOfFile(file))
3354 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3357 if (real_chunk_size >= chunk_size)
3364 level->file_has_custom_elements = TRUE;
3366 return real_chunk_size;
3369 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3370 struct LevelFileInfo *level_file_info,
3371 boolean level_info_only)
3373 char *filename = level_file_info->filename;
3374 char cookie[MAX_LINE_LEN];
3375 char chunk_name[CHUNK_ID_LEN + 1];
3379 if (!(file = openFile(filename, MODE_READ)))
3381 level->no_valid_file = TRUE;
3382 level->no_level_file = TRUE;
3384 if (level_info_only)
3387 Warn("cannot read level '%s' -- using empty level", filename);
3389 if (!setup.editor.use_template_for_new_levels)
3392 // if level file not found, try to initialize level data from template
3393 filename = getGlobalLevelTemplateFilename();
3395 if (!(file = openFile(filename, MODE_READ)))
3398 // default: for empty levels, use level template for custom elements
3399 level->use_custom_template = TRUE;
3401 level->no_valid_file = FALSE;
3404 getFileChunkBE(file, chunk_name, NULL);
3405 if (strEqual(chunk_name, "RND1"))
3407 getFile32BitBE(file); // not used
3409 getFileChunkBE(file, chunk_name, NULL);
3410 if (!strEqual(chunk_name, "CAVE"))
3412 level->no_valid_file = TRUE;
3414 Warn("unknown format of level file '%s'", filename);
3421 else // check for pre-2.0 file format with cookie string
3423 strcpy(cookie, chunk_name);
3424 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3426 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3427 cookie[strlen(cookie) - 1] = '\0';
3429 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3431 level->no_valid_file = TRUE;
3433 Warn("unknown format of level file '%s'", filename);
3440 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3442 level->no_valid_file = TRUE;
3444 Warn("unsupported version of level file '%s'", filename);
3451 // pre-2.0 level files have no game version, so use file version here
3452 level->game_version = level->file_version;
3455 if (level->file_version < FILE_VERSION_1_2)
3457 // level files from versions before 1.2.0 without chunk structure
3458 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3459 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3467 int (*loader)(File *, int, struct LevelInfo *);
3471 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3472 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3473 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3474 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3475 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3476 { "INFO", -1, LoadLevel_INFO },
3477 { "BODY", -1, LoadLevel_BODY },
3478 { "CONT", -1, LoadLevel_CONT },
3479 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3480 { "CNT3", -1, LoadLevel_CNT3 },
3481 { "CUS1", -1, LoadLevel_CUS1 },
3482 { "CUS2", -1, LoadLevel_CUS2 },
3483 { "CUS3", -1, LoadLevel_CUS3 },
3484 { "CUS4", -1, LoadLevel_CUS4 },
3485 { "GRP1", -1, LoadLevel_GRP1 },
3486 { "CONF", -1, LoadLevel_CONF },
3487 { "ELEM", -1, LoadLevel_ELEM },
3488 { "NOTE", -1, LoadLevel_NOTE },
3489 { "CUSX", -1, LoadLevel_CUSX },
3490 { "GRPX", -1, LoadLevel_GRPX },
3495 while (getFileChunkBE(file, chunk_name, &chunk_size))
3499 while (chunk_info[i].name != NULL &&
3500 !strEqual(chunk_name, chunk_info[i].name))
3503 if (chunk_info[i].name == NULL)
3505 Warn("unknown chunk '%s' in level file '%s'",
3506 chunk_name, filename);
3508 ReadUnusedBytesFromFile(file, chunk_size);
3510 else if (chunk_info[i].size != -1 &&
3511 chunk_info[i].size != chunk_size)
3513 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3514 chunk_size, chunk_name, filename);
3516 ReadUnusedBytesFromFile(file, chunk_size);
3520 // call function to load this level chunk
3521 int chunk_size_expected =
3522 (chunk_info[i].loader)(file, chunk_size, level);
3524 // the size of some chunks cannot be checked before reading other
3525 // chunks first (like "HEAD" and "BODY") that contain some header
3526 // information, so check them here
3527 if (chunk_size_expected != chunk_size)
3529 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3530 chunk_size, chunk_name, filename);
3540 // ----------------------------------------------------------------------------
3541 // functions for loading EM level
3542 // ----------------------------------------------------------------------------
3544 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3546 static int ball_xy[8][2] =
3557 struct LevelInfo_EM *level_em = level->native_em_level;
3558 struct CAVE *cav = level_em->cav;
3561 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3562 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3564 cav->time_seconds = level->time;
3565 cav->gems_needed = level->gems_needed;
3567 cav->emerald_score = level->score[SC_EMERALD];
3568 cav->diamond_score = level->score[SC_DIAMOND];
3569 cav->alien_score = level->score[SC_ROBOT];
3570 cav->tank_score = level->score[SC_SPACESHIP];
3571 cav->bug_score = level->score[SC_BUG];
3572 cav->eater_score = level->score[SC_YAMYAM];
3573 cav->nut_score = level->score[SC_NUT];
3574 cav->dynamite_score = level->score[SC_DYNAMITE];
3575 cav->key_score = level->score[SC_KEY];
3576 cav->exit_score = level->score[SC_TIME_BONUS];
3578 cav->num_eater_arrays = level->num_yamyam_contents;
3580 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3581 for (y = 0; y < 3; y++)
3582 for (x = 0; x < 3; x++)
3583 cav->eater_array[i][y * 3 + x] =
3584 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3586 cav->amoeba_time = level->amoeba_speed;
3587 cav->wonderwall_time = level->time_magic_wall;
3588 cav->wheel_time = level->time_wheel;
3590 cav->android_move_time = level->android_move_time;
3591 cav->android_clone_time = level->android_clone_time;
3592 cav->ball_random = level->ball_random;
3593 cav->ball_active = level->ball_active_initial;
3594 cav->ball_time = level->ball_time;
3595 cav->num_ball_arrays = level->num_ball_contents;
3597 cav->lenses_score = level->lenses_score;
3598 cav->magnify_score = level->magnify_score;
3599 cav->slurp_score = level->slurp_score;
3601 cav->lenses_time = level->lenses_time;
3602 cav->magnify_time = level->magnify_time;
3604 cav->wind_direction =
3605 map_direction_RND_to_EM(level->wind_direction_initial);
3607 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3608 for (j = 0; j < 8; j++)
3609 cav->ball_array[i][j] =
3610 map_element_RND_to_EM_cave(level->ball_content[i].
3611 e[ball_xy[j][0]][ball_xy[j][1]]);
3613 map_android_clone_elements_RND_to_EM(level);
3615 // first fill the complete playfield with the empty space element
3616 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3617 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3618 cav->cave[x][y] = Cblank;
3620 // then copy the real level contents from level file into the playfield
3621 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3623 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3625 if (level->field[x][y] == EL_AMOEBA_DEAD)
3626 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3628 cav->cave[x][y] = new_element;
3631 for (i = 0; i < MAX_PLAYERS; i++)
3633 cav->player_x[i] = -1;
3634 cav->player_y[i] = -1;
3637 // initialize player positions and delete players from the playfield
3638 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3640 if (ELEM_IS_PLAYER(level->field[x][y]))
3642 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3644 cav->player_x[player_nr] = x;
3645 cav->player_y[player_nr] = y;
3647 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3652 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3654 static int ball_xy[8][2] =
3665 struct LevelInfo_EM *level_em = level->native_em_level;
3666 struct CAVE *cav = level_em->cav;
3669 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3670 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3672 level->time = cav->time_seconds;
3673 level->gems_needed = cav->gems_needed;
3675 sprintf(level->name, "Level %d", level->file_info.nr);
3677 level->score[SC_EMERALD] = cav->emerald_score;
3678 level->score[SC_DIAMOND] = cav->diamond_score;
3679 level->score[SC_ROBOT] = cav->alien_score;
3680 level->score[SC_SPACESHIP] = cav->tank_score;
3681 level->score[SC_BUG] = cav->bug_score;
3682 level->score[SC_YAMYAM] = cav->eater_score;
3683 level->score[SC_NUT] = cav->nut_score;
3684 level->score[SC_DYNAMITE] = cav->dynamite_score;
3685 level->score[SC_KEY] = cav->key_score;
3686 level->score[SC_TIME_BONUS] = cav->exit_score;
3688 level->num_yamyam_contents = cav->num_eater_arrays;
3690 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3691 for (y = 0; y < 3; y++)
3692 for (x = 0; x < 3; x++)
3693 level->yamyam_content[i].e[x][y] =
3694 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3696 level->amoeba_speed = cav->amoeba_time;
3697 level->time_magic_wall = cav->wonderwall_time;
3698 level->time_wheel = cav->wheel_time;
3700 level->android_move_time = cav->android_move_time;
3701 level->android_clone_time = cav->android_clone_time;
3702 level->ball_random = cav->ball_random;
3703 level->ball_active_initial = cav->ball_active;
3704 level->ball_time = cav->ball_time;
3705 level->num_ball_contents = cav->num_ball_arrays;
3707 level->lenses_score = cav->lenses_score;
3708 level->magnify_score = cav->magnify_score;
3709 level->slurp_score = cav->slurp_score;
3711 level->lenses_time = cav->lenses_time;
3712 level->magnify_time = cav->magnify_time;
3714 level->wind_direction_initial =
3715 map_direction_EM_to_RND(cav->wind_direction);
3717 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3718 for (j = 0; j < 8; j++)
3719 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3720 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3722 map_android_clone_elements_EM_to_RND(level);
3724 // convert the playfield (some elements need special treatment)
3725 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3727 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3729 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3730 new_element = EL_AMOEBA_DEAD;
3732 level->field[x][y] = new_element;
3735 for (i = 0; i < MAX_PLAYERS; i++)
3737 // in case of all players set to the same field, use the first player
3738 int nr = MAX_PLAYERS - i - 1;
3739 int jx = cav->player_x[nr];
3740 int jy = cav->player_y[nr];
3742 if (jx != -1 && jy != -1)
3743 level->field[jx][jy] = EL_PLAYER_1 + nr;
3746 // time score is counted for each 10 seconds left in Emerald Mine levels
3747 level->time_score_base = 10;
3751 // ----------------------------------------------------------------------------
3752 // functions for loading SP level
3753 // ----------------------------------------------------------------------------
3755 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3757 struct LevelInfo_SP *level_sp = level->native_sp_level;
3758 LevelInfoType *header = &level_sp->header;
3761 level_sp->width = level->fieldx;
3762 level_sp->height = level->fieldy;
3764 for (x = 0; x < level->fieldx; x++)
3765 for (y = 0; y < level->fieldy; y++)
3766 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3768 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3770 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3771 header->LevelTitle[i] = level->name[i];
3772 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3774 header->InfotronsNeeded = level->gems_needed;
3776 header->SpecialPortCount = 0;
3778 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3780 boolean gravity_port_found = FALSE;
3781 boolean gravity_port_valid = FALSE;
3782 int gravity_port_flag;
3783 int gravity_port_base_element;
3784 int element = level->field[x][y];
3786 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3787 element <= EL_SP_GRAVITY_ON_PORT_UP)
3789 gravity_port_found = TRUE;
3790 gravity_port_valid = TRUE;
3791 gravity_port_flag = 1;
3792 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3794 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3795 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3797 gravity_port_found = TRUE;
3798 gravity_port_valid = TRUE;
3799 gravity_port_flag = 0;
3800 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3802 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3803 element <= EL_SP_GRAVITY_PORT_UP)
3805 // change R'n'D style gravity inverting special port to normal port
3806 // (there are no gravity inverting ports in native Supaplex engine)
3808 gravity_port_found = TRUE;
3809 gravity_port_valid = FALSE;
3810 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3813 if (gravity_port_found)
3815 if (gravity_port_valid &&
3816 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3818 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3820 port->PortLocation = (y * level->fieldx + x) * 2;
3821 port->Gravity = gravity_port_flag;
3823 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3825 header->SpecialPortCount++;
3829 // change special gravity port to normal port
3831 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3834 level_sp->playfield[x][y] = element - EL_SP_START;
3839 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3841 struct LevelInfo_SP *level_sp = level->native_sp_level;
3842 LevelInfoType *header = &level_sp->header;
3843 boolean num_invalid_elements = 0;
3846 level->fieldx = level_sp->width;
3847 level->fieldy = level_sp->height;
3849 for (x = 0; x < level->fieldx; x++)
3851 for (y = 0; y < level->fieldy; y++)
3853 int element_old = level_sp->playfield[x][y];
3854 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3856 if (element_new == EL_UNKNOWN)
3858 num_invalid_elements++;
3860 Debug("level:native:SP", "invalid element %d at position %d, %d",
3864 level->field[x][y] = element_new;
3868 if (num_invalid_elements > 0)
3869 Warn("found %d invalid elements%s", num_invalid_elements,
3870 (!options.debug ? " (use '--debug' for more details)" : ""));
3872 for (i = 0; i < MAX_PLAYERS; i++)
3873 level->initial_player_gravity[i] =
3874 (header->InitialGravity == 1 ? TRUE : FALSE);
3876 // skip leading spaces
3877 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3878 if (header->LevelTitle[i] != ' ')
3882 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3883 level->name[j] = header->LevelTitle[i];
3884 level->name[j] = '\0';
3886 // cut trailing spaces
3888 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3889 level->name[j - 1] = '\0';
3891 level->gems_needed = header->InfotronsNeeded;
3893 for (i = 0; i < header->SpecialPortCount; i++)
3895 SpecialPortType *port = &header->SpecialPort[i];
3896 int port_location = port->PortLocation;
3897 int gravity = port->Gravity;
3898 int port_x, port_y, port_element;
3900 port_x = (port_location / 2) % level->fieldx;
3901 port_y = (port_location / 2) / level->fieldx;
3903 if (port_x < 0 || port_x >= level->fieldx ||
3904 port_y < 0 || port_y >= level->fieldy)
3906 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3911 port_element = level->field[port_x][port_y];
3913 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3914 port_element > EL_SP_GRAVITY_PORT_UP)
3916 Warn("no special port at position (%d, %d)", port_x, port_y);
3921 // change previous (wrong) gravity inverting special port to either
3922 // gravity enabling special port or gravity disabling special port
3923 level->field[port_x][port_y] +=
3924 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3925 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3928 // change special gravity ports without database entries to normal ports
3929 for (x = 0; x < level->fieldx; x++)
3930 for (y = 0; y < level->fieldy; y++)
3931 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3932 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3933 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3935 level->time = 0; // no time limit
3936 level->amoeba_speed = 0;
3937 level->time_magic_wall = 0;
3938 level->time_wheel = 0;
3939 level->amoeba_content = EL_EMPTY;
3942 // original Supaplex does not use score values -- use default values
3944 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3945 level->score[i] = 0;
3948 // there are no yamyams in supaplex levels
3949 for (i = 0; i < level->num_yamyam_contents; i++)
3950 for (x = 0; x < 3; x++)
3951 for (y = 0; y < 3; y++)
3952 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3955 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3957 struct LevelInfo_SP *level_sp = level->native_sp_level;
3958 struct DemoInfo_SP *demo = &level_sp->demo;
3961 // always start with reliable default values
3962 demo->is_available = FALSE;
3965 if (TAPE_IS_EMPTY(tape))
3968 demo->level_nr = tape.level_nr; // (currently not used)
3970 level_sp->header.DemoRandomSeed = tape.random_seed;
3974 for (i = 0; i < tape.length; i++)
3976 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3977 int demo_repeat = tape.pos[i].delay;
3978 int demo_entries = (demo_repeat + 15) / 16;
3980 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3982 Warn("tape truncated: size exceeds maximum SP demo size %d",
3988 for (j = 0; j < demo_repeat / 16; j++)
3989 demo->data[demo->length++] = 0xf0 | demo_action;
3991 if (demo_repeat % 16)
3992 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3995 demo->is_available = TRUE;
3998 static void setTapeInfoToDefaults(void);
4000 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4002 struct LevelInfo_SP *level_sp = level->native_sp_level;
4003 struct DemoInfo_SP *demo = &level_sp->demo;
4004 char *filename = level->file_info.filename;
4007 // always start with reliable default values
4008 setTapeInfoToDefaults();
4010 if (!demo->is_available)
4013 tape.level_nr = demo->level_nr; // (currently not used)
4014 tape.random_seed = level_sp->header.DemoRandomSeed;
4016 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4019 tape.pos[tape.counter].delay = 0;
4021 for (i = 0; i < demo->length; i++)
4023 int demo_action = demo->data[i] & 0x0f;
4024 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4025 int tape_action = map_key_SP_to_RND(demo_action);
4026 int tape_repeat = demo_repeat + 1;
4027 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4028 boolean success = 0;
4031 for (j = 0; j < tape_repeat; j++)
4032 success = TapeAddAction(action);
4036 Warn("SP demo truncated: size exceeds maximum tape size %d",
4043 TapeHaltRecording();
4047 // ----------------------------------------------------------------------------
4048 // functions for loading MM level
4049 // ----------------------------------------------------------------------------
4051 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4053 struct LevelInfo_MM *level_mm = level->native_mm_level;
4056 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4057 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4059 level_mm->time = level->time;
4060 level_mm->kettles_needed = level->gems_needed;
4061 level_mm->auto_count_kettles = level->auto_count_gems;
4063 level_mm->laser_red = level->mm_laser_red;
4064 level_mm->laser_green = level->mm_laser_green;
4065 level_mm->laser_blue = level->mm_laser_blue;
4067 strcpy(level_mm->name, level->name);
4068 strcpy(level_mm->author, level->author);
4070 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4071 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4072 level_mm->score[SC_KEY] = level->score[SC_KEY];
4073 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4074 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4076 level_mm->amoeba_speed = level->amoeba_speed;
4077 level_mm->time_fuse = level->mm_time_fuse;
4078 level_mm->time_bomb = level->mm_time_bomb;
4079 level_mm->time_ball = level->mm_time_ball;
4080 level_mm->time_block = level->mm_time_block;
4082 for (x = 0; x < level->fieldx; x++)
4083 for (y = 0; y < level->fieldy; y++)
4085 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4088 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4090 struct LevelInfo_MM *level_mm = level->native_mm_level;
4093 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4094 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4096 level->time = level_mm->time;
4097 level->gems_needed = level_mm->kettles_needed;
4098 level->auto_count_gems = level_mm->auto_count_kettles;
4100 level->mm_laser_red = level_mm->laser_red;
4101 level->mm_laser_green = level_mm->laser_green;
4102 level->mm_laser_blue = level_mm->laser_blue;
4104 strcpy(level->name, level_mm->name);
4106 // only overwrite author from 'levelinfo.conf' if author defined in level
4107 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4108 strcpy(level->author, level_mm->author);
4110 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4111 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4112 level->score[SC_KEY] = level_mm->score[SC_KEY];
4113 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4114 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4116 level->amoeba_speed = level_mm->amoeba_speed;
4117 level->mm_time_fuse = level_mm->time_fuse;
4118 level->mm_time_bomb = level_mm->time_bomb;
4119 level->mm_time_ball = level_mm->time_ball;
4120 level->mm_time_block = level_mm->time_block;
4122 for (x = 0; x < level->fieldx; x++)
4123 for (y = 0; y < level->fieldy; y++)
4124 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4128 // ----------------------------------------------------------------------------
4129 // functions for loading DC level
4130 // ----------------------------------------------------------------------------
4132 #define DC_LEVEL_HEADER_SIZE 344
4134 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4137 static int last_data_encoded;
4141 int diff_hi, diff_lo;
4142 int data_hi, data_lo;
4143 unsigned short data_decoded;
4147 last_data_encoded = 0;
4154 diff = data_encoded - last_data_encoded;
4155 diff_hi = diff & ~0xff;
4156 diff_lo = diff & 0xff;
4160 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4161 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4162 data_hi = data_hi & 0xff00;
4164 data_decoded = data_hi | data_lo;
4166 last_data_encoded = data_encoded;
4168 offset1 = (offset1 + 1) % 31;
4169 offset2 = offset2 & 0xff;
4171 return data_decoded;
4174 static int getMappedElement_DC(int element)
4182 // 0x0117 - 0x036e: (?)
4185 // 0x042d - 0x0684: (?)
4201 element = EL_CRYSTAL;
4204 case 0x0e77: // quicksand (boulder)
4205 element = EL_QUICKSAND_FAST_FULL;
4208 case 0x0e99: // slow quicksand (boulder)
4209 element = EL_QUICKSAND_FULL;
4213 element = EL_EM_EXIT_OPEN;
4217 element = EL_EM_EXIT_CLOSED;
4221 element = EL_EM_STEEL_EXIT_OPEN;
4225 element = EL_EM_STEEL_EXIT_CLOSED;
4228 case 0x0f4f: // dynamite (lit 1)
4229 element = EL_EM_DYNAMITE_ACTIVE;
4232 case 0x0f57: // dynamite (lit 2)
4233 element = EL_EM_DYNAMITE_ACTIVE;
4236 case 0x0f5f: // dynamite (lit 3)
4237 element = EL_EM_DYNAMITE_ACTIVE;
4240 case 0x0f67: // dynamite (lit 4)
4241 element = EL_EM_DYNAMITE_ACTIVE;
4248 element = EL_AMOEBA_WET;
4252 element = EL_AMOEBA_DROP;
4256 element = EL_DC_MAGIC_WALL;
4260 element = EL_SPACESHIP_UP;
4264 element = EL_SPACESHIP_DOWN;
4268 element = EL_SPACESHIP_LEFT;
4272 element = EL_SPACESHIP_RIGHT;
4276 element = EL_BUG_UP;
4280 element = EL_BUG_DOWN;
4284 element = EL_BUG_LEFT;
4288 element = EL_BUG_RIGHT;
4292 element = EL_MOLE_UP;
4296 element = EL_MOLE_DOWN;
4300 element = EL_MOLE_LEFT;
4304 element = EL_MOLE_RIGHT;
4312 element = EL_YAMYAM_UP;
4316 element = EL_SWITCHGATE_OPEN;
4320 element = EL_SWITCHGATE_CLOSED;
4324 element = EL_DC_SWITCHGATE_SWITCH_UP;
4328 element = EL_TIMEGATE_CLOSED;
4331 case 0x144c: // conveyor belt switch (green)
4332 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4335 case 0x144f: // conveyor belt switch (red)
4336 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4339 case 0x1452: // conveyor belt switch (blue)
4340 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4344 element = EL_CONVEYOR_BELT_3_MIDDLE;
4348 element = EL_CONVEYOR_BELT_3_LEFT;
4352 element = EL_CONVEYOR_BELT_3_RIGHT;
4356 element = EL_CONVEYOR_BELT_1_MIDDLE;
4360 element = EL_CONVEYOR_BELT_1_LEFT;
4364 element = EL_CONVEYOR_BELT_1_RIGHT;
4368 element = EL_CONVEYOR_BELT_4_MIDDLE;
4372 element = EL_CONVEYOR_BELT_4_LEFT;
4376 element = EL_CONVEYOR_BELT_4_RIGHT;
4380 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4384 element = EL_EXPANDABLE_WALL_VERTICAL;
4388 element = EL_EXPANDABLE_WALL_ANY;
4391 case 0x14ce: // growing steel wall (left/right)
4392 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4395 case 0x14df: // growing steel wall (up/down)
4396 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4399 case 0x14e8: // growing steel wall (up/down/left/right)
4400 element = EL_EXPANDABLE_STEELWALL_ANY;
4404 element = EL_SHIELD_DEADLY;
4408 element = EL_EXTRA_TIME;
4416 element = EL_EMPTY_SPACE;
4419 case 0x1578: // quicksand (empty)
4420 element = EL_QUICKSAND_FAST_EMPTY;
4423 case 0x1579: // slow quicksand (empty)
4424 element = EL_QUICKSAND_EMPTY;
4434 element = EL_EM_DYNAMITE;
4437 case 0x15a1: // key (red)
4438 element = EL_EM_KEY_1;
4441 case 0x15a2: // key (yellow)
4442 element = EL_EM_KEY_2;
4445 case 0x15a3: // key (blue)
4446 element = EL_EM_KEY_4;
4449 case 0x15a4: // key (green)
4450 element = EL_EM_KEY_3;
4453 case 0x15a5: // key (white)
4454 element = EL_DC_KEY_WHITE;
4458 element = EL_WALL_SLIPPERY;
4465 case 0x15a8: // wall (not round)
4469 case 0x15a9: // (blue)
4470 element = EL_CHAR_A;
4473 case 0x15aa: // (blue)
4474 element = EL_CHAR_B;
4477 case 0x15ab: // (blue)
4478 element = EL_CHAR_C;
4481 case 0x15ac: // (blue)
4482 element = EL_CHAR_D;
4485 case 0x15ad: // (blue)
4486 element = EL_CHAR_E;
4489 case 0x15ae: // (blue)
4490 element = EL_CHAR_F;
4493 case 0x15af: // (blue)
4494 element = EL_CHAR_G;
4497 case 0x15b0: // (blue)
4498 element = EL_CHAR_H;
4501 case 0x15b1: // (blue)
4502 element = EL_CHAR_I;
4505 case 0x15b2: // (blue)
4506 element = EL_CHAR_J;
4509 case 0x15b3: // (blue)
4510 element = EL_CHAR_K;
4513 case 0x15b4: // (blue)
4514 element = EL_CHAR_L;
4517 case 0x15b5: // (blue)
4518 element = EL_CHAR_M;
4521 case 0x15b6: // (blue)
4522 element = EL_CHAR_N;
4525 case 0x15b7: // (blue)
4526 element = EL_CHAR_O;
4529 case 0x15b8: // (blue)
4530 element = EL_CHAR_P;
4533 case 0x15b9: // (blue)
4534 element = EL_CHAR_Q;
4537 case 0x15ba: // (blue)
4538 element = EL_CHAR_R;
4541 case 0x15bb: // (blue)
4542 element = EL_CHAR_S;
4545 case 0x15bc: // (blue)
4546 element = EL_CHAR_T;
4549 case 0x15bd: // (blue)
4550 element = EL_CHAR_U;
4553 case 0x15be: // (blue)
4554 element = EL_CHAR_V;
4557 case 0x15bf: // (blue)
4558 element = EL_CHAR_W;
4561 case 0x15c0: // (blue)
4562 element = EL_CHAR_X;
4565 case 0x15c1: // (blue)
4566 element = EL_CHAR_Y;
4569 case 0x15c2: // (blue)
4570 element = EL_CHAR_Z;
4573 case 0x15c3: // (blue)
4574 element = EL_CHAR_AUMLAUT;
4577 case 0x15c4: // (blue)
4578 element = EL_CHAR_OUMLAUT;
4581 case 0x15c5: // (blue)
4582 element = EL_CHAR_UUMLAUT;
4585 case 0x15c6: // (blue)
4586 element = EL_CHAR_0;
4589 case 0x15c7: // (blue)
4590 element = EL_CHAR_1;
4593 case 0x15c8: // (blue)
4594 element = EL_CHAR_2;
4597 case 0x15c9: // (blue)
4598 element = EL_CHAR_3;
4601 case 0x15ca: // (blue)
4602 element = EL_CHAR_4;
4605 case 0x15cb: // (blue)
4606 element = EL_CHAR_5;
4609 case 0x15cc: // (blue)
4610 element = EL_CHAR_6;
4613 case 0x15cd: // (blue)
4614 element = EL_CHAR_7;
4617 case 0x15ce: // (blue)
4618 element = EL_CHAR_8;
4621 case 0x15cf: // (blue)
4622 element = EL_CHAR_9;
4625 case 0x15d0: // (blue)
4626 element = EL_CHAR_PERIOD;
4629 case 0x15d1: // (blue)
4630 element = EL_CHAR_EXCLAM;
4633 case 0x15d2: // (blue)
4634 element = EL_CHAR_COLON;
4637 case 0x15d3: // (blue)
4638 element = EL_CHAR_LESS;
4641 case 0x15d4: // (blue)
4642 element = EL_CHAR_GREATER;
4645 case 0x15d5: // (blue)
4646 element = EL_CHAR_QUESTION;
4649 case 0x15d6: // (blue)
4650 element = EL_CHAR_COPYRIGHT;
4653 case 0x15d7: // (blue)
4654 element = EL_CHAR_UP;
4657 case 0x15d8: // (blue)
4658 element = EL_CHAR_DOWN;
4661 case 0x15d9: // (blue)
4662 element = EL_CHAR_BUTTON;
4665 case 0x15da: // (blue)
4666 element = EL_CHAR_PLUS;
4669 case 0x15db: // (blue)
4670 element = EL_CHAR_MINUS;
4673 case 0x15dc: // (blue)
4674 element = EL_CHAR_APOSTROPHE;
4677 case 0x15dd: // (blue)
4678 element = EL_CHAR_PARENLEFT;
4681 case 0x15de: // (blue)
4682 element = EL_CHAR_PARENRIGHT;
4685 case 0x15df: // (green)
4686 element = EL_CHAR_A;
4689 case 0x15e0: // (green)
4690 element = EL_CHAR_B;
4693 case 0x15e1: // (green)
4694 element = EL_CHAR_C;
4697 case 0x15e2: // (green)
4698 element = EL_CHAR_D;
4701 case 0x15e3: // (green)
4702 element = EL_CHAR_E;
4705 case 0x15e4: // (green)
4706 element = EL_CHAR_F;
4709 case 0x15e5: // (green)
4710 element = EL_CHAR_G;
4713 case 0x15e6: // (green)
4714 element = EL_CHAR_H;
4717 case 0x15e7: // (green)
4718 element = EL_CHAR_I;
4721 case 0x15e8: // (green)
4722 element = EL_CHAR_J;
4725 case 0x15e9: // (green)
4726 element = EL_CHAR_K;
4729 case 0x15ea: // (green)
4730 element = EL_CHAR_L;
4733 case 0x15eb: // (green)
4734 element = EL_CHAR_M;
4737 case 0x15ec: // (green)
4738 element = EL_CHAR_N;
4741 case 0x15ed: // (green)
4742 element = EL_CHAR_O;
4745 case 0x15ee: // (green)
4746 element = EL_CHAR_P;
4749 case 0x15ef: // (green)
4750 element = EL_CHAR_Q;
4753 case 0x15f0: // (green)
4754 element = EL_CHAR_R;
4757 case 0x15f1: // (green)
4758 element = EL_CHAR_S;
4761 case 0x15f2: // (green)
4762 element = EL_CHAR_T;
4765 case 0x15f3: // (green)
4766 element = EL_CHAR_U;
4769 case 0x15f4: // (green)
4770 element = EL_CHAR_V;
4773 case 0x15f5: // (green)
4774 element = EL_CHAR_W;
4777 case 0x15f6: // (green)
4778 element = EL_CHAR_X;
4781 case 0x15f7: // (green)
4782 element = EL_CHAR_Y;
4785 case 0x15f8: // (green)
4786 element = EL_CHAR_Z;
4789 case 0x15f9: // (green)
4790 element = EL_CHAR_AUMLAUT;
4793 case 0x15fa: // (green)
4794 element = EL_CHAR_OUMLAUT;
4797 case 0x15fb: // (green)
4798 element = EL_CHAR_UUMLAUT;
4801 case 0x15fc: // (green)
4802 element = EL_CHAR_0;
4805 case 0x15fd: // (green)
4806 element = EL_CHAR_1;
4809 case 0x15fe: // (green)
4810 element = EL_CHAR_2;
4813 case 0x15ff: // (green)
4814 element = EL_CHAR_3;
4817 case 0x1600: // (green)
4818 element = EL_CHAR_4;
4821 case 0x1601: // (green)
4822 element = EL_CHAR_5;
4825 case 0x1602: // (green)
4826 element = EL_CHAR_6;
4829 case 0x1603: // (green)
4830 element = EL_CHAR_7;
4833 case 0x1604: // (green)
4834 element = EL_CHAR_8;
4837 case 0x1605: // (green)
4838 element = EL_CHAR_9;
4841 case 0x1606: // (green)
4842 element = EL_CHAR_PERIOD;
4845 case 0x1607: // (green)
4846 element = EL_CHAR_EXCLAM;
4849 case 0x1608: // (green)
4850 element = EL_CHAR_COLON;
4853 case 0x1609: // (green)
4854 element = EL_CHAR_LESS;
4857 case 0x160a: // (green)
4858 element = EL_CHAR_GREATER;
4861 case 0x160b: // (green)
4862 element = EL_CHAR_QUESTION;
4865 case 0x160c: // (green)
4866 element = EL_CHAR_COPYRIGHT;
4869 case 0x160d: // (green)
4870 element = EL_CHAR_UP;
4873 case 0x160e: // (green)
4874 element = EL_CHAR_DOWN;
4877 case 0x160f: // (green)
4878 element = EL_CHAR_BUTTON;
4881 case 0x1610: // (green)
4882 element = EL_CHAR_PLUS;
4885 case 0x1611: // (green)
4886 element = EL_CHAR_MINUS;
4889 case 0x1612: // (green)
4890 element = EL_CHAR_APOSTROPHE;
4893 case 0x1613: // (green)
4894 element = EL_CHAR_PARENLEFT;
4897 case 0x1614: // (green)
4898 element = EL_CHAR_PARENRIGHT;
4901 case 0x1615: // (blue steel)
4902 element = EL_STEEL_CHAR_A;
4905 case 0x1616: // (blue steel)
4906 element = EL_STEEL_CHAR_B;
4909 case 0x1617: // (blue steel)
4910 element = EL_STEEL_CHAR_C;
4913 case 0x1618: // (blue steel)
4914 element = EL_STEEL_CHAR_D;
4917 case 0x1619: // (blue steel)
4918 element = EL_STEEL_CHAR_E;
4921 case 0x161a: // (blue steel)
4922 element = EL_STEEL_CHAR_F;
4925 case 0x161b: // (blue steel)
4926 element = EL_STEEL_CHAR_G;
4929 case 0x161c: // (blue steel)
4930 element = EL_STEEL_CHAR_H;
4933 case 0x161d: // (blue steel)
4934 element = EL_STEEL_CHAR_I;
4937 case 0x161e: // (blue steel)
4938 element = EL_STEEL_CHAR_J;
4941 case 0x161f: // (blue steel)
4942 element = EL_STEEL_CHAR_K;
4945 case 0x1620: // (blue steel)
4946 element = EL_STEEL_CHAR_L;
4949 case 0x1621: // (blue steel)
4950 element = EL_STEEL_CHAR_M;
4953 case 0x1622: // (blue steel)
4954 element = EL_STEEL_CHAR_N;
4957 case 0x1623: // (blue steel)
4958 element = EL_STEEL_CHAR_O;
4961 case 0x1624: // (blue steel)
4962 element = EL_STEEL_CHAR_P;
4965 case 0x1625: // (blue steel)
4966 element = EL_STEEL_CHAR_Q;
4969 case 0x1626: // (blue steel)
4970 element = EL_STEEL_CHAR_R;
4973 case 0x1627: // (blue steel)
4974 element = EL_STEEL_CHAR_S;
4977 case 0x1628: // (blue steel)
4978 element = EL_STEEL_CHAR_T;
4981 case 0x1629: // (blue steel)
4982 element = EL_STEEL_CHAR_U;
4985 case 0x162a: // (blue steel)
4986 element = EL_STEEL_CHAR_V;
4989 case 0x162b: // (blue steel)
4990 element = EL_STEEL_CHAR_W;
4993 case 0x162c: // (blue steel)
4994 element = EL_STEEL_CHAR_X;
4997 case 0x162d: // (blue steel)
4998 element = EL_STEEL_CHAR_Y;
5001 case 0x162e: // (blue steel)
5002 element = EL_STEEL_CHAR_Z;
5005 case 0x162f: // (blue steel)
5006 element = EL_STEEL_CHAR_AUMLAUT;
5009 case 0x1630: // (blue steel)
5010 element = EL_STEEL_CHAR_OUMLAUT;
5013 case 0x1631: // (blue steel)
5014 element = EL_STEEL_CHAR_UUMLAUT;
5017 case 0x1632: // (blue steel)
5018 element = EL_STEEL_CHAR_0;
5021 case 0x1633: // (blue steel)
5022 element = EL_STEEL_CHAR_1;
5025 case 0x1634: // (blue steel)
5026 element = EL_STEEL_CHAR_2;
5029 case 0x1635: // (blue steel)
5030 element = EL_STEEL_CHAR_3;
5033 case 0x1636: // (blue steel)
5034 element = EL_STEEL_CHAR_4;
5037 case 0x1637: // (blue steel)
5038 element = EL_STEEL_CHAR_5;
5041 case 0x1638: // (blue steel)
5042 element = EL_STEEL_CHAR_6;
5045 case 0x1639: // (blue steel)
5046 element = EL_STEEL_CHAR_7;
5049 case 0x163a: // (blue steel)
5050 element = EL_STEEL_CHAR_8;
5053 case 0x163b: // (blue steel)
5054 element = EL_STEEL_CHAR_9;
5057 case 0x163c: // (blue steel)
5058 element = EL_STEEL_CHAR_PERIOD;
5061 case 0x163d: // (blue steel)
5062 element = EL_STEEL_CHAR_EXCLAM;
5065 case 0x163e: // (blue steel)
5066 element = EL_STEEL_CHAR_COLON;
5069 case 0x163f: // (blue steel)
5070 element = EL_STEEL_CHAR_LESS;
5073 case 0x1640: // (blue steel)
5074 element = EL_STEEL_CHAR_GREATER;
5077 case 0x1641: // (blue steel)
5078 element = EL_STEEL_CHAR_QUESTION;
5081 case 0x1642: // (blue steel)
5082 element = EL_STEEL_CHAR_COPYRIGHT;
5085 case 0x1643: // (blue steel)
5086 element = EL_STEEL_CHAR_UP;
5089 case 0x1644: // (blue steel)
5090 element = EL_STEEL_CHAR_DOWN;
5093 case 0x1645: // (blue steel)
5094 element = EL_STEEL_CHAR_BUTTON;
5097 case 0x1646: // (blue steel)
5098 element = EL_STEEL_CHAR_PLUS;
5101 case 0x1647: // (blue steel)
5102 element = EL_STEEL_CHAR_MINUS;
5105 case 0x1648: // (blue steel)
5106 element = EL_STEEL_CHAR_APOSTROPHE;
5109 case 0x1649: // (blue steel)
5110 element = EL_STEEL_CHAR_PARENLEFT;
5113 case 0x164a: // (blue steel)
5114 element = EL_STEEL_CHAR_PARENRIGHT;
5117 case 0x164b: // (green steel)
5118 element = EL_STEEL_CHAR_A;
5121 case 0x164c: // (green steel)
5122 element = EL_STEEL_CHAR_B;
5125 case 0x164d: // (green steel)
5126 element = EL_STEEL_CHAR_C;
5129 case 0x164e: // (green steel)
5130 element = EL_STEEL_CHAR_D;
5133 case 0x164f: // (green steel)
5134 element = EL_STEEL_CHAR_E;
5137 case 0x1650: // (green steel)
5138 element = EL_STEEL_CHAR_F;
5141 case 0x1651: // (green steel)
5142 element = EL_STEEL_CHAR_G;
5145 case 0x1652: // (green steel)
5146 element = EL_STEEL_CHAR_H;
5149 case 0x1653: // (green steel)
5150 element = EL_STEEL_CHAR_I;
5153 case 0x1654: // (green steel)
5154 element = EL_STEEL_CHAR_J;
5157 case 0x1655: // (green steel)
5158 element = EL_STEEL_CHAR_K;
5161 case 0x1656: // (green steel)
5162 element = EL_STEEL_CHAR_L;
5165 case 0x1657: // (green steel)
5166 element = EL_STEEL_CHAR_M;
5169 case 0x1658: // (green steel)
5170 element = EL_STEEL_CHAR_N;
5173 case 0x1659: // (green steel)
5174 element = EL_STEEL_CHAR_O;
5177 case 0x165a: // (green steel)
5178 element = EL_STEEL_CHAR_P;
5181 case 0x165b: // (green steel)
5182 element = EL_STEEL_CHAR_Q;
5185 case 0x165c: // (green steel)
5186 element = EL_STEEL_CHAR_R;
5189 case 0x165d: // (green steel)
5190 element = EL_STEEL_CHAR_S;
5193 case 0x165e: // (green steel)
5194 element = EL_STEEL_CHAR_T;
5197 case 0x165f: // (green steel)
5198 element = EL_STEEL_CHAR_U;
5201 case 0x1660: // (green steel)
5202 element = EL_STEEL_CHAR_V;
5205 case 0x1661: // (green steel)
5206 element = EL_STEEL_CHAR_W;
5209 case 0x1662: // (green steel)
5210 element = EL_STEEL_CHAR_X;
5213 case 0x1663: // (green steel)
5214 element = EL_STEEL_CHAR_Y;
5217 case 0x1664: // (green steel)
5218 element = EL_STEEL_CHAR_Z;
5221 case 0x1665: // (green steel)
5222 element = EL_STEEL_CHAR_AUMLAUT;
5225 case 0x1666: // (green steel)
5226 element = EL_STEEL_CHAR_OUMLAUT;
5229 case 0x1667: // (green steel)
5230 element = EL_STEEL_CHAR_UUMLAUT;
5233 case 0x1668: // (green steel)
5234 element = EL_STEEL_CHAR_0;
5237 case 0x1669: // (green steel)
5238 element = EL_STEEL_CHAR_1;
5241 case 0x166a: // (green steel)
5242 element = EL_STEEL_CHAR_2;
5245 case 0x166b: // (green steel)
5246 element = EL_STEEL_CHAR_3;
5249 case 0x166c: // (green steel)
5250 element = EL_STEEL_CHAR_4;
5253 case 0x166d: // (green steel)
5254 element = EL_STEEL_CHAR_5;
5257 case 0x166e: // (green steel)
5258 element = EL_STEEL_CHAR_6;
5261 case 0x166f: // (green steel)
5262 element = EL_STEEL_CHAR_7;
5265 case 0x1670: // (green steel)
5266 element = EL_STEEL_CHAR_8;
5269 case 0x1671: // (green steel)
5270 element = EL_STEEL_CHAR_9;
5273 case 0x1672: // (green steel)
5274 element = EL_STEEL_CHAR_PERIOD;
5277 case 0x1673: // (green steel)
5278 element = EL_STEEL_CHAR_EXCLAM;
5281 case 0x1674: // (green steel)
5282 element = EL_STEEL_CHAR_COLON;
5285 case 0x1675: // (green steel)
5286 element = EL_STEEL_CHAR_LESS;
5289 case 0x1676: // (green steel)
5290 element = EL_STEEL_CHAR_GREATER;
5293 case 0x1677: // (green steel)
5294 element = EL_STEEL_CHAR_QUESTION;
5297 case 0x1678: // (green steel)
5298 element = EL_STEEL_CHAR_COPYRIGHT;
5301 case 0x1679: // (green steel)
5302 element = EL_STEEL_CHAR_UP;
5305 case 0x167a: // (green steel)
5306 element = EL_STEEL_CHAR_DOWN;
5309 case 0x167b: // (green steel)
5310 element = EL_STEEL_CHAR_BUTTON;
5313 case 0x167c: // (green steel)
5314 element = EL_STEEL_CHAR_PLUS;
5317 case 0x167d: // (green steel)
5318 element = EL_STEEL_CHAR_MINUS;
5321 case 0x167e: // (green steel)
5322 element = EL_STEEL_CHAR_APOSTROPHE;
5325 case 0x167f: // (green steel)
5326 element = EL_STEEL_CHAR_PARENLEFT;
5329 case 0x1680: // (green steel)
5330 element = EL_STEEL_CHAR_PARENRIGHT;
5333 case 0x1681: // gate (red)
5334 element = EL_EM_GATE_1;
5337 case 0x1682: // secret gate (red)
5338 element = EL_EM_GATE_1_GRAY;
5341 case 0x1683: // gate (yellow)
5342 element = EL_EM_GATE_2;
5345 case 0x1684: // secret gate (yellow)
5346 element = EL_EM_GATE_2_GRAY;
5349 case 0x1685: // gate (blue)
5350 element = EL_EM_GATE_4;
5353 case 0x1686: // secret gate (blue)
5354 element = EL_EM_GATE_4_GRAY;
5357 case 0x1687: // gate (green)
5358 element = EL_EM_GATE_3;
5361 case 0x1688: // secret gate (green)
5362 element = EL_EM_GATE_3_GRAY;
5365 case 0x1689: // gate (white)
5366 element = EL_DC_GATE_WHITE;
5369 case 0x168a: // secret gate (white)
5370 element = EL_DC_GATE_WHITE_GRAY;
5373 case 0x168b: // secret gate (no key)
5374 element = EL_DC_GATE_FAKE_GRAY;
5378 element = EL_ROBOT_WHEEL;
5382 element = EL_DC_TIMEGATE_SWITCH;
5386 element = EL_ACID_POOL_BOTTOM;
5390 element = EL_ACID_POOL_TOPLEFT;
5394 element = EL_ACID_POOL_TOPRIGHT;
5398 element = EL_ACID_POOL_BOTTOMLEFT;
5402 element = EL_ACID_POOL_BOTTOMRIGHT;
5406 element = EL_STEELWALL;
5410 element = EL_STEELWALL_SLIPPERY;
5413 case 0x1695: // steel wall (not round)
5414 element = EL_STEELWALL;
5417 case 0x1696: // steel wall (left)
5418 element = EL_DC_STEELWALL_1_LEFT;
5421 case 0x1697: // steel wall (bottom)
5422 element = EL_DC_STEELWALL_1_BOTTOM;
5425 case 0x1698: // steel wall (right)
5426 element = EL_DC_STEELWALL_1_RIGHT;
5429 case 0x1699: // steel wall (top)
5430 element = EL_DC_STEELWALL_1_TOP;
5433 case 0x169a: // steel wall (left/bottom)
5434 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5437 case 0x169b: // steel wall (right/bottom)
5438 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5441 case 0x169c: // steel wall (right/top)
5442 element = EL_DC_STEELWALL_1_TOPRIGHT;
5445 case 0x169d: // steel wall (left/top)
5446 element = EL_DC_STEELWALL_1_TOPLEFT;
5449 case 0x169e: // steel wall (right/bottom small)
5450 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5453 case 0x169f: // steel wall (left/bottom small)
5454 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5457 case 0x16a0: // steel wall (right/top small)
5458 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5461 case 0x16a1: // steel wall (left/top small)
5462 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5465 case 0x16a2: // steel wall (left/right)
5466 element = EL_DC_STEELWALL_1_VERTICAL;
5469 case 0x16a3: // steel wall (top/bottom)
5470 element = EL_DC_STEELWALL_1_HORIZONTAL;
5473 case 0x16a4: // steel wall 2 (left end)
5474 element = EL_DC_STEELWALL_2_LEFT;
5477 case 0x16a5: // steel wall 2 (right end)
5478 element = EL_DC_STEELWALL_2_RIGHT;
5481 case 0x16a6: // steel wall 2 (top end)
5482 element = EL_DC_STEELWALL_2_TOP;
5485 case 0x16a7: // steel wall 2 (bottom end)
5486 element = EL_DC_STEELWALL_2_BOTTOM;
5489 case 0x16a8: // steel wall 2 (left/right)
5490 element = EL_DC_STEELWALL_2_HORIZONTAL;
5493 case 0x16a9: // steel wall 2 (up/down)
5494 element = EL_DC_STEELWALL_2_VERTICAL;
5497 case 0x16aa: // steel wall 2 (mid)
5498 element = EL_DC_STEELWALL_2_MIDDLE;
5502 element = EL_SIGN_EXCLAMATION;
5506 element = EL_SIGN_RADIOACTIVITY;
5510 element = EL_SIGN_STOP;
5514 element = EL_SIGN_WHEELCHAIR;
5518 element = EL_SIGN_PARKING;
5522 element = EL_SIGN_NO_ENTRY;
5526 element = EL_SIGN_HEART;
5530 element = EL_SIGN_GIVE_WAY;
5534 element = EL_SIGN_ENTRY_FORBIDDEN;
5538 element = EL_SIGN_EMERGENCY_EXIT;
5542 element = EL_SIGN_YIN_YANG;
5546 element = EL_WALL_EMERALD;
5550 element = EL_WALL_DIAMOND;
5554 element = EL_WALL_PEARL;
5558 element = EL_WALL_CRYSTAL;
5562 element = EL_INVISIBLE_WALL;
5566 element = EL_INVISIBLE_STEELWALL;
5570 // EL_INVISIBLE_SAND
5573 element = EL_LIGHT_SWITCH;
5577 element = EL_ENVELOPE_1;
5581 if (element >= 0x0117 && element <= 0x036e) // (?)
5582 element = EL_DIAMOND;
5583 else if (element >= 0x042d && element <= 0x0684) // (?)
5584 element = EL_EMERALD;
5585 else if (element >= 0x157c && element <= 0x158b)
5587 else if (element >= 0x1590 && element <= 0x159f)
5588 element = EL_DC_LANDMINE;
5589 else if (element >= 0x16bc && element <= 0x16cb)
5590 element = EL_INVISIBLE_SAND;
5593 Warn("unknown Diamond Caves element 0x%04x", element);
5595 element = EL_UNKNOWN;
5600 return getMappedElement(element);
5603 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5606 byte header[DC_LEVEL_HEADER_SIZE];
5608 int envelope_header_pos = 62;
5609 int envelope_content_pos = 94;
5610 int level_name_pos = 251;
5611 int level_author_pos = 292;
5612 int envelope_header_len;
5613 int envelope_content_len;
5615 int level_author_len;
5617 int num_yamyam_contents;
5620 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5622 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5624 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5626 header[i * 2 + 0] = header_word >> 8;
5627 header[i * 2 + 1] = header_word & 0xff;
5630 // read some values from level header to check level decoding integrity
5631 fieldx = header[6] | (header[7] << 8);
5632 fieldy = header[8] | (header[9] << 8);
5633 num_yamyam_contents = header[60] | (header[61] << 8);
5635 // do some simple sanity checks to ensure that level was correctly decoded
5636 if (fieldx < 1 || fieldx > 256 ||
5637 fieldy < 1 || fieldy > 256 ||
5638 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5640 level->no_valid_file = TRUE;
5642 Warn("cannot decode level from stream -- using empty level");
5647 // maximum envelope header size is 31 bytes
5648 envelope_header_len = header[envelope_header_pos];
5649 // maximum envelope content size is 110 (156?) bytes
5650 envelope_content_len = header[envelope_content_pos];
5652 // maximum level title size is 40 bytes
5653 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5654 // maximum level author size is 30 (51?) bytes
5655 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5659 for (i = 0; i < envelope_header_len; i++)
5660 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5661 level->envelope[0].text[envelope_size++] =
5662 header[envelope_header_pos + 1 + i];
5664 if (envelope_header_len > 0 && envelope_content_len > 0)
5666 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5667 level->envelope[0].text[envelope_size++] = '\n';
5668 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5669 level->envelope[0].text[envelope_size++] = '\n';
5672 for (i = 0; i < envelope_content_len; i++)
5673 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5674 level->envelope[0].text[envelope_size++] =
5675 header[envelope_content_pos + 1 + i];
5677 level->envelope[0].text[envelope_size] = '\0';
5679 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5680 level->envelope[0].ysize = 10;
5681 level->envelope[0].autowrap = TRUE;
5682 level->envelope[0].centered = TRUE;
5684 for (i = 0; i < level_name_len; i++)
5685 level->name[i] = header[level_name_pos + 1 + i];
5686 level->name[level_name_len] = '\0';
5688 for (i = 0; i < level_author_len; i++)
5689 level->author[i] = header[level_author_pos + 1 + i];
5690 level->author[level_author_len] = '\0';
5692 num_yamyam_contents = header[60] | (header[61] << 8);
5693 level->num_yamyam_contents =
5694 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5696 for (i = 0; i < num_yamyam_contents; i++)
5698 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5700 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5701 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5703 if (i < MAX_ELEMENT_CONTENTS)
5704 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5708 fieldx = header[6] | (header[7] << 8);
5709 fieldy = header[8] | (header[9] << 8);
5710 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5711 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5713 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5715 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5716 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5718 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5719 level->field[x][y] = getMappedElement_DC(element_dc);
5722 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5723 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5724 level->field[x][y] = EL_PLAYER_1;
5726 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5727 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5728 level->field[x][y] = EL_PLAYER_2;
5730 level->gems_needed = header[18] | (header[19] << 8);
5732 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5733 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5734 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5735 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5736 level->score[SC_NUT] = header[28] | (header[29] << 8);
5737 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5738 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5739 level->score[SC_BUG] = header[34] | (header[35] << 8);
5740 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5741 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5742 level->score[SC_KEY] = header[40] | (header[41] << 8);
5743 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5745 level->time = header[44] | (header[45] << 8);
5747 level->amoeba_speed = header[46] | (header[47] << 8);
5748 level->time_light = header[48] | (header[49] << 8);
5749 level->time_timegate = header[50] | (header[51] << 8);
5750 level->time_wheel = header[52] | (header[53] << 8);
5751 level->time_magic_wall = header[54] | (header[55] << 8);
5752 level->extra_time = header[56] | (header[57] << 8);
5753 level->shield_normal_time = header[58] | (header[59] << 8);
5755 // shield and extra time elements do not have a score
5756 level->score[SC_SHIELD] = 0;
5757 level->extra_time_score = 0;
5759 // set time for normal and deadly shields to the same value
5760 level->shield_deadly_time = level->shield_normal_time;
5762 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5763 // can slip down from flat walls, like normal walls and steel walls
5764 level->em_slippery_gems = TRUE;
5766 // time score is counted for each 10 seconds left in Diamond Caves levels
5767 level->time_score_base = 10;
5770 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5771 struct LevelFileInfo *level_file_info,
5772 boolean level_info_only)
5774 char *filename = level_file_info->filename;
5776 int num_magic_bytes = 8;
5777 char magic_bytes[num_magic_bytes + 1];
5778 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5780 if (!(file = openFile(filename, MODE_READ)))
5782 level->no_valid_file = TRUE;
5784 if (!level_info_only)
5785 Warn("cannot read level '%s' -- using empty level", filename);
5790 // fseek(file, 0x0000, SEEK_SET);
5792 if (level_file_info->packed)
5794 // read "magic bytes" from start of file
5795 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5796 magic_bytes[0] = '\0';
5798 // check "magic bytes" for correct file format
5799 if (!strPrefix(magic_bytes, "DC2"))
5801 level->no_valid_file = TRUE;
5803 Warn("unknown DC level file '%s' -- using empty level", filename);
5808 if (strPrefix(magic_bytes, "DC2Win95") ||
5809 strPrefix(magic_bytes, "DC2Win98"))
5811 int position_first_level = 0x00fa;
5812 int extra_bytes = 4;
5815 // advance file stream to first level inside the level package
5816 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5818 // each block of level data is followed by block of non-level data
5819 num_levels_to_skip *= 2;
5821 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5822 while (num_levels_to_skip >= 0)
5824 // advance file stream to next level inside the level package
5825 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5827 level->no_valid_file = TRUE;
5829 Warn("cannot fseek in file '%s' -- using empty level", filename);
5834 // skip apparently unused extra bytes following each level
5835 ReadUnusedBytesFromFile(file, extra_bytes);
5837 // read size of next level in level package
5838 skip_bytes = getFile32BitLE(file);
5840 num_levels_to_skip--;
5845 level->no_valid_file = TRUE;
5847 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5853 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5859 // ----------------------------------------------------------------------------
5860 // functions for loading SB level
5861 // ----------------------------------------------------------------------------
5863 int getMappedElement_SB(int element_ascii, boolean use_ces)
5871 sb_element_mapping[] =
5873 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5874 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5875 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5876 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5877 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5878 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5879 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5880 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5887 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5888 if (element_ascii == sb_element_mapping[i].ascii)
5889 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5891 return EL_UNDEFINED;
5894 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5895 struct LevelFileInfo *level_file_info,
5896 boolean level_info_only)
5898 char *filename = level_file_info->filename;
5899 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5900 char last_comment[MAX_LINE_LEN];
5901 char level_name[MAX_LINE_LEN];
5904 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5905 boolean read_continued_line = FALSE;
5906 boolean reading_playfield = FALSE;
5907 boolean got_valid_playfield_line = FALSE;
5908 boolean invalid_playfield_char = FALSE;
5909 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5910 int file_level_nr = 0;
5912 int x = 0, y = 0; // initialized to make compilers happy
5914 last_comment[0] = '\0';
5915 level_name[0] = '\0';
5917 if (!(file = openFile(filename, MODE_READ)))
5919 level->no_valid_file = TRUE;
5921 if (!level_info_only)
5922 Warn("cannot read level '%s' -- using empty level", filename);
5927 while (!checkEndOfFile(file))
5929 // level successfully read, but next level may follow here
5930 if (!got_valid_playfield_line && reading_playfield)
5932 // read playfield from single level file -- skip remaining file
5933 if (!level_file_info->packed)
5936 if (file_level_nr >= num_levels_to_skip)
5941 last_comment[0] = '\0';
5942 level_name[0] = '\0';
5944 reading_playfield = FALSE;
5947 got_valid_playfield_line = FALSE;
5949 // read next line of input file
5950 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5953 // check if line was completely read and is terminated by line break
5954 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5957 // cut trailing line break (this can be newline and/or carriage return)
5958 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5959 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5962 // copy raw input line for later use (mainly debugging output)
5963 strcpy(line_raw, line);
5965 if (read_continued_line)
5967 // append new line to existing line, if there is enough space
5968 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5969 strcat(previous_line, line_ptr);
5971 strcpy(line, previous_line); // copy storage buffer to line
5973 read_continued_line = FALSE;
5976 // if the last character is '\', continue at next line
5977 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5979 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5980 strcpy(previous_line, line); // copy line to storage buffer
5982 read_continued_line = TRUE;
5988 if (line[0] == '\0')
5991 // extract comment text from comment line
5994 for (line_ptr = line; *line_ptr; line_ptr++)
5995 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5998 strcpy(last_comment, line_ptr);
6003 // extract level title text from line containing level title
6004 if (line[0] == '\'')
6006 strcpy(level_name, &line[1]);
6008 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6009 level_name[strlen(level_name) - 1] = '\0';
6014 // skip lines containing only spaces (or empty lines)
6015 for (line_ptr = line; *line_ptr; line_ptr++)
6016 if (*line_ptr != ' ')
6018 if (*line_ptr == '\0')
6021 // at this point, we have found a line containing part of a playfield
6023 got_valid_playfield_line = TRUE;
6025 if (!reading_playfield)
6027 reading_playfield = TRUE;
6028 invalid_playfield_char = FALSE;
6030 for (x = 0; x < MAX_LEV_FIELDX; x++)
6031 for (y = 0; y < MAX_LEV_FIELDY; y++)
6032 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6037 // start with topmost tile row
6041 // skip playfield line if larger row than allowed
6042 if (y >= MAX_LEV_FIELDY)
6045 // start with leftmost tile column
6048 // read playfield elements from line
6049 for (line_ptr = line; *line_ptr; line_ptr++)
6051 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6053 // stop parsing playfield line if larger column than allowed
6054 if (x >= MAX_LEV_FIELDX)
6057 if (mapped_sb_element == EL_UNDEFINED)
6059 invalid_playfield_char = TRUE;
6064 level->field[x][y] = mapped_sb_element;
6066 // continue with next tile column
6069 level->fieldx = MAX(x, level->fieldx);
6072 if (invalid_playfield_char)
6074 // if first playfield line, treat invalid lines as comment lines
6076 reading_playfield = FALSE;
6081 // continue with next tile row
6089 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6090 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6092 if (!reading_playfield)
6094 level->no_valid_file = TRUE;
6096 Warn("cannot read level '%s' -- using empty level", filename);
6101 if (*level_name != '\0')
6103 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6104 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6106 else if (*last_comment != '\0')
6108 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6109 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6113 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6116 // set all empty fields beyond the border walls to invisible steel wall
6117 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6119 if ((x == 0 || x == level->fieldx - 1 ||
6120 y == 0 || y == level->fieldy - 1) &&
6121 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6122 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6123 level->field, level->fieldx, level->fieldy);
6126 // set special level settings for Sokoban levels
6129 level->use_step_counter = TRUE;
6131 if (load_xsb_to_ces)
6133 // special global settings can now be set in level template
6135 level->use_custom_template = TRUE;
6140 // -------------------------------------------------------------------------
6141 // functions for handling native levels
6142 // -------------------------------------------------------------------------
6144 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6145 struct LevelFileInfo *level_file_info,
6146 boolean level_info_only)
6148 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6149 level->no_valid_file = TRUE;
6152 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6153 struct LevelFileInfo *level_file_info,
6154 boolean level_info_only)
6158 // determine position of requested level inside level package
6159 if (level_file_info->packed)
6160 pos = level_file_info->nr - leveldir_current->first_level;
6162 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6163 level->no_valid_file = TRUE;
6166 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6167 struct LevelFileInfo *level_file_info,
6168 boolean level_info_only)
6170 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6171 level->no_valid_file = TRUE;
6174 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6176 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6177 CopyNativeLevel_RND_to_EM(level);
6178 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6179 CopyNativeLevel_RND_to_SP(level);
6180 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6181 CopyNativeLevel_RND_to_MM(level);
6184 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6186 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6187 CopyNativeLevel_EM_to_RND(level);
6188 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6189 CopyNativeLevel_SP_to_RND(level);
6190 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6191 CopyNativeLevel_MM_to_RND(level);
6194 void SaveNativeLevel(struct LevelInfo *level)
6196 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6198 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6199 char *filename = getLevelFilenameFromBasename(basename);
6201 CopyNativeLevel_RND_to_SP(level);
6202 CopyNativeTape_RND_to_SP(level);
6204 SaveNativeLevel_SP(filename);
6209 // ----------------------------------------------------------------------------
6210 // functions for loading generic level
6211 // ----------------------------------------------------------------------------
6213 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6214 struct LevelFileInfo *level_file_info,
6215 boolean level_info_only)
6217 // always start with reliable default values
6218 setLevelInfoToDefaults(level, level_info_only, TRUE);
6220 switch (level_file_info->type)
6222 case LEVEL_FILE_TYPE_RND:
6223 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6226 case LEVEL_FILE_TYPE_EM:
6227 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6228 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6231 case LEVEL_FILE_TYPE_SP:
6232 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6233 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6236 case LEVEL_FILE_TYPE_MM:
6237 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6238 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6241 case LEVEL_FILE_TYPE_DC:
6242 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6245 case LEVEL_FILE_TYPE_SB:
6246 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6250 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6254 // if level file is invalid, restore level structure to default values
6255 if (level->no_valid_file)
6256 setLevelInfoToDefaults(level, level_info_only, FALSE);
6258 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6259 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6261 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6262 CopyNativeLevel_Native_to_RND(level);
6265 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6267 static struct LevelFileInfo level_file_info;
6269 // always start with reliable default values
6270 setFileInfoToDefaults(&level_file_info);
6272 level_file_info.nr = 0; // unknown level number
6273 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6275 setString(&level_file_info.filename, filename);
6277 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6280 static void LoadLevel_InitVersion(struct LevelInfo *level)
6284 if (leveldir_current == NULL) // only when dumping level
6287 // all engine modifications also valid for levels which use latest engine
6288 if (level->game_version < VERSION_IDENT(3,2,0,5))
6290 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6291 level->time_score_base = 10;
6294 if (leveldir_current->latest_engine)
6296 // ---------- use latest game engine --------------------------------------
6298 /* For all levels which are forced to use the latest game engine version
6299 (normally all but user contributed, private and undefined levels), set
6300 the game engine version to the actual version; this allows for actual
6301 corrections in the game engine to take effect for existing, converted
6302 levels (from "classic" or other existing games) to make the emulation
6303 of the corresponding game more accurate, while (hopefully) not breaking
6304 existing levels created from other players. */
6306 level->game_version = GAME_VERSION_ACTUAL;
6308 /* Set special EM style gems behaviour: EM style gems slip down from
6309 normal, steel and growing wall. As this is a more fundamental change,
6310 it seems better to set the default behaviour to "off" (as it is more
6311 natural) and make it configurable in the level editor (as a property
6312 of gem style elements). Already existing converted levels (neither
6313 private nor contributed levels) are changed to the new behaviour. */
6315 if (level->file_version < FILE_VERSION_2_0)
6316 level->em_slippery_gems = TRUE;
6321 // ---------- use game engine the level was created with --------------------
6323 /* For all levels which are not forced to use the latest game engine
6324 version (normally user contributed, private and undefined levels),
6325 use the version of the game engine the levels were created for.
6327 Since 2.0.1, the game engine version is now directly stored
6328 in the level file (chunk "VERS"), so there is no need anymore
6329 to set the game version from the file version (except for old,
6330 pre-2.0 levels, where the game version is still taken from the
6331 file format version used to store the level -- see above). */
6333 // player was faster than enemies in 1.0.0 and before
6334 if (level->file_version == FILE_VERSION_1_0)
6335 for (i = 0; i < MAX_PLAYERS; i++)
6336 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6338 // default behaviour for EM style gems was "slippery" only in 2.0.1
6339 if (level->game_version == VERSION_IDENT(2,0,1,0))
6340 level->em_slippery_gems = TRUE;
6342 // springs could be pushed over pits before (pre-release version) 2.2.0
6343 if (level->game_version < VERSION_IDENT(2,2,0,0))
6344 level->use_spring_bug = TRUE;
6346 if (level->game_version < VERSION_IDENT(3,2,0,5))
6348 // time orb caused limited time in endless time levels before 3.2.0-5
6349 level->use_time_orb_bug = TRUE;
6351 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6352 level->block_snap_field = FALSE;
6354 // extra time score was same value as time left score before 3.2.0-5
6355 level->extra_time_score = level->score[SC_TIME_BONUS];
6358 if (level->game_version < VERSION_IDENT(3,2,0,7))
6360 // default behaviour for snapping was "not continuous" before 3.2.0-7
6361 level->continuous_snapping = FALSE;
6364 // only few elements were able to actively move into acid before 3.1.0
6365 // trigger settings did not exist before 3.1.0; set to default "any"
6366 if (level->game_version < VERSION_IDENT(3,1,0,0))
6368 // correct "can move into acid" settings (all zero in old levels)
6370 level->can_move_into_acid_bits = 0; // nothing can move into acid
6371 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6373 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6374 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6375 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6376 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6378 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6379 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6381 // correct trigger settings (stored as zero == "none" in old levels)
6383 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6385 int element = EL_CUSTOM_START + i;
6386 struct ElementInfo *ei = &element_info[element];
6388 for (j = 0; j < ei->num_change_pages; j++)
6390 struct ElementChangeInfo *change = &ei->change_page[j];
6392 change->trigger_player = CH_PLAYER_ANY;
6393 change->trigger_page = CH_PAGE_ANY;
6398 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6400 int element = EL_CUSTOM_256;
6401 struct ElementInfo *ei = &element_info[element];
6402 struct ElementChangeInfo *change = &ei->change_page[0];
6404 /* This is needed to fix a problem that was caused by a bugfix in function
6405 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6406 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6407 not replace walkable elements, but instead just placed the player on it,
6408 without placing the Sokoban field under the player). Unfortunately, this
6409 breaks "Snake Bite" style levels when the snake is halfway through a door
6410 that just closes (the snake head is still alive and can be moved in this
6411 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6412 player (without Sokoban element) which then gets killed as designed). */
6414 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6415 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6416 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6417 change->target_element = EL_PLAYER_1;
6420 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6421 if (level->game_version < VERSION_IDENT(3,2,5,0))
6423 /* This is needed to fix a problem that was caused by a bugfix in function
6424 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6425 corrects the behaviour when a custom element changes to another custom
6426 element with a higher element number that has change actions defined.
6427 Normally, only one change per frame is allowed for custom elements.
6428 Therefore, it is checked if a custom element already changed in the
6429 current frame; if it did, subsequent changes are suppressed.
6430 Unfortunately, this is only checked for element changes, but not for
6431 change actions, which are still executed. As the function above loops
6432 through all custom elements from lower to higher, an element change
6433 resulting in a lower CE number won't be checked again, while a target
6434 element with a higher number will also be checked, and potential change
6435 actions will get executed for this CE, too (which is wrong), while
6436 further changes are ignored (which is correct). As this bugfix breaks
6437 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6438 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6439 behaviour for existing levels and tapes that make use of this bug */
6441 level->use_action_after_change_bug = TRUE;
6444 // not centering level after relocating player was default only in 3.2.3
6445 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6446 level->shifted_relocation = TRUE;
6448 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6449 if (level->game_version < VERSION_IDENT(3,2,6,0))
6450 level->em_explodes_by_fire = TRUE;
6452 // levels were solved by the first player entering an exit up to 4.1.0.0
6453 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6454 level->solved_by_one_player = TRUE;
6456 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6457 if (level->game_version < VERSION_IDENT(4,1,1,1))
6458 level->use_life_bugs = TRUE;
6460 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6461 if (level->game_version < VERSION_IDENT(4,1,1,1))
6462 level->sb_objects_needed = FALSE;
6464 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6465 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6466 level->finish_dig_collect = FALSE;
6469 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6473 // map elements that have changed in newer versions
6474 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6475 level->game_version);
6476 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6477 for (x = 0; x < 3; x++)
6478 for (y = 0; y < 3; y++)
6479 level->yamyam_content[i].e[x][y] =
6480 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6481 level->game_version);
6485 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6489 // map custom element change events that have changed in newer versions
6490 // (these following values were accidentally changed in version 3.0.1)
6491 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6492 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6494 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6496 int element = EL_CUSTOM_START + i;
6498 // order of checking and copying events to be mapped is important
6499 // (do not change the start and end value -- they are constant)
6500 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6502 if (HAS_CHANGE_EVENT(element, j - 2))
6504 SET_CHANGE_EVENT(element, j - 2, FALSE);
6505 SET_CHANGE_EVENT(element, j, TRUE);
6509 // order of checking and copying events to be mapped is important
6510 // (do not change the start and end value -- they are constant)
6511 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6513 if (HAS_CHANGE_EVENT(element, j - 1))
6515 SET_CHANGE_EVENT(element, j - 1, FALSE);
6516 SET_CHANGE_EVENT(element, j, TRUE);
6522 // initialize "can_change" field for old levels with only one change page
6523 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6525 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6527 int element = EL_CUSTOM_START + i;
6529 if (CAN_CHANGE(element))
6530 element_info[element].change->can_change = TRUE;
6534 // correct custom element values (for old levels without these options)
6535 if (level->game_version < VERSION_IDENT(3,1,1,0))
6537 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6539 int element = EL_CUSTOM_START + i;
6540 struct ElementInfo *ei = &element_info[element];
6542 if (ei->access_direction == MV_NO_DIRECTION)
6543 ei->access_direction = MV_ALL_DIRECTIONS;
6547 // correct custom element values (fix invalid values for all versions)
6550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6552 int element = EL_CUSTOM_START + i;
6553 struct ElementInfo *ei = &element_info[element];
6555 for (j = 0; j < ei->num_change_pages; j++)
6557 struct ElementChangeInfo *change = &ei->change_page[j];
6559 if (change->trigger_player == CH_PLAYER_NONE)
6560 change->trigger_player = CH_PLAYER_ANY;
6562 if (change->trigger_side == CH_SIDE_NONE)
6563 change->trigger_side = CH_SIDE_ANY;
6568 // initialize "can_explode" field for old levels which did not store this
6569 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6570 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6572 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6574 int element = EL_CUSTOM_START + i;
6576 if (EXPLODES_1X1_OLD(element))
6577 element_info[element].explosion_type = EXPLODES_1X1;
6579 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6580 EXPLODES_SMASHED(element) ||
6581 EXPLODES_IMPACT(element)));
6585 // correct previously hard-coded move delay values for maze runner style
6586 if (level->game_version < VERSION_IDENT(3,1,1,0))
6588 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6590 int element = EL_CUSTOM_START + i;
6592 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6594 // previously hard-coded and therefore ignored
6595 element_info[element].move_delay_fixed = 9;
6596 element_info[element].move_delay_random = 0;
6601 // set some other uninitialized values of custom elements in older levels
6602 if (level->game_version < VERSION_IDENT(3,1,0,0))
6604 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6606 int element = EL_CUSTOM_START + i;
6608 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6610 element_info[element].explosion_delay = 17;
6611 element_info[element].ignition_delay = 8;
6616 static void LoadLevel_InitElements(struct LevelInfo *level)
6618 LoadLevel_InitStandardElements(level);
6620 if (level->file_has_custom_elements)
6621 LoadLevel_InitCustomElements(level);
6623 // initialize element properties for level editor etc.
6624 InitElementPropertiesEngine(level->game_version);
6625 InitElementPropertiesGfxElement();
6628 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6632 // map elements that have changed in newer versions
6633 for (y = 0; y < level->fieldy; y++)
6634 for (x = 0; x < level->fieldx; x++)
6635 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6636 level->game_version);
6638 // clear unused playfield data (nicer if level gets resized in editor)
6639 for (x = 0; x < MAX_LEV_FIELDX; x++)
6640 for (y = 0; y < MAX_LEV_FIELDY; y++)
6641 if (x >= level->fieldx || y >= level->fieldy)
6642 level->field[x][y] = EL_EMPTY;
6644 // copy elements to runtime playfield array
6645 for (x = 0; x < MAX_LEV_FIELDX; x++)
6646 for (y = 0; y < MAX_LEV_FIELDY; y++)
6647 Tile[x][y] = level->field[x][y];
6649 // initialize level size variables for faster access
6650 lev_fieldx = level->fieldx;
6651 lev_fieldy = level->fieldy;
6653 // determine border element for this level
6654 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6655 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6660 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6662 struct LevelFileInfo *level_file_info = &level->file_info;
6664 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6665 CopyNativeLevel_RND_to_Native(level);
6668 static void LoadLevelTemplate_LoadAndInit(void)
6670 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6672 LoadLevel_InitVersion(&level_template);
6673 LoadLevel_InitElements(&level_template);
6675 ActivateLevelTemplate();
6678 void LoadLevelTemplate(int nr)
6680 if (!fileExists(getGlobalLevelTemplateFilename()))
6682 Warn("no level template found for this level");
6687 setLevelFileInfo(&level_template.file_info, nr);
6689 LoadLevelTemplate_LoadAndInit();
6692 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6694 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6696 LoadLevelTemplate_LoadAndInit();
6699 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6701 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6703 if (level.use_custom_template)
6705 if (network_level != NULL)
6706 LoadNetworkLevelTemplate(network_level);
6708 LoadLevelTemplate(-1);
6711 LoadLevel_InitVersion(&level);
6712 LoadLevel_InitElements(&level);
6713 LoadLevel_InitPlayfield(&level);
6715 LoadLevel_InitNativeEngines(&level);
6718 void LoadLevel(int nr)
6720 SetLevelSetInfo(leveldir_current->identifier, nr);
6722 setLevelFileInfo(&level.file_info, nr);
6724 LoadLevel_LoadAndInit(NULL);
6727 void LoadLevelInfoOnly(int nr)
6729 setLevelFileInfo(&level.file_info, nr);
6731 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6734 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6736 SetLevelSetInfo(network_level->leveldir_identifier,
6737 network_level->file_info.nr);
6739 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6741 LoadLevel_LoadAndInit(network_level);
6744 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6748 chunk_size += putFileVersion(file, level->file_version);
6749 chunk_size += putFileVersion(file, level->game_version);
6754 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6758 chunk_size += putFile16BitBE(file, level->creation_date.year);
6759 chunk_size += putFile8Bit(file, level->creation_date.month);
6760 chunk_size += putFile8Bit(file, level->creation_date.day);
6765 #if ENABLE_HISTORIC_CHUNKS
6766 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6770 putFile8Bit(file, level->fieldx);
6771 putFile8Bit(file, level->fieldy);
6773 putFile16BitBE(file, level->time);
6774 putFile16BitBE(file, level->gems_needed);
6776 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6777 putFile8Bit(file, level->name[i]);
6779 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6780 putFile8Bit(file, level->score[i]);
6782 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6783 for (y = 0; y < 3; y++)
6784 for (x = 0; x < 3; x++)
6785 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6786 level->yamyam_content[i].e[x][y]));
6787 putFile8Bit(file, level->amoeba_speed);
6788 putFile8Bit(file, level->time_magic_wall);
6789 putFile8Bit(file, level->time_wheel);
6790 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6791 level->amoeba_content));
6792 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6793 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6794 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6795 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6797 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6799 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6800 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6801 putFile32BitBE(file, level->can_move_into_acid_bits);
6802 putFile8Bit(file, level->dont_collide_with_bits);
6804 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6805 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6807 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6808 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6809 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6811 putFile8Bit(file, level->game_engine_type);
6813 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6817 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6822 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6823 chunk_size += putFile8Bit(file, level->name[i]);
6828 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6833 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6834 chunk_size += putFile8Bit(file, level->author[i]);
6839 #if ENABLE_HISTORIC_CHUNKS
6840 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6845 for (y = 0; y < level->fieldy; y++)
6846 for (x = 0; x < level->fieldx; x++)
6847 if (level->encoding_16bit_field)
6848 chunk_size += putFile16BitBE(file, level->field[x][y]);
6850 chunk_size += putFile8Bit(file, level->field[x][y]);
6856 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6861 for (y = 0; y < level->fieldy; y++)
6862 for (x = 0; x < level->fieldx; x++)
6863 chunk_size += putFile16BitBE(file, level->field[x][y]);
6868 #if ENABLE_HISTORIC_CHUNKS
6869 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6873 putFile8Bit(file, EL_YAMYAM);
6874 putFile8Bit(file, level->num_yamyam_contents);
6875 putFile8Bit(file, 0);
6876 putFile8Bit(file, 0);
6878 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6879 for (y = 0; y < 3; y++)
6880 for (x = 0; x < 3; x++)
6881 if (level->encoding_16bit_field)
6882 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6884 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6888 #if ENABLE_HISTORIC_CHUNKS
6889 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6892 int num_contents, content_xsize, content_ysize;
6893 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6895 if (element == EL_YAMYAM)
6897 num_contents = level->num_yamyam_contents;
6901 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6902 for (y = 0; y < 3; y++)
6903 for (x = 0; x < 3; x++)
6904 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6906 else if (element == EL_BD_AMOEBA)
6912 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6913 for (y = 0; y < 3; y++)
6914 for (x = 0; x < 3; x++)
6915 content_array[i][x][y] = EL_EMPTY;
6916 content_array[0][0][0] = level->amoeba_content;
6920 // chunk header already written -- write empty chunk data
6921 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6923 Warn("cannot save content for element '%d'", element);
6928 putFile16BitBE(file, element);
6929 putFile8Bit(file, num_contents);
6930 putFile8Bit(file, content_xsize);
6931 putFile8Bit(file, content_ysize);
6933 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6935 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6936 for (y = 0; y < 3; y++)
6937 for (x = 0; x < 3; x++)
6938 putFile16BitBE(file, content_array[i][x][y]);
6942 #if ENABLE_HISTORIC_CHUNKS
6943 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6945 int envelope_nr = element - EL_ENVELOPE_1;
6946 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6950 chunk_size += putFile16BitBE(file, element);
6951 chunk_size += putFile16BitBE(file, envelope_len);
6952 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6953 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6955 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6956 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6958 for (i = 0; i < envelope_len; i++)
6959 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6965 #if ENABLE_HISTORIC_CHUNKS
6966 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6967 int num_changed_custom_elements)
6971 putFile16BitBE(file, num_changed_custom_elements);
6973 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6975 int element = EL_CUSTOM_START + i;
6977 struct ElementInfo *ei = &element_info[element];
6979 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6981 if (check < num_changed_custom_elements)
6983 putFile16BitBE(file, element);
6984 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6991 if (check != num_changed_custom_elements) // should not happen
6992 Warn("inconsistent number of custom element properties");
6996 #if ENABLE_HISTORIC_CHUNKS
6997 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6998 int num_changed_custom_elements)
7002 putFile16BitBE(file, num_changed_custom_elements);
7004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7006 int element = EL_CUSTOM_START + i;
7008 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7010 if (check < num_changed_custom_elements)
7012 putFile16BitBE(file, element);
7013 putFile16BitBE(file, element_info[element].change->target_element);
7020 if (check != num_changed_custom_elements) // should not happen
7021 Warn("inconsistent number of custom target elements");
7025 #if ENABLE_HISTORIC_CHUNKS
7026 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7027 int num_changed_custom_elements)
7029 int i, j, x, y, check = 0;
7031 putFile16BitBE(file, num_changed_custom_elements);
7033 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7035 int element = EL_CUSTOM_START + i;
7036 struct ElementInfo *ei = &element_info[element];
7038 if (ei->modified_settings)
7040 if (check < num_changed_custom_elements)
7042 putFile16BitBE(file, element);
7044 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7045 putFile8Bit(file, ei->description[j]);
7047 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7049 // some free bytes for future properties and padding
7050 WriteUnusedBytesToFile(file, 7);
7052 putFile8Bit(file, ei->use_gfx_element);
7053 putFile16BitBE(file, ei->gfx_element_initial);
7055 putFile8Bit(file, ei->collect_score_initial);
7056 putFile8Bit(file, ei->collect_count_initial);
7058 putFile16BitBE(file, ei->push_delay_fixed);
7059 putFile16BitBE(file, ei->push_delay_random);
7060 putFile16BitBE(file, ei->move_delay_fixed);
7061 putFile16BitBE(file, ei->move_delay_random);
7063 putFile16BitBE(file, ei->move_pattern);
7064 putFile8Bit(file, ei->move_direction_initial);
7065 putFile8Bit(file, ei->move_stepsize);
7067 for (y = 0; y < 3; y++)
7068 for (x = 0; x < 3; x++)
7069 putFile16BitBE(file, ei->content.e[x][y]);
7071 putFile32BitBE(file, ei->change->events);
7073 putFile16BitBE(file, ei->change->target_element);
7075 putFile16BitBE(file, ei->change->delay_fixed);
7076 putFile16BitBE(file, ei->change->delay_random);
7077 putFile16BitBE(file, ei->change->delay_frames);
7079 putFile16BitBE(file, ei->change->initial_trigger_element);
7081 putFile8Bit(file, ei->change->explode);
7082 putFile8Bit(file, ei->change->use_target_content);
7083 putFile8Bit(file, ei->change->only_if_complete);
7084 putFile8Bit(file, ei->change->use_random_replace);
7086 putFile8Bit(file, ei->change->random_percentage);
7087 putFile8Bit(file, ei->change->replace_when);
7089 for (y = 0; y < 3; y++)
7090 for (x = 0; x < 3; x++)
7091 putFile16BitBE(file, ei->change->content.e[x][y]);
7093 putFile8Bit(file, ei->slippery_type);
7095 // some free bytes for future properties and padding
7096 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7103 if (check != num_changed_custom_elements) // should not happen
7104 Warn("inconsistent number of custom element properties");
7108 #if ENABLE_HISTORIC_CHUNKS
7109 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7111 struct ElementInfo *ei = &element_info[element];
7114 // ---------- custom element base property values (96 bytes) ----------------
7116 putFile16BitBE(file, element);
7118 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7119 putFile8Bit(file, ei->description[i]);
7121 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7123 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7125 putFile8Bit(file, ei->num_change_pages);
7127 putFile16BitBE(file, ei->ce_value_fixed_initial);
7128 putFile16BitBE(file, ei->ce_value_random_initial);
7129 putFile8Bit(file, ei->use_last_ce_value);
7131 putFile8Bit(file, ei->use_gfx_element);
7132 putFile16BitBE(file, ei->gfx_element_initial);
7134 putFile8Bit(file, ei->collect_score_initial);
7135 putFile8Bit(file, ei->collect_count_initial);
7137 putFile8Bit(file, ei->drop_delay_fixed);
7138 putFile8Bit(file, ei->push_delay_fixed);
7139 putFile8Bit(file, ei->drop_delay_random);
7140 putFile8Bit(file, ei->push_delay_random);
7141 putFile16BitBE(file, ei->move_delay_fixed);
7142 putFile16BitBE(file, ei->move_delay_random);
7144 // bits 0 - 15 of "move_pattern" ...
7145 putFile16BitBE(file, ei->move_pattern & 0xffff);
7146 putFile8Bit(file, ei->move_direction_initial);
7147 putFile8Bit(file, ei->move_stepsize);
7149 putFile8Bit(file, ei->slippery_type);
7151 for (y = 0; y < 3; y++)
7152 for (x = 0; x < 3; x++)
7153 putFile16BitBE(file, ei->content.e[x][y]);
7155 putFile16BitBE(file, ei->move_enter_element);
7156 putFile16BitBE(file, ei->move_leave_element);
7157 putFile8Bit(file, ei->move_leave_type);
7159 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7160 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7162 putFile8Bit(file, ei->access_direction);
7164 putFile8Bit(file, ei->explosion_delay);
7165 putFile8Bit(file, ei->ignition_delay);
7166 putFile8Bit(file, ei->explosion_type);
7168 // some free bytes for future custom property values and padding
7169 WriteUnusedBytesToFile(file, 1);
7171 // ---------- change page property values (48 bytes) ------------------------
7173 for (i = 0; i < ei->num_change_pages; i++)
7175 struct ElementChangeInfo *change = &ei->change_page[i];
7176 unsigned int event_bits;
7178 // bits 0 - 31 of "has_event[]" ...
7180 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7181 if (change->has_event[j])
7182 event_bits |= (1 << j);
7183 putFile32BitBE(file, event_bits);
7185 putFile16BitBE(file, change->target_element);
7187 putFile16BitBE(file, change->delay_fixed);
7188 putFile16BitBE(file, change->delay_random);
7189 putFile16BitBE(file, change->delay_frames);
7191 putFile16BitBE(file, change->initial_trigger_element);
7193 putFile8Bit(file, change->explode);
7194 putFile8Bit(file, change->use_target_content);
7195 putFile8Bit(file, change->only_if_complete);
7196 putFile8Bit(file, change->use_random_replace);
7198 putFile8Bit(file, change->random_percentage);
7199 putFile8Bit(file, change->replace_when);
7201 for (y = 0; y < 3; y++)
7202 for (x = 0; x < 3; x++)
7203 putFile16BitBE(file, change->target_content.e[x][y]);
7205 putFile8Bit(file, change->can_change);
7207 putFile8Bit(file, change->trigger_side);
7209 putFile8Bit(file, change->trigger_player);
7210 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7211 log_2(change->trigger_page)));
7213 putFile8Bit(file, change->has_action);
7214 putFile8Bit(file, change->action_type);
7215 putFile8Bit(file, change->action_mode);
7216 putFile16BitBE(file, change->action_arg);
7218 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7220 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7221 if (change->has_event[j])
7222 event_bits |= (1 << (j - 32));
7223 putFile8Bit(file, event_bits);
7228 #if ENABLE_HISTORIC_CHUNKS
7229 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7231 struct ElementInfo *ei = &element_info[element];
7232 struct ElementGroupInfo *group = ei->group;
7235 putFile16BitBE(file, element);
7237 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7238 putFile8Bit(file, ei->description[i]);
7240 putFile8Bit(file, group->num_elements);
7242 putFile8Bit(file, ei->use_gfx_element);
7243 putFile16BitBE(file, ei->gfx_element_initial);
7245 putFile8Bit(file, group->choice_mode);
7247 // some free bytes for future values and padding
7248 WriteUnusedBytesToFile(file, 3);
7250 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7251 putFile16BitBE(file, group->element[i]);
7255 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7256 boolean write_element)
7258 int save_type = entry->save_type;
7259 int data_type = entry->data_type;
7260 int conf_type = entry->conf_type;
7261 int byte_mask = conf_type & CONF_MASK_BYTES;
7262 int element = entry->element;
7263 int default_value = entry->default_value;
7265 boolean modified = FALSE;
7267 if (byte_mask != CONF_MASK_MULTI_BYTES)
7269 void *value_ptr = entry->value;
7270 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7273 // check if any settings have been modified before saving them
7274 if (value != default_value)
7277 // do not save if explicitly told or if unmodified default settings
7278 if ((save_type == SAVE_CONF_NEVER) ||
7279 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7283 num_bytes += putFile16BitBE(file, element);
7285 num_bytes += putFile8Bit(file, conf_type);
7286 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7287 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7288 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7291 else if (data_type == TYPE_STRING)
7293 char *default_string = entry->default_string;
7294 char *string = (char *)(entry->value);
7295 int string_length = strlen(string);
7298 // check if any settings have been modified before saving them
7299 if (!strEqual(string, default_string))
7302 // do not save if explicitly told or if unmodified default settings
7303 if ((save_type == SAVE_CONF_NEVER) ||
7304 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7308 num_bytes += putFile16BitBE(file, element);
7310 num_bytes += putFile8Bit(file, conf_type);
7311 num_bytes += putFile16BitBE(file, string_length);
7313 for (i = 0; i < string_length; i++)
7314 num_bytes += putFile8Bit(file, string[i]);
7316 else if (data_type == TYPE_ELEMENT_LIST)
7318 int *element_array = (int *)(entry->value);
7319 int num_elements = *(int *)(entry->num_entities);
7322 // check if any settings have been modified before saving them
7323 for (i = 0; i < num_elements; i++)
7324 if (element_array[i] != default_value)
7327 // do not save if explicitly told or if unmodified default settings
7328 if ((save_type == SAVE_CONF_NEVER) ||
7329 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7333 num_bytes += putFile16BitBE(file, element);
7335 num_bytes += putFile8Bit(file, conf_type);
7336 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7338 for (i = 0; i < num_elements; i++)
7339 num_bytes += putFile16BitBE(file, element_array[i]);
7341 else if (data_type == TYPE_CONTENT_LIST)
7343 struct Content *content = (struct Content *)(entry->value);
7344 int num_contents = *(int *)(entry->num_entities);
7347 // check if any settings have been modified before saving them
7348 for (i = 0; i < num_contents; i++)
7349 for (y = 0; y < 3; y++)
7350 for (x = 0; x < 3; x++)
7351 if (content[i].e[x][y] != default_value)
7354 // do not save if explicitly told or if unmodified default settings
7355 if ((save_type == SAVE_CONF_NEVER) ||
7356 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7360 num_bytes += putFile16BitBE(file, element);
7362 num_bytes += putFile8Bit(file, conf_type);
7363 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7365 for (i = 0; i < num_contents; i++)
7366 for (y = 0; y < 3; y++)
7367 for (x = 0; x < 3; x++)
7368 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7374 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7379 li = *level; // copy level data into temporary buffer
7381 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7382 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7387 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7392 li = *level; // copy level data into temporary buffer
7394 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7395 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7400 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7402 int envelope_nr = element - EL_ENVELOPE_1;
7406 chunk_size += putFile16BitBE(file, element);
7408 // copy envelope data into temporary buffer
7409 xx_envelope = level->envelope[envelope_nr];
7411 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7412 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7417 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7419 struct ElementInfo *ei = &element_info[element];
7423 chunk_size += putFile16BitBE(file, element);
7425 xx_ei = *ei; // copy element data into temporary buffer
7427 // set default description string for this specific element
7428 strcpy(xx_default_description, getDefaultElementDescription(ei));
7430 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7431 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7433 for (i = 0; i < ei->num_change_pages; i++)
7435 struct ElementChangeInfo *change = &ei->change_page[i];
7437 xx_current_change_page = i;
7439 xx_change = *change; // copy change data into temporary buffer
7442 setEventBitsFromEventFlags(change);
7444 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7445 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7452 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7454 struct ElementInfo *ei = &element_info[element];
7455 struct ElementGroupInfo *group = ei->group;
7459 chunk_size += putFile16BitBE(file, element);
7461 xx_ei = *ei; // copy element data into temporary buffer
7462 xx_group = *group; // copy group data into temporary buffer
7464 // set default description string for this specific element
7465 strcpy(xx_default_description, getDefaultElementDescription(ei));
7467 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7468 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7473 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7474 boolean save_as_template)
7480 if (!(file = fopen(filename, MODE_WRITE)))
7482 Warn("cannot save level file '%s'", filename);
7487 level->file_version = FILE_VERSION_ACTUAL;
7488 level->game_version = GAME_VERSION_ACTUAL;
7490 level->creation_date = getCurrentDate();
7492 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7493 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7495 chunk_size = SaveLevel_VERS(NULL, level);
7496 putFileChunkBE(file, "VERS", chunk_size);
7497 SaveLevel_VERS(file, level);
7499 chunk_size = SaveLevel_DATE(NULL, level);
7500 putFileChunkBE(file, "DATE", chunk_size);
7501 SaveLevel_DATE(file, level);
7503 chunk_size = SaveLevel_NAME(NULL, level);
7504 putFileChunkBE(file, "NAME", chunk_size);
7505 SaveLevel_NAME(file, level);
7507 chunk_size = SaveLevel_AUTH(NULL, level);
7508 putFileChunkBE(file, "AUTH", chunk_size);
7509 SaveLevel_AUTH(file, level);
7511 chunk_size = SaveLevel_INFO(NULL, level);
7512 putFileChunkBE(file, "INFO", chunk_size);
7513 SaveLevel_INFO(file, level);
7515 chunk_size = SaveLevel_BODY(NULL, level);
7516 putFileChunkBE(file, "BODY", chunk_size);
7517 SaveLevel_BODY(file, level);
7519 chunk_size = SaveLevel_ELEM(NULL, level);
7520 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7522 putFileChunkBE(file, "ELEM", chunk_size);
7523 SaveLevel_ELEM(file, level);
7526 for (i = 0; i < NUM_ENVELOPES; i++)
7528 int element = EL_ENVELOPE_1 + i;
7530 chunk_size = SaveLevel_NOTE(NULL, level, element);
7531 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7533 putFileChunkBE(file, "NOTE", chunk_size);
7534 SaveLevel_NOTE(file, level, element);
7538 // if not using template level, check for non-default custom/group elements
7539 if (!level->use_custom_template || save_as_template)
7541 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7543 int element = EL_CUSTOM_START + i;
7545 chunk_size = SaveLevel_CUSX(NULL, level, element);
7546 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7548 putFileChunkBE(file, "CUSX", chunk_size);
7549 SaveLevel_CUSX(file, level, element);
7553 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7555 int element = EL_GROUP_START + i;
7557 chunk_size = SaveLevel_GRPX(NULL, level, element);
7558 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7560 putFileChunkBE(file, "GRPX", chunk_size);
7561 SaveLevel_GRPX(file, level, element);
7568 SetFilePermissions(filename, PERMS_PRIVATE);
7571 void SaveLevel(int nr)
7573 char *filename = getDefaultLevelFilename(nr);
7575 SaveLevelFromFilename(&level, filename, FALSE);
7578 void SaveLevelTemplate(void)
7580 char *filename = getLocalLevelTemplateFilename();
7582 SaveLevelFromFilename(&level, filename, TRUE);
7585 boolean SaveLevelChecked(int nr)
7587 char *filename = getDefaultLevelFilename(nr);
7588 boolean new_level = !fileExists(filename);
7589 boolean level_saved = FALSE;
7591 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7596 Request("Level saved!", REQ_CONFIRM);
7604 void DumpLevel(struct LevelInfo *level)
7606 if (level->no_level_file || level->no_valid_file)
7608 Warn("cannot dump -- no valid level file found");
7614 Print("Level xxx (file version %08d, game version %08d)\n",
7615 level->file_version, level->game_version);
7618 Print("Level author: '%s'\n", level->author);
7619 Print("Level title: '%s'\n", level->name);
7621 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7623 Print("Level time: %d seconds\n", level->time);
7624 Print("Gems needed: %d\n", level->gems_needed);
7626 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7627 Print("Time for wheel: %d seconds\n", level->time_wheel);
7628 Print("Time for light: %d seconds\n", level->time_light);
7629 Print("Time for timegate: %d seconds\n", level->time_timegate);
7631 Print("Amoeba speed: %d\n", level->amoeba_speed);
7634 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7635 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7636 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7637 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7638 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7644 // ============================================================================
7645 // tape file functions
7646 // ============================================================================
7648 static void setTapeInfoToDefaults(void)
7652 // always start with reliable default values (empty tape)
7655 // default values (also for pre-1.2 tapes) with only the first player
7656 tape.player_participates[0] = TRUE;
7657 for (i = 1; i < MAX_PLAYERS; i++)
7658 tape.player_participates[i] = FALSE;
7660 // at least one (default: the first) player participates in every tape
7661 tape.num_participating_players = 1;
7663 tape.property_bits = TAPE_PROPERTY_NONE;
7665 tape.level_nr = level_nr;
7667 tape.changed = FALSE;
7669 tape.recording = FALSE;
7670 tape.playing = FALSE;
7671 tape.pausing = FALSE;
7673 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7674 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7676 tape.no_valid_file = FALSE;
7679 static int getTapePosSize(struct TapeInfo *tape)
7681 int tape_pos_size = 0;
7683 if (tape->use_key_actions)
7684 tape_pos_size += tape->num_participating_players;
7686 if (tape->use_mouse_actions)
7687 tape_pos_size += 3; // x and y position and mouse button mask
7689 tape_pos_size += 1; // tape action delay value
7691 return tape_pos_size;
7694 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7696 tape->use_key_actions = FALSE;
7697 tape->use_mouse_actions = FALSE;
7699 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7700 tape->use_key_actions = TRUE;
7702 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7703 tape->use_mouse_actions = TRUE;
7706 static int getTapeActionValue(struct TapeInfo *tape)
7708 return (tape->use_key_actions &&
7709 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7710 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7711 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7712 TAPE_ACTIONS_DEFAULT);
7715 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7717 tape->file_version = getFileVersion(file);
7718 tape->game_version = getFileVersion(file);
7723 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7727 tape->random_seed = getFile32BitBE(file);
7728 tape->date = getFile32BitBE(file);
7729 tape->length = getFile32BitBE(file);
7731 // read header fields that are new since version 1.2
7732 if (tape->file_version >= FILE_VERSION_1_2)
7734 byte store_participating_players = getFile8Bit(file);
7737 // since version 1.2, tapes store which players participate in the tape
7738 tape->num_participating_players = 0;
7739 for (i = 0; i < MAX_PLAYERS; i++)
7741 tape->player_participates[i] = FALSE;
7743 if (store_participating_players & (1 << i))
7745 tape->player_participates[i] = TRUE;
7746 tape->num_participating_players++;
7750 setTapeActionFlags(tape, getFile8Bit(file));
7752 tape->property_bits = getFile8Bit(file);
7754 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7756 engine_version = getFileVersion(file);
7757 if (engine_version > 0)
7758 tape->engine_version = engine_version;
7760 tape->engine_version = tape->game_version;
7766 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7768 tape->scr_fieldx = getFile8Bit(file);
7769 tape->scr_fieldy = getFile8Bit(file);
7774 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7776 char *level_identifier = NULL;
7777 int level_identifier_size;
7780 level_identifier_size = getFile16BitBE(file);
7782 level_identifier = checked_malloc(level_identifier_size);
7784 for (i = 0; i < level_identifier_size; i++)
7785 level_identifier[i] = getFile8Bit(file);
7787 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7788 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7790 checked_free(level_identifier);
7792 tape->level_nr = getFile16BitBE(file);
7794 chunk_size = 2 + level_identifier_size + 2;
7799 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7802 int tape_pos_size = getTapePosSize(tape);
7803 int chunk_size_expected = tape_pos_size * tape->length;
7805 if (chunk_size_expected != chunk_size)
7807 ReadUnusedBytesFromFile(file, chunk_size);
7808 return chunk_size_expected;
7811 for (i = 0; i < tape->length; i++)
7813 if (i >= MAX_TAPE_LEN)
7815 Warn("tape truncated -- size exceeds maximum tape size %d",
7818 // tape too large; read and ignore remaining tape data from this chunk
7819 for (;i < tape->length; i++)
7820 ReadUnusedBytesFromFile(file, tape_pos_size);
7825 if (tape->use_key_actions)
7827 for (j = 0; j < MAX_PLAYERS; j++)
7829 tape->pos[i].action[j] = MV_NONE;
7831 if (tape->player_participates[j])
7832 tape->pos[i].action[j] = getFile8Bit(file);
7836 if (tape->use_mouse_actions)
7838 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7839 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7840 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7843 tape->pos[i].delay = getFile8Bit(file);
7845 if (tape->file_version == FILE_VERSION_1_0)
7847 // eliminate possible diagonal moves in old tapes
7848 // this is only for backward compatibility
7850 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7851 byte action = tape->pos[i].action[0];
7852 int k, num_moves = 0;
7854 for (k = 0; k<4; k++)
7856 if (action & joy_dir[k])
7858 tape->pos[i + num_moves].action[0] = joy_dir[k];
7860 tape->pos[i + num_moves].delay = 0;
7869 tape->length += num_moves;
7872 else if (tape->file_version < FILE_VERSION_2_0)
7874 // convert pre-2.0 tapes to new tape format
7876 if (tape->pos[i].delay > 1)
7879 tape->pos[i + 1] = tape->pos[i];
7880 tape->pos[i + 1].delay = 1;
7883 for (j = 0; j < MAX_PLAYERS; j++)
7884 tape->pos[i].action[j] = MV_NONE;
7885 tape->pos[i].delay--;
7892 if (checkEndOfFile(file))
7896 if (i != tape->length)
7897 chunk_size = tape_pos_size * i;
7902 static void LoadTape_SokobanSolution(char *filename)
7905 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7907 if (!(file = openFile(filename, MODE_READ)))
7909 tape.no_valid_file = TRUE;
7914 while (!checkEndOfFile(file))
7916 unsigned char c = getByteFromFile(file);
7918 if (checkEndOfFile(file))
7925 tape.pos[tape.length].action[0] = MV_UP;
7926 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7932 tape.pos[tape.length].action[0] = MV_DOWN;
7933 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7939 tape.pos[tape.length].action[0] = MV_LEFT;
7940 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7946 tape.pos[tape.length].action[0] = MV_RIGHT;
7947 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7955 // ignore white-space characters
7959 tape.no_valid_file = TRUE;
7961 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7969 if (tape.no_valid_file)
7972 tape.length_frames = GetTapeLengthFrames();
7973 tape.length_seconds = GetTapeLengthSeconds();
7976 void LoadTapeFromFilename(char *filename)
7978 char cookie[MAX_LINE_LEN];
7979 char chunk_name[CHUNK_ID_LEN + 1];
7983 // always start with reliable default values
7984 setTapeInfoToDefaults();
7986 if (strSuffix(filename, ".sln"))
7988 LoadTape_SokobanSolution(filename);
7993 if (!(file = openFile(filename, MODE_READ)))
7995 tape.no_valid_file = TRUE;
8000 getFileChunkBE(file, chunk_name, NULL);
8001 if (strEqual(chunk_name, "RND1"))
8003 getFile32BitBE(file); // not used
8005 getFileChunkBE(file, chunk_name, NULL);
8006 if (!strEqual(chunk_name, "TAPE"))
8008 tape.no_valid_file = TRUE;
8010 Warn("unknown format of tape file '%s'", filename);
8017 else // check for pre-2.0 file format with cookie string
8019 strcpy(cookie, chunk_name);
8020 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8022 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8023 cookie[strlen(cookie) - 1] = '\0';
8025 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8027 tape.no_valid_file = TRUE;
8029 Warn("unknown format of tape file '%s'", filename);
8036 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8038 tape.no_valid_file = TRUE;
8040 Warn("unsupported version of tape file '%s'", filename);
8047 // pre-2.0 tape files have no game version, so use file version here
8048 tape.game_version = tape.file_version;
8051 if (tape.file_version < FILE_VERSION_1_2)
8053 // tape files from versions before 1.2.0 without chunk structure
8054 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8055 LoadTape_BODY(file, 2 * tape.length, &tape);
8063 int (*loader)(File *, int, struct TapeInfo *);
8067 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8068 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8069 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8070 { "INFO", -1, LoadTape_INFO },
8071 { "BODY", -1, LoadTape_BODY },
8075 while (getFileChunkBE(file, chunk_name, &chunk_size))
8079 while (chunk_info[i].name != NULL &&
8080 !strEqual(chunk_name, chunk_info[i].name))
8083 if (chunk_info[i].name == NULL)
8085 Warn("unknown chunk '%s' in tape file '%s'",
8086 chunk_name, filename);
8088 ReadUnusedBytesFromFile(file, chunk_size);
8090 else if (chunk_info[i].size != -1 &&
8091 chunk_info[i].size != chunk_size)
8093 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8094 chunk_size, chunk_name, filename);
8096 ReadUnusedBytesFromFile(file, chunk_size);
8100 // call function to load this tape chunk
8101 int chunk_size_expected =
8102 (chunk_info[i].loader)(file, chunk_size, &tape);
8104 // the size of some chunks cannot be checked before reading other
8105 // chunks first (like "HEAD" and "BODY") that contain some header
8106 // information, so check them here
8107 if (chunk_size_expected != chunk_size)
8109 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8110 chunk_size, chunk_name, filename);
8118 tape.length_frames = GetTapeLengthFrames();
8119 tape.length_seconds = GetTapeLengthSeconds();
8122 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8124 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8126 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8127 tape.engine_version);
8131 void LoadTape(int nr)
8133 char *filename = getTapeFilename(nr);
8135 LoadTapeFromFilename(filename);
8138 void LoadSolutionTape(int nr)
8140 char *filename = getSolutionTapeFilename(nr);
8142 LoadTapeFromFilename(filename);
8144 if (TAPE_IS_EMPTY(tape) &&
8145 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8146 level.native_sp_level->demo.is_available)
8147 CopyNativeTape_SP_to_RND(&level);
8150 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8152 // chunk required for team mode tapes with non-default screen size
8153 return (tape->num_participating_players > 1 &&
8154 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8155 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8158 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8160 putFileVersion(file, tape->file_version);
8161 putFileVersion(file, tape->game_version);
8164 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8167 byte store_participating_players = 0;
8169 // set bits for participating players for compact storage
8170 for (i = 0; i < MAX_PLAYERS; i++)
8171 if (tape->player_participates[i])
8172 store_participating_players |= (1 << i);
8174 putFile32BitBE(file, tape->random_seed);
8175 putFile32BitBE(file, tape->date);
8176 putFile32BitBE(file, tape->length);
8178 putFile8Bit(file, store_participating_players);
8180 putFile8Bit(file, getTapeActionValue(tape));
8182 putFile8Bit(file, tape->property_bits);
8184 // unused bytes not at the end here for 4-byte alignment of engine_version
8185 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8187 putFileVersion(file, tape->engine_version);
8190 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8192 putFile8Bit(file, tape->scr_fieldx);
8193 putFile8Bit(file, tape->scr_fieldy);
8196 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8198 int level_identifier_size = strlen(tape->level_identifier) + 1;
8201 putFile16BitBE(file, level_identifier_size);
8203 for (i = 0; i < level_identifier_size; i++)
8204 putFile8Bit(file, tape->level_identifier[i]);
8206 putFile16BitBE(file, tape->level_nr);
8209 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8213 for (i = 0; i < tape->length; i++)
8215 if (tape->use_key_actions)
8217 for (j = 0; j < MAX_PLAYERS; j++)
8218 if (tape->player_participates[j])
8219 putFile8Bit(file, tape->pos[i].action[j]);
8222 if (tape->use_mouse_actions)
8224 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8225 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8226 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8229 putFile8Bit(file, tape->pos[i].delay);
8233 void SaveTapeToFilename(char *filename)
8237 int info_chunk_size;
8238 int body_chunk_size;
8240 if (!(file = fopen(filename, MODE_WRITE)))
8242 Warn("cannot save level recording file '%s'", filename);
8247 tape_pos_size = getTapePosSize(&tape);
8249 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8250 body_chunk_size = tape_pos_size * tape.length;
8252 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8253 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8255 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8256 SaveTape_VERS(file, &tape);
8258 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8259 SaveTape_HEAD(file, &tape);
8261 if (checkSaveTape_SCRN(&tape))
8263 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8264 SaveTape_SCRN(file, &tape);
8267 putFileChunkBE(file, "INFO", info_chunk_size);
8268 SaveTape_INFO(file, &tape);
8270 putFileChunkBE(file, "BODY", body_chunk_size);
8271 SaveTape_BODY(file, &tape);
8275 SetFilePermissions(filename, PERMS_PRIVATE);
8278 void SaveTape(int nr)
8280 char *filename = getTapeFilename(nr);
8283 InitTapeDirectory(leveldir_current->subdir);
8285 tape.file_version = FILE_VERSION_ACTUAL;
8286 tape.game_version = GAME_VERSION_ACTUAL;
8288 tape.num_participating_players = 0;
8290 // count number of participating players
8291 for (i = 0; i < MAX_PLAYERS; i++)
8292 if (tape.player_participates[i])
8293 tape.num_participating_players++;
8295 SaveTapeToFilename(filename);
8297 tape.changed = FALSE;
8300 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8301 unsigned int req_state_added)
8303 char *filename = getTapeFilename(nr);
8304 boolean new_tape = !fileExists(filename);
8305 boolean tape_saved = FALSE;
8307 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8312 Request(msg_saved, REQ_CONFIRM | req_state_added);
8320 boolean SaveTapeChecked(int nr)
8322 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8325 boolean SaveTapeChecked_LevelSolved(int nr)
8327 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8328 "Level solved! Tape saved!", REQ_STAY_OPEN);
8331 void DumpTape(struct TapeInfo *tape)
8333 int tape_frame_counter;
8336 if (tape->no_valid_file)
8338 Warn("cannot dump -- no valid tape file found");
8344 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8345 tape->level_nr, tape->file_version, tape->game_version);
8346 Print(" (effective engine version %08d)\n",
8347 tape->engine_version);
8348 Print("Level series identifier: '%s'\n", tape->level_identifier);
8351 tape_frame_counter = 0;
8353 for (i = 0; i < tape->length; i++)
8355 if (i >= MAX_TAPE_LEN)
8360 for (j = 0; j < MAX_PLAYERS; j++)
8362 if (tape->player_participates[j])
8364 int action = tape->pos[i].action[j];
8366 Print("%d:%02x ", j, action);
8367 Print("[%c%c%c%c|%c%c] - ",
8368 (action & JOY_LEFT ? '<' : ' '),
8369 (action & JOY_RIGHT ? '>' : ' '),
8370 (action & JOY_UP ? '^' : ' '),
8371 (action & JOY_DOWN ? 'v' : ' '),
8372 (action & JOY_BUTTON_1 ? '1' : ' '),
8373 (action & JOY_BUTTON_2 ? '2' : ' '));
8377 Print("(%03d) ", tape->pos[i].delay);
8378 Print("[%05d]\n", tape_frame_counter);
8380 tape_frame_counter += tape->pos[i].delay;
8387 // ============================================================================
8388 // score file functions
8389 // ============================================================================
8391 static void setScoreInfoToDefaults(void)
8395 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8397 strcpy(scores.entry[i].name, EMPTY_PLAYER_NAME);
8398 scores.entry[i].score = 0;
8399 scores.entry[i].time = 0;
8403 static void LoadScore_OLD(int nr)
8406 char *filename = getScoreFilename(nr);
8407 char cookie[MAX_LINE_LEN];
8408 char line[MAX_LINE_LEN];
8412 if (!(file = fopen(filename, MODE_READ)))
8415 // check file identifier
8416 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8418 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8419 cookie[strlen(cookie) - 1] = '\0';
8421 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8423 Warn("unknown format of score file '%s'", filename);
8430 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8432 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8433 Warn("fscanf() failed; %s", strerror(errno));
8435 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8438 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8439 line[strlen(line) - 1] = '\0';
8441 for (line_ptr = line; *line_ptr; line_ptr++)
8443 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8445 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8446 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8455 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8457 scores->file_version = getFileVersion(file);
8458 scores->game_version = getFileVersion(file);
8463 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8465 char *level_identifier = NULL;
8466 int level_identifier_size;
8469 level_identifier_size = getFile16BitBE(file);
8471 level_identifier = checked_malloc(level_identifier_size);
8473 for (i = 0; i < level_identifier_size; i++)
8474 level_identifier[i] = getFile8Bit(file);
8476 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8477 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8479 checked_free(level_identifier);
8481 scores->level_nr = getFile16BitBE(file);
8482 scores->num_entries = getFile16BitBE(file);
8484 chunk_size = 2 + level_identifier_size + 2 + 2;
8489 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8493 for (i = 0; i < scores->num_entries; i++)
8495 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8496 scores->entry[i].name[j] = getFile8Bit(file);
8498 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8501 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8506 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8510 for (i = 0; i < scores->num_entries; i++)
8511 scores->entry[i].score = getFile16BitBE(file);
8513 chunk_size = scores->num_entries * 2;
8518 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8522 for (i = 0; i < scores->num_entries; i++)
8523 scores->entry[i].time = getFile32BitBE(file);
8525 chunk_size = scores->num_entries * 4;
8530 void LoadScore(int nr)
8532 char *filename = getScoreFilename(nr);
8533 char cookie[MAX_LINE_LEN];
8534 char chunk_name[CHUNK_ID_LEN + 1];
8536 boolean old_score_file_format = FALSE;
8539 // always start with reliable default values
8540 setScoreInfoToDefaults();
8542 if (!(file = openFile(filename, MODE_READ)))
8545 getFileChunkBE(file, chunk_name, NULL);
8546 if (strEqual(chunk_name, "RND1"))
8548 getFile32BitBE(file); // not used
8550 getFileChunkBE(file, chunk_name, NULL);
8551 if (!strEqual(chunk_name, "SCOR"))
8553 Warn("unknown format of score file '%s'", filename);
8560 else // check for old file format with cookie string
8562 strcpy(cookie, chunk_name);
8563 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8565 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8566 cookie[strlen(cookie) - 1] = '\0';
8568 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8570 Warn("unknown format of score file '%s'", filename);
8577 old_score_file_format = TRUE;
8580 if (old_score_file_format)
8582 // score files from versions before 4.2.4.0 without chunk structure
8591 int (*loader)(File *, int, struct ScoreInfo *);
8595 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8596 { "INFO", -1, LoadScore_INFO },
8597 { "NAME", -1, LoadScore_NAME },
8598 { "SCOR", -1, LoadScore_SCOR },
8599 { "TIME", -1, LoadScore_TIME },
8604 while (getFileChunkBE(file, chunk_name, &chunk_size))
8608 while (chunk_info[i].name != NULL &&
8609 !strEqual(chunk_name, chunk_info[i].name))
8612 if (chunk_info[i].name == NULL)
8614 Warn("unknown chunk '%s' in score file '%s'",
8615 chunk_name, filename);
8617 ReadUnusedBytesFromFile(file, chunk_size);
8619 else if (chunk_info[i].size != -1 &&
8620 chunk_info[i].size != chunk_size)
8622 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8623 chunk_size, chunk_name, filename);
8625 ReadUnusedBytesFromFile(file, chunk_size);
8629 // call function to load this score chunk
8630 int chunk_size_expected =
8631 (chunk_info[i].loader)(file, chunk_size, &scores);
8633 // the size of some chunks cannot be checked before reading other
8634 // chunks first (like "HEAD" and "BODY") that contain some header
8635 // information, so check them here
8636 if (chunk_size_expected != chunk_size)
8638 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8639 chunk_size, chunk_name, filename);
8648 #if ENABLE_HISTORIC_CHUNKS
8649 void SaveScore_OLD(int nr)
8652 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8653 char *filename = getScoreFilename(nr);
8656 // used instead of "leveldir_current->subdir" (for network games)
8657 InitScoreDirectory(levelset.identifier);
8659 if (!(file = fopen(filename, MODE_WRITE)))
8661 Warn("cannot save score for level %d", nr);
8666 fprintf(file, "%s\n\n", SCORE_COOKIE);
8668 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8669 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8673 SetFilePermissions(filename, permissions);
8677 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
8679 putFileVersion(file, scores->file_version);
8680 putFileVersion(file, scores->game_version);
8683 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
8685 int level_identifier_size = strlen(scores->level_identifier) + 1;
8688 putFile16BitBE(file, level_identifier_size);
8690 for (i = 0; i < level_identifier_size; i++)
8691 putFile8Bit(file, scores->level_identifier[i]);
8693 putFile16BitBE(file, scores->level_nr);
8694 putFile16BitBE(file, scores->num_entries);
8697 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
8701 for (i = 0; i < scores->num_entries; i++)
8703 int name_size = strlen(scores->entry[i].name);
8705 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8706 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
8710 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
8714 for (i = 0; i < scores->num_entries; i++)
8715 putFile16BitBE(file, scores->entry[i].score);
8718 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
8722 for (i = 0; i < scores->num_entries; i++)
8723 putFile32BitBE(file, scores->entry[i].time);
8726 static void SaveScoreToFilename(char *filename)
8729 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8730 int info_chunk_size;
8731 int name_chunk_size;
8732 int scor_chunk_size;
8733 int time_chunk_size;
8735 if (!(file = fopen(filename, MODE_WRITE)))
8737 Warn("cannot save score file '%s'", filename);
8742 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
8743 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
8744 scor_chunk_size = scores.num_entries * 2;
8745 time_chunk_size = scores.num_entries * 4;
8747 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8748 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
8750 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
8751 SaveScore_VERS(file, &scores);
8753 putFileChunkBE(file, "INFO", info_chunk_size);
8754 SaveScore_INFO(file, &scores);
8756 putFileChunkBE(file, "NAME", name_chunk_size);
8757 SaveScore_NAME(file, &scores);
8759 putFileChunkBE(file, "SCOR", scor_chunk_size);
8760 SaveScore_SCOR(file, &scores);
8762 putFileChunkBE(file, "TIME", time_chunk_size);
8763 SaveScore_TIME(file, &scores);
8767 SetFilePermissions(filename, permissions);
8770 void SaveScore(int nr)
8772 char *filename = getScoreFilename(nr);
8775 // used instead of "leveldir_current->subdir" (for network games)
8776 InitScoreDirectory(levelset.identifier);
8778 scores.file_version = FILE_VERSION_ACTUAL;
8779 scores.game_version = GAME_VERSION_ACTUAL;
8781 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
8782 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
8783 scores.level_nr = level_nr;
8785 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8786 if (scores.entry[i].score == 0 &&
8787 scores.entry[i].time == 0 &&
8788 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
8791 scores.num_entries = i;
8793 if (scores.num_entries == 0)
8796 SaveScoreToFilename(filename);
8800 // ============================================================================
8801 // setup file functions
8802 // ============================================================================
8804 #define TOKEN_STR_PLAYER_PREFIX "player_"
8807 static struct TokenInfo global_setup_tokens[] =
8811 &setup.player_name, "player_name"
8815 &setup.multiple_users, "multiple_users"
8819 &setup.sound, "sound"
8823 &setup.sound_loops, "repeating_sound_loops"
8827 &setup.sound_music, "background_music"
8831 &setup.sound_simple, "simple_sound_effects"
8835 &setup.toons, "toons"
8839 &setup.scroll_delay, "scroll_delay"
8843 &setup.forced_scroll_delay, "forced_scroll_delay"
8847 &setup.scroll_delay_value, "scroll_delay_value"
8851 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8855 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8859 &setup.fade_screens, "fade_screens"
8863 &setup.autorecord, "automatic_tape_recording"
8867 &setup.show_titlescreen, "show_titlescreen"
8871 &setup.quick_doors, "quick_doors"
8875 &setup.team_mode, "team_mode"
8879 &setup.handicap, "handicap"
8883 &setup.skip_levels, "skip_levels"
8887 &setup.increment_levels, "increment_levels"
8891 &setup.auto_play_next_level, "auto_play_next_level"
8895 &setup.count_score_after_game, "count_score_after_game"
8899 &setup.show_scores_after_game, "show_scores_after_game"
8903 &setup.time_limit, "time_limit"
8907 &setup.fullscreen, "fullscreen"
8911 &setup.window_scaling_percent, "window_scaling_percent"
8915 &setup.window_scaling_quality, "window_scaling_quality"
8919 &setup.screen_rendering_mode, "screen_rendering_mode"
8923 &setup.vsync_mode, "vsync_mode"
8927 &setup.ask_on_escape, "ask_on_escape"
8931 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8935 &setup.ask_on_game_over, "ask_on_game_over"
8939 &setup.ask_on_quit_game, "ask_on_quit_game"
8943 &setup.ask_on_quit_program, "ask_on_quit_program"
8947 &setup.quick_switch, "quick_player_switch"
8951 &setup.input_on_focus, "input_on_focus"
8955 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8959 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8963 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8967 &setup.game_speed_extended, "game_speed_extended"
8971 &setup.game_frame_delay, "game_frame_delay"
8975 &setup.sp_show_border_elements, "sp_show_border_elements"
8979 &setup.small_game_graphics, "small_game_graphics"
8983 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8987 &setup.graphics_set, "graphics_set"
8991 &setup.sounds_set, "sounds_set"
8995 &setup.music_set, "music_set"
8999 &setup.override_level_graphics, "override_level_graphics"
9003 &setup.override_level_sounds, "override_level_sounds"
9007 &setup.override_level_music, "override_level_music"
9011 &setup.volume_simple, "volume_simple"
9015 &setup.volume_loops, "volume_loops"
9019 &setup.volume_music, "volume_music"
9023 &setup.network_mode, "network_mode"
9027 &setup.network_player_nr, "network_player"
9031 &setup.network_server_hostname, "network_server_hostname"
9035 &setup.touch.control_type, "touch.control_type"
9039 &setup.touch.move_distance, "touch.move_distance"
9043 &setup.touch.drop_distance, "touch.drop_distance"
9047 &setup.touch.transparency, "touch.transparency"
9051 &setup.touch.draw_outlined, "touch.draw_outlined"
9055 &setup.touch.draw_pressed, "touch.draw_pressed"
9059 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9063 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9067 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9071 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9075 static struct TokenInfo auto_setup_tokens[] =
9079 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9083 static struct TokenInfo editor_setup_tokens[] =
9087 &setup.editor.el_classic, "editor.el_classic"
9091 &setup.editor.el_custom, "editor.el_custom"
9095 &setup.editor.el_user_defined, "editor.el_user_defined"
9099 &setup.editor.el_dynamic, "editor.el_dynamic"
9103 &setup.editor.el_headlines, "editor.el_headlines"
9107 &setup.editor.show_element_token, "editor.show_element_token"
9111 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9115 static struct TokenInfo editor_cascade_setup_tokens[] =
9119 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9123 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9127 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9131 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9135 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9139 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9143 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9147 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9151 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9155 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9159 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9163 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9167 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9171 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9175 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9179 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9183 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9187 static struct TokenInfo shortcut_setup_tokens[] =
9191 &setup.shortcut.save_game, "shortcut.save_game"
9195 &setup.shortcut.load_game, "shortcut.load_game"
9199 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9203 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9207 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9211 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9215 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9219 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9223 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9227 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9231 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9235 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9239 &setup.shortcut.tape_record, "shortcut.tape_record"
9243 &setup.shortcut.tape_play, "shortcut.tape_play"
9247 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9251 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9255 &setup.shortcut.sound_music, "shortcut.sound_music"
9259 &setup.shortcut.snap_left, "shortcut.snap_left"
9263 &setup.shortcut.snap_right, "shortcut.snap_right"
9267 &setup.shortcut.snap_up, "shortcut.snap_up"
9271 &setup.shortcut.snap_down, "shortcut.snap_down"
9275 static struct SetupInputInfo setup_input;
9276 static struct TokenInfo player_setup_tokens[] =
9280 &setup_input.use_joystick, ".use_joystick"
9284 &setup_input.joy.device_name, ".joy.device_name"
9288 &setup_input.joy.xleft, ".joy.xleft"
9292 &setup_input.joy.xmiddle, ".joy.xmiddle"
9296 &setup_input.joy.xright, ".joy.xright"
9300 &setup_input.joy.yupper, ".joy.yupper"
9304 &setup_input.joy.ymiddle, ".joy.ymiddle"
9308 &setup_input.joy.ylower, ".joy.ylower"
9312 &setup_input.joy.snap, ".joy.snap_field"
9316 &setup_input.joy.drop, ".joy.place_bomb"
9320 &setup_input.key.left, ".key.move_left"
9324 &setup_input.key.right, ".key.move_right"
9328 &setup_input.key.up, ".key.move_up"
9332 &setup_input.key.down, ".key.move_down"
9336 &setup_input.key.snap, ".key.snap_field"
9340 &setup_input.key.drop, ".key.place_bomb"
9344 static struct TokenInfo system_setup_tokens[] =
9348 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
9352 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9356 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9360 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9364 static struct TokenInfo internal_setup_tokens[] =
9368 &setup.internal.program_title, "program_title"
9372 &setup.internal.program_version, "program_version"
9376 &setup.internal.program_author, "program_author"
9380 &setup.internal.program_email, "program_email"
9384 &setup.internal.program_website, "program_website"
9388 &setup.internal.program_copyright, "program_copyright"
9392 &setup.internal.program_company, "program_company"
9396 &setup.internal.program_icon_file, "program_icon_file"
9400 &setup.internal.default_graphics_set, "default_graphics_set"
9404 &setup.internal.default_sounds_set, "default_sounds_set"
9408 &setup.internal.default_music_set, "default_music_set"
9412 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9416 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9420 &setup.internal.fallback_music_file, "fallback_music_file"
9424 &setup.internal.default_level_series, "default_level_series"
9428 &setup.internal.default_window_width, "default_window_width"
9432 &setup.internal.default_window_height, "default_window_height"
9436 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9440 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9444 &setup.internal.create_user_levelset, "create_user_levelset"
9448 &setup.internal.menu_game, "menu_game"
9452 &setup.internal.menu_editor, "menu_editor"
9456 &setup.internal.menu_graphics, "menu_graphics"
9460 &setup.internal.menu_sound, "menu_sound"
9464 &setup.internal.menu_artwork, "menu_artwork"
9468 &setup.internal.menu_input, "menu_input"
9472 &setup.internal.menu_touch, "menu_touch"
9476 &setup.internal.menu_shortcuts, "menu_shortcuts"
9480 &setup.internal.menu_exit, "menu_exit"
9484 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9488 static struct TokenInfo debug_setup_tokens[] =
9492 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9496 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9500 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9504 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9508 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9512 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9516 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9520 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9524 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9528 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9532 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9536 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9540 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9544 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9548 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9552 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9556 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9560 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9564 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9568 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9572 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9575 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9579 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9583 &setup.debug.xsn_mode, "debug.xsn_mode"
9587 &setup.debug.xsn_percent, "debug.xsn_percent"
9591 static struct TokenInfo options_setup_tokens[] =
9595 &setup.options.verbose, "options.verbose"
9599 static void setSetupInfoToDefaults(struct SetupInfo *si)
9603 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9605 si->multiple_users = TRUE;
9608 si->sound_loops = TRUE;
9609 si->sound_music = TRUE;
9610 si->sound_simple = TRUE;
9612 si->scroll_delay = TRUE;
9613 si->forced_scroll_delay = FALSE;
9614 si->scroll_delay_value = STD_SCROLL_DELAY;
9615 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9616 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9617 si->fade_screens = TRUE;
9618 si->autorecord = TRUE;
9619 si->show_titlescreen = TRUE;
9620 si->quick_doors = FALSE;
9621 si->team_mode = FALSE;
9622 si->handicap = TRUE;
9623 si->skip_levels = TRUE;
9624 si->increment_levels = TRUE;
9625 si->auto_play_next_level = TRUE;
9626 si->count_score_after_game = TRUE;
9627 si->show_scores_after_game = TRUE;
9628 si->time_limit = TRUE;
9629 si->fullscreen = FALSE;
9630 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9631 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9632 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9633 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9634 si->ask_on_escape = TRUE;
9635 si->ask_on_escape_editor = TRUE;
9636 si->ask_on_game_over = TRUE;
9637 si->ask_on_quit_game = TRUE;
9638 si->ask_on_quit_program = TRUE;
9639 si->quick_switch = FALSE;
9640 si->input_on_focus = FALSE;
9641 si->prefer_aga_graphics = TRUE;
9642 si->prefer_lowpass_sounds = FALSE;
9643 si->prefer_extra_panel_items = TRUE;
9644 si->game_speed_extended = FALSE;
9645 si->game_frame_delay = GAME_FRAME_DELAY;
9646 si->sp_show_border_elements = FALSE;
9647 si->small_game_graphics = FALSE;
9648 si->show_snapshot_buttons = FALSE;
9650 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9651 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9652 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9654 si->override_level_graphics = FALSE;
9655 si->override_level_sounds = FALSE;
9656 si->override_level_music = FALSE;
9658 si->volume_simple = 100; // percent
9659 si->volume_loops = 100; // percent
9660 si->volume_music = 100; // percent
9662 si->network_mode = FALSE;
9663 si->network_player_nr = 0; // first player
9664 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9666 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9667 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9668 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9669 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9670 si->touch.draw_outlined = TRUE;
9671 si->touch.draw_pressed = TRUE;
9673 for (i = 0; i < 2; i++)
9675 char *default_grid_button[6][2] =
9681 { "111222", " vv " },
9682 { "111222", " vv " }
9684 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9685 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9686 int min_xsize = MIN(6, grid_xsize);
9687 int min_ysize = MIN(6, grid_ysize);
9688 int startx = grid_xsize - min_xsize;
9689 int starty = grid_ysize - min_ysize;
9692 // virtual buttons grid can only be set to defaults if video is initialized
9693 // (this will be repeated if virtual buttons are not loaded from setup file)
9694 if (video.initialized)
9696 si->touch.grid_xsize[i] = grid_xsize;
9697 si->touch.grid_ysize[i] = grid_ysize;
9701 si->touch.grid_xsize[i] = -1;
9702 si->touch.grid_ysize[i] = -1;
9705 for (x = 0; x < MAX_GRID_XSIZE; x++)
9706 for (y = 0; y < MAX_GRID_YSIZE; y++)
9707 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9709 for (x = 0; x < min_xsize; x++)
9710 for (y = 0; y < min_ysize; y++)
9711 si->touch.grid_button[i][x][starty + y] =
9712 default_grid_button[y][0][x];
9714 for (x = 0; x < min_xsize; x++)
9715 for (y = 0; y < min_ysize; y++)
9716 si->touch.grid_button[i][startx + x][starty + y] =
9717 default_grid_button[y][1][x];
9720 si->touch.grid_initialized = video.initialized;
9722 si->editor.el_boulderdash = TRUE;
9723 si->editor.el_emerald_mine = TRUE;
9724 si->editor.el_emerald_mine_club = TRUE;
9725 si->editor.el_more = TRUE;
9726 si->editor.el_sokoban = TRUE;
9727 si->editor.el_supaplex = TRUE;
9728 si->editor.el_diamond_caves = TRUE;
9729 si->editor.el_dx_boulderdash = TRUE;
9731 si->editor.el_mirror_magic = TRUE;
9732 si->editor.el_deflektor = TRUE;
9734 si->editor.el_chars = TRUE;
9735 si->editor.el_steel_chars = TRUE;
9737 si->editor.el_classic = TRUE;
9738 si->editor.el_custom = TRUE;
9740 si->editor.el_user_defined = FALSE;
9741 si->editor.el_dynamic = TRUE;
9743 si->editor.el_headlines = TRUE;
9745 si->editor.show_element_token = FALSE;
9747 si->editor.show_read_only_warning = TRUE;
9749 si->editor.use_template_for_new_levels = TRUE;
9751 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9752 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9753 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9755 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9756 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9757 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9758 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9759 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9761 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9762 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9763 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9764 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9765 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9766 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9768 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9769 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9770 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9772 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9773 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9774 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9775 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9777 for (i = 0; i < MAX_PLAYERS; i++)
9779 si->input[i].use_joystick = FALSE;
9780 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9781 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9782 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9783 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9784 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9785 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9786 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9787 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9788 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9789 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9790 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9791 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9792 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9793 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9794 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9797 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9798 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9799 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9800 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9802 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9803 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9804 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9805 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9806 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9807 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9808 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9810 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9812 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9813 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9814 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9816 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9817 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9818 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9820 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9821 si->internal.choose_from_top_leveldir = FALSE;
9822 si->internal.show_scaling_in_title = TRUE;
9823 si->internal.create_user_levelset = TRUE;
9825 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9826 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9828 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9829 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9830 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9831 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9832 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9833 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9834 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9835 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9836 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9837 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9839 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9840 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9841 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9842 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9843 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9844 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9845 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9846 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9847 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9848 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9850 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9851 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9853 si->debug.show_frames_per_second = FALSE;
9855 si->debug.xsn_mode = AUTO;
9856 si->debug.xsn_percent = 0;
9858 si->options.verbose = FALSE;
9860 #if defined(PLATFORM_ANDROID)
9861 si->fullscreen = TRUE;
9864 setHideSetupEntry(&setup.debug.xsn_mode);
9867 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9869 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9872 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9874 si->editor_cascade.el_bd = TRUE;
9875 si->editor_cascade.el_em = TRUE;
9876 si->editor_cascade.el_emc = TRUE;
9877 si->editor_cascade.el_rnd = TRUE;
9878 si->editor_cascade.el_sb = TRUE;
9879 si->editor_cascade.el_sp = TRUE;
9880 si->editor_cascade.el_dc = TRUE;
9881 si->editor_cascade.el_dx = TRUE;
9883 si->editor_cascade.el_mm = TRUE;
9884 si->editor_cascade.el_df = TRUE;
9886 si->editor_cascade.el_chars = FALSE;
9887 si->editor_cascade.el_steel_chars = FALSE;
9888 si->editor_cascade.el_ce = FALSE;
9889 si->editor_cascade.el_ge = FALSE;
9890 si->editor_cascade.el_ref = FALSE;
9891 si->editor_cascade.el_user = FALSE;
9892 si->editor_cascade.el_dynamic = FALSE;
9895 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9897 static char *getHideSetupToken(void *setup_value)
9899 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9901 if (setup_value != NULL)
9902 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9904 return hide_setup_token;
9907 void setHideSetupEntry(void *setup_value)
9909 char *hide_setup_token = getHideSetupToken(setup_value);
9911 if (hide_setup_hash == NULL)
9912 hide_setup_hash = newSetupFileHash();
9914 if (setup_value != NULL)
9915 setHashEntry(hide_setup_hash, hide_setup_token, "");
9918 void removeHideSetupEntry(void *setup_value)
9920 char *hide_setup_token = getHideSetupToken(setup_value);
9922 if (setup_value != NULL)
9923 removeHashEntry(hide_setup_hash, hide_setup_token);
9926 boolean hideSetupEntry(void *setup_value)
9928 char *hide_setup_token = getHideSetupToken(setup_value);
9930 return (setup_value != NULL &&
9931 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9934 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9935 struct TokenInfo *token_info,
9936 int token_nr, char *token_text)
9938 char *token_hide_text = getStringCat2(token_text, ".hide");
9939 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9941 // set the value of this setup option in the setup option structure
9942 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9944 // check if this setup option should be hidden in the setup menu
9945 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9946 setHideSetupEntry(token_info[token_nr].value);
9948 free(token_hide_text);
9951 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9952 struct TokenInfo *token_info,
9955 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9956 token_info[token_nr].text);
9959 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9963 if (!setup_file_hash)
9966 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9967 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9969 setup.touch.grid_initialized = TRUE;
9970 for (i = 0; i < 2; i++)
9972 int grid_xsize = setup.touch.grid_xsize[i];
9973 int grid_ysize = setup.touch.grid_ysize[i];
9976 // if virtual buttons are not loaded from setup file, repeat initializing
9977 // virtual buttons grid with default values later when video is initialized
9978 if (grid_xsize == -1 ||
9981 setup.touch.grid_initialized = FALSE;
9986 for (y = 0; y < grid_ysize; y++)
9988 char token_string[MAX_LINE_LEN];
9990 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9992 char *value_string = getHashEntry(setup_file_hash, token_string);
9994 if (value_string == NULL)
9997 for (x = 0; x < grid_xsize; x++)
9999 char c = value_string[x];
10001 setup.touch.grid_button[i][x][y] =
10002 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10007 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10008 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10010 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10011 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10013 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10017 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10019 setup_input = setup.input[pnr];
10020 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10022 char full_token[100];
10024 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10025 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10028 setup.input[pnr] = setup_input;
10031 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10032 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10034 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10035 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10037 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10038 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10040 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10041 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10043 setHideRelatedSetupEntries();
10046 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10050 if (!setup_file_hash)
10053 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10054 setSetupInfo(auto_setup_tokens, i,
10055 getHashEntry(setup_file_hash,
10056 auto_setup_tokens[i].text));
10059 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10063 if (!setup_file_hash)
10066 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10067 setSetupInfo(editor_cascade_setup_tokens, i,
10068 getHashEntry(setup_file_hash,
10069 editor_cascade_setup_tokens[i].text));
10072 void LoadUserNames(void)
10074 int last_user_nr = user.nr;
10077 if (global.user_names != NULL)
10079 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10080 checked_free(global.user_names[i]);
10082 checked_free(global.user_names);
10085 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10087 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10091 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10093 if (setup_file_hash)
10095 char *player_name = getHashEntry(setup_file_hash, "player_name");
10097 global.user_names[i] = getFixedUserName(player_name);
10099 freeSetupFileHash(setup_file_hash);
10102 if (global.user_names[i] == NULL)
10103 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10106 user.nr = last_user_nr;
10109 void LoadSetupFromFilename(char *filename)
10111 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10113 if (setup_file_hash)
10115 decodeSetupFileHash(setup_file_hash);
10117 freeSetupFileHash(setup_file_hash);
10121 Debug("setup", "using default setup values");
10125 static void LoadSetup_SpecialPostProcessing(void)
10127 char *player_name_new;
10129 // needed to work around problems with fixed length strings
10130 player_name_new = getFixedUserName(setup.player_name);
10131 free(setup.player_name);
10132 setup.player_name = player_name_new;
10134 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10135 if (setup.scroll_delay == FALSE)
10137 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10138 setup.scroll_delay = TRUE; // now always "on"
10141 // make sure that scroll delay value stays inside valid range
10142 setup.scroll_delay_value =
10143 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10146 void LoadSetup(void)
10150 // always start with reliable default values
10151 setSetupInfoToDefaults(&setup);
10153 // try to load setup values from default setup file
10154 filename = getDefaultSetupFilename();
10156 if (fileExists(filename))
10157 LoadSetupFromFilename(filename);
10159 // try to load setup values from user setup file
10160 filename = getSetupFilename();
10162 LoadSetupFromFilename(filename);
10164 LoadSetup_SpecialPostProcessing();
10167 void LoadSetup_AutoSetup(void)
10169 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10170 SetupFileHash *setup_file_hash = NULL;
10172 // always start with reliable default values
10173 setSetupInfoToDefaults_AutoSetup(&setup);
10175 setup_file_hash = loadSetupFileHash(filename);
10177 if (setup_file_hash)
10179 decodeSetupFileHash_AutoSetup(setup_file_hash);
10181 freeSetupFileHash(setup_file_hash);
10187 void LoadSetup_EditorCascade(void)
10189 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10190 SetupFileHash *setup_file_hash = NULL;
10192 // always start with reliable default values
10193 setSetupInfoToDefaults_EditorCascade(&setup);
10195 setup_file_hash = loadSetupFileHash(filename);
10197 if (setup_file_hash)
10199 decodeSetupFileHash_EditorCascade(setup_file_hash);
10201 freeSetupFileHash(setup_file_hash);
10207 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10208 char *mapping_line)
10210 char mapping_guid[MAX_LINE_LEN];
10211 char *mapping_start, *mapping_end;
10213 // get GUID from game controller mapping line: copy complete line
10214 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10215 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10217 // get GUID from game controller mapping line: cut after GUID part
10218 mapping_start = strchr(mapping_guid, ',');
10219 if (mapping_start != NULL)
10220 *mapping_start = '\0';
10222 // cut newline from game controller mapping line
10223 mapping_end = strchr(mapping_line, '\n');
10224 if (mapping_end != NULL)
10225 *mapping_end = '\0';
10227 // add mapping entry to game controller mappings hash
10228 setHashEntry(mappings_hash, mapping_guid, mapping_line);
10231 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
10236 if (!(file = fopen(filename, MODE_READ)))
10238 Warn("cannot read game controller mappings file '%s'", filename);
10243 while (!feof(file))
10245 char line[MAX_LINE_LEN];
10247 if (!fgets(line, MAX_LINE_LEN, file))
10250 addGameControllerMappingToHash(mappings_hash, line);
10256 void SaveSetup(void)
10258 char *filename = getSetupFilename();
10262 InitUserDataDirectory();
10264 if (!(file = fopen(filename, MODE_WRITE)))
10266 Warn("cannot write setup file '%s'", filename);
10271 fprintFileHeader(file, SETUP_FILENAME);
10273 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10275 // just to make things nicer :)
10276 if (global_setup_tokens[i].value == &setup.multiple_users ||
10277 global_setup_tokens[i].value == &setup.sound ||
10278 global_setup_tokens[i].value == &setup.graphics_set ||
10279 global_setup_tokens[i].value == &setup.volume_simple ||
10280 global_setup_tokens[i].value == &setup.network_mode ||
10281 global_setup_tokens[i].value == &setup.touch.control_type ||
10282 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
10283 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
10284 fprintf(file, "\n");
10286 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
10289 for (i = 0; i < 2; i++)
10291 int grid_xsize = setup.touch.grid_xsize[i];
10292 int grid_ysize = setup.touch.grid_ysize[i];
10295 fprintf(file, "\n");
10297 for (y = 0; y < grid_ysize; y++)
10299 char token_string[MAX_LINE_LEN];
10300 char value_string[MAX_LINE_LEN];
10302 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10304 for (x = 0; x < grid_xsize; x++)
10306 char c = setup.touch.grid_button[i][x][y];
10308 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
10311 value_string[grid_xsize] = '\0';
10313 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
10317 fprintf(file, "\n");
10318 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10319 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
10321 fprintf(file, "\n");
10322 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10323 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
10325 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10329 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10330 fprintf(file, "\n");
10332 setup_input = setup.input[pnr];
10333 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10334 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
10337 fprintf(file, "\n");
10338 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10339 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
10341 // (internal setup values not saved to user setup file)
10343 fprintf(file, "\n");
10344 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10345 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10346 setup.debug.xsn_mode != AUTO)
10347 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10349 fprintf(file, "\n");
10350 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10351 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10355 SetFilePermissions(filename, PERMS_PRIVATE);
10358 void SaveSetup_AutoSetup(void)
10360 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10364 InitUserDataDirectory();
10366 if (!(file = fopen(filename, MODE_WRITE)))
10368 Warn("cannot write auto setup file '%s'", filename);
10375 fprintFileHeader(file, AUTOSETUP_FILENAME);
10377 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10378 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10382 SetFilePermissions(filename, PERMS_PRIVATE);
10387 void SaveSetup_EditorCascade(void)
10389 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10393 InitUserDataDirectory();
10395 if (!(file = fopen(filename, MODE_WRITE)))
10397 Warn("cannot write editor cascade state file '%s'", filename);
10404 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10406 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10407 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10411 SetFilePermissions(filename, PERMS_PRIVATE);
10416 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10421 if (!(file = fopen(filename, MODE_WRITE)))
10423 Warn("cannot write game controller mappings file '%s'", filename);
10428 BEGIN_HASH_ITERATION(mappings_hash, itr)
10430 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10432 END_HASH_ITERATION(mappings_hash, itr)
10437 void SaveSetup_AddGameControllerMapping(char *mapping)
10439 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10440 SetupFileHash *mappings_hash = newSetupFileHash();
10442 InitUserDataDirectory();
10444 // load existing personal game controller mappings
10445 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10447 // add new mapping to personal game controller mappings
10448 addGameControllerMappingToHash(mappings_hash, mapping);
10450 // save updated personal game controller mappings
10451 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10453 freeSetupFileHash(mappings_hash);
10457 void LoadCustomElementDescriptions(void)
10459 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10460 SetupFileHash *setup_file_hash;
10463 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10465 if (element_info[i].custom_description != NULL)
10467 free(element_info[i].custom_description);
10468 element_info[i].custom_description = NULL;
10472 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10475 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10477 char *token = getStringCat2(element_info[i].token_name, ".name");
10478 char *value = getHashEntry(setup_file_hash, token);
10481 element_info[i].custom_description = getStringCopy(value);
10486 freeSetupFileHash(setup_file_hash);
10489 static int getElementFromToken(char *token)
10491 char *value = getHashEntry(element_token_hash, token);
10494 return atoi(value);
10496 Warn("unknown element token '%s'", token);
10498 return EL_UNDEFINED;
10501 void FreeGlobalAnimEventInfo(void)
10503 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10505 if (gaei->event_list == NULL)
10510 for (i = 0; i < gaei->num_event_lists; i++)
10512 checked_free(gaei->event_list[i]->event_value);
10513 checked_free(gaei->event_list[i]);
10516 checked_free(gaei->event_list);
10518 gaei->event_list = NULL;
10519 gaei->num_event_lists = 0;
10522 static int AddGlobalAnimEventList(void)
10524 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10525 int list_pos = gaei->num_event_lists++;
10527 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10528 sizeof(struct GlobalAnimEventListInfo *));
10530 gaei->event_list[list_pos] =
10531 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10533 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10535 gaeli->event_value = NULL;
10536 gaeli->num_event_values = 0;
10541 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10543 // do not add empty global animation events
10544 if (event_value == ANIM_EVENT_NONE)
10547 // if list position is undefined, create new list
10548 if (list_pos == ANIM_EVENT_UNDEFINED)
10549 list_pos = AddGlobalAnimEventList();
10551 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10552 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10553 int value_pos = gaeli->num_event_values++;
10555 gaeli->event_value = checked_realloc(gaeli->event_value,
10556 gaeli->num_event_values * sizeof(int *));
10558 gaeli->event_value[value_pos] = event_value;
10563 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10565 if (list_pos == ANIM_EVENT_UNDEFINED)
10566 return ANIM_EVENT_NONE;
10568 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10569 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10571 return gaeli->event_value[value_pos];
10574 int GetGlobalAnimEventValueCount(int list_pos)
10576 if (list_pos == ANIM_EVENT_UNDEFINED)
10579 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10580 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10582 return gaeli->num_event_values;
10585 // This function checks if a string <s> of the format "string1, string2, ..."
10586 // exactly contains a string <s_contained>.
10588 static boolean string_has_parameter(char *s, char *s_contained)
10592 if (s == NULL || s_contained == NULL)
10595 if (strlen(s_contained) > strlen(s))
10598 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10600 char next_char = s[strlen(s_contained)];
10602 // check if next character is delimiter or whitespace
10603 return (next_char == ',' || next_char == '\0' ||
10604 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10607 // check if string contains another parameter string after a comma
10608 substring = strchr(s, ',');
10609 if (substring == NULL) // string does not contain a comma
10612 // advance string pointer to next character after the comma
10615 // skip potential whitespaces after the comma
10616 while (*substring == ' ' || *substring == '\t')
10619 return string_has_parameter(substring, s_contained);
10622 static int get_anim_parameter_value(char *s)
10624 int event_value[] =
10632 char *pattern_1[] =
10640 char *pattern_2 = ".part_";
10641 char *matching_char = NULL;
10643 int pattern_1_len = 0;
10644 int result = ANIM_EVENT_NONE;
10647 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10649 matching_char = strstr(s_ptr, pattern_1[i]);
10650 pattern_1_len = strlen(pattern_1[i]);
10651 result = event_value[i];
10653 if (matching_char != NULL)
10657 if (matching_char == NULL)
10658 return ANIM_EVENT_NONE;
10660 s_ptr = matching_char + pattern_1_len;
10662 // check for main animation number ("anim_X" or "anim_XX")
10663 if (*s_ptr >= '0' && *s_ptr <= '9')
10665 int gic_anim_nr = (*s_ptr++ - '0');
10667 if (*s_ptr >= '0' && *s_ptr <= '9')
10668 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10670 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10671 return ANIM_EVENT_NONE;
10673 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10677 // invalid main animation number specified
10679 return ANIM_EVENT_NONE;
10682 // check for animation part number ("part_X" or "part_XX") (optional)
10683 if (strPrefix(s_ptr, pattern_2))
10685 s_ptr += strlen(pattern_2);
10687 if (*s_ptr >= '0' && *s_ptr <= '9')
10689 int gic_part_nr = (*s_ptr++ - '0');
10691 if (*s_ptr >= '0' && *s_ptr <= '9')
10692 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10694 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10695 return ANIM_EVENT_NONE;
10697 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10701 // invalid animation part number specified
10703 return ANIM_EVENT_NONE;
10707 // discard result if next character is neither delimiter nor whitespace
10708 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10709 *s_ptr == ' ' || *s_ptr == '\t'))
10710 return ANIM_EVENT_NONE;
10715 static int get_anim_parameter_values(char *s)
10717 int list_pos = ANIM_EVENT_UNDEFINED;
10718 int event_value = ANIM_EVENT_DEFAULT;
10720 if (string_has_parameter(s, "any"))
10721 event_value |= ANIM_EVENT_ANY;
10723 if (string_has_parameter(s, "click:self") ||
10724 string_has_parameter(s, "click") ||
10725 string_has_parameter(s, "self"))
10726 event_value |= ANIM_EVENT_SELF;
10728 if (string_has_parameter(s, "unclick:any"))
10729 event_value |= ANIM_EVENT_UNCLICK_ANY;
10731 // if animation event found, add it to global animation event list
10732 if (event_value != ANIM_EVENT_NONE)
10733 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10737 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10738 event_value = get_anim_parameter_value(s);
10740 // if animation event found, add it to global animation event list
10741 if (event_value != ANIM_EVENT_NONE)
10742 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10744 // continue with next part of the string, starting with next comma
10745 s = strchr(s + 1, ',');
10751 static int get_anim_action_parameter_value(char *token)
10753 // check most common default case first to massively speed things up
10754 if (strEqual(token, ARG_UNDEFINED))
10755 return ANIM_EVENT_ACTION_NONE;
10757 int result = getImageIDFromToken(token);
10761 char *gfx_token = getStringCat2("gfx.", token);
10763 result = getImageIDFromToken(gfx_token);
10765 checked_free(gfx_token);
10770 Key key = getKeyFromX11KeyName(token);
10772 if (key != KSYM_UNDEFINED)
10773 result = -(int)key;
10777 result = ANIM_EVENT_ACTION_NONE;
10782 int get_parameter_value(char *value_raw, char *suffix, int type)
10784 char *value = getStringToLower(value_raw);
10785 int result = 0; // probably a save default value
10787 if (strEqual(suffix, ".direction"))
10789 result = (strEqual(value, "left") ? MV_LEFT :
10790 strEqual(value, "right") ? MV_RIGHT :
10791 strEqual(value, "up") ? MV_UP :
10792 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10794 else if (strEqual(suffix, ".position"))
10796 result = (strEqual(value, "left") ? POS_LEFT :
10797 strEqual(value, "right") ? POS_RIGHT :
10798 strEqual(value, "top") ? POS_TOP :
10799 strEqual(value, "upper") ? POS_UPPER :
10800 strEqual(value, "middle") ? POS_MIDDLE :
10801 strEqual(value, "lower") ? POS_LOWER :
10802 strEqual(value, "bottom") ? POS_BOTTOM :
10803 strEqual(value, "any") ? POS_ANY :
10804 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10806 else if (strEqual(suffix, ".align"))
10808 result = (strEqual(value, "left") ? ALIGN_LEFT :
10809 strEqual(value, "right") ? ALIGN_RIGHT :
10810 strEqual(value, "center") ? ALIGN_CENTER :
10811 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10813 else if (strEqual(suffix, ".valign"))
10815 result = (strEqual(value, "top") ? VALIGN_TOP :
10816 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10817 strEqual(value, "middle") ? VALIGN_MIDDLE :
10818 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10820 else if (strEqual(suffix, ".anim_mode"))
10822 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10823 string_has_parameter(value, "loop") ? ANIM_LOOP :
10824 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10825 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10826 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10827 string_has_parameter(value, "random") ? ANIM_RANDOM :
10828 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10829 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10830 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10831 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10832 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10833 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10834 string_has_parameter(value, "all") ? ANIM_ALL :
10837 if (string_has_parameter(value, "once"))
10838 result |= ANIM_ONCE;
10840 if (string_has_parameter(value, "reverse"))
10841 result |= ANIM_REVERSE;
10843 if (string_has_parameter(value, "opaque_player"))
10844 result |= ANIM_OPAQUE_PLAYER;
10846 if (string_has_parameter(value, "static_panel"))
10847 result |= ANIM_STATIC_PANEL;
10849 else if (strEqual(suffix, ".init_event") ||
10850 strEqual(suffix, ".anim_event"))
10852 result = get_anim_parameter_values(value);
10854 else if (strEqual(suffix, ".init_delay_action") ||
10855 strEqual(suffix, ".anim_delay_action") ||
10856 strEqual(suffix, ".post_delay_action") ||
10857 strEqual(suffix, ".init_event_action") ||
10858 strEqual(suffix, ".anim_event_action"))
10860 result = get_anim_action_parameter_value(value_raw);
10862 else if (strEqual(suffix, ".class"))
10864 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10865 get_hash_from_key(value));
10867 else if (strEqual(suffix, ".style"))
10869 result = STYLE_DEFAULT;
10871 if (string_has_parameter(value, "accurate_borders"))
10872 result |= STYLE_ACCURATE_BORDERS;
10874 if (string_has_parameter(value, "inner_corners"))
10875 result |= STYLE_INNER_CORNERS;
10877 if (string_has_parameter(value, "reverse"))
10878 result |= STYLE_REVERSE;
10880 if (string_has_parameter(value, "leftmost_position"))
10881 result |= STYLE_LEFTMOST_POSITION;
10883 if (string_has_parameter(value, "block_clicks"))
10884 result |= STYLE_BLOCK;
10886 if (string_has_parameter(value, "passthrough_clicks"))
10887 result |= STYLE_PASSTHROUGH;
10889 if (string_has_parameter(value, "multiple_actions"))
10890 result |= STYLE_MULTIPLE_ACTIONS;
10892 else if (strEqual(suffix, ".fade_mode"))
10894 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10895 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10896 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10897 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10898 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10899 FADE_MODE_DEFAULT);
10901 else if (strEqual(suffix, ".auto_delay_unit"))
10903 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10904 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10905 AUTO_DELAY_UNIT_DEFAULT);
10907 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10909 result = gfx.get_font_from_token_function(value);
10911 else // generic parameter of type integer or boolean
10913 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10914 type == TYPE_INTEGER ? get_integer_from_string(value) :
10915 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10916 ARG_UNDEFINED_VALUE);
10924 static int get_token_parameter_value(char *token, char *value_raw)
10928 if (token == NULL || value_raw == NULL)
10929 return ARG_UNDEFINED_VALUE;
10931 suffix = strrchr(token, '.');
10932 if (suffix == NULL)
10935 if (strEqual(suffix, ".element"))
10936 return getElementFromToken(value_raw);
10938 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10939 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10942 void InitMenuDesignSettings_Static(void)
10946 // always start with reliable default values from static default config
10947 for (i = 0; image_config_vars[i].token != NULL; i++)
10949 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10952 *image_config_vars[i].value =
10953 get_token_parameter_value(image_config_vars[i].token, value);
10957 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10961 // the following initializes hierarchical values from static configuration
10963 // special case: initialize "ARG_DEFAULT" values in static default config
10964 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10965 titlescreen_initial_first_default.fade_mode =
10966 title_initial_first_default.fade_mode;
10967 titlescreen_initial_first_default.fade_delay =
10968 title_initial_first_default.fade_delay;
10969 titlescreen_initial_first_default.post_delay =
10970 title_initial_first_default.post_delay;
10971 titlescreen_initial_first_default.auto_delay =
10972 title_initial_first_default.auto_delay;
10973 titlescreen_initial_first_default.auto_delay_unit =
10974 title_initial_first_default.auto_delay_unit;
10975 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10976 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10977 titlescreen_first_default.post_delay = title_first_default.post_delay;
10978 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10979 titlescreen_first_default.auto_delay_unit =
10980 title_first_default.auto_delay_unit;
10981 titlemessage_initial_first_default.fade_mode =
10982 title_initial_first_default.fade_mode;
10983 titlemessage_initial_first_default.fade_delay =
10984 title_initial_first_default.fade_delay;
10985 titlemessage_initial_first_default.post_delay =
10986 title_initial_first_default.post_delay;
10987 titlemessage_initial_first_default.auto_delay =
10988 title_initial_first_default.auto_delay;
10989 titlemessage_initial_first_default.auto_delay_unit =
10990 title_initial_first_default.auto_delay_unit;
10991 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10992 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10993 titlemessage_first_default.post_delay = title_first_default.post_delay;
10994 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10995 titlemessage_first_default.auto_delay_unit =
10996 title_first_default.auto_delay_unit;
10998 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10999 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11000 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11001 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11002 titlescreen_initial_default.auto_delay_unit =
11003 title_initial_default.auto_delay_unit;
11004 titlescreen_default.fade_mode = title_default.fade_mode;
11005 titlescreen_default.fade_delay = title_default.fade_delay;
11006 titlescreen_default.post_delay = title_default.post_delay;
11007 titlescreen_default.auto_delay = title_default.auto_delay;
11008 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11009 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11010 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11011 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11012 titlemessage_initial_default.auto_delay_unit =
11013 title_initial_default.auto_delay_unit;
11014 titlemessage_default.fade_mode = title_default.fade_mode;
11015 titlemessage_default.fade_delay = title_default.fade_delay;
11016 titlemessage_default.post_delay = title_default.post_delay;
11017 titlemessage_default.auto_delay = title_default.auto_delay;
11018 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11020 // special case: initialize "ARG_DEFAULT" values in static default config
11021 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11022 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11024 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11025 titlescreen_first[i] = titlescreen_first_default;
11026 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11027 titlemessage_first[i] = titlemessage_first_default;
11029 titlescreen_initial[i] = titlescreen_initial_default;
11030 titlescreen[i] = titlescreen_default;
11031 titlemessage_initial[i] = titlemessage_initial_default;
11032 titlemessage[i] = titlemessage_default;
11035 // special case: initialize "ARG_DEFAULT" values in static default config
11036 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11037 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11039 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11042 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11043 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11044 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11047 // special case: initialize "ARG_DEFAULT" values in static default config
11048 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11049 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11051 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11052 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11053 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11055 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11058 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11062 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11066 struct XY *dst, *src;
11068 game_buttons_xy[] =
11070 { &game.button.save, &game.button.stop },
11071 { &game.button.pause2, &game.button.pause },
11072 { &game.button.load, &game.button.play },
11073 { &game.button.undo, &game.button.stop },
11074 { &game.button.redo, &game.button.play },
11080 // special case: initialize later added SETUP list size from LEVELS value
11081 if (menu.list_size[GAME_MODE_SETUP] == -1)
11082 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11084 // set default position for snapshot buttons to stop/pause/play buttons
11085 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11086 if ((*game_buttons_xy[i].dst).x == -1 &&
11087 (*game_buttons_xy[i].dst).y == -1)
11088 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11090 // --------------------------------------------------------------------------
11091 // dynamic viewports (including playfield margins, borders and alignments)
11092 // --------------------------------------------------------------------------
11094 // dynamic viewports currently only supported for landscape mode
11095 int display_width = MAX(video.display_width, video.display_height);
11096 int display_height = MIN(video.display_width, video.display_height);
11098 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11100 struct RectWithBorder *vp_window = &viewport.window[i];
11101 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11102 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11103 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11104 boolean dynamic_window_width = (vp_window->min_width != -1);
11105 boolean dynamic_window_height = (vp_window->min_height != -1);
11106 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11107 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11109 // adjust window size if min/max width/height is specified
11111 if (vp_window->min_width != -1)
11113 int window_width = display_width;
11115 // when using static window height, use aspect ratio of display
11116 if (vp_window->min_height == -1)
11117 window_width = vp_window->height * display_width / display_height;
11119 vp_window->width = MAX(vp_window->min_width, window_width);
11122 if (vp_window->min_height != -1)
11124 int window_height = display_height;
11126 // when using static window width, use aspect ratio of display
11127 if (vp_window->min_width == -1)
11128 window_height = vp_window->width * display_height / display_width;
11130 vp_window->height = MAX(vp_window->min_height, window_height);
11133 if (vp_window->max_width != -1)
11134 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11136 if (vp_window->max_height != -1)
11137 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11139 int playfield_width = vp_window->width;
11140 int playfield_height = vp_window->height;
11142 // adjust playfield size and position according to specified margins
11144 playfield_width -= vp_playfield->margin_left;
11145 playfield_width -= vp_playfield->margin_right;
11147 playfield_height -= vp_playfield->margin_top;
11148 playfield_height -= vp_playfield->margin_bottom;
11150 // adjust playfield size if min/max width/height is specified
11152 if (vp_playfield->min_width != -1)
11153 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11155 if (vp_playfield->min_height != -1)
11156 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11158 if (vp_playfield->max_width != -1)
11159 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11161 if (vp_playfield->max_height != -1)
11162 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
11164 // adjust playfield position according to specified alignment
11166 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
11167 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
11168 else if (vp_playfield->align == ALIGN_CENTER)
11169 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
11170 else if (vp_playfield->align == ALIGN_RIGHT)
11171 vp_playfield->x += playfield_width - vp_playfield->width;
11173 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
11174 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
11175 else if (vp_playfield->valign == VALIGN_MIDDLE)
11176 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
11177 else if (vp_playfield->valign == VALIGN_BOTTOM)
11178 vp_playfield->y += playfield_height - vp_playfield->height;
11180 vp_playfield->x += vp_playfield->margin_left;
11181 vp_playfield->y += vp_playfield->margin_top;
11183 // adjust individual playfield borders if only default border is specified
11185 if (vp_playfield->border_left == -1)
11186 vp_playfield->border_left = vp_playfield->border_size;
11187 if (vp_playfield->border_right == -1)
11188 vp_playfield->border_right = vp_playfield->border_size;
11189 if (vp_playfield->border_top == -1)
11190 vp_playfield->border_top = vp_playfield->border_size;
11191 if (vp_playfield->border_bottom == -1)
11192 vp_playfield->border_bottom = vp_playfield->border_size;
11194 // set dynamic playfield borders if borders are specified as undefined
11195 // (but only if window size was dynamic and playfield size was static)
11197 if (dynamic_window_width && !dynamic_playfield_width)
11199 if (vp_playfield->border_left == -1)
11201 vp_playfield->border_left = (vp_playfield->x -
11202 vp_playfield->margin_left);
11203 vp_playfield->x -= vp_playfield->border_left;
11204 vp_playfield->width += vp_playfield->border_left;
11207 if (vp_playfield->border_right == -1)
11209 vp_playfield->border_right = (vp_window->width -
11211 vp_playfield->width -
11212 vp_playfield->margin_right);
11213 vp_playfield->width += vp_playfield->border_right;
11217 if (dynamic_window_height && !dynamic_playfield_height)
11219 if (vp_playfield->border_top == -1)
11221 vp_playfield->border_top = (vp_playfield->y -
11222 vp_playfield->margin_top);
11223 vp_playfield->y -= vp_playfield->border_top;
11224 vp_playfield->height += vp_playfield->border_top;
11227 if (vp_playfield->border_bottom == -1)
11229 vp_playfield->border_bottom = (vp_window->height -
11231 vp_playfield->height -
11232 vp_playfield->margin_bottom);
11233 vp_playfield->height += vp_playfield->border_bottom;
11237 // adjust playfield size to be a multiple of a defined alignment tile size
11239 int align_size = vp_playfield->align_size;
11240 int playfield_xtiles = vp_playfield->width / align_size;
11241 int playfield_ytiles = vp_playfield->height / align_size;
11242 int playfield_width_corrected = playfield_xtiles * align_size;
11243 int playfield_height_corrected = playfield_ytiles * align_size;
11244 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
11245 i == GFX_SPECIAL_ARG_EDITOR);
11247 if (is_playfield_mode &&
11248 dynamic_playfield_width &&
11249 vp_playfield->width != playfield_width_corrected)
11251 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
11253 vp_playfield->width = playfield_width_corrected;
11255 if (vp_playfield->align == ALIGN_LEFT)
11257 vp_playfield->border_left += playfield_xdiff;
11259 else if (vp_playfield->align == ALIGN_RIGHT)
11261 vp_playfield->border_right += playfield_xdiff;
11263 else if (vp_playfield->align == ALIGN_CENTER)
11265 int border_left_diff = playfield_xdiff / 2;
11266 int border_right_diff = playfield_xdiff - border_left_diff;
11268 vp_playfield->border_left += border_left_diff;
11269 vp_playfield->border_right += border_right_diff;
11273 if (is_playfield_mode &&
11274 dynamic_playfield_height &&
11275 vp_playfield->height != playfield_height_corrected)
11277 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
11279 vp_playfield->height = playfield_height_corrected;
11281 if (vp_playfield->valign == VALIGN_TOP)
11283 vp_playfield->border_top += playfield_ydiff;
11285 else if (vp_playfield->align == VALIGN_BOTTOM)
11287 vp_playfield->border_right += playfield_ydiff;
11289 else if (vp_playfield->align == VALIGN_MIDDLE)
11291 int border_top_diff = playfield_ydiff / 2;
11292 int border_bottom_diff = playfield_ydiff - border_top_diff;
11294 vp_playfield->border_top += border_top_diff;
11295 vp_playfield->border_bottom += border_bottom_diff;
11299 // adjust door positions according to specified alignment
11301 for (j = 0; j < 2; j++)
11303 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
11305 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
11306 vp_door->x = ALIGNED_VP_XPOS(vp_door);
11307 else if (vp_door->align == ALIGN_CENTER)
11308 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
11309 else if (vp_door->align == ALIGN_RIGHT)
11310 vp_door->x += vp_window->width - vp_door->width;
11312 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
11313 vp_door->y = ALIGNED_VP_YPOS(vp_door);
11314 else if (vp_door->valign == VALIGN_MIDDLE)
11315 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
11316 else if (vp_door->valign == VALIGN_BOTTOM)
11317 vp_door->y += vp_window->height - vp_door->height;
11322 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
11326 struct XYTileSize *dst, *src;
11329 editor_buttons_xy[] =
11332 &editor.button.element_left, &editor.palette.element_left,
11333 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
11336 &editor.button.element_middle, &editor.palette.element_middle,
11337 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
11340 &editor.button.element_right, &editor.palette.element_right,
11341 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
11348 // set default position for element buttons to element graphics
11349 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11351 if ((*editor_buttons_xy[i].dst).x == -1 &&
11352 (*editor_buttons_xy[i].dst).y == -1)
11354 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11356 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11358 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11362 // adjust editor palette rows and columns if specified to be dynamic
11364 if (editor.palette.cols == -1)
11366 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11367 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11368 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11370 editor.palette.cols = (vp_width - sc_width) / bt_width;
11372 if (editor.palette.x == -1)
11374 int palette_width = editor.palette.cols * bt_width + sc_width;
11376 editor.palette.x = (vp_width - palette_width) / 2;
11380 if (editor.palette.rows == -1)
11382 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11383 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11384 int tx_height = getFontHeight(FONT_TEXT_2);
11386 editor.palette.rows = (vp_height - tx_height) / bt_height;
11388 if (editor.palette.y == -1)
11390 int palette_height = editor.palette.rows * bt_height + tx_height;
11392 editor.palette.y = (vp_height - palette_height) / 2;
11397 static void LoadMenuDesignSettingsFromFilename(char *filename)
11399 static struct TitleFadingInfo tfi;
11400 static struct TitleMessageInfo tmi;
11401 static struct TokenInfo title_tokens[] =
11403 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11404 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11405 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11406 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11407 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11411 static struct TokenInfo titlemessage_tokens[] =
11413 { TYPE_INTEGER, &tmi.x, ".x" },
11414 { TYPE_INTEGER, &tmi.y, ".y" },
11415 { TYPE_INTEGER, &tmi.width, ".width" },
11416 { TYPE_INTEGER, &tmi.height, ".height" },
11417 { TYPE_INTEGER, &tmi.chars, ".chars" },
11418 { TYPE_INTEGER, &tmi.lines, ".lines" },
11419 { TYPE_INTEGER, &tmi.align, ".align" },
11420 { TYPE_INTEGER, &tmi.valign, ".valign" },
11421 { TYPE_INTEGER, &tmi.font, ".font" },
11422 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11423 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11424 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11425 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11426 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11427 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11428 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11429 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11430 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11436 struct TitleFadingInfo *info;
11441 // initialize first titles from "enter screen" definitions, if defined
11442 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11443 { &title_first_default, "menu.enter_screen.TITLE" },
11445 // initialize title screens from "next screen" definitions, if defined
11446 { &title_initial_default, "menu.next_screen.TITLE" },
11447 { &title_default, "menu.next_screen.TITLE" },
11453 struct TitleMessageInfo *array;
11456 titlemessage_arrays[] =
11458 // initialize first titles from "enter screen" definitions, if defined
11459 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11460 { titlescreen_first, "menu.enter_screen.TITLE" },
11461 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11462 { titlemessage_first, "menu.enter_screen.TITLE" },
11464 // initialize titles from "next screen" definitions, if defined
11465 { titlescreen_initial, "menu.next_screen.TITLE" },
11466 { titlescreen, "menu.next_screen.TITLE" },
11467 { titlemessage_initial, "menu.next_screen.TITLE" },
11468 { titlemessage, "menu.next_screen.TITLE" },
11470 // overwrite titles with title definitions, if defined
11471 { titlescreen_initial_first, "[title_initial]" },
11472 { titlescreen_first, "[title]" },
11473 { titlemessage_initial_first, "[title_initial]" },
11474 { titlemessage_first, "[title]" },
11476 { titlescreen_initial, "[title_initial]" },
11477 { titlescreen, "[title]" },
11478 { titlemessage_initial, "[title_initial]" },
11479 { titlemessage, "[title]" },
11481 // overwrite titles with title screen/message definitions, if defined
11482 { titlescreen_initial_first, "[titlescreen_initial]" },
11483 { titlescreen_first, "[titlescreen]" },
11484 { titlemessage_initial_first, "[titlemessage_initial]" },
11485 { titlemessage_first, "[titlemessage]" },
11487 { titlescreen_initial, "[titlescreen_initial]" },
11488 { titlescreen, "[titlescreen]" },
11489 { titlemessage_initial, "[titlemessage_initial]" },
11490 { titlemessage, "[titlemessage]" },
11494 SetupFileHash *setup_file_hash;
11497 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11500 // the following initializes hierarchical values from dynamic configuration
11502 // special case: initialize with default values that may be overwritten
11503 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11504 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11506 struct TokenIntPtrInfo menu_config[] =
11508 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11509 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11510 { "menu.list_size", &menu.list_size[i] }
11513 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11515 char *token = menu_config[j].token;
11516 char *value = getHashEntry(setup_file_hash, token);
11519 *menu_config[j].value = get_integer_from_string(value);
11523 // special case: initialize with default values that may be overwritten
11524 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11525 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11527 struct TokenIntPtrInfo menu_config[] =
11529 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11530 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11531 { "menu.list_size.INFO", &menu.list_size_info[i] }
11534 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11536 char *token = menu_config[j].token;
11537 char *value = getHashEntry(setup_file_hash, token);
11540 *menu_config[j].value = get_integer_from_string(value);
11544 // special case: initialize with default values that may be overwritten
11545 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11546 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11548 struct TokenIntPtrInfo menu_config[] =
11550 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11551 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11554 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11556 char *token = menu_config[j].token;
11557 char *value = getHashEntry(setup_file_hash, token);
11560 *menu_config[j].value = get_integer_from_string(value);
11564 // special case: initialize with default values that may be overwritten
11565 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11566 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11568 struct TokenIntPtrInfo menu_config[] =
11570 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11571 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11572 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11573 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11574 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11575 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11576 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11577 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11578 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11581 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11583 char *token = menu_config[j].token;
11584 char *value = getHashEntry(setup_file_hash, token);
11587 *menu_config[j].value = get_integer_from_string(value);
11591 // special case: initialize with default values that may be overwritten
11592 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11593 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11595 struct TokenIntPtrInfo menu_config[] =
11597 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11598 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11599 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11600 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11601 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11602 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11603 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11604 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11605 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11608 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11610 char *token = menu_config[j].token;
11611 char *value = getHashEntry(setup_file_hash, token);
11614 *menu_config[j].value = get_token_parameter_value(token, value);
11618 // special case: initialize with default values that may be overwritten
11619 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11620 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11624 char *token_prefix;
11625 struct RectWithBorder *struct_ptr;
11629 { "viewport.window", &viewport.window[i] },
11630 { "viewport.playfield", &viewport.playfield[i] },
11631 { "viewport.door_1", &viewport.door_1[i] },
11632 { "viewport.door_2", &viewport.door_2[i] }
11635 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11637 struct TokenIntPtrInfo vp_config[] =
11639 { ".x", &vp_struct[j].struct_ptr->x },
11640 { ".y", &vp_struct[j].struct_ptr->y },
11641 { ".width", &vp_struct[j].struct_ptr->width },
11642 { ".height", &vp_struct[j].struct_ptr->height },
11643 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11644 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11645 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11646 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11647 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11648 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11649 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11650 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11651 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11652 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11653 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11654 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11655 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11656 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11657 { ".align", &vp_struct[j].struct_ptr->align },
11658 { ".valign", &vp_struct[j].struct_ptr->valign }
11661 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11663 char *token = getStringCat2(vp_struct[j].token_prefix,
11664 vp_config[k].token);
11665 char *value = getHashEntry(setup_file_hash, token);
11668 *vp_config[k].value = get_token_parameter_value(token, value);
11675 // special case: initialize with default values that may be overwritten
11676 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11677 for (i = 0; title_info[i].info != NULL; i++)
11679 struct TitleFadingInfo *info = title_info[i].info;
11680 char *base_token = title_info[i].text;
11682 for (j = 0; title_tokens[j].type != -1; j++)
11684 char *token = getStringCat2(base_token, title_tokens[j].text);
11685 char *value = getHashEntry(setup_file_hash, token);
11689 int parameter_value = get_token_parameter_value(token, value);
11693 *(int *)title_tokens[j].value = (int)parameter_value;
11702 // special case: initialize with default values that may be overwritten
11703 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11704 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11706 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11707 char *base_token = titlemessage_arrays[i].text;
11709 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11711 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11712 char *value = getHashEntry(setup_file_hash, token);
11716 int parameter_value = get_token_parameter_value(token, value);
11718 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11722 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11723 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11725 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11735 // special case: check if network and preview player positions are redefined,
11736 // to compare this later against the main menu level preview being redefined
11737 struct TokenIntPtrInfo menu_config_players[] =
11739 { "main.network_players.x", &menu.main.network_players.redefined },
11740 { "main.network_players.y", &menu.main.network_players.redefined },
11741 { "main.preview_players.x", &menu.main.preview_players.redefined },
11742 { "main.preview_players.y", &menu.main.preview_players.redefined },
11743 { "preview.x", &preview.redefined },
11744 { "preview.y", &preview.redefined }
11747 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11748 *menu_config_players[i].value = FALSE;
11750 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11751 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11752 *menu_config_players[i].value = TRUE;
11754 // read (and overwrite with) values that may be specified in config file
11755 for (i = 0; image_config_vars[i].token != NULL; i++)
11757 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11759 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11760 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11761 *image_config_vars[i].value =
11762 get_token_parameter_value(image_config_vars[i].token, value);
11765 freeSetupFileHash(setup_file_hash);
11768 void LoadMenuDesignSettings(void)
11770 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11772 InitMenuDesignSettings_Static();
11773 InitMenuDesignSettings_SpecialPreProcessing();
11775 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11777 // first look for special settings configured in level series config
11778 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11780 if (fileExists(filename_base))
11781 LoadMenuDesignSettingsFromFilename(filename_base);
11784 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11786 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11787 LoadMenuDesignSettingsFromFilename(filename_local);
11789 InitMenuDesignSettings_SpecialPostProcessing();
11792 void LoadMenuDesignSettings_AfterGraphics(void)
11794 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11797 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11799 char *filename = getEditorSetupFilename();
11800 SetupFileList *setup_file_list, *list;
11801 SetupFileHash *element_hash;
11802 int num_unknown_tokens = 0;
11805 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11808 element_hash = newSetupFileHash();
11810 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11811 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11813 // determined size may be larger than needed (due to unknown elements)
11815 for (list = setup_file_list; list != NULL; list = list->next)
11818 // add space for up to 3 more elements for padding that may be needed
11819 *num_elements += 3;
11821 // free memory for old list of elements, if needed
11822 checked_free(*elements);
11824 // allocate memory for new list of elements
11825 *elements = checked_malloc(*num_elements * sizeof(int));
11828 for (list = setup_file_list; list != NULL; list = list->next)
11830 char *value = getHashEntry(element_hash, list->token);
11832 if (value == NULL) // try to find obsolete token mapping
11834 char *mapped_token = get_mapped_token(list->token);
11836 if (mapped_token != NULL)
11838 value = getHashEntry(element_hash, mapped_token);
11840 free(mapped_token);
11846 (*elements)[(*num_elements)++] = atoi(value);
11850 if (num_unknown_tokens == 0)
11853 Warn("unknown token(s) found in config file:");
11854 Warn("- config file: '%s'", filename);
11856 num_unknown_tokens++;
11859 Warn("- token: '%s'", list->token);
11863 if (num_unknown_tokens > 0)
11866 while (*num_elements % 4) // pad with empty elements, if needed
11867 (*elements)[(*num_elements)++] = EL_EMPTY;
11869 freeSetupFileList(setup_file_list);
11870 freeSetupFileHash(element_hash);
11873 for (i = 0; i < *num_elements; i++)
11874 Debug("editor", "element '%s' [%d]\n",
11875 element_info[(*elements)[i]].token_name, (*elements)[i]);
11879 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11882 SetupFileHash *setup_file_hash = NULL;
11883 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11884 char *filename_music, *filename_prefix, *filename_info;
11890 token_to_value_ptr[] =
11892 { "title_header", &tmp_music_file_info.title_header },
11893 { "artist_header", &tmp_music_file_info.artist_header },
11894 { "album_header", &tmp_music_file_info.album_header },
11895 { "year_header", &tmp_music_file_info.year_header },
11897 { "title", &tmp_music_file_info.title },
11898 { "artist", &tmp_music_file_info.artist },
11899 { "album", &tmp_music_file_info.album },
11900 { "year", &tmp_music_file_info.year },
11906 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11907 getCustomMusicFilename(basename));
11909 if (filename_music == NULL)
11912 // ---------- try to replace file extension ----------
11914 filename_prefix = getStringCopy(filename_music);
11915 if (strrchr(filename_prefix, '.') != NULL)
11916 *strrchr(filename_prefix, '.') = '\0';
11917 filename_info = getStringCat2(filename_prefix, ".txt");
11919 if (fileExists(filename_info))
11920 setup_file_hash = loadSetupFileHash(filename_info);
11922 free(filename_prefix);
11923 free(filename_info);
11925 if (setup_file_hash == NULL)
11927 // ---------- try to add file extension ----------
11929 filename_prefix = getStringCopy(filename_music);
11930 filename_info = getStringCat2(filename_prefix, ".txt");
11932 if (fileExists(filename_info))
11933 setup_file_hash = loadSetupFileHash(filename_info);
11935 free(filename_prefix);
11936 free(filename_info);
11939 if (setup_file_hash == NULL)
11942 // ---------- music file info found ----------
11944 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11946 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11948 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11950 *token_to_value_ptr[i].value_ptr =
11951 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11954 tmp_music_file_info.basename = getStringCopy(basename);
11955 tmp_music_file_info.music = music;
11956 tmp_music_file_info.is_sound = is_sound;
11958 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11959 *new_music_file_info = tmp_music_file_info;
11961 return new_music_file_info;
11964 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11966 return get_music_file_info_ext(basename, music, FALSE);
11969 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11971 return get_music_file_info_ext(basename, sound, TRUE);
11974 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11975 char *basename, boolean is_sound)
11977 for (; list != NULL; list = list->next)
11978 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11984 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11986 return music_info_listed_ext(list, basename, FALSE);
11989 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11991 return music_info_listed_ext(list, basename, TRUE);
11994 void LoadMusicInfo(void)
11996 char *music_directory = getCustomMusicDirectory();
11997 int num_music = getMusicListSize();
11998 int num_music_noconf = 0;
11999 int num_sounds = getSoundListSize();
12001 DirectoryEntry *dir_entry;
12002 struct FileInfo *music, *sound;
12003 struct MusicFileInfo *next, **new;
12006 while (music_file_info != NULL)
12008 next = music_file_info->next;
12010 checked_free(music_file_info->basename);
12012 checked_free(music_file_info->title_header);
12013 checked_free(music_file_info->artist_header);
12014 checked_free(music_file_info->album_header);
12015 checked_free(music_file_info->year_header);
12017 checked_free(music_file_info->title);
12018 checked_free(music_file_info->artist);
12019 checked_free(music_file_info->album);
12020 checked_free(music_file_info->year);
12022 free(music_file_info);
12024 music_file_info = next;
12027 new = &music_file_info;
12029 for (i = 0; i < num_music; i++)
12031 music = getMusicListEntry(i);
12033 if (music->filename == NULL)
12036 if (strEqual(music->filename, UNDEFINED_FILENAME))
12039 // a configured file may be not recognized as music
12040 if (!FileIsMusic(music->filename))
12043 if (!music_info_listed(music_file_info, music->filename))
12045 *new = get_music_file_info(music->filename, i);
12048 new = &(*new)->next;
12052 if ((dir = openDirectory(music_directory)) == NULL)
12054 Warn("cannot read music directory '%s'", music_directory);
12059 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12061 char *basename = dir_entry->basename;
12062 boolean music_already_used = FALSE;
12065 // skip all music files that are configured in music config file
12066 for (i = 0; i < num_music; i++)
12068 music = getMusicListEntry(i);
12070 if (music->filename == NULL)
12073 if (strEqual(basename, music->filename))
12075 music_already_used = TRUE;
12080 if (music_already_used)
12083 if (!FileIsMusic(dir_entry->filename))
12086 if (!music_info_listed(music_file_info, basename))
12088 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12091 new = &(*new)->next;
12094 num_music_noconf++;
12097 closeDirectory(dir);
12099 for (i = 0; i < num_sounds; i++)
12101 sound = getSoundListEntry(i);
12103 if (sound->filename == NULL)
12106 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12109 // a configured file may be not recognized as sound
12110 if (!FileIsSound(sound->filename))
12113 if (!sound_info_listed(music_file_info, sound->filename))
12115 *new = get_sound_file_info(sound->filename, i);
12117 new = &(*new)->next;
12122 static void add_helpanim_entry(int element, int action, int direction,
12123 int delay, int *num_list_entries)
12125 struct HelpAnimInfo *new_list_entry;
12126 (*num_list_entries)++;
12129 checked_realloc(helpanim_info,
12130 *num_list_entries * sizeof(struct HelpAnimInfo));
12131 new_list_entry = &helpanim_info[*num_list_entries - 1];
12133 new_list_entry->element = element;
12134 new_list_entry->action = action;
12135 new_list_entry->direction = direction;
12136 new_list_entry->delay = delay;
12139 static void print_unknown_token(char *filename, char *token, int token_nr)
12144 Warn("unknown token(s) found in config file:");
12145 Warn("- config file: '%s'", filename);
12148 Warn("- token: '%s'", token);
12151 static void print_unknown_token_end(int token_nr)
12157 void LoadHelpAnimInfo(void)
12159 char *filename = getHelpAnimFilename();
12160 SetupFileList *setup_file_list = NULL, *list;
12161 SetupFileHash *element_hash, *action_hash, *direction_hash;
12162 int num_list_entries = 0;
12163 int num_unknown_tokens = 0;
12166 if (fileExists(filename))
12167 setup_file_list = loadSetupFileList(filename);
12169 if (setup_file_list == NULL)
12171 // use reliable default values from static configuration
12172 SetupFileList *insert_ptr;
12174 insert_ptr = setup_file_list =
12175 newSetupFileList(helpanim_config[0].token,
12176 helpanim_config[0].value);
12178 for (i = 1; helpanim_config[i].token; i++)
12179 insert_ptr = addListEntry(insert_ptr,
12180 helpanim_config[i].token,
12181 helpanim_config[i].value);
12184 element_hash = newSetupFileHash();
12185 action_hash = newSetupFileHash();
12186 direction_hash = newSetupFileHash();
12188 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
12189 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12191 for (i = 0; i < NUM_ACTIONS; i++)
12192 setHashEntry(action_hash, element_action_info[i].suffix,
12193 i_to_a(element_action_info[i].value));
12195 // do not store direction index (bit) here, but direction value!
12196 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
12197 setHashEntry(direction_hash, element_direction_info[i].suffix,
12198 i_to_a(1 << element_direction_info[i].value));
12200 for (list = setup_file_list; list != NULL; list = list->next)
12202 char *element_token, *action_token, *direction_token;
12203 char *element_value, *action_value, *direction_value;
12204 int delay = atoi(list->value);
12206 if (strEqual(list->token, "end"))
12208 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12213 /* first try to break element into element/action/direction parts;
12214 if this does not work, also accept combined "element[.act][.dir]"
12215 elements (like "dynamite.active"), which are unique elements */
12217 if (strchr(list->token, '.') == NULL) // token contains no '.'
12219 element_value = getHashEntry(element_hash, list->token);
12220 if (element_value != NULL) // element found
12221 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12222 &num_list_entries);
12225 // no further suffixes found -- this is not an element
12226 print_unknown_token(filename, list->token, num_unknown_tokens++);
12232 // token has format "<prefix>.<something>"
12234 action_token = strchr(list->token, '.'); // suffix may be action ...
12235 direction_token = action_token; // ... or direction
12237 element_token = getStringCopy(list->token);
12238 *strchr(element_token, '.') = '\0';
12240 element_value = getHashEntry(element_hash, element_token);
12242 if (element_value == NULL) // this is no element
12244 element_value = getHashEntry(element_hash, list->token);
12245 if (element_value != NULL) // combined element found
12246 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12247 &num_list_entries);
12249 print_unknown_token(filename, list->token, num_unknown_tokens++);
12251 free(element_token);
12256 action_value = getHashEntry(action_hash, action_token);
12258 if (action_value != NULL) // action found
12260 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
12261 &num_list_entries);
12263 free(element_token);
12268 direction_value = getHashEntry(direction_hash, direction_token);
12270 if (direction_value != NULL) // direction found
12272 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
12273 &num_list_entries);
12275 free(element_token);
12280 if (strchr(action_token + 1, '.') == NULL)
12282 // no further suffixes found -- this is not an action nor direction
12284 element_value = getHashEntry(element_hash, list->token);
12285 if (element_value != NULL) // combined element found
12286 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12287 &num_list_entries);
12289 print_unknown_token(filename, list->token, num_unknown_tokens++);
12291 free(element_token);
12296 // token has format "<prefix>.<suffix>.<something>"
12298 direction_token = strchr(action_token + 1, '.');
12300 action_token = getStringCopy(action_token);
12301 *strchr(action_token + 1, '.') = '\0';
12303 action_value = getHashEntry(action_hash, action_token);
12305 if (action_value == NULL) // this is no action
12307 element_value = getHashEntry(element_hash, list->token);
12308 if (element_value != NULL) // combined element found
12309 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12310 &num_list_entries);
12312 print_unknown_token(filename, list->token, num_unknown_tokens++);
12314 free(element_token);
12315 free(action_token);
12320 direction_value = getHashEntry(direction_hash, direction_token);
12322 if (direction_value != NULL) // direction found
12324 add_helpanim_entry(atoi(element_value), atoi(action_value),
12325 atoi(direction_value), delay, &num_list_entries);
12327 free(element_token);
12328 free(action_token);
12333 // this is no direction
12335 element_value = getHashEntry(element_hash, list->token);
12336 if (element_value != NULL) // combined element found
12337 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12338 &num_list_entries);
12340 print_unknown_token(filename, list->token, num_unknown_tokens++);
12342 free(element_token);
12343 free(action_token);
12346 print_unknown_token_end(num_unknown_tokens);
12348 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12349 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
12351 freeSetupFileList(setup_file_list);
12352 freeSetupFileHash(element_hash);
12353 freeSetupFileHash(action_hash);
12354 freeSetupFileHash(direction_hash);
12357 for (i = 0; i < num_list_entries; i++)
12358 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12359 EL_NAME(helpanim_info[i].element),
12360 helpanim_info[i].element,
12361 helpanim_info[i].action,
12362 helpanim_info[i].direction,
12363 helpanim_info[i].delay);
12367 void LoadHelpTextInfo(void)
12369 char *filename = getHelpTextFilename();
12372 if (helptext_info != NULL)
12374 freeSetupFileHash(helptext_info);
12375 helptext_info = NULL;
12378 if (fileExists(filename))
12379 helptext_info = loadSetupFileHash(filename);
12381 if (helptext_info == NULL)
12383 // use reliable default values from static configuration
12384 helptext_info = newSetupFileHash();
12386 for (i = 0; helptext_config[i].token; i++)
12387 setHashEntry(helptext_info,
12388 helptext_config[i].token,
12389 helptext_config[i].value);
12393 BEGIN_HASH_ITERATION(helptext_info, itr)
12395 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12396 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12398 END_HASH_ITERATION(hash, itr)
12403 // ----------------------------------------------------------------------------
12405 // ----------------------------------------------------------------------------
12407 #define MAX_NUM_CONVERT_LEVELS 1000
12409 void ConvertLevels(void)
12411 static LevelDirTree *convert_leveldir = NULL;
12412 static int convert_level_nr = -1;
12413 static int num_levels_handled = 0;
12414 static int num_levels_converted = 0;
12415 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12418 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12419 global.convert_leveldir);
12421 if (convert_leveldir == NULL)
12422 Fail("no such level identifier: '%s'", global.convert_leveldir);
12424 leveldir_current = convert_leveldir;
12426 if (global.convert_level_nr != -1)
12428 convert_leveldir->first_level = global.convert_level_nr;
12429 convert_leveldir->last_level = global.convert_level_nr;
12432 convert_level_nr = convert_leveldir->first_level;
12434 PrintLine("=", 79);
12435 Print("Converting levels\n");
12436 PrintLine("-", 79);
12437 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12438 Print("Level series name: '%s'\n", convert_leveldir->name);
12439 Print("Level series author: '%s'\n", convert_leveldir->author);
12440 Print("Number of levels: %d\n", convert_leveldir->levels);
12441 PrintLine("=", 79);
12444 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12445 levels_failed[i] = FALSE;
12447 while (convert_level_nr <= convert_leveldir->last_level)
12449 char *level_filename;
12452 level_nr = convert_level_nr++;
12454 Print("Level %03d: ", level_nr);
12456 LoadLevel(level_nr);
12457 if (level.no_level_file || level.no_valid_file)
12459 Print("(no level)\n");
12463 Print("converting level ... ");
12465 level_filename = getDefaultLevelFilename(level_nr);
12466 new_level = !fileExists(level_filename);
12470 SaveLevel(level_nr);
12472 num_levels_converted++;
12474 Print("converted.\n");
12478 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12479 levels_failed[level_nr] = TRUE;
12481 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12484 num_levels_handled++;
12488 PrintLine("=", 79);
12489 Print("Number of levels handled: %d\n", num_levels_handled);
12490 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12491 (num_levels_handled ?
12492 num_levels_converted * 100 / num_levels_handled : 0));
12493 PrintLine("-", 79);
12494 Print("Summary (for automatic parsing by scripts):\n");
12495 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12496 convert_leveldir->identifier, num_levels_converted,
12497 num_levels_handled,
12498 (num_levels_handled ?
12499 num_levels_converted * 100 / num_levels_handled : 0));
12501 if (num_levels_handled != num_levels_converted)
12503 Print(", FAILED:");
12504 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12505 if (levels_failed[i])
12510 PrintLine("=", 79);
12512 CloseAllAndExit(0);
12516 // ----------------------------------------------------------------------------
12517 // create and save images for use in level sketches (raw BMP format)
12518 // ----------------------------------------------------------------------------
12520 void CreateLevelSketchImages(void)
12526 InitElementPropertiesGfxElement();
12528 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12529 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12531 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12533 int element = getMappedElement(i);
12534 char basename1[16];
12535 char basename2[16];
12539 sprintf(basename1, "%04d.bmp", i);
12540 sprintf(basename2, "%04ds.bmp", i);
12542 filename1 = getPath2(global.create_images_dir, basename1);
12543 filename2 = getPath2(global.create_images_dir, basename2);
12545 DrawSizedElement(0, 0, element, TILESIZE);
12546 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12548 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12549 Fail("cannot save level sketch image file '%s'", filename1);
12551 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12552 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12554 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12555 Fail("cannot save level sketch image file '%s'", filename2);
12560 // create corresponding SQL statements (for normal and small images)
12563 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12564 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12567 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12568 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12570 // optional: create content for forum level sketch demonstration post
12572 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12575 FreeBitmap(bitmap1);
12576 FreeBitmap(bitmap2);
12579 fprintf(stderr, "\n");
12581 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12583 CloseAllAndExit(0);
12587 // ----------------------------------------------------------------------------
12588 // create and save images for custom and group elements (raw BMP format)
12589 // ----------------------------------------------------------------------------
12591 void CreateCustomElementImages(char *directory)
12593 char *src_basename = "RocksCE-template.ilbm";
12594 char *dst_basename = "RocksCE.bmp";
12595 char *src_filename = getPath2(directory, src_basename);
12596 char *dst_filename = getPath2(directory, dst_basename);
12597 Bitmap *src_bitmap;
12599 int yoffset_ce = 0;
12600 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12603 InitVideoDefaults();
12605 ReCreateBitmap(&backbuffer, video.width, video.height);
12607 src_bitmap = LoadImage(src_filename);
12609 bitmap = CreateBitmap(TILEX * 16 * 2,
12610 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12613 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12620 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12621 TILEX * x, TILEY * y + yoffset_ce);
12623 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12625 TILEX * x + TILEX * 16,
12626 TILEY * y + yoffset_ce);
12628 for (j = 2; j >= 0; j--)
12632 BlitBitmap(src_bitmap, bitmap,
12633 TILEX + c * 7, 0, 6, 10,
12634 TILEX * x + 6 + j * 7,
12635 TILEY * y + 11 + yoffset_ce);
12637 BlitBitmap(src_bitmap, bitmap,
12638 TILEX + c * 8, TILEY, 6, 10,
12639 TILEX * 16 + TILEX * x + 6 + j * 8,
12640 TILEY * y + 10 + yoffset_ce);
12646 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12653 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12654 TILEX * x, TILEY * y + yoffset_ge);
12656 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12658 TILEX * x + TILEX * 16,
12659 TILEY * y + yoffset_ge);
12661 for (j = 1; j >= 0; j--)
12665 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12666 TILEX * x + 6 + j * 10,
12667 TILEY * y + 11 + yoffset_ge);
12669 BlitBitmap(src_bitmap, bitmap,
12670 TILEX + c * 8, TILEY + 12, 6, 10,
12671 TILEX * 16 + TILEX * x + 10 + j * 8,
12672 TILEY * y + 10 + yoffset_ge);
12678 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12679 Fail("cannot save CE graphics file '%s'", dst_filename);
12681 FreeBitmap(bitmap);
12683 CloseAllAndExit(0);