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"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
279 &li.bd_intermission, FALSE
284 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
285 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
291 &li.bd_pal_timing, FALSE
296 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
297 &li.bd_cycle_delay_ms, 200
302 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
303 &li.bd_cycle_delay_c64, 0
308 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
309 &li.bd_hatching_delay_cycles, 21
314 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
315 &li.bd_hatching_delay_seconds, 2
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
321 &li.bd_line_shifting_borders, FALSE
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
327 &li.bd_wraparound_objects, FALSE
332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
333 &li.bd_scan_first_and_last_row, TRUE
338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
339 &li.bd_short_explosions, TRUE
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(24),
345 &li.bd_gravity_affects_all, TRUE
355 static struct LevelFileConfigInfo chunk_config_ELEM[] =
357 // (these values are the same for each player)
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
361 &li.block_last_field, FALSE // default case for EM levels
365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
366 &li.sp_block_last_field, TRUE // default case for SP levels
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
371 &li.instant_relocation, FALSE
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
376 &li.can_pass_to_walkable, FALSE
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
381 &li.block_snap_field, TRUE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
386 &li.continuous_snapping, TRUE
390 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
391 &li.shifted_relocation, FALSE
395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
396 &li.lazy_relocation, FALSE
400 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
401 &li.finish_dig_collect, TRUE
405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
406 &li.keep_walkable_ce, FALSE
409 // (these values are different for each player)
412 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
413 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
418 &li.initial_player_gravity[0], FALSE
422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
423 &li.use_start_element[0], FALSE
427 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
428 &li.start_element[0], EL_PLAYER_1
432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
433 &li.use_artwork_element[0], FALSE
437 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
438 &li.artwork_element[0], EL_PLAYER_1
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
443 &li.use_explosion_element[0], FALSE
447 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
448 &li.explosion_element[0], EL_PLAYER_1
452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
453 &li.use_initial_inventory[0], FALSE
457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
458 &li.initial_inventory_size[0], 1
462 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
463 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
464 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
469 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
470 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
475 &li.initial_player_gravity[1], FALSE
479 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
480 &li.use_start_element[1], FALSE
484 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
485 &li.start_element[1], EL_PLAYER_2
489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
490 &li.use_artwork_element[1], FALSE
494 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
495 &li.artwork_element[1], EL_PLAYER_2
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
500 &li.use_explosion_element[1], FALSE
504 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
505 &li.explosion_element[1], EL_PLAYER_2
509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
510 &li.use_initial_inventory[1], FALSE
514 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
515 &li.initial_inventory_size[1], 1
519 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
520 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
521 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
526 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
527 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
532 &li.initial_player_gravity[2], FALSE
536 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
537 &li.use_start_element[2], FALSE
541 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
542 &li.start_element[2], EL_PLAYER_3
546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
547 &li.use_artwork_element[2], FALSE
551 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
552 &li.artwork_element[2], EL_PLAYER_3
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
557 &li.use_explosion_element[2], FALSE
561 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
562 &li.explosion_element[2], EL_PLAYER_3
566 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
567 &li.use_initial_inventory[2], FALSE
571 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
572 &li.initial_inventory_size[2], 1
576 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
577 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
578 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
583 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
584 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
588 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
589 &li.initial_player_gravity[3], FALSE
593 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
594 &li.use_start_element[3], FALSE
598 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
599 &li.start_element[3], EL_PLAYER_4
603 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
604 &li.use_artwork_element[3], FALSE
608 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
609 &li.artwork_element[3], EL_PLAYER_4
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
614 &li.use_explosion_element[3], FALSE
618 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
619 &li.explosion_element[3], EL_PLAYER_4
623 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
624 &li.use_initial_inventory[3], FALSE
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
629 &li.initial_inventory_size[3], 1
633 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
634 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
635 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
638 // (these values are only valid for BD style levels)
641 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
642 &li.bd_diagonal_movements, FALSE
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 &li.score[SC_DIAMOND_EXTRA], 20
651 // (the following values are related to various game elements)
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_EMERALD], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_DIAMOND], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_BUG], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_SPACESHIP], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_PACMAN], 10
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.score[SC_NUT], 10
691 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
692 &li.score[SC_DYNAMITE], 10
697 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
698 &li.score[SC_KEY], 10
703 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
704 &li.score[SC_PEARL], 10
709 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
710 &li.score[SC_CRYSTAL], 10
715 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
716 &li.amoeba_content, EL_DIAMOND
720 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
725 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
726 &li.grow_into_diggable, TRUE
731 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
732 &li.yamyam_content, EL_ROCK, NULL,
733 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
737 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
738 &li.score[SC_YAMYAM], 10
743 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
744 &li.score[SC_ROBOT], 10
748 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
754 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
761 &li.time_magic_wall, 10
766 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
767 &li.game_of_life[0], 2
771 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
772 &li.game_of_life[1], 3
776 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
777 &li.game_of_life[2], 3
781 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
782 &li.game_of_life[3], 3
786 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
787 &li.use_life_bugs, FALSE
792 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
797 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
802 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
807 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
812 EL_TIMEGATE_SWITCH, -1,
813 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
814 &li.time_timegate, 10
818 EL_LIGHT_SWITCH_ACTIVE, -1,
819 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
824 EL_SHIELD_NORMAL, -1,
825 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
826 &li.shield_normal_time, 10
829 EL_SHIELD_NORMAL, -1,
830 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
831 &li.score[SC_SHIELD], 10
835 EL_SHIELD_DEADLY, -1,
836 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 &li.shield_deadly_time, 10
840 EL_SHIELD_DEADLY, -1,
841 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
842 &li.score[SC_SHIELD], 10
847 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
853 &li.extra_time_score, 10
857 EL_TIME_ORB_FULL, -1,
858 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
859 &li.time_orb_time, 10
862 EL_TIME_ORB_FULL, -1,
863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
864 &li.use_time_orb_bug, FALSE
869 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
870 &li.use_spring_bug, FALSE
875 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
876 &li.android_move_time, 10
880 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
881 &li.android_clone_time, 10
884 EL_EMC_ANDROID, SAVE_CONF_NEVER,
885 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
886 &li.android_clone_element[0], EL_EMPTY, NULL,
887 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
891 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
892 &li.android_clone_element[0], EL_EMPTY, NULL,
893 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
908 EL_EMC_MAGNIFIER, -1,
909 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 &li.magnify_score, 10
913 EL_EMC_MAGNIFIER, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
919 EL_EMC_MAGIC_BALL, -1,
920 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
924 EL_EMC_MAGIC_BALL, -1,
925 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
926 &li.ball_random, FALSE
929 EL_EMC_MAGIC_BALL, -1,
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
931 &li.ball_active_initial, FALSE
934 EL_EMC_MAGIC_BALL, -1,
935 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
936 &li.ball_content, EL_EMPTY, NULL,
937 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
941 EL_SOKOBAN_FIELD_EMPTY, -1,
942 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
943 &li.sb_fields_needed, TRUE
947 EL_SOKOBAN_OBJECT, -1,
948 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
949 &li.sb_objects_needed, TRUE
954 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
955 &li.mm_laser_red, FALSE
959 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
960 &li.mm_laser_green, FALSE
964 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
965 &li.mm_laser_blue, TRUE
970 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
971 &li.df_laser_red, TRUE
975 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
976 &li.df_laser_green, TRUE
980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
981 &li.df_laser_blue, FALSE
985 EL_MM_FUSE_ACTIVE, -1,
986 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
991 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
997 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1001 EL_MM_GRAY_BALL, -1,
1002 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1003 &li.mm_ball_choice_mode, ANIM_RANDOM
1006 EL_MM_GRAY_BALL, -1,
1007 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1008 &li.mm_ball_content, EL_EMPTY, NULL,
1009 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1012 EL_MM_GRAY_BALL, -1,
1013 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1014 &li.rotate_mm_ball_content, TRUE
1017 EL_MM_GRAY_BALL, -1,
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1019 &li.explode_mm_ball, FALSE
1023 EL_MM_STEEL_BLOCK, -1,
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1025 &li.mm_time_block, 75
1028 EL_MM_LIGHTBALL, -1,
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1030 &li.score[SC_ELEM_BONUS], 10
1040 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1044 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1045 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1049 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1050 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1055 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1056 &xx_envelope.autowrap, FALSE
1060 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1061 &xx_envelope.centered, FALSE
1066 TYPE_STRING, CONF_VALUE_BYTES(1),
1067 &xx_envelope.text, -1, NULL,
1068 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1069 &xx_default_string_empty[0]
1079 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1083 TYPE_STRING, CONF_VALUE_BYTES(1),
1084 &xx_ei.description[0], -1,
1085 &yy_ei.description[0],
1086 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1087 &xx_default_description[0]
1092 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1093 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1094 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1096 #if ENABLE_RESERVED_CODE
1097 // (reserved for later use)
1100 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1101 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1102 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1108 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1109 &xx_ei.use_gfx_element, FALSE,
1110 &yy_ei.use_gfx_element
1114 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1115 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1116 &yy_ei.gfx_element_initial
1121 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1122 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1123 &yy_ei.access_direction
1128 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1129 &xx_ei.collect_score_initial, 10,
1130 &yy_ei.collect_score_initial
1134 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1135 &xx_ei.collect_count_initial, 1,
1136 &yy_ei.collect_count_initial
1141 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1142 &xx_ei.ce_value_fixed_initial, 0,
1143 &yy_ei.ce_value_fixed_initial
1147 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1148 &xx_ei.ce_value_random_initial, 0,
1149 &yy_ei.ce_value_random_initial
1153 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1154 &xx_ei.use_last_ce_value, FALSE,
1155 &yy_ei.use_last_ce_value
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1161 &xx_ei.push_delay_fixed, 8,
1162 &yy_ei.push_delay_fixed
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1167 &xx_ei.push_delay_random, 8,
1168 &yy_ei.push_delay_random
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1173 &xx_ei.drop_delay_fixed, 0,
1174 &yy_ei.drop_delay_fixed
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1179 &xx_ei.drop_delay_random, 0,
1180 &yy_ei.drop_delay_random
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1185 &xx_ei.move_delay_fixed, 0,
1186 &yy_ei.move_delay_fixed
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1191 &xx_ei.move_delay_random, 0,
1192 &yy_ei.move_delay_random
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1197 &xx_ei.step_delay_fixed, 0,
1198 &yy_ei.step_delay_fixed
1202 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1203 &xx_ei.step_delay_random, 0,
1204 &yy_ei.step_delay_random
1209 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1210 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1215 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1216 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1217 &yy_ei.move_direction_initial
1221 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1222 &xx_ei.move_stepsize, TILEX / 8,
1223 &yy_ei.move_stepsize
1228 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1229 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1230 &yy_ei.move_enter_element
1234 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1235 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1236 &yy_ei.move_leave_element
1240 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1241 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1242 &yy_ei.move_leave_type
1247 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1248 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1249 &yy_ei.slippery_type
1254 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1255 &xx_ei.explosion_type, EXPLODES_3X3,
1256 &yy_ei.explosion_type
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1261 &xx_ei.explosion_delay, 16,
1262 &yy_ei.explosion_delay
1266 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1267 &xx_ei.ignition_delay, 8,
1268 &yy_ei.ignition_delay
1273 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1274 &xx_ei.content, EL_EMPTY_SPACE,
1276 &xx_num_contents, 1, 1
1279 // ---------- "num_change_pages" must be the last entry ---------------------
1282 -1, SAVE_CONF_ALWAYS,
1283 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1284 &xx_ei.num_change_pages, 1,
1285 &yy_ei.num_change_pages
1296 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1298 // ---------- "current_change_page" must be the first entry -----------------
1301 -1, SAVE_CONF_ALWAYS,
1302 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1303 &xx_current_change_page, -1
1306 // ---------- (the remaining entries can be in any order) -------------------
1310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1311 &xx_change.can_change, FALSE
1316 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1317 &xx_event_bits[0], 0
1321 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1322 &xx_event_bits[1], 0
1327 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1328 &xx_change.trigger_player, CH_PLAYER_ANY
1332 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1333 &xx_change.trigger_side, CH_SIDE_ANY
1337 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1338 &xx_change.trigger_page, CH_PAGE_ANY
1343 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1344 &xx_change.target_element, EL_EMPTY_SPACE
1349 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1350 &xx_change.delay_fixed, 0
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1355 &xx_change.delay_random, 0
1359 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1360 &xx_change.delay_frames, FRAMES_PER_SECOND
1365 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1366 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1372 &xx_change.explode, FALSE
1376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1377 &xx_change.use_target_content, FALSE
1381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1382 &xx_change.only_if_complete, FALSE
1386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1387 &xx_change.use_random_replace, FALSE
1391 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1392 &xx_change.random_percentage, 100
1396 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1397 &xx_change.replace_when, CP_WHEN_EMPTY
1402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1403 &xx_change.has_action, FALSE
1407 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1408 &xx_change.action_type, CA_NO_ACTION
1412 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1413 &xx_change.action_mode, CA_MODE_UNDEFINED
1417 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1418 &xx_change.action_arg, CA_ARG_UNDEFINED
1423 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1424 &xx_change.action_element, EL_EMPTY_SPACE
1429 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1430 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1431 &xx_num_contents, 1, 1
1441 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1445 TYPE_STRING, CONF_VALUE_BYTES(1),
1446 &xx_ei.description[0], -1, NULL,
1447 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1448 &xx_default_description[0]
1453 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1454 &xx_ei.use_gfx_element, FALSE
1458 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1459 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1464 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1465 &xx_group.choice_mode, ANIM_RANDOM
1470 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1471 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1472 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1482 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1486 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1487 &xx_ei.use_gfx_element, FALSE
1491 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1492 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1502 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1506 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1507 &li.block_snap_field, TRUE
1511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1512 &li.continuous_snapping, TRUE
1516 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1517 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1522 &li.use_start_element[0], FALSE
1526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1527 &li.start_element[0], EL_PLAYER_1
1531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1532 &li.use_artwork_element[0], FALSE
1536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1537 &li.artwork_element[0], EL_PLAYER_1
1541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1542 &li.use_explosion_element[0], FALSE
1546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1547 &li.explosion_element[0], EL_PLAYER_1
1562 filetype_id_list[] =
1564 { LEVEL_FILE_TYPE_RND, "RND" },
1565 { LEVEL_FILE_TYPE_BD, "BD" },
1566 { LEVEL_FILE_TYPE_EM, "EM" },
1567 { LEVEL_FILE_TYPE_SP, "SP" },
1568 { LEVEL_FILE_TYPE_DX, "DX" },
1569 { LEVEL_FILE_TYPE_SB, "SB" },
1570 { LEVEL_FILE_TYPE_DC, "DC" },
1571 { LEVEL_FILE_TYPE_MM, "MM" },
1572 { LEVEL_FILE_TYPE_MM, "DF" },
1577 // ============================================================================
1578 // level file functions
1579 // ============================================================================
1581 static boolean check_special_flags(char *flag)
1583 if (strEqual(options.special_flags, flag) ||
1584 strEqual(leveldir_current->special_flags, flag))
1590 static struct DateInfo getCurrentDate(void)
1592 time_t epoch_seconds = time(NULL);
1593 struct tm *now = localtime(&epoch_seconds);
1594 struct DateInfo date;
1596 date.year = now->tm_year + 1900;
1597 date.month = now->tm_mon + 1;
1598 date.day = now->tm_mday;
1600 date.src = DATE_SRC_CLOCK;
1605 static void resetEventFlags(struct ElementChangeInfo *change)
1609 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1610 change->has_event[i] = FALSE;
1613 static void resetEventBits(void)
1617 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1618 xx_event_bits[i] = 0;
1621 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1625 /* important: only change event flag if corresponding event bit is set
1626 (this is because all xx_event_bits[] values are loaded separately,
1627 and all xx_event_bits[] values are set back to zero before loading
1628 another value xx_event_bits[x] (each value representing 32 flags)) */
1630 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1631 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1632 change->has_event[i] = TRUE;
1635 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1639 /* in contrast to the above function setEventFlagsFromEventBits(), it
1640 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1641 depending on the corresponding change->has_event[i] values here, as
1642 all xx_event_bits[] values are reset in resetEventBits() before */
1644 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1645 if (change->has_event[i])
1646 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1649 static char *getDefaultElementDescription(struct ElementInfo *ei)
1651 static char description[MAX_ELEMENT_NAME_LEN + 1];
1652 char *default_description = (ei->custom_description != NULL ?
1653 ei->custom_description :
1654 ei->editor_description);
1657 // always start with reliable default values
1658 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1659 description[i] = '\0';
1661 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1662 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1664 return &description[0];
1667 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1669 char *default_description = getDefaultElementDescription(ei);
1672 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1673 ei->description[i] = default_description[i];
1676 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1680 for (i = 0; conf[i].data_type != -1; i++)
1682 int default_value = conf[i].default_value;
1683 int data_type = conf[i].data_type;
1684 int conf_type = conf[i].conf_type;
1685 int byte_mask = conf_type & CONF_MASK_BYTES;
1687 if (byte_mask == CONF_MASK_MULTI_BYTES)
1689 int default_num_entities = conf[i].default_num_entities;
1690 int max_num_entities = conf[i].max_num_entities;
1692 *(int *)(conf[i].num_entities) = default_num_entities;
1694 if (data_type == TYPE_STRING)
1696 char *default_string = conf[i].default_string;
1697 char *string = (char *)(conf[i].value);
1699 strncpy(string, default_string, max_num_entities);
1701 else if (data_type == TYPE_ELEMENT_LIST)
1703 int *element_array = (int *)(conf[i].value);
1706 for (j = 0; j < max_num_entities; j++)
1707 element_array[j] = default_value;
1709 else if (data_type == TYPE_CONTENT_LIST)
1711 struct Content *content = (struct Content *)(conf[i].value);
1714 for (c = 0; c < max_num_entities; c++)
1715 for (y = 0; y < 3; y++)
1716 for (x = 0; x < 3; x++)
1717 content[c].e[x][y] = default_value;
1720 else // constant size configuration data (1, 2 or 4 bytes)
1722 if (data_type == TYPE_BOOLEAN)
1723 *(boolean *)(conf[i].value) = default_value;
1725 *(int *) (conf[i].value) = default_value;
1730 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1734 for (i = 0; conf[i].data_type != -1; i++)
1736 int data_type = conf[i].data_type;
1737 int conf_type = conf[i].conf_type;
1738 int byte_mask = conf_type & CONF_MASK_BYTES;
1740 if (byte_mask == CONF_MASK_MULTI_BYTES)
1742 int max_num_entities = conf[i].max_num_entities;
1744 if (data_type == TYPE_STRING)
1746 char *string = (char *)(conf[i].value);
1747 char *string_copy = (char *)(conf[i].value_copy);
1749 strncpy(string_copy, string, max_num_entities);
1751 else if (data_type == TYPE_ELEMENT_LIST)
1753 int *element_array = (int *)(conf[i].value);
1754 int *element_array_copy = (int *)(conf[i].value_copy);
1757 for (j = 0; j < max_num_entities; j++)
1758 element_array_copy[j] = element_array[j];
1760 else if (data_type == TYPE_CONTENT_LIST)
1762 struct Content *content = (struct Content *)(conf[i].value);
1763 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1766 for (c = 0; c < max_num_entities; c++)
1767 for (y = 0; y < 3; y++)
1768 for (x = 0; x < 3; x++)
1769 content_copy[c].e[x][y] = content[c].e[x][y];
1772 else // constant size configuration data (1, 2 or 4 bytes)
1774 if (data_type == TYPE_BOOLEAN)
1775 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1777 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1782 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1786 xx_ei = *ei_from; // copy element data into temporary buffer
1787 yy_ei = *ei_to; // copy element data into temporary buffer
1789 copyConfigFromConfigList(chunk_config_CUSX_base);
1794 // ---------- reinitialize and copy change pages ----------
1796 ei_to->num_change_pages = ei_from->num_change_pages;
1797 ei_to->current_change_page = ei_from->current_change_page;
1799 setElementChangePages(ei_to, ei_to->num_change_pages);
1801 for (i = 0; i < ei_to->num_change_pages; i++)
1802 ei_to->change_page[i] = ei_from->change_page[i];
1804 // ---------- copy group element info ----------
1805 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1806 *ei_to->group = *ei_from->group;
1808 // mark this custom element as modified
1809 ei_to->modified_settings = TRUE;
1812 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1814 int change_page_size = sizeof(struct ElementChangeInfo);
1816 ei->num_change_pages = MAX(1, change_pages);
1819 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1821 if (ei->current_change_page >= ei->num_change_pages)
1822 ei->current_change_page = ei->num_change_pages - 1;
1824 ei->change = &ei->change_page[ei->current_change_page];
1827 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1829 xx_change = *change; // copy change data into temporary buffer
1831 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1833 *change = xx_change;
1835 resetEventFlags(change);
1837 change->direct_action = 0;
1838 change->other_action = 0;
1840 change->pre_change_function = NULL;
1841 change->change_function = NULL;
1842 change->post_change_function = NULL;
1845 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1849 li = *level; // copy level data into temporary buffer
1850 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1851 *level = li; // copy temporary buffer back to level data
1853 setLevelInfoToDefaults_BD();
1854 setLevelInfoToDefaults_EM();
1855 setLevelInfoToDefaults_SP();
1856 setLevelInfoToDefaults_MM();
1858 level->native_bd_level = &native_bd_level;
1859 level->native_em_level = &native_em_level;
1860 level->native_sp_level = &native_sp_level;
1861 level->native_mm_level = &native_mm_level;
1863 level->file_version = FILE_VERSION_ACTUAL;
1864 level->game_version = GAME_VERSION_ACTUAL;
1866 level->creation_date = getCurrentDate();
1868 level->encoding_16bit_field = TRUE;
1869 level->encoding_16bit_yamyam = TRUE;
1870 level->encoding_16bit_amoeba = TRUE;
1872 // clear level name and level author string buffers
1873 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1874 level->name[i] = '\0';
1875 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1876 level->author[i] = '\0';
1878 // set level name and level author to default values
1879 strcpy(level->name, NAMELESS_LEVEL_NAME);
1880 strcpy(level->author, ANONYMOUS_NAME);
1882 // set level playfield to playable default level with player and exit
1883 for (x = 0; x < MAX_LEV_FIELDX; x++)
1884 for (y = 0; y < MAX_LEV_FIELDY; y++)
1885 level->field[x][y] = EL_SAND;
1887 level->field[0][0] = EL_PLAYER_1;
1888 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1890 BorderElement = EL_STEELWALL;
1892 // detect custom elements when loading them
1893 level->file_has_custom_elements = FALSE;
1895 // set all bug compatibility flags to "false" => do not emulate this bug
1896 level->use_action_after_change_bug = FALSE;
1898 if (leveldir_current)
1900 // try to determine better author name than 'anonymous'
1901 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1903 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1904 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1908 switch (LEVELCLASS(leveldir_current))
1910 case LEVELCLASS_TUTORIAL:
1911 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1914 case LEVELCLASS_CONTRIB:
1915 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1916 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1919 case LEVELCLASS_PRIVATE:
1920 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1921 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1925 // keep default value
1932 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1934 static boolean clipboard_elements_initialized = FALSE;
1937 InitElementPropertiesStatic();
1939 li = *level; // copy level data into temporary buffer
1940 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1941 *level = li; // copy temporary buffer back to level data
1943 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1946 struct ElementInfo *ei = &element_info[element];
1948 if (element == EL_MM_GRAY_BALL)
1950 struct LevelInfo_MM *level_mm = level->native_mm_level;
1953 for (j = 0; j < level->num_mm_ball_contents; j++)
1954 level->mm_ball_content[j] =
1955 map_element_MM_to_RND(level_mm->ball_content[j]);
1958 // never initialize clipboard elements after the very first time
1959 // (to be able to use clipboard elements between several levels)
1960 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1963 if (IS_ENVELOPE(element))
1965 int envelope_nr = element - EL_ENVELOPE_1;
1967 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1969 level->envelope[envelope_nr] = xx_envelope;
1972 if (IS_CUSTOM_ELEMENT(element) ||
1973 IS_GROUP_ELEMENT(element) ||
1974 IS_INTERNAL_ELEMENT(element))
1976 xx_ei = *ei; // copy element data into temporary buffer
1978 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1983 setElementChangePages(ei, 1);
1984 setElementChangeInfoToDefaults(ei->change);
1986 if (IS_CUSTOM_ELEMENT(element) ||
1987 IS_GROUP_ELEMENT(element))
1989 setElementDescriptionToDefault(ei);
1991 ei->modified_settings = FALSE;
1994 if (IS_CUSTOM_ELEMENT(element) ||
1995 IS_INTERNAL_ELEMENT(element))
1997 // internal values used in level editor
1999 ei->access_type = 0;
2000 ei->access_layer = 0;
2001 ei->access_protected = 0;
2002 ei->walk_to_action = 0;
2003 ei->smash_targets = 0;
2006 ei->can_explode_by_fire = FALSE;
2007 ei->can_explode_smashed = FALSE;
2008 ei->can_explode_impact = FALSE;
2010 ei->current_change_page = 0;
2013 if (IS_GROUP_ELEMENT(element) ||
2014 IS_INTERNAL_ELEMENT(element))
2016 struct ElementGroupInfo *group;
2018 // initialize memory for list of elements in group
2019 if (ei->group == NULL)
2020 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2024 xx_group = *group; // copy group data into temporary buffer
2026 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2031 if (IS_EMPTY_ELEMENT(element) ||
2032 IS_INTERNAL_ELEMENT(element))
2034 xx_ei = *ei; // copy element data into temporary buffer
2036 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2042 clipboard_elements_initialized = TRUE;
2045 static void setLevelInfoToDefaults(struct LevelInfo *level,
2046 boolean level_info_only,
2047 boolean reset_file_status)
2049 setLevelInfoToDefaults_Level(level);
2051 if (!level_info_only)
2052 setLevelInfoToDefaults_Elements(level);
2054 if (reset_file_status)
2056 level->no_valid_file = FALSE;
2057 level->no_level_file = FALSE;
2060 level->changed = FALSE;
2063 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2065 level_file_info->nr = 0;
2066 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2067 level_file_info->packed = FALSE;
2069 setString(&level_file_info->basename, NULL);
2070 setString(&level_file_info->filename, NULL);
2073 int getMappedElement_SB(int, boolean);
2075 static void ActivateLevelTemplate(void)
2079 if (check_special_flags("load_xsb_to_ces"))
2081 // fill smaller playfields with padding "beyond border wall" elements
2082 if (level.fieldx < level_template.fieldx ||
2083 level.fieldy < level_template.fieldy)
2085 short field[level.fieldx][level.fieldy];
2086 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2087 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2088 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2089 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2091 // copy old playfield (which is smaller than the visible area)
2092 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2093 field[x][y] = level.field[x][y];
2095 // fill new, larger playfield with "beyond border wall" elements
2096 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2097 level.field[x][y] = getMappedElement_SB('_', TRUE);
2099 // copy the old playfield to the middle of the new playfield
2100 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2101 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2103 level.fieldx = new_fieldx;
2104 level.fieldy = new_fieldy;
2108 // Currently there is no special action needed to activate the template
2109 // data, because 'element_info' property settings overwrite the original
2110 // level data, while all other variables do not change.
2112 // Exception: 'from_level_template' elements in the original level playfield
2113 // are overwritten with the corresponding elements at the same position in
2114 // playfield from the level template.
2116 for (x = 0; x < level.fieldx; x++)
2117 for (y = 0; y < level.fieldy; y++)
2118 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2119 level.field[x][y] = level_template.field[x][y];
2121 if (check_special_flags("load_xsb_to_ces"))
2123 struct LevelInfo level_backup = level;
2125 // overwrite all individual level settings from template level settings
2126 level = level_template;
2128 // restore level file info
2129 level.file_info = level_backup.file_info;
2131 // restore playfield size
2132 level.fieldx = level_backup.fieldx;
2133 level.fieldy = level_backup.fieldy;
2135 // restore playfield content
2136 for (x = 0; x < level.fieldx; x++)
2137 for (y = 0; y < level.fieldy; y++)
2138 level.field[x][y] = level_backup.field[x][y];
2140 // restore name and author from individual level
2141 strcpy(level.name, level_backup.name);
2142 strcpy(level.author, level_backup.author);
2144 // restore flag "use_custom_template"
2145 level.use_custom_template = level_backup.use_custom_template;
2149 static boolean checkForPackageFromBasename_BD(char *basename)
2151 // check for native BD level file extensions
2152 if (!strSuffixLower(basename, ".bd") &&
2153 !strSuffixLower(basename, ".bdr") &&
2154 !strSuffixLower(basename, ".brc") &&
2155 !strSuffixLower(basename, ".gds"))
2158 // check for standard single-level BD files (like "001.bd")
2159 if (strSuffixLower(basename, ".bd") &&
2160 strlen(basename) == 6 &&
2161 basename[0] >= '0' && basename[0] <= '9' &&
2162 basename[1] >= '0' && basename[1] <= '9' &&
2163 basename[2] >= '0' && basename[2] <= '9')
2166 // this is a level package in native BD file format
2170 static char *getLevelFilenameFromBasename(char *basename)
2172 static char *filename = NULL;
2174 checked_free(filename);
2176 filename = getPath2(getCurrentLevelDir(), basename);
2181 static int getFileTypeFromBasename(char *basename)
2183 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2185 static char *filename = NULL;
2186 struct stat file_status;
2188 // ---------- try to determine file type from filename ----------
2190 // check for typical filename of a Supaplex level package file
2191 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2192 return LEVEL_FILE_TYPE_SP;
2194 // check for typical filename of a Diamond Caves II level package file
2195 if (strSuffixLower(basename, ".dc") ||
2196 strSuffixLower(basename, ".dc2"))
2197 return LEVEL_FILE_TYPE_DC;
2199 // check for typical filename of a Sokoban level package file
2200 if (strSuffixLower(basename, ".xsb") &&
2201 strchr(basename, '%') == NULL)
2202 return LEVEL_FILE_TYPE_SB;
2204 // check for typical filename of a Boulder Dash (GDash) level package file
2205 if (checkForPackageFromBasename_BD(basename))
2206 return LEVEL_FILE_TYPE_BD;
2208 // ---------- try to determine file type from filesize ----------
2210 checked_free(filename);
2211 filename = getPath2(getCurrentLevelDir(), basename);
2213 if (stat(filename, &file_status) == 0)
2215 // check for typical filesize of a Supaplex level package file
2216 if (file_status.st_size == 170496)
2217 return LEVEL_FILE_TYPE_SP;
2220 return LEVEL_FILE_TYPE_UNKNOWN;
2223 static int getFileTypeFromMagicBytes(char *filename, int type)
2227 if ((file = openFile(filename, MODE_READ)))
2229 char chunk_name[CHUNK_ID_LEN + 1];
2231 getFileChunkBE(file, chunk_name, NULL);
2233 if (strEqual(chunk_name, "MMII") ||
2234 strEqual(chunk_name, "MIRR"))
2235 type = LEVEL_FILE_TYPE_MM;
2243 static boolean checkForPackageFromBasename(char *basename)
2245 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2246 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2248 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2251 static char *getSingleLevelBasenameExt(int nr, char *extension)
2253 static char basename[MAX_FILENAME_LEN];
2256 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2258 sprintf(basename, "%03d.%s", nr, extension);
2263 static char *getSingleLevelBasename(int nr)
2265 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2268 static char *getPackedLevelBasename(int type)
2270 static char basename[MAX_FILENAME_LEN];
2271 char *directory = getCurrentLevelDir();
2273 DirectoryEntry *dir_entry;
2275 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2277 if ((dir = openDirectory(directory)) == NULL)
2279 Warn("cannot read current level directory '%s'", directory);
2284 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2286 char *entry_basename = dir_entry->basename;
2287 int entry_type = getFileTypeFromBasename(entry_basename);
2289 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2291 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2294 strcpy(basename, entry_basename);
2301 closeDirectory(dir);
2306 static char *getSingleLevelFilename(int nr)
2308 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2311 #if ENABLE_UNUSED_CODE
2312 static char *getPackedLevelFilename(int type)
2314 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2318 char *getDefaultLevelFilename(int nr)
2320 return getSingleLevelFilename(nr);
2323 #if ENABLE_UNUSED_CODE
2324 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2328 lfi->packed = FALSE;
2330 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2331 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2335 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2336 int type, char *format, ...)
2338 static char basename[MAX_FILENAME_LEN];
2341 va_start(ap, format);
2342 vsprintf(basename, format, ap);
2346 lfi->packed = FALSE;
2348 setString(&lfi->basename, basename);
2349 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2352 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2358 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2359 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2362 static int getFiletypeFromID(char *filetype_id)
2364 char *filetype_id_lower;
2365 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2368 if (filetype_id == NULL)
2369 return LEVEL_FILE_TYPE_UNKNOWN;
2371 filetype_id_lower = getStringToLower(filetype_id);
2373 for (i = 0; filetype_id_list[i].id != NULL; i++)
2375 char *id_lower = getStringToLower(filetype_id_list[i].id);
2377 if (strEqual(filetype_id_lower, id_lower))
2378 filetype = filetype_id_list[i].filetype;
2382 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2386 free(filetype_id_lower);
2391 char *getLocalLevelTemplateFilename(void)
2393 return getDefaultLevelFilename(-1);
2396 char *getGlobalLevelTemplateFilename(void)
2398 // global variable "leveldir_current" must be modified in the loop below
2399 LevelDirTree *leveldir_current_last = leveldir_current;
2400 char *filename = NULL;
2402 // check for template level in path from current to topmost tree node
2404 while (leveldir_current != NULL)
2406 filename = getDefaultLevelFilename(-1);
2408 if (fileExists(filename))
2411 leveldir_current = leveldir_current->node_parent;
2414 // restore global variable "leveldir_current" modified in above loop
2415 leveldir_current = leveldir_current_last;
2420 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2424 // special case: level number is negative => check for level template file
2427 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2428 getSingleLevelBasename(-1));
2430 // replace local level template filename with global template filename
2431 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2433 // no fallback if template file not existing
2437 // special case: check for file name/pattern specified in "levelinfo.conf"
2438 if (leveldir_current->level_filename != NULL)
2440 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2442 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2443 leveldir_current->level_filename, nr);
2445 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2447 if (fileExists(lfi->filename))
2450 else if (leveldir_current->level_filetype != NULL)
2452 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2454 // check for specified native level file with standard file name
2455 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2456 "%03d.%s", nr, LEVELFILE_EXTENSION);
2457 if (fileExists(lfi->filename))
2461 // check for native Rocks'n'Diamonds level file
2462 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2463 "%03d.%s", nr, LEVELFILE_EXTENSION);
2464 if (fileExists(lfi->filename))
2467 // check for native Boulder Dash level file
2468 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2469 if (fileExists(lfi->filename))
2472 // check for Emerald Mine level file (V1)
2473 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2474 'a' + (nr / 10) % 26, '0' + nr % 10);
2475 if (fileExists(lfi->filename))
2477 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2478 'A' + (nr / 10) % 26, '0' + nr % 10);
2479 if (fileExists(lfi->filename))
2482 // check for Emerald Mine level file (V2 to V5)
2483 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2484 if (fileExists(lfi->filename))
2487 // check for Emerald Mine level file (V6 / single mode)
2488 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2489 if (fileExists(lfi->filename))
2491 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2492 if (fileExists(lfi->filename))
2495 // check for Emerald Mine level file (V6 / teamwork mode)
2496 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2497 if (fileExists(lfi->filename))
2499 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2500 if (fileExists(lfi->filename))
2503 // check for various packed level file formats
2504 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2505 if (fileExists(lfi->filename))
2508 // no known level file found -- use default values (and fail later)
2509 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2510 "%03d.%s", nr, LEVELFILE_EXTENSION);
2513 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2515 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2516 lfi->type = getFileTypeFromBasename(lfi->basename);
2518 if (lfi->type == LEVEL_FILE_TYPE_RND)
2519 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2522 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2524 // always start with reliable default values
2525 setFileInfoToDefaults(level_file_info);
2527 level_file_info->nr = nr; // set requested level number
2529 determineLevelFileInfo_Filename(level_file_info);
2530 determineLevelFileInfo_Filetype(level_file_info);
2533 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2534 struct LevelFileInfo *lfi_to)
2536 lfi_to->nr = lfi_from->nr;
2537 lfi_to->type = lfi_from->type;
2538 lfi_to->packed = lfi_from->packed;
2540 setString(&lfi_to->basename, lfi_from->basename);
2541 setString(&lfi_to->filename, lfi_from->filename);
2544 // ----------------------------------------------------------------------------
2545 // functions for loading R'n'D level
2546 // ----------------------------------------------------------------------------
2548 int getMappedElement(int element)
2550 // remap some (historic, now obsolete) elements
2554 case EL_PLAYER_OBSOLETE:
2555 element = EL_PLAYER_1;
2558 case EL_KEY_OBSOLETE:
2562 case EL_EM_KEY_1_FILE_OBSOLETE:
2563 element = EL_EM_KEY_1;
2566 case EL_EM_KEY_2_FILE_OBSOLETE:
2567 element = EL_EM_KEY_2;
2570 case EL_EM_KEY_3_FILE_OBSOLETE:
2571 element = EL_EM_KEY_3;
2574 case EL_EM_KEY_4_FILE_OBSOLETE:
2575 element = EL_EM_KEY_4;
2578 case EL_ENVELOPE_OBSOLETE:
2579 element = EL_ENVELOPE_1;
2587 if (element >= NUM_FILE_ELEMENTS)
2589 Warn("invalid level element %d", element);
2591 element = EL_UNKNOWN;
2599 static int getMappedElementByVersion(int element, int game_version)
2601 // remap some elements due to certain game version
2603 if (game_version <= VERSION_IDENT(2,2,0,0))
2605 // map game font elements
2606 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2607 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2608 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2609 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2612 if (game_version < VERSION_IDENT(3,0,0,0))
2614 // map Supaplex gravity tube elements
2615 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2616 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2617 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2618 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2625 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2627 level->file_version = getFileVersion(file);
2628 level->game_version = getFileVersion(file);
2633 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2635 level->creation_date.year = getFile16BitBE(file);
2636 level->creation_date.month = getFile8Bit(file);
2637 level->creation_date.day = getFile8Bit(file);
2639 level->creation_date.src = DATE_SRC_LEVELFILE;
2644 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2646 int initial_player_stepsize;
2647 int initial_player_gravity;
2650 level->fieldx = getFile8Bit(file);
2651 level->fieldy = getFile8Bit(file);
2653 level->time = getFile16BitBE(file);
2654 level->gems_needed = getFile16BitBE(file);
2656 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2657 level->name[i] = getFile8Bit(file);
2658 level->name[MAX_LEVEL_NAME_LEN] = 0;
2660 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2661 level->score[i] = getFile8Bit(file);
2663 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2664 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2665 for (y = 0; y < 3; y++)
2666 for (x = 0; x < 3; x++)
2667 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2669 level->amoeba_speed = getFile8Bit(file);
2670 level->time_magic_wall = getFile8Bit(file);
2671 level->time_wheel = getFile8Bit(file);
2672 level->amoeba_content = getMappedElement(getFile8Bit(file));
2674 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2677 for (i = 0; i < MAX_PLAYERS; i++)
2678 level->initial_player_stepsize[i] = initial_player_stepsize;
2680 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2682 for (i = 0; i < MAX_PLAYERS; i++)
2683 level->initial_player_gravity[i] = initial_player_gravity;
2685 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2686 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2688 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2690 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2691 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2692 level->can_move_into_acid_bits = getFile32BitBE(file);
2693 level->dont_collide_with_bits = getFile8Bit(file);
2695 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2696 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2698 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2699 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2700 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2702 level->game_engine_type = getFile8Bit(file);
2704 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2709 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2713 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2714 level->name[i] = getFile8Bit(file);
2715 level->name[MAX_LEVEL_NAME_LEN] = 0;
2720 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2724 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2725 level->author[i] = getFile8Bit(file);
2726 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2731 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2734 int chunk_size_expected = level->fieldx * level->fieldy;
2736 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2737 stored with 16-bit encoding (and should be twice as big then).
2738 Even worse, playfield data was stored 16-bit when only yamyam content
2739 contained 16-bit elements and vice versa. */
2741 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2742 chunk_size_expected *= 2;
2744 if (chunk_size_expected != chunk_size)
2746 ReadUnusedBytesFromFile(file, chunk_size);
2747 return chunk_size_expected;
2750 for (y = 0; y < level->fieldy; y++)
2751 for (x = 0; x < level->fieldx; x++)
2752 level->field[x][y] =
2753 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2758 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2761 int header_size = 4;
2762 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2763 int chunk_size_expected = header_size + content_size;
2765 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2766 stored with 16-bit encoding (and should be twice as big then).
2767 Even worse, playfield data was stored 16-bit when only yamyam content
2768 contained 16-bit elements and vice versa. */
2770 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2771 chunk_size_expected += content_size;
2773 if (chunk_size_expected != chunk_size)
2775 ReadUnusedBytesFromFile(file, chunk_size);
2776 return chunk_size_expected;
2780 level->num_yamyam_contents = getFile8Bit(file);
2784 // correct invalid number of content fields -- should never happen
2785 if (level->num_yamyam_contents < 1 ||
2786 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2787 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2790 for (y = 0; y < 3; y++)
2791 for (x = 0; x < 3; x++)
2792 level->yamyam_content[i].e[x][y] =
2793 getMappedElement(level->encoding_16bit_field ?
2794 getFile16BitBE(file) : getFile8Bit(file));
2798 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2803 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2805 element = getMappedElement(getFile16BitBE(file));
2806 num_contents = getFile8Bit(file);
2808 getFile8Bit(file); // content x size (unused)
2809 getFile8Bit(file); // content y size (unused)
2811 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2813 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2814 for (y = 0; y < 3; y++)
2815 for (x = 0; x < 3; x++)
2816 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2818 // correct invalid number of content fields -- should never happen
2819 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2820 num_contents = STD_ELEMENT_CONTENTS;
2822 if (element == EL_YAMYAM)
2824 level->num_yamyam_contents = num_contents;
2826 for (i = 0; i < num_contents; i++)
2827 for (y = 0; y < 3; y++)
2828 for (x = 0; x < 3; x++)
2829 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2831 else if (element == EL_BD_AMOEBA)
2833 level->amoeba_content = content_array[0][0][0];
2837 Warn("cannot load content for element '%d'", element);
2843 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2849 int chunk_size_expected;
2851 element = getMappedElement(getFile16BitBE(file));
2852 if (!IS_ENVELOPE(element))
2853 element = EL_ENVELOPE_1;
2855 envelope_nr = element - EL_ENVELOPE_1;
2857 envelope_len = getFile16BitBE(file);
2859 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2860 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2862 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2864 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2865 if (chunk_size_expected != chunk_size)
2867 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2868 return chunk_size_expected;
2871 for (i = 0; i < envelope_len; i++)
2872 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2877 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2879 int num_changed_custom_elements = getFile16BitBE(file);
2880 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2883 if (chunk_size_expected != chunk_size)
2885 ReadUnusedBytesFromFile(file, chunk_size - 2);
2886 return chunk_size_expected;
2889 for (i = 0; i < num_changed_custom_elements; i++)
2891 int element = getMappedElement(getFile16BitBE(file));
2892 int properties = getFile32BitBE(file);
2894 if (IS_CUSTOM_ELEMENT(element))
2895 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2897 Warn("invalid custom element number %d", element);
2899 // older game versions that wrote level files with CUS1 chunks used
2900 // different default push delay values (not yet stored in level file)
2901 element_info[element].push_delay_fixed = 2;
2902 element_info[element].push_delay_random = 8;
2905 level->file_has_custom_elements = TRUE;
2910 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2912 int num_changed_custom_elements = getFile16BitBE(file);
2913 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2916 if (chunk_size_expected != chunk_size)
2918 ReadUnusedBytesFromFile(file, chunk_size - 2);
2919 return chunk_size_expected;
2922 for (i = 0; i < num_changed_custom_elements; i++)
2924 int element = getMappedElement(getFile16BitBE(file));
2925 int custom_target_element = getMappedElement(getFile16BitBE(file));
2927 if (IS_CUSTOM_ELEMENT(element))
2928 element_info[element].change->target_element = custom_target_element;
2930 Warn("invalid custom element number %d", element);
2933 level->file_has_custom_elements = TRUE;
2938 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2940 int num_changed_custom_elements = getFile16BitBE(file);
2941 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2944 if (chunk_size_expected != chunk_size)
2946 ReadUnusedBytesFromFile(file, chunk_size - 2);
2947 return chunk_size_expected;
2950 for (i = 0; i < num_changed_custom_elements; i++)
2952 int element = getMappedElement(getFile16BitBE(file));
2953 struct ElementInfo *ei = &element_info[element];
2954 unsigned int event_bits;
2956 if (!IS_CUSTOM_ELEMENT(element))
2958 Warn("invalid custom element number %d", element);
2960 element = EL_INTERNAL_DUMMY;
2963 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2964 ei->description[j] = getFile8Bit(file);
2965 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2967 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2969 // some free bytes for future properties and padding
2970 ReadUnusedBytesFromFile(file, 7);
2972 ei->use_gfx_element = getFile8Bit(file);
2973 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2975 ei->collect_score_initial = getFile8Bit(file);
2976 ei->collect_count_initial = getFile8Bit(file);
2978 ei->push_delay_fixed = getFile16BitBE(file);
2979 ei->push_delay_random = getFile16BitBE(file);
2980 ei->move_delay_fixed = getFile16BitBE(file);
2981 ei->move_delay_random = getFile16BitBE(file);
2983 ei->move_pattern = getFile16BitBE(file);
2984 ei->move_direction_initial = getFile8Bit(file);
2985 ei->move_stepsize = getFile8Bit(file);
2987 for (y = 0; y < 3; y++)
2988 for (x = 0; x < 3; x++)
2989 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2991 // bits 0 - 31 of "has_event[]"
2992 event_bits = getFile32BitBE(file);
2993 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2994 if (event_bits & (1u << j))
2995 ei->change->has_event[j] = TRUE;
2997 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2999 ei->change->delay_fixed = getFile16BitBE(file);
3000 ei->change->delay_random = getFile16BitBE(file);
3001 ei->change->delay_frames = getFile16BitBE(file);
3003 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3005 ei->change->explode = getFile8Bit(file);
3006 ei->change->use_target_content = getFile8Bit(file);
3007 ei->change->only_if_complete = getFile8Bit(file);
3008 ei->change->use_random_replace = getFile8Bit(file);
3010 ei->change->random_percentage = getFile8Bit(file);
3011 ei->change->replace_when = getFile8Bit(file);
3013 for (y = 0; y < 3; y++)
3014 for (x = 0; x < 3; x++)
3015 ei->change->target_content.e[x][y] =
3016 getMappedElement(getFile16BitBE(file));
3018 ei->slippery_type = getFile8Bit(file);
3020 // some free bytes for future properties and padding
3021 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3023 // mark that this custom element has been modified
3024 ei->modified_settings = TRUE;
3027 level->file_has_custom_elements = TRUE;
3032 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3034 struct ElementInfo *ei;
3035 int chunk_size_expected;
3039 // ---------- custom element base property values (96 bytes) ----------------
3041 element = getMappedElement(getFile16BitBE(file));
3043 if (!IS_CUSTOM_ELEMENT(element))
3045 Warn("invalid custom element number %d", element);
3047 ReadUnusedBytesFromFile(file, chunk_size - 2);
3052 ei = &element_info[element];
3054 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3055 ei->description[i] = getFile8Bit(file);
3056 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3058 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3060 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3062 ei->num_change_pages = getFile8Bit(file);
3064 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3065 if (chunk_size_expected != chunk_size)
3067 ReadUnusedBytesFromFile(file, chunk_size - 43);
3068 return chunk_size_expected;
3071 ei->ce_value_fixed_initial = getFile16BitBE(file);
3072 ei->ce_value_random_initial = getFile16BitBE(file);
3073 ei->use_last_ce_value = getFile8Bit(file);
3075 ei->use_gfx_element = getFile8Bit(file);
3076 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3078 ei->collect_score_initial = getFile8Bit(file);
3079 ei->collect_count_initial = getFile8Bit(file);
3081 ei->drop_delay_fixed = getFile8Bit(file);
3082 ei->push_delay_fixed = getFile8Bit(file);
3083 ei->drop_delay_random = getFile8Bit(file);
3084 ei->push_delay_random = getFile8Bit(file);
3085 ei->move_delay_fixed = getFile16BitBE(file);
3086 ei->move_delay_random = getFile16BitBE(file);
3088 // bits 0 - 15 of "move_pattern" ...
3089 ei->move_pattern = getFile16BitBE(file);
3090 ei->move_direction_initial = getFile8Bit(file);
3091 ei->move_stepsize = getFile8Bit(file);
3093 ei->slippery_type = getFile8Bit(file);
3095 for (y = 0; y < 3; y++)
3096 for (x = 0; x < 3; x++)
3097 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3099 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3100 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3101 ei->move_leave_type = getFile8Bit(file);
3103 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3104 ei->move_pattern |= (getFile16BitBE(file) << 16);
3106 ei->access_direction = getFile8Bit(file);
3108 ei->explosion_delay = getFile8Bit(file);
3109 ei->ignition_delay = getFile8Bit(file);
3110 ei->explosion_type = getFile8Bit(file);
3112 // some free bytes for future custom property values and padding
3113 ReadUnusedBytesFromFile(file, 1);
3115 // ---------- change page property values (48 bytes) ------------------------
3117 setElementChangePages(ei, ei->num_change_pages);
3119 for (i = 0; i < ei->num_change_pages; i++)
3121 struct ElementChangeInfo *change = &ei->change_page[i];
3122 unsigned int event_bits;
3124 // always start with reliable default values
3125 setElementChangeInfoToDefaults(change);
3127 // bits 0 - 31 of "has_event[]" ...
3128 event_bits = getFile32BitBE(file);
3129 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3130 if (event_bits & (1u << j))
3131 change->has_event[j] = TRUE;
3133 change->target_element = getMappedElement(getFile16BitBE(file));
3135 change->delay_fixed = getFile16BitBE(file);
3136 change->delay_random = getFile16BitBE(file);
3137 change->delay_frames = getFile16BitBE(file);
3139 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3141 change->explode = getFile8Bit(file);
3142 change->use_target_content = getFile8Bit(file);
3143 change->only_if_complete = getFile8Bit(file);
3144 change->use_random_replace = getFile8Bit(file);
3146 change->random_percentage = getFile8Bit(file);
3147 change->replace_when = getFile8Bit(file);
3149 for (y = 0; y < 3; y++)
3150 for (x = 0; x < 3; x++)
3151 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3153 change->can_change = getFile8Bit(file);
3155 change->trigger_side = getFile8Bit(file);
3157 change->trigger_player = getFile8Bit(file);
3158 change->trigger_page = getFile8Bit(file);
3160 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3161 CH_PAGE_ANY : (1 << change->trigger_page));
3163 change->has_action = getFile8Bit(file);
3164 change->action_type = getFile8Bit(file);
3165 change->action_mode = getFile8Bit(file);
3166 change->action_arg = getFile16BitBE(file);
3168 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3169 event_bits = getFile8Bit(file);
3170 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3171 if (event_bits & (1u << (j - 32)))
3172 change->has_event[j] = TRUE;
3175 // mark this custom element as modified
3176 ei->modified_settings = TRUE;
3178 level->file_has_custom_elements = TRUE;
3183 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3185 struct ElementInfo *ei;
3186 struct ElementGroupInfo *group;
3190 element = getMappedElement(getFile16BitBE(file));
3192 if (!IS_GROUP_ELEMENT(element))
3194 Warn("invalid group element number %d", element);
3196 ReadUnusedBytesFromFile(file, chunk_size - 2);
3201 ei = &element_info[element];
3203 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3204 ei->description[i] = getFile8Bit(file);
3205 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3207 group = element_info[element].group;
3209 group->num_elements = getFile8Bit(file);
3211 ei->use_gfx_element = getFile8Bit(file);
3212 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3214 group->choice_mode = getFile8Bit(file);
3216 // some free bytes for future values and padding
3217 ReadUnusedBytesFromFile(file, 3);
3219 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3220 group->element[i] = getMappedElement(getFile16BitBE(file));
3222 // mark this group element as modified
3223 element_info[element].modified_settings = TRUE;
3225 level->file_has_custom_elements = TRUE;
3230 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3231 int element, int real_element)
3233 int micro_chunk_size = 0;
3234 int conf_type = getFile8Bit(file);
3235 int byte_mask = conf_type & CONF_MASK_BYTES;
3236 boolean element_found = FALSE;
3239 micro_chunk_size += 1;
3241 if (byte_mask == CONF_MASK_MULTI_BYTES)
3243 int num_bytes = getFile16BitBE(file);
3244 byte *buffer = checked_malloc(num_bytes);
3246 ReadBytesFromFile(file, buffer, num_bytes);
3248 for (i = 0; conf[i].data_type != -1; i++)
3250 if (conf[i].element == element &&
3251 conf[i].conf_type == conf_type)
3253 int data_type = conf[i].data_type;
3254 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3255 int max_num_entities = conf[i].max_num_entities;
3257 if (num_entities > max_num_entities)
3259 Warn("truncating number of entities for element %d from %d to %d",
3260 element, num_entities, max_num_entities);
3262 num_entities = max_num_entities;
3265 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3266 data_type == TYPE_CONTENT_LIST))
3268 // for element and content lists, zero entities are not allowed
3269 Warn("found empty list of entities for element %d", element);
3271 // do not set "num_entities" here to prevent reading behind buffer
3273 *(int *)(conf[i].num_entities) = 1; // at least one is required
3277 *(int *)(conf[i].num_entities) = num_entities;
3280 element_found = TRUE;
3282 if (data_type == TYPE_STRING)
3284 char *string = (char *)(conf[i].value);
3287 for (j = 0; j < max_num_entities; j++)
3288 string[j] = (j < num_entities ? buffer[j] : '\0');
3290 else if (data_type == TYPE_ELEMENT_LIST)
3292 int *element_array = (int *)(conf[i].value);
3295 for (j = 0; j < num_entities; j++)
3297 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3299 else if (data_type == TYPE_CONTENT_LIST)
3301 struct Content *content= (struct Content *)(conf[i].value);
3304 for (c = 0; c < num_entities; c++)
3305 for (y = 0; y < 3; y++)
3306 for (x = 0; x < 3; x++)
3307 content[c].e[x][y] =
3308 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3311 element_found = FALSE;
3317 checked_free(buffer);
3319 micro_chunk_size += 2 + num_bytes;
3321 else // constant size configuration data (1, 2 or 4 bytes)
3323 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3324 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3325 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3327 for (i = 0; conf[i].data_type != -1; i++)
3329 if (conf[i].element == element &&
3330 conf[i].conf_type == conf_type)
3332 int data_type = conf[i].data_type;
3334 if (data_type == TYPE_ELEMENT)
3335 value = getMappedElement(value);
3337 if (data_type == TYPE_BOOLEAN)
3338 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3340 *(int *) (conf[i].value) = value;
3342 element_found = TRUE;
3348 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3353 char *error_conf_chunk_bytes =
3354 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3355 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3356 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3357 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3358 int error_element = real_element;
3360 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3361 error_conf_chunk_bytes, error_conf_chunk_token,
3362 error_element, EL_NAME(error_element));
3365 return micro_chunk_size;
3368 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3370 int real_chunk_size = 0;
3372 li = *level; // copy level data into temporary buffer
3374 while (!checkEndOfFile(file))
3376 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3378 if (real_chunk_size >= chunk_size)
3382 *level = li; // copy temporary buffer back to level data
3384 return real_chunk_size;
3387 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3389 int real_chunk_size = 0;
3391 li = *level; // copy level data into temporary buffer
3393 while (!checkEndOfFile(file))
3395 int element = getMappedElement(getFile16BitBE(file));
3397 real_chunk_size += 2;
3398 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3400 if (real_chunk_size >= chunk_size)
3404 *level = li; // copy temporary buffer back to level data
3406 return real_chunk_size;
3409 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3411 int real_chunk_size = 0;
3413 li = *level; // copy level data into temporary buffer
3415 while (!checkEndOfFile(file))
3417 int element = getMappedElement(getFile16BitBE(file));
3419 real_chunk_size += 2;
3420 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3422 if (real_chunk_size >= chunk_size)
3426 *level = li; // copy temporary buffer back to level data
3428 return real_chunk_size;
3431 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3433 int element = getMappedElement(getFile16BitBE(file));
3434 int envelope_nr = element - EL_ENVELOPE_1;
3435 int real_chunk_size = 2;
3437 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3439 while (!checkEndOfFile(file))
3441 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3444 if (real_chunk_size >= chunk_size)
3448 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3450 return real_chunk_size;
3453 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3455 int element = getMappedElement(getFile16BitBE(file));
3456 int real_chunk_size = 2;
3457 struct ElementInfo *ei = &element_info[element];
3460 xx_ei = *ei; // copy element data into temporary buffer
3462 xx_ei.num_change_pages = -1;
3464 while (!checkEndOfFile(file))
3466 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3468 if (xx_ei.num_change_pages != -1)
3471 if (real_chunk_size >= chunk_size)
3477 if (ei->num_change_pages == -1)
3479 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3482 ei->num_change_pages = 1;
3484 setElementChangePages(ei, 1);
3485 setElementChangeInfoToDefaults(ei->change);
3487 return real_chunk_size;
3490 // initialize number of change pages stored for this custom element
3491 setElementChangePages(ei, ei->num_change_pages);
3492 for (i = 0; i < ei->num_change_pages; i++)
3493 setElementChangeInfoToDefaults(&ei->change_page[i]);
3495 // start with reading properties for the first change page
3496 xx_current_change_page = 0;
3498 while (!checkEndOfFile(file))
3500 // level file might contain invalid change page number
3501 if (xx_current_change_page >= ei->num_change_pages)
3504 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3506 xx_change = *change; // copy change data into temporary buffer
3508 resetEventBits(); // reset bits; change page might have changed
3510 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3513 *change = xx_change;
3515 setEventFlagsFromEventBits(change);
3517 if (real_chunk_size >= chunk_size)
3521 level->file_has_custom_elements = TRUE;
3523 return real_chunk_size;
3526 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3528 int element = getMappedElement(getFile16BitBE(file));
3529 int real_chunk_size = 2;
3530 struct ElementInfo *ei = &element_info[element];
3531 struct ElementGroupInfo *group = ei->group;
3536 xx_ei = *ei; // copy element data into temporary buffer
3537 xx_group = *group; // copy group data into temporary buffer
3539 while (!checkEndOfFile(file))
3541 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3544 if (real_chunk_size >= chunk_size)
3551 level->file_has_custom_elements = TRUE;
3553 return real_chunk_size;
3556 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3558 int element = getMappedElement(getFile16BitBE(file));
3559 int real_chunk_size = 2;
3560 struct ElementInfo *ei = &element_info[element];
3562 xx_ei = *ei; // copy element data into temporary buffer
3564 while (!checkEndOfFile(file))
3566 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3569 if (real_chunk_size >= chunk_size)
3575 level->file_has_custom_elements = TRUE;
3577 return real_chunk_size;
3580 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3581 struct LevelFileInfo *level_file_info,
3582 boolean level_info_only)
3584 char *filename = level_file_info->filename;
3585 char cookie[MAX_LINE_LEN];
3586 char chunk_name[CHUNK_ID_LEN + 1];
3590 if (!(file = openFile(filename, MODE_READ)))
3592 level->no_valid_file = TRUE;
3593 level->no_level_file = TRUE;
3595 if (level_info_only)
3598 Warn("cannot read level '%s' -- using empty level", filename);
3600 if (!setup.editor.use_template_for_new_levels)
3603 // if level file not found, try to initialize level data from template
3604 filename = getGlobalLevelTemplateFilename();
3606 if (!(file = openFile(filename, MODE_READ)))
3609 // default: for empty levels, use level template for custom elements
3610 level->use_custom_template = TRUE;
3612 level->no_valid_file = FALSE;
3615 getFileChunkBE(file, chunk_name, NULL);
3616 if (strEqual(chunk_name, "RND1"))
3618 getFile32BitBE(file); // not used
3620 getFileChunkBE(file, chunk_name, NULL);
3621 if (!strEqual(chunk_name, "CAVE"))
3623 level->no_valid_file = TRUE;
3625 Warn("unknown format of level file '%s'", filename);
3632 else // check for pre-2.0 file format with cookie string
3634 strcpy(cookie, chunk_name);
3635 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3637 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3638 cookie[strlen(cookie) - 1] = '\0';
3640 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3642 level->no_valid_file = TRUE;
3644 Warn("unknown format of level file '%s'", filename);
3651 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3653 level->no_valid_file = TRUE;
3655 Warn("unsupported version of level file '%s'", filename);
3662 // pre-2.0 level files have no game version, so use file version here
3663 level->game_version = level->file_version;
3666 if (level->file_version < FILE_VERSION_1_2)
3668 // level files from versions before 1.2.0 without chunk structure
3669 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3670 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3678 int (*loader)(File *, int, struct LevelInfo *);
3682 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3683 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3684 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3685 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3686 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3687 { "INFO", -1, LoadLevel_INFO },
3688 { "BODY", -1, LoadLevel_BODY },
3689 { "CONT", -1, LoadLevel_CONT },
3690 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3691 { "CNT3", -1, LoadLevel_CNT3 },
3692 { "CUS1", -1, LoadLevel_CUS1 },
3693 { "CUS2", -1, LoadLevel_CUS2 },
3694 { "CUS3", -1, LoadLevel_CUS3 },
3695 { "CUS4", -1, LoadLevel_CUS4 },
3696 { "GRP1", -1, LoadLevel_GRP1 },
3697 { "CONF", -1, LoadLevel_CONF },
3698 { "ELEM", -1, LoadLevel_ELEM },
3699 { "NOTE", -1, LoadLevel_NOTE },
3700 { "CUSX", -1, LoadLevel_CUSX },
3701 { "GRPX", -1, LoadLevel_GRPX },
3702 { "EMPX", -1, LoadLevel_EMPX },
3707 while (getFileChunkBE(file, chunk_name, &chunk_size))
3711 while (chunk_info[i].name != NULL &&
3712 !strEqual(chunk_name, chunk_info[i].name))
3715 if (chunk_info[i].name == NULL)
3717 Warn("unknown chunk '%s' in level file '%s'",
3718 chunk_name, filename);
3720 ReadUnusedBytesFromFile(file, chunk_size);
3722 else if (chunk_info[i].size != -1 &&
3723 chunk_info[i].size != chunk_size)
3725 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3726 chunk_size, chunk_name, filename);
3728 ReadUnusedBytesFromFile(file, chunk_size);
3732 // call function to load this level chunk
3733 int chunk_size_expected =
3734 (chunk_info[i].loader)(file, chunk_size, level);
3736 if (chunk_size_expected < 0)
3738 Warn("error reading chunk '%s' in level file '%s'",
3739 chunk_name, filename);
3744 // the size of some chunks cannot be checked before reading other
3745 // chunks first (like "HEAD" and "BODY") that contain some header
3746 // information, so check them here
3747 if (chunk_size_expected != chunk_size)
3749 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3750 chunk_size, chunk_name, filename);
3762 // ----------------------------------------------------------------------------
3763 // functions for loading BD level
3764 // ----------------------------------------------------------------------------
3766 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3768 struct LevelInfo_BD *level_bd = level->native_bd_level;
3769 GdCave *cave = NULL; // will be changed below
3770 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3771 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3774 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3776 // cave and map newly allocated when set to defaults above
3777 cave = level_bd->cave;
3779 for (i = 0; i < 5; i++)
3781 cave->level_time[i] = level->time;
3782 cave->level_diamonds[i] = level->gems_needed;
3783 cave->level_magic_wall_time[i] = level->time_magic_wall;
3785 cave->level_speed[i] = level->bd_cycle_delay_ms;
3786 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3787 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3788 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3790 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3793 cave->diamond_value = level->score[SC_EMERALD];
3794 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3796 cave->scheduling = level->bd_scheduling_type;
3797 cave->pal_timing = level->bd_pal_timing;
3798 cave->intermission = level->bd_intermission;
3799 cave->diagonal_movements = level->bd_diagonal_movements;
3801 cave->lineshift = level->bd_line_shifting_borders;
3802 cave->wraparound_objects = level->bd_wraparound_objects;
3803 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3804 cave->short_explosions = level->bd_short_explosions;
3805 cave->gravity_affects_all = level->bd_gravity_affects_all;
3807 strncpy(cave->name, level->name, sizeof(GdString));
3808 cave->name[sizeof(GdString) - 1] = '\0';
3810 for (x = 0; x < cave->w; x++)
3811 for (y = 0; y < cave->h; y++)
3812 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3815 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3817 struct LevelInfo_BD *level_bd = level->native_bd_level;
3818 GdCave *cave = level_bd->cave;
3819 int bd_level_nr = level_bd->level_nr;
3822 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3823 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3825 level->time = cave->level_time[bd_level_nr];
3826 level->gems_needed = cave->level_diamonds[bd_level_nr];
3827 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3829 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3830 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3831 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3832 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3834 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3835 level->score[SC_EMERALD] = cave->diamond_value;
3836 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3838 level->bd_scheduling_type = cave->scheduling;
3839 level->bd_pal_timing = cave->pal_timing;
3840 level->bd_intermission = cave->intermission;
3841 level->bd_diagonal_movements = cave->diagonal_movements;
3843 level->bd_line_shifting_borders = cave->lineshift;
3844 level->bd_wraparound_objects = cave->wraparound_objects;
3845 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3846 level->bd_short_explosions = cave->short_explosions;
3847 level->bd_gravity_affects_all = cave->gravity_affects_all;
3849 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3851 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3852 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3854 for (x = 0; x < level->fieldx; x++)
3855 for (y = 0; y < level->fieldy; y++)
3856 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3858 checked_free(cave_name);
3861 static void setTapeInfoToDefaults(void);
3863 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3865 struct LevelInfo_BD *level_bd = level->native_bd_level;
3866 GdCave *cave = level_bd->cave;
3867 GdReplay *replay = level_bd->replay;
3873 // always start with reliable default values
3874 setTapeInfoToDefaults();
3876 tape.level_nr = level_nr; // (currently not used)
3877 tape.random_seed = replay->seed;
3879 TapeSetDateFromIsoDateString(replay->date);
3882 tape.pos[tape.counter].delay = 0;
3884 tape.bd_replay = TRUE;
3886 // all time calculations only used to display approximate tape time
3887 int cave_speed = cave->speed;
3888 int milliseconds_game = 0;
3889 int milliseconds_elapsed = 20;
3891 for (i = 0; i < replay->movements->len; i++)
3893 int replay_action = replay->movements->data[i];
3894 int tape_action = map_action_BD_to_RND(replay_action);
3895 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3896 boolean success = 0;
3900 success = TapeAddAction(action);
3902 milliseconds_game += milliseconds_elapsed;
3904 if (milliseconds_game >= cave_speed)
3906 milliseconds_game -= cave_speed;
3913 tape.pos[tape.counter].delay = 0;
3914 tape.pos[tape.counter].action[0] = 0;
3918 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3924 TapeHaltRecording();
3928 // ----------------------------------------------------------------------------
3929 // functions for loading EM level
3930 // ----------------------------------------------------------------------------
3932 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3934 static int ball_xy[8][2] =
3945 struct LevelInfo_EM *level_em = level->native_em_level;
3946 struct CAVE *cav = level_em->cav;
3949 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3950 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3952 cav->time_seconds = level->time;
3953 cav->gems_needed = level->gems_needed;
3955 cav->emerald_score = level->score[SC_EMERALD];
3956 cav->diamond_score = level->score[SC_DIAMOND];
3957 cav->alien_score = level->score[SC_ROBOT];
3958 cav->tank_score = level->score[SC_SPACESHIP];
3959 cav->bug_score = level->score[SC_BUG];
3960 cav->eater_score = level->score[SC_YAMYAM];
3961 cav->nut_score = level->score[SC_NUT];
3962 cav->dynamite_score = level->score[SC_DYNAMITE];
3963 cav->key_score = level->score[SC_KEY];
3964 cav->exit_score = level->score[SC_TIME_BONUS];
3966 cav->num_eater_arrays = level->num_yamyam_contents;
3968 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3969 for (y = 0; y < 3; y++)
3970 for (x = 0; x < 3; x++)
3971 cav->eater_array[i][y * 3 + x] =
3972 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3974 cav->amoeba_time = level->amoeba_speed;
3975 cav->wonderwall_time = level->time_magic_wall;
3976 cav->wheel_time = level->time_wheel;
3978 cav->android_move_time = level->android_move_time;
3979 cav->android_clone_time = level->android_clone_time;
3980 cav->ball_random = level->ball_random;
3981 cav->ball_active = level->ball_active_initial;
3982 cav->ball_time = level->ball_time;
3983 cav->num_ball_arrays = level->num_ball_contents;
3985 cav->lenses_score = level->lenses_score;
3986 cav->magnify_score = level->magnify_score;
3987 cav->slurp_score = level->slurp_score;
3989 cav->lenses_time = level->lenses_time;
3990 cav->magnify_time = level->magnify_time;
3992 cav->wind_time = 9999;
3993 cav->wind_direction =
3994 map_direction_RND_to_EM(level->wind_direction_initial);
3996 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3997 for (j = 0; j < 8; j++)
3998 cav->ball_array[i][j] =
3999 map_element_RND_to_EM_cave(level->ball_content[i].
4000 e[ball_xy[j][0]][ball_xy[j][1]]);
4002 map_android_clone_elements_RND_to_EM(level);
4004 // first fill the complete playfield with the empty space element
4005 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4006 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4007 cav->cave[x][y] = Cblank;
4009 // then copy the real level contents from level file into the playfield
4010 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4012 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4014 if (level->field[x][y] == EL_AMOEBA_DEAD)
4015 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4017 cav->cave[x][y] = new_element;
4020 for (i = 0; i < MAX_PLAYERS; i++)
4022 cav->player_x[i] = -1;
4023 cav->player_y[i] = -1;
4026 // initialize player positions and delete players from the playfield
4027 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4029 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4031 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4033 cav->player_x[player_nr] = x;
4034 cav->player_y[player_nr] = y;
4036 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4041 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4043 static int ball_xy[8][2] =
4054 struct LevelInfo_EM *level_em = level->native_em_level;
4055 struct CAVE *cav = level_em->cav;
4058 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4059 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4061 level->time = cav->time_seconds;
4062 level->gems_needed = cav->gems_needed;
4064 sprintf(level->name, "Level %d", level->file_info.nr);
4066 level->score[SC_EMERALD] = cav->emerald_score;
4067 level->score[SC_DIAMOND] = cav->diamond_score;
4068 level->score[SC_ROBOT] = cav->alien_score;
4069 level->score[SC_SPACESHIP] = cav->tank_score;
4070 level->score[SC_BUG] = cav->bug_score;
4071 level->score[SC_YAMYAM] = cav->eater_score;
4072 level->score[SC_NUT] = cav->nut_score;
4073 level->score[SC_DYNAMITE] = cav->dynamite_score;
4074 level->score[SC_KEY] = cav->key_score;
4075 level->score[SC_TIME_BONUS] = cav->exit_score;
4077 level->num_yamyam_contents = cav->num_eater_arrays;
4079 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4080 for (y = 0; y < 3; y++)
4081 for (x = 0; x < 3; x++)
4082 level->yamyam_content[i].e[x][y] =
4083 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4085 level->amoeba_speed = cav->amoeba_time;
4086 level->time_magic_wall = cav->wonderwall_time;
4087 level->time_wheel = cav->wheel_time;
4089 level->android_move_time = cav->android_move_time;
4090 level->android_clone_time = cav->android_clone_time;
4091 level->ball_random = cav->ball_random;
4092 level->ball_active_initial = cav->ball_active;
4093 level->ball_time = cav->ball_time;
4094 level->num_ball_contents = cav->num_ball_arrays;
4096 level->lenses_score = cav->lenses_score;
4097 level->magnify_score = cav->magnify_score;
4098 level->slurp_score = cav->slurp_score;
4100 level->lenses_time = cav->lenses_time;
4101 level->magnify_time = cav->magnify_time;
4103 level->wind_direction_initial =
4104 map_direction_EM_to_RND(cav->wind_direction);
4106 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4107 for (j = 0; j < 8; j++)
4108 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4109 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4111 map_android_clone_elements_EM_to_RND(level);
4113 // convert the playfield (some elements need special treatment)
4114 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4116 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4118 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4119 new_element = EL_AMOEBA_DEAD;
4121 level->field[x][y] = new_element;
4124 for (i = 0; i < MAX_PLAYERS; i++)
4126 // in case of all players set to the same field, use the first player
4127 int nr = MAX_PLAYERS - i - 1;
4128 int jx = cav->player_x[nr];
4129 int jy = cav->player_y[nr];
4131 if (jx != -1 && jy != -1)
4132 level->field[jx][jy] = EL_PLAYER_1 + nr;
4135 // time score is counted for each 10 seconds left in Emerald Mine levels
4136 level->time_score_base = 10;
4140 // ----------------------------------------------------------------------------
4141 // functions for loading SP level
4142 // ----------------------------------------------------------------------------
4144 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4146 struct LevelInfo_SP *level_sp = level->native_sp_level;
4147 LevelInfoType *header = &level_sp->header;
4150 level_sp->width = level->fieldx;
4151 level_sp->height = level->fieldy;
4153 for (x = 0; x < level->fieldx; x++)
4154 for (y = 0; y < level->fieldy; y++)
4155 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4157 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4159 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4160 header->LevelTitle[i] = level->name[i];
4161 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4163 header->InfotronsNeeded = level->gems_needed;
4165 header->SpecialPortCount = 0;
4167 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4169 boolean gravity_port_found = FALSE;
4170 boolean gravity_port_valid = FALSE;
4171 int gravity_port_flag;
4172 int gravity_port_base_element;
4173 int element = level->field[x][y];
4175 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4176 element <= EL_SP_GRAVITY_ON_PORT_UP)
4178 gravity_port_found = TRUE;
4179 gravity_port_valid = TRUE;
4180 gravity_port_flag = 1;
4181 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4183 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4184 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4186 gravity_port_found = TRUE;
4187 gravity_port_valid = TRUE;
4188 gravity_port_flag = 0;
4189 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4191 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4192 element <= EL_SP_GRAVITY_PORT_UP)
4194 // change R'n'D style gravity inverting special port to normal port
4195 // (there are no gravity inverting ports in native Supaplex engine)
4197 gravity_port_found = TRUE;
4198 gravity_port_valid = FALSE;
4199 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4202 if (gravity_port_found)
4204 if (gravity_port_valid &&
4205 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4207 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4209 port->PortLocation = (y * level->fieldx + x) * 2;
4210 port->Gravity = gravity_port_flag;
4212 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4214 header->SpecialPortCount++;
4218 // change special gravity port to normal port
4220 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4223 level_sp->playfield[x][y] = element - EL_SP_START;
4228 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4230 struct LevelInfo_SP *level_sp = level->native_sp_level;
4231 LevelInfoType *header = &level_sp->header;
4232 boolean num_invalid_elements = 0;
4235 level->fieldx = level_sp->width;
4236 level->fieldy = level_sp->height;
4238 for (x = 0; x < level->fieldx; x++)
4240 for (y = 0; y < level->fieldy; y++)
4242 int element_old = level_sp->playfield[x][y];
4243 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4245 if (element_new == EL_UNKNOWN)
4247 num_invalid_elements++;
4249 Debug("level:native:SP", "invalid element %d at position %d, %d",
4253 level->field[x][y] = element_new;
4257 if (num_invalid_elements > 0)
4258 Warn("found %d invalid elements%s", num_invalid_elements,
4259 (!options.debug ? " (use '--debug' for more details)" : ""));
4261 for (i = 0; i < MAX_PLAYERS; i++)
4262 level->initial_player_gravity[i] =
4263 (header->InitialGravity == 1 ? TRUE : FALSE);
4265 // skip leading spaces
4266 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4267 if (header->LevelTitle[i] != ' ')
4271 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4272 level->name[j] = header->LevelTitle[i];
4273 level->name[j] = '\0';
4275 // cut trailing spaces
4277 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4278 level->name[j - 1] = '\0';
4280 level->gems_needed = header->InfotronsNeeded;
4282 for (i = 0; i < header->SpecialPortCount; i++)
4284 SpecialPortType *port = &header->SpecialPort[i];
4285 int port_location = port->PortLocation;
4286 int gravity = port->Gravity;
4287 int port_x, port_y, port_element;
4289 port_x = (port_location / 2) % level->fieldx;
4290 port_y = (port_location / 2) / level->fieldx;
4292 if (port_x < 0 || port_x >= level->fieldx ||
4293 port_y < 0 || port_y >= level->fieldy)
4295 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4300 port_element = level->field[port_x][port_y];
4302 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4303 port_element > EL_SP_GRAVITY_PORT_UP)
4305 Warn("no special port at position (%d, %d)", port_x, port_y);
4310 // change previous (wrong) gravity inverting special port to either
4311 // gravity enabling special port or gravity disabling special port
4312 level->field[port_x][port_y] +=
4313 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4314 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4317 // change special gravity ports without database entries to normal ports
4318 for (x = 0; x < level->fieldx; x++)
4319 for (y = 0; y < level->fieldy; y++)
4320 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4321 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4322 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4324 level->time = 0; // no time limit
4325 level->amoeba_speed = 0;
4326 level->time_magic_wall = 0;
4327 level->time_wheel = 0;
4328 level->amoeba_content = EL_EMPTY;
4330 // original Supaplex does not use score values -- rate by playing time
4331 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4332 level->score[i] = 0;
4334 level->rate_time_over_score = TRUE;
4336 // there are no yamyams in supaplex levels
4337 for (i = 0; i < level->num_yamyam_contents; i++)
4338 for (x = 0; x < 3; x++)
4339 for (y = 0; y < 3; y++)
4340 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4343 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4345 struct LevelInfo_SP *level_sp = level->native_sp_level;
4346 struct DemoInfo_SP *demo = &level_sp->demo;
4349 // always start with reliable default values
4350 demo->is_available = FALSE;
4353 if (TAPE_IS_EMPTY(tape))
4356 demo->level_nr = tape.level_nr; // (currently not used)
4358 level_sp->header.DemoRandomSeed = tape.random_seed;
4362 for (i = 0; i < tape.length; i++)
4364 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4365 int demo_repeat = tape.pos[i].delay;
4366 int demo_entries = (demo_repeat + 15) / 16;
4368 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4370 Warn("tape truncated: size exceeds maximum SP demo size %d",
4376 for (j = 0; j < demo_repeat / 16; j++)
4377 demo->data[demo->length++] = 0xf0 | demo_action;
4379 if (demo_repeat % 16)
4380 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4383 demo->is_available = TRUE;
4386 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4388 struct LevelInfo_SP *level_sp = level->native_sp_level;
4389 struct DemoInfo_SP *demo = &level_sp->demo;
4390 char *filename = level->file_info.filename;
4393 // always start with reliable default values
4394 setTapeInfoToDefaults();
4396 if (!demo->is_available)
4399 tape.level_nr = demo->level_nr; // (currently not used)
4400 tape.random_seed = level_sp->header.DemoRandomSeed;
4402 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4405 tape.pos[tape.counter].delay = 0;
4407 for (i = 0; i < demo->length; i++)
4409 int demo_action = demo->data[i] & 0x0f;
4410 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4411 int tape_action = map_key_SP_to_RND(demo_action);
4412 int tape_repeat = demo_repeat + 1;
4413 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4414 boolean success = 0;
4417 for (j = 0; j < tape_repeat; j++)
4418 success = TapeAddAction(action);
4422 Warn("SP demo truncated: size exceeds maximum tape size %d",
4429 TapeHaltRecording();
4433 // ----------------------------------------------------------------------------
4434 // functions for loading MM level
4435 // ----------------------------------------------------------------------------
4437 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4439 struct LevelInfo_MM *level_mm = level->native_mm_level;
4442 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4443 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4445 level_mm->time = level->time;
4446 level_mm->kettles_needed = level->gems_needed;
4447 level_mm->auto_count_kettles = level->auto_count_gems;
4449 level_mm->mm_laser_red = level->mm_laser_red;
4450 level_mm->mm_laser_green = level->mm_laser_green;
4451 level_mm->mm_laser_blue = level->mm_laser_blue;
4453 level_mm->df_laser_red = level->df_laser_red;
4454 level_mm->df_laser_green = level->df_laser_green;
4455 level_mm->df_laser_blue = level->df_laser_blue;
4457 strcpy(level_mm->name, level->name);
4458 strcpy(level_mm->author, level->author);
4460 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4461 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4462 level_mm->score[SC_KEY] = level->score[SC_KEY];
4463 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4464 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4466 level_mm->amoeba_speed = level->amoeba_speed;
4467 level_mm->time_fuse = level->mm_time_fuse;
4468 level_mm->time_bomb = level->mm_time_bomb;
4469 level_mm->time_ball = level->mm_time_ball;
4470 level_mm->time_block = level->mm_time_block;
4472 level_mm->num_ball_contents = level->num_mm_ball_contents;
4473 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4474 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4475 level_mm->explode_ball = level->explode_mm_ball;
4477 for (i = 0; i < level->num_mm_ball_contents; i++)
4478 level_mm->ball_content[i] =
4479 map_element_RND_to_MM(level->mm_ball_content[i]);
4481 for (x = 0; x < level->fieldx; x++)
4482 for (y = 0; y < level->fieldy; y++)
4484 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4487 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4489 struct LevelInfo_MM *level_mm = level->native_mm_level;
4492 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4493 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4495 level->time = level_mm->time;
4496 level->gems_needed = level_mm->kettles_needed;
4497 level->auto_count_gems = level_mm->auto_count_kettles;
4499 level->mm_laser_red = level_mm->mm_laser_red;
4500 level->mm_laser_green = level_mm->mm_laser_green;
4501 level->mm_laser_blue = level_mm->mm_laser_blue;
4503 level->df_laser_red = level_mm->df_laser_red;
4504 level->df_laser_green = level_mm->df_laser_green;
4505 level->df_laser_blue = level_mm->df_laser_blue;
4507 strcpy(level->name, level_mm->name);
4509 // only overwrite author from 'levelinfo.conf' if author defined in level
4510 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4511 strcpy(level->author, level_mm->author);
4513 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4514 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4515 level->score[SC_KEY] = level_mm->score[SC_KEY];
4516 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4517 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4519 level->amoeba_speed = level_mm->amoeba_speed;
4520 level->mm_time_fuse = level_mm->time_fuse;
4521 level->mm_time_bomb = level_mm->time_bomb;
4522 level->mm_time_ball = level_mm->time_ball;
4523 level->mm_time_block = level_mm->time_block;
4525 level->num_mm_ball_contents = level_mm->num_ball_contents;
4526 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4527 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4528 level->explode_mm_ball = level_mm->explode_ball;
4530 for (i = 0; i < level->num_mm_ball_contents; i++)
4531 level->mm_ball_content[i] =
4532 map_element_MM_to_RND(level_mm->ball_content[i]);
4534 for (x = 0; x < level->fieldx; x++)
4535 for (y = 0; y < level->fieldy; y++)
4536 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4540 // ----------------------------------------------------------------------------
4541 // functions for loading DC level
4542 // ----------------------------------------------------------------------------
4544 #define DC_LEVEL_HEADER_SIZE 344
4546 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4549 static int last_data_encoded;
4553 int diff_hi, diff_lo;
4554 int data_hi, data_lo;
4555 unsigned short data_decoded;
4559 last_data_encoded = 0;
4566 diff = data_encoded - last_data_encoded;
4567 diff_hi = diff & ~0xff;
4568 diff_lo = diff & 0xff;
4572 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4573 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4574 data_hi = data_hi & 0xff00;
4576 data_decoded = data_hi | data_lo;
4578 last_data_encoded = data_encoded;
4580 offset1 = (offset1 + 1) % 31;
4581 offset2 = offset2 & 0xff;
4583 return data_decoded;
4586 static int getMappedElement_DC(int element)
4594 // 0x0117 - 0x036e: (?)
4597 // 0x042d - 0x0684: (?)
4613 element = EL_CRYSTAL;
4616 case 0x0e77: // quicksand (boulder)
4617 element = EL_QUICKSAND_FAST_FULL;
4620 case 0x0e99: // slow quicksand (boulder)
4621 element = EL_QUICKSAND_FULL;
4625 element = EL_EM_EXIT_OPEN;
4629 element = EL_EM_EXIT_CLOSED;
4633 element = EL_EM_STEEL_EXIT_OPEN;
4637 element = EL_EM_STEEL_EXIT_CLOSED;
4640 case 0x0f4f: // dynamite (lit 1)
4641 element = EL_EM_DYNAMITE_ACTIVE;
4644 case 0x0f57: // dynamite (lit 2)
4645 element = EL_EM_DYNAMITE_ACTIVE;
4648 case 0x0f5f: // dynamite (lit 3)
4649 element = EL_EM_DYNAMITE_ACTIVE;
4652 case 0x0f67: // dynamite (lit 4)
4653 element = EL_EM_DYNAMITE_ACTIVE;
4660 element = EL_AMOEBA_WET;
4664 element = EL_AMOEBA_DROP;
4668 element = EL_DC_MAGIC_WALL;
4672 element = EL_SPACESHIP_UP;
4676 element = EL_SPACESHIP_DOWN;
4680 element = EL_SPACESHIP_LEFT;
4684 element = EL_SPACESHIP_RIGHT;
4688 element = EL_BUG_UP;
4692 element = EL_BUG_DOWN;
4696 element = EL_BUG_LEFT;
4700 element = EL_BUG_RIGHT;
4704 element = EL_MOLE_UP;
4708 element = EL_MOLE_DOWN;
4712 element = EL_MOLE_LEFT;
4716 element = EL_MOLE_RIGHT;
4724 element = EL_YAMYAM_UP;
4728 element = EL_SWITCHGATE_OPEN;
4732 element = EL_SWITCHGATE_CLOSED;
4736 element = EL_DC_SWITCHGATE_SWITCH_UP;
4740 element = EL_TIMEGATE_CLOSED;
4743 case 0x144c: // conveyor belt switch (green)
4744 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4747 case 0x144f: // conveyor belt switch (red)
4748 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4751 case 0x1452: // conveyor belt switch (blue)
4752 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4756 element = EL_CONVEYOR_BELT_3_MIDDLE;
4760 element = EL_CONVEYOR_BELT_3_LEFT;
4764 element = EL_CONVEYOR_BELT_3_RIGHT;
4768 element = EL_CONVEYOR_BELT_1_MIDDLE;
4772 element = EL_CONVEYOR_BELT_1_LEFT;
4776 element = EL_CONVEYOR_BELT_1_RIGHT;
4780 element = EL_CONVEYOR_BELT_4_MIDDLE;
4784 element = EL_CONVEYOR_BELT_4_LEFT;
4788 element = EL_CONVEYOR_BELT_4_RIGHT;
4792 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4796 element = EL_EXPANDABLE_WALL_VERTICAL;
4800 element = EL_EXPANDABLE_WALL_ANY;
4803 case 0x14ce: // growing steel wall (left/right)
4804 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4807 case 0x14df: // growing steel wall (up/down)
4808 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4811 case 0x14e8: // growing steel wall (up/down/left/right)
4812 element = EL_EXPANDABLE_STEELWALL_ANY;
4816 element = EL_SHIELD_DEADLY;
4820 element = EL_EXTRA_TIME;
4828 element = EL_EMPTY_SPACE;
4831 case 0x1578: // quicksand (empty)
4832 element = EL_QUICKSAND_FAST_EMPTY;
4835 case 0x1579: // slow quicksand (empty)
4836 element = EL_QUICKSAND_EMPTY;
4846 element = EL_EM_DYNAMITE;
4849 case 0x15a1: // key (red)
4850 element = EL_EM_KEY_1;
4853 case 0x15a2: // key (yellow)
4854 element = EL_EM_KEY_2;
4857 case 0x15a3: // key (blue)
4858 element = EL_EM_KEY_4;
4861 case 0x15a4: // key (green)
4862 element = EL_EM_KEY_3;
4865 case 0x15a5: // key (white)
4866 element = EL_DC_KEY_WHITE;
4870 element = EL_WALL_SLIPPERY;
4877 case 0x15a8: // wall (not round)
4881 case 0x15a9: // (blue)
4882 element = EL_CHAR_A;
4885 case 0x15aa: // (blue)
4886 element = EL_CHAR_B;
4889 case 0x15ab: // (blue)
4890 element = EL_CHAR_C;
4893 case 0x15ac: // (blue)
4894 element = EL_CHAR_D;
4897 case 0x15ad: // (blue)
4898 element = EL_CHAR_E;
4901 case 0x15ae: // (blue)
4902 element = EL_CHAR_F;
4905 case 0x15af: // (blue)
4906 element = EL_CHAR_G;
4909 case 0x15b0: // (blue)
4910 element = EL_CHAR_H;
4913 case 0x15b1: // (blue)
4914 element = EL_CHAR_I;
4917 case 0x15b2: // (blue)
4918 element = EL_CHAR_J;
4921 case 0x15b3: // (blue)
4922 element = EL_CHAR_K;
4925 case 0x15b4: // (blue)
4926 element = EL_CHAR_L;
4929 case 0x15b5: // (blue)
4930 element = EL_CHAR_M;
4933 case 0x15b6: // (blue)
4934 element = EL_CHAR_N;
4937 case 0x15b7: // (blue)
4938 element = EL_CHAR_O;
4941 case 0x15b8: // (blue)
4942 element = EL_CHAR_P;
4945 case 0x15b9: // (blue)
4946 element = EL_CHAR_Q;
4949 case 0x15ba: // (blue)
4950 element = EL_CHAR_R;
4953 case 0x15bb: // (blue)
4954 element = EL_CHAR_S;
4957 case 0x15bc: // (blue)
4958 element = EL_CHAR_T;
4961 case 0x15bd: // (blue)
4962 element = EL_CHAR_U;
4965 case 0x15be: // (blue)
4966 element = EL_CHAR_V;
4969 case 0x15bf: // (blue)
4970 element = EL_CHAR_W;
4973 case 0x15c0: // (blue)
4974 element = EL_CHAR_X;
4977 case 0x15c1: // (blue)
4978 element = EL_CHAR_Y;
4981 case 0x15c2: // (blue)
4982 element = EL_CHAR_Z;
4985 case 0x15c3: // (blue)
4986 element = EL_CHAR_AUMLAUT;
4989 case 0x15c4: // (blue)
4990 element = EL_CHAR_OUMLAUT;
4993 case 0x15c5: // (blue)
4994 element = EL_CHAR_UUMLAUT;
4997 case 0x15c6: // (blue)
4998 element = EL_CHAR_0;
5001 case 0x15c7: // (blue)
5002 element = EL_CHAR_1;
5005 case 0x15c8: // (blue)
5006 element = EL_CHAR_2;
5009 case 0x15c9: // (blue)
5010 element = EL_CHAR_3;
5013 case 0x15ca: // (blue)
5014 element = EL_CHAR_4;
5017 case 0x15cb: // (blue)
5018 element = EL_CHAR_5;
5021 case 0x15cc: // (blue)
5022 element = EL_CHAR_6;
5025 case 0x15cd: // (blue)
5026 element = EL_CHAR_7;
5029 case 0x15ce: // (blue)
5030 element = EL_CHAR_8;
5033 case 0x15cf: // (blue)
5034 element = EL_CHAR_9;
5037 case 0x15d0: // (blue)
5038 element = EL_CHAR_PERIOD;
5041 case 0x15d1: // (blue)
5042 element = EL_CHAR_EXCLAM;
5045 case 0x15d2: // (blue)
5046 element = EL_CHAR_COLON;
5049 case 0x15d3: // (blue)
5050 element = EL_CHAR_LESS;
5053 case 0x15d4: // (blue)
5054 element = EL_CHAR_GREATER;
5057 case 0x15d5: // (blue)
5058 element = EL_CHAR_QUESTION;
5061 case 0x15d6: // (blue)
5062 element = EL_CHAR_COPYRIGHT;
5065 case 0x15d7: // (blue)
5066 element = EL_CHAR_UP;
5069 case 0x15d8: // (blue)
5070 element = EL_CHAR_DOWN;
5073 case 0x15d9: // (blue)
5074 element = EL_CHAR_BUTTON;
5077 case 0x15da: // (blue)
5078 element = EL_CHAR_PLUS;
5081 case 0x15db: // (blue)
5082 element = EL_CHAR_MINUS;
5085 case 0x15dc: // (blue)
5086 element = EL_CHAR_APOSTROPHE;
5089 case 0x15dd: // (blue)
5090 element = EL_CHAR_PARENLEFT;
5093 case 0x15de: // (blue)
5094 element = EL_CHAR_PARENRIGHT;
5097 case 0x15df: // (green)
5098 element = EL_CHAR_A;
5101 case 0x15e0: // (green)
5102 element = EL_CHAR_B;
5105 case 0x15e1: // (green)
5106 element = EL_CHAR_C;
5109 case 0x15e2: // (green)
5110 element = EL_CHAR_D;
5113 case 0x15e3: // (green)
5114 element = EL_CHAR_E;
5117 case 0x15e4: // (green)
5118 element = EL_CHAR_F;
5121 case 0x15e5: // (green)
5122 element = EL_CHAR_G;
5125 case 0x15e6: // (green)
5126 element = EL_CHAR_H;
5129 case 0x15e7: // (green)
5130 element = EL_CHAR_I;
5133 case 0x15e8: // (green)
5134 element = EL_CHAR_J;
5137 case 0x15e9: // (green)
5138 element = EL_CHAR_K;
5141 case 0x15ea: // (green)
5142 element = EL_CHAR_L;
5145 case 0x15eb: // (green)
5146 element = EL_CHAR_M;
5149 case 0x15ec: // (green)
5150 element = EL_CHAR_N;
5153 case 0x15ed: // (green)
5154 element = EL_CHAR_O;
5157 case 0x15ee: // (green)
5158 element = EL_CHAR_P;
5161 case 0x15ef: // (green)
5162 element = EL_CHAR_Q;
5165 case 0x15f0: // (green)
5166 element = EL_CHAR_R;
5169 case 0x15f1: // (green)
5170 element = EL_CHAR_S;
5173 case 0x15f2: // (green)
5174 element = EL_CHAR_T;
5177 case 0x15f3: // (green)
5178 element = EL_CHAR_U;
5181 case 0x15f4: // (green)
5182 element = EL_CHAR_V;
5185 case 0x15f5: // (green)
5186 element = EL_CHAR_W;
5189 case 0x15f6: // (green)
5190 element = EL_CHAR_X;
5193 case 0x15f7: // (green)
5194 element = EL_CHAR_Y;
5197 case 0x15f8: // (green)
5198 element = EL_CHAR_Z;
5201 case 0x15f9: // (green)
5202 element = EL_CHAR_AUMLAUT;
5205 case 0x15fa: // (green)
5206 element = EL_CHAR_OUMLAUT;
5209 case 0x15fb: // (green)
5210 element = EL_CHAR_UUMLAUT;
5213 case 0x15fc: // (green)
5214 element = EL_CHAR_0;
5217 case 0x15fd: // (green)
5218 element = EL_CHAR_1;
5221 case 0x15fe: // (green)
5222 element = EL_CHAR_2;
5225 case 0x15ff: // (green)
5226 element = EL_CHAR_3;
5229 case 0x1600: // (green)
5230 element = EL_CHAR_4;
5233 case 0x1601: // (green)
5234 element = EL_CHAR_5;
5237 case 0x1602: // (green)
5238 element = EL_CHAR_6;
5241 case 0x1603: // (green)
5242 element = EL_CHAR_7;
5245 case 0x1604: // (green)
5246 element = EL_CHAR_8;
5249 case 0x1605: // (green)
5250 element = EL_CHAR_9;
5253 case 0x1606: // (green)
5254 element = EL_CHAR_PERIOD;
5257 case 0x1607: // (green)
5258 element = EL_CHAR_EXCLAM;
5261 case 0x1608: // (green)
5262 element = EL_CHAR_COLON;
5265 case 0x1609: // (green)
5266 element = EL_CHAR_LESS;
5269 case 0x160a: // (green)
5270 element = EL_CHAR_GREATER;
5273 case 0x160b: // (green)
5274 element = EL_CHAR_QUESTION;
5277 case 0x160c: // (green)
5278 element = EL_CHAR_COPYRIGHT;
5281 case 0x160d: // (green)
5282 element = EL_CHAR_UP;
5285 case 0x160e: // (green)
5286 element = EL_CHAR_DOWN;
5289 case 0x160f: // (green)
5290 element = EL_CHAR_BUTTON;
5293 case 0x1610: // (green)
5294 element = EL_CHAR_PLUS;
5297 case 0x1611: // (green)
5298 element = EL_CHAR_MINUS;
5301 case 0x1612: // (green)
5302 element = EL_CHAR_APOSTROPHE;
5305 case 0x1613: // (green)
5306 element = EL_CHAR_PARENLEFT;
5309 case 0x1614: // (green)
5310 element = EL_CHAR_PARENRIGHT;
5313 case 0x1615: // (blue steel)
5314 element = EL_STEEL_CHAR_A;
5317 case 0x1616: // (blue steel)
5318 element = EL_STEEL_CHAR_B;
5321 case 0x1617: // (blue steel)
5322 element = EL_STEEL_CHAR_C;
5325 case 0x1618: // (blue steel)
5326 element = EL_STEEL_CHAR_D;
5329 case 0x1619: // (blue steel)
5330 element = EL_STEEL_CHAR_E;
5333 case 0x161a: // (blue steel)
5334 element = EL_STEEL_CHAR_F;
5337 case 0x161b: // (blue steel)
5338 element = EL_STEEL_CHAR_G;
5341 case 0x161c: // (blue steel)
5342 element = EL_STEEL_CHAR_H;
5345 case 0x161d: // (blue steel)
5346 element = EL_STEEL_CHAR_I;
5349 case 0x161e: // (blue steel)
5350 element = EL_STEEL_CHAR_J;
5353 case 0x161f: // (blue steel)
5354 element = EL_STEEL_CHAR_K;
5357 case 0x1620: // (blue steel)
5358 element = EL_STEEL_CHAR_L;
5361 case 0x1621: // (blue steel)
5362 element = EL_STEEL_CHAR_M;
5365 case 0x1622: // (blue steel)
5366 element = EL_STEEL_CHAR_N;
5369 case 0x1623: // (blue steel)
5370 element = EL_STEEL_CHAR_O;
5373 case 0x1624: // (blue steel)
5374 element = EL_STEEL_CHAR_P;
5377 case 0x1625: // (blue steel)
5378 element = EL_STEEL_CHAR_Q;
5381 case 0x1626: // (blue steel)
5382 element = EL_STEEL_CHAR_R;
5385 case 0x1627: // (blue steel)
5386 element = EL_STEEL_CHAR_S;
5389 case 0x1628: // (blue steel)
5390 element = EL_STEEL_CHAR_T;
5393 case 0x1629: // (blue steel)
5394 element = EL_STEEL_CHAR_U;
5397 case 0x162a: // (blue steel)
5398 element = EL_STEEL_CHAR_V;
5401 case 0x162b: // (blue steel)
5402 element = EL_STEEL_CHAR_W;
5405 case 0x162c: // (blue steel)
5406 element = EL_STEEL_CHAR_X;
5409 case 0x162d: // (blue steel)
5410 element = EL_STEEL_CHAR_Y;
5413 case 0x162e: // (blue steel)
5414 element = EL_STEEL_CHAR_Z;
5417 case 0x162f: // (blue steel)
5418 element = EL_STEEL_CHAR_AUMLAUT;
5421 case 0x1630: // (blue steel)
5422 element = EL_STEEL_CHAR_OUMLAUT;
5425 case 0x1631: // (blue steel)
5426 element = EL_STEEL_CHAR_UUMLAUT;
5429 case 0x1632: // (blue steel)
5430 element = EL_STEEL_CHAR_0;
5433 case 0x1633: // (blue steel)
5434 element = EL_STEEL_CHAR_1;
5437 case 0x1634: // (blue steel)
5438 element = EL_STEEL_CHAR_2;
5441 case 0x1635: // (blue steel)
5442 element = EL_STEEL_CHAR_3;
5445 case 0x1636: // (blue steel)
5446 element = EL_STEEL_CHAR_4;
5449 case 0x1637: // (blue steel)
5450 element = EL_STEEL_CHAR_5;
5453 case 0x1638: // (blue steel)
5454 element = EL_STEEL_CHAR_6;
5457 case 0x1639: // (blue steel)
5458 element = EL_STEEL_CHAR_7;
5461 case 0x163a: // (blue steel)
5462 element = EL_STEEL_CHAR_8;
5465 case 0x163b: // (blue steel)
5466 element = EL_STEEL_CHAR_9;
5469 case 0x163c: // (blue steel)
5470 element = EL_STEEL_CHAR_PERIOD;
5473 case 0x163d: // (blue steel)
5474 element = EL_STEEL_CHAR_EXCLAM;
5477 case 0x163e: // (blue steel)
5478 element = EL_STEEL_CHAR_COLON;
5481 case 0x163f: // (blue steel)
5482 element = EL_STEEL_CHAR_LESS;
5485 case 0x1640: // (blue steel)
5486 element = EL_STEEL_CHAR_GREATER;
5489 case 0x1641: // (blue steel)
5490 element = EL_STEEL_CHAR_QUESTION;
5493 case 0x1642: // (blue steel)
5494 element = EL_STEEL_CHAR_COPYRIGHT;
5497 case 0x1643: // (blue steel)
5498 element = EL_STEEL_CHAR_UP;
5501 case 0x1644: // (blue steel)
5502 element = EL_STEEL_CHAR_DOWN;
5505 case 0x1645: // (blue steel)
5506 element = EL_STEEL_CHAR_BUTTON;
5509 case 0x1646: // (blue steel)
5510 element = EL_STEEL_CHAR_PLUS;
5513 case 0x1647: // (blue steel)
5514 element = EL_STEEL_CHAR_MINUS;
5517 case 0x1648: // (blue steel)
5518 element = EL_STEEL_CHAR_APOSTROPHE;
5521 case 0x1649: // (blue steel)
5522 element = EL_STEEL_CHAR_PARENLEFT;
5525 case 0x164a: // (blue steel)
5526 element = EL_STEEL_CHAR_PARENRIGHT;
5529 case 0x164b: // (green steel)
5530 element = EL_STEEL_CHAR_A;
5533 case 0x164c: // (green steel)
5534 element = EL_STEEL_CHAR_B;
5537 case 0x164d: // (green steel)
5538 element = EL_STEEL_CHAR_C;
5541 case 0x164e: // (green steel)
5542 element = EL_STEEL_CHAR_D;
5545 case 0x164f: // (green steel)
5546 element = EL_STEEL_CHAR_E;
5549 case 0x1650: // (green steel)
5550 element = EL_STEEL_CHAR_F;
5553 case 0x1651: // (green steel)
5554 element = EL_STEEL_CHAR_G;
5557 case 0x1652: // (green steel)
5558 element = EL_STEEL_CHAR_H;
5561 case 0x1653: // (green steel)
5562 element = EL_STEEL_CHAR_I;
5565 case 0x1654: // (green steel)
5566 element = EL_STEEL_CHAR_J;
5569 case 0x1655: // (green steel)
5570 element = EL_STEEL_CHAR_K;
5573 case 0x1656: // (green steel)
5574 element = EL_STEEL_CHAR_L;
5577 case 0x1657: // (green steel)
5578 element = EL_STEEL_CHAR_M;
5581 case 0x1658: // (green steel)
5582 element = EL_STEEL_CHAR_N;
5585 case 0x1659: // (green steel)
5586 element = EL_STEEL_CHAR_O;
5589 case 0x165a: // (green steel)
5590 element = EL_STEEL_CHAR_P;
5593 case 0x165b: // (green steel)
5594 element = EL_STEEL_CHAR_Q;
5597 case 0x165c: // (green steel)
5598 element = EL_STEEL_CHAR_R;
5601 case 0x165d: // (green steel)
5602 element = EL_STEEL_CHAR_S;
5605 case 0x165e: // (green steel)
5606 element = EL_STEEL_CHAR_T;
5609 case 0x165f: // (green steel)
5610 element = EL_STEEL_CHAR_U;
5613 case 0x1660: // (green steel)
5614 element = EL_STEEL_CHAR_V;
5617 case 0x1661: // (green steel)
5618 element = EL_STEEL_CHAR_W;
5621 case 0x1662: // (green steel)
5622 element = EL_STEEL_CHAR_X;
5625 case 0x1663: // (green steel)
5626 element = EL_STEEL_CHAR_Y;
5629 case 0x1664: // (green steel)
5630 element = EL_STEEL_CHAR_Z;
5633 case 0x1665: // (green steel)
5634 element = EL_STEEL_CHAR_AUMLAUT;
5637 case 0x1666: // (green steel)
5638 element = EL_STEEL_CHAR_OUMLAUT;
5641 case 0x1667: // (green steel)
5642 element = EL_STEEL_CHAR_UUMLAUT;
5645 case 0x1668: // (green steel)
5646 element = EL_STEEL_CHAR_0;
5649 case 0x1669: // (green steel)
5650 element = EL_STEEL_CHAR_1;
5653 case 0x166a: // (green steel)
5654 element = EL_STEEL_CHAR_2;
5657 case 0x166b: // (green steel)
5658 element = EL_STEEL_CHAR_3;
5661 case 0x166c: // (green steel)
5662 element = EL_STEEL_CHAR_4;
5665 case 0x166d: // (green steel)
5666 element = EL_STEEL_CHAR_5;
5669 case 0x166e: // (green steel)
5670 element = EL_STEEL_CHAR_6;
5673 case 0x166f: // (green steel)
5674 element = EL_STEEL_CHAR_7;
5677 case 0x1670: // (green steel)
5678 element = EL_STEEL_CHAR_8;
5681 case 0x1671: // (green steel)
5682 element = EL_STEEL_CHAR_9;
5685 case 0x1672: // (green steel)
5686 element = EL_STEEL_CHAR_PERIOD;
5689 case 0x1673: // (green steel)
5690 element = EL_STEEL_CHAR_EXCLAM;
5693 case 0x1674: // (green steel)
5694 element = EL_STEEL_CHAR_COLON;
5697 case 0x1675: // (green steel)
5698 element = EL_STEEL_CHAR_LESS;
5701 case 0x1676: // (green steel)
5702 element = EL_STEEL_CHAR_GREATER;
5705 case 0x1677: // (green steel)
5706 element = EL_STEEL_CHAR_QUESTION;
5709 case 0x1678: // (green steel)
5710 element = EL_STEEL_CHAR_COPYRIGHT;
5713 case 0x1679: // (green steel)
5714 element = EL_STEEL_CHAR_UP;
5717 case 0x167a: // (green steel)
5718 element = EL_STEEL_CHAR_DOWN;
5721 case 0x167b: // (green steel)
5722 element = EL_STEEL_CHAR_BUTTON;
5725 case 0x167c: // (green steel)
5726 element = EL_STEEL_CHAR_PLUS;
5729 case 0x167d: // (green steel)
5730 element = EL_STEEL_CHAR_MINUS;
5733 case 0x167e: // (green steel)
5734 element = EL_STEEL_CHAR_APOSTROPHE;
5737 case 0x167f: // (green steel)
5738 element = EL_STEEL_CHAR_PARENLEFT;
5741 case 0x1680: // (green steel)
5742 element = EL_STEEL_CHAR_PARENRIGHT;
5745 case 0x1681: // gate (red)
5746 element = EL_EM_GATE_1;
5749 case 0x1682: // secret gate (red)
5750 element = EL_EM_GATE_1_GRAY;
5753 case 0x1683: // gate (yellow)
5754 element = EL_EM_GATE_2;
5757 case 0x1684: // secret gate (yellow)
5758 element = EL_EM_GATE_2_GRAY;
5761 case 0x1685: // gate (blue)
5762 element = EL_EM_GATE_4;
5765 case 0x1686: // secret gate (blue)
5766 element = EL_EM_GATE_4_GRAY;
5769 case 0x1687: // gate (green)
5770 element = EL_EM_GATE_3;
5773 case 0x1688: // secret gate (green)
5774 element = EL_EM_GATE_3_GRAY;
5777 case 0x1689: // gate (white)
5778 element = EL_DC_GATE_WHITE;
5781 case 0x168a: // secret gate (white)
5782 element = EL_DC_GATE_WHITE_GRAY;
5785 case 0x168b: // secret gate (no key)
5786 element = EL_DC_GATE_FAKE_GRAY;
5790 element = EL_ROBOT_WHEEL;
5794 element = EL_DC_TIMEGATE_SWITCH;
5798 element = EL_ACID_POOL_BOTTOM;
5802 element = EL_ACID_POOL_TOPLEFT;
5806 element = EL_ACID_POOL_TOPRIGHT;
5810 element = EL_ACID_POOL_BOTTOMLEFT;
5814 element = EL_ACID_POOL_BOTTOMRIGHT;
5818 element = EL_STEELWALL;
5822 element = EL_STEELWALL_SLIPPERY;
5825 case 0x1695: // steel wall (not round)
5826 element = EL_STEELWALL;
5829 case 0x1696: // steel wall (left)
5830 element = EL_DC_STEELWALL_1_LEFT;
5833 case 0x1697: // steel wall (bottom)
5834 element = EL_DC_STEELWALL_1_BOTTOM;
5837 case 0x1698: // steel wall (right)
5838 element = EL_DC_STEELWALL_1_RIGHT;
5841 case 0x1699: // steel wall (top)
5842 element = EL_DC_STEELWALL_1_TOP;
5845 case 0x169a: // steel wall (left/bottom)
5846 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5849 case 0x169b: // steel wall (right/bottom)
5850 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5853 case 0x169c: // steel wall (right/top)
5854 element = EL_DC_STEELWALL_1_TOPRIGHT;
5857 case 0x169d: // steel wall (left/top)
5858 element = EL_DC_STEELWALL_1_TOPLEFT;
5861 case 0x169e: // steel wall (right/bottom small)
5862 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5865 case 0x169f: // steel wall (left/bottom small)
5866 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5869 case 0x16a0: // steel wall (right/top small)
5870 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5873 case 0x16a1: // steel wall (left/top small)
5874 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5877 case 0x16a2: // steel wall (left/right)
5878 element = EL_DC_STEELWALL_1_VERTICAL;
5881 case 0x16a3: // steel wall (top/bottom)
5882 element = EL_DC_STEELWALL_1_HORIZONTAL;
5885 case 0x16a4: // steel wall 2 (left end)
5886 element = EL_DC_STEELWALL_2_LEFT;
5889 case 0x16a5: // steel wall 2 (right end)
5890 element = EL_DC_STEELWALL_2_RIGHT;
5893 case 0x16a6: // steel wall 2 (top end)
5894 element = EL_DC_STEELWALL_2_TOP;
5897 case 0x16a7: // steel wall 2 (bottom end)
5898 element = EL_DC_STEELWALL_2_BOTTOM;
5901 case 0x16a8: // steel wall 2 (left/right)
5902 element = EL_DC_STEELWALL_2_HORIZONTAL;
5905 case 0x16a9: // steel wall 2 (up/down)
5906 element = EL_DC_STEELWALL_2_VERTICAL;
5909 case 0x16aa: // steel wall 2 (mid)
5910 element = EL_DC_STEELWALL_2_MIDDLE;
5914 element = EL_SIGN_EXCLAMATION;
5918 element = EL_SIGN_RADIOACTIVITY;
5922 element = EL_SIGN_STOP;
5926 element = EL_SIGN_WHEELCHAIR;
5930 element = EL_SIGN_PARKING;
5934 element = EL_SIGN_NO_ENTRY;
5938 element = EL_SIGN_HEART;
5942 element = EL_SIGN_GIVE_WAY;
5946 element = EL_SIGN_ENTRY_FORBIDDEN;
5950 element = EL_SIGN_EMERGENCY_EXIT;
5954 element = EL_SIGN_YIN_YANG;
5958 element = EL_WALL_EMERALD;
5962 element = EL_WALL_DIAMOND;
5966 element = EL_WALL_PEARL;
5970 element = EL_WALL_CRYSTAL;
5974 element = EL_INVISIBLE_WALL;
5978 element = EL_INVISIBLE_STEELWALL;
5982 // EL_INVISIBLE_SAND
5985 element = EL_LIGHT_SWITCH;
5989 element = EL_ENVELOPE_1;
5993 if (element >= 0x0117 && element <= 0x036e) // (?)
5994 element = EL_DIAMOND;
5995 else if (element >= 0x042d && element <= 0x0684) // (?)
5996 element = EL_EMERALD;
5997 else if (element >= 0x157c && element <= 0x158b)
5999 else if (element >= 0x1590 && element <= 0x159f)
6000 element = EL_DC_LANDMINE;
6001 else if (element >= 0x16bc && element <= 0x16cb)
6002 element = EL_INVISIBLE_SAND;
6005 Warn("unknown Diamond Caves element 0x%04x", element);
6007 element = EL_UNKNOWN;
6012 return getMappedElement(element);
6015 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6017 byte header[DC_LEVEL_HEADER_SIZE];
6019 int envelope_header_pos = 62;
6020 int envelope_content_pos = 94;
6021 int level_name_pos = 251;
6022 int level_author_pos = 292;
6023 int envelope_header_len;
6024 int envelope_content_len;
6026 int level_author_len;
6028 int num_yamyam_contents;
6031 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6033 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6035 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6037 header[i * 2 + 0] = header_word >> 8;
6038 header[i * 2 + 1] = header_word & 0xff;
6041 // read some values from level header to check level decoding integrity
6042 fieldx = header[6] | (header[7] << 8);
6043 fieldy = header[8] | (header[9] << 8);
6044 num_yamyam_contents = header[60] | (header[61] << 8);
6046 // do some simple sanity checks to ensure that level was correctly decoded
6047 if (fieldx < 1 || fieldx > 256 ||
6048 fieldy < 1 || fieldy > 256 ||
6049 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6051 level->no_valid_file = TRUE;
6053 Warn("cannot decode level from stream -- using empty level");
6058 // maximum envelope header size is 31 bytes
6059 envelope_header_len = header[envelope_header_pos];
6060 // maximum envelope content size is 110 (156?) bytes
6061 envelope_content_len = header[envelope_content_pos];
6063 // maximum level title size is 40 bytes
6064 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6065 // maximum level author size is 30 (51?) bytes
6066 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6070 for (i = 0; i < envelope_header_len; i++)
6071 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6072 level->envelope[0].text[envelope_size++] =
6073 header[envelope_header_pos + 1 + i];
6075 if (envelope_header_len > 0 && envelope_content_len > 0)
6077 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6078 level->envelope[0].text[envelope_size++] = '\n';
6079 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6080 level->envelope[0].text[envelope_size++] = '\n';
6083 for (i = 0; i < envelope_content_len; i++)
6084 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6085 level->envelope[0].text[envelope_size++] =
6086 header[envelope_content_pos + 1 + i];
6088 level->envelope[0].text[envelope_size] = '\0';
6090 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6091 level->envelope[0].ysize = 10;
6092 level->envelope[0].autowrap = TRUE;
6093 level->envelope[0].centered = TRUE;
6095 for (i = 0; i < level_name_len; i++)
6096 level->name[i] = header[level_name_pos + 1 + i];
6097 level->name[level_name_len] = '\0';
6099 for (i = 0; i < level_author_len; i++)
6100 level->author[i] = header[level_author_pos + 1 + i];
6101 level->author[level_author_len] = '\0';
6103 num_yamyam_contents = header[60] | (header[61] << 8);
6104 level->num_yamyam_contents =
6105 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6107 for (i = 0; i < num_yamyam_contents; i++)
6109 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6111 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6112 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6114 if (i < MAX_ELEMENT_CONTENTS)
6115 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6119 fieldx = header[6] | (header[7] << 8);
6120 fieldy = header[8] | (header[9] << 8);
6121 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6122 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6124 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6126 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6127 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6129 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6130 level->field[x][y] = getMappedElement_DC(element_dc);
6133 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6134 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6135 level->field[x][y] = EL_PLAYER_1;
6137 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6138 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6139 level->field[x][y] = EL_PLAYER_2;
6141 level->gems_needed = header[18] | (header[19] << 8);
6143 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6144 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6145 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6146 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6147 level->score[SC_NUT] = header[28] | (header[29] << 8);
6148 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6149 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6150 level->score[SC_BUG] = header[34] | (header[35] << 8);
6151 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6152 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6153 level->score[SC_KEY] = header[40] | (header[41] << 8);
6154 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6156 level->time = header[44] | (header[45] << 8);
6158 level->amoeba_speed = header[46] | (header[47] << 8);
6159 level->time_light = header[48] | (header[49] << 8);
6160 level->time_timegate = header[50] | (header[51] << 8);
6161 level->time_wheel = header[52] | (header[53] << 8);
6162 level->time_magic_wall = header[54] | (header[55] << 8);
6163 level->extra_time = header[56] | (header[57] << 8);
6164 level->shield_normal_time = header[58] | (header[59] << 8);
6166 // shield and extra time elements do not have a score
6167 level->score[SC_SHIELD] = 0;
6168 level->extra_time_score = 0;
6170 // set time for normal and deadly shields to the same value
6171 level->shield_deadly_time = level->shield_normal_time;
6173 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6174 // can slip down from flat walls, like normal walls and steel walls
6175 level->em_slippery_gems = TRUE;
6177 // time score is counted for each 10 seconds left in Diamond Caves levels
6178 level->time_score_base = 10;
6181 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6182 struct LevelFileInfo *level_file_info,
6183 boolean level_info_only)
6185 char *filename = level_file_info->filename;
6187 int num_magic_bytes = 8;
6188 char magic_bytes[num_magic_bytes + 1];
6189 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6191 if (!(file = openFile(filename, MODE_READ)))
6193 level->no_valid_file = TRUE;
6195 if (!level_info_only)
6196 Warn("cannot read level '%s' -- using empty level", filename);
6201 // fseek(file, 0x0000, SEEK_SET);
6203 if (level_file_info->packed)
6205 // read "magic bytes" from start of file
6206 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6207 magic_bytes[0] = '\0';
6209 // check "magic bytes" for correct file format
6210 if (!strPrefix(magic_bytes, "DC2"))
6212 level->no_valid_file = TRUE;
6214 Warn("unknown DC level file '%s' -- using empty level", filename);
6219 if (strPrefix(magic_bytes, "DC2Win95") ||
6220 strPrefix(magic_bytes, "DC2Win98"))
6222 int position_first_level = 0x00fa;
6223 int extra_bytes = 4;
6226 // advance file stream to first level inside the level package
6227 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6229 // each block of level data is followed by block of non-level data
6230 num_levels_to_skip *= 2;
6232 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6233 while (num_levels_to_skip >= 0)
6235 // advance file stream to next level inside the level package
6236 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6238 level->no_valid_file = TRUE;
6240 Warn("cannot fseek in file '%s' -- using empty level", filename);
6245 // skip apparently unused extra bytes following each level
6246 ReadUnusedBytesFromFile(file, extra_bytes);
6248 // read size of next level in level package
6249 skip_bytes = getFile32BitLE(file);
6251 num_levels_to_skip--;
6256 level->no_valid_file = TRUE;
6258 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6264 LoadLevelFromFileStream_DC(file, level);
6270 // ----------------------------------------------------------------------------
6271 // functions for loading SB level
6272 // ----------------------------------------------------------------------------
6274 int getMappedElement_SB(int element_ascii, boolean use_ces)
6282 sb_element_mapping[] =
6284 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6285 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6286 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6287 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6288 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6289 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6290 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6291 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6298 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6299 if (element_ascii == sb_element_mapping[i].ascii)
6300 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6302 return EL_UNDEFINED;
6305 static void SetLevelSettings_SB(struct LevelInfo *level)
6309 level->use_step_counter = TRUE;
6312 level->score[SC_TIME_BONUS] = 0;
6313 level->time_score_base = 1;
6314 level->rate_time_over_score = TRUE;
6317 level->auto_exit_sokoban = TRUE;
6320 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6321 struct LevelFileInfo *level_file_info,
6322 boolean level_info_only)
6324 char *filename = level_file_info->filename;
6325 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6326 char last_comment[MAX_LINE_LEN];
6327 char level_name[MAX_LINE_LEN];
6330 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6331 boolean read_continued_line = FALSE;
6332 boolean reading_playfield = FALSE;
6333 boolean got_valid_playfield_line = FALSE;
6334 boolean invalid_playfield_char = FALSE;
6335 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6336 int file_level_nr = 0;
6337 int x = 0, y = 0; // initialized to make compilers happy
6339 last_comment[0] = '\0';
6340 level_name[0] = '\0';
6342 if (!(file = openFile(filename, MODE_READ)))
6344 level->no_valid_file = TRUE;
6346 if (!level_info_only)
6347 Warn("cannot read level '%s' -- using empty level", filename);
6352 while (!checkEndOfFile(file))
6354 // level successfully read, but next level may follow here
6355 if (!got_valid_playfield_line && reading_playfield)
6357 // read playfield from single level file -- skip remaining file
6358 if (!level_file_info->packed)
6361 if (file_level_nr >= num_levels_to_skip)
6366 last_comment[0] = '\0';
6367 level_name[0] = '\0';
6369 reading_playfield = FALSE;
6372 got_valid_playfield_line = FALSE;
6374 // read next line of input file
6375 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6378 // cut trailing line break (this can be newline and/or carriage return)
6379 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6380 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6383 // copy raw input line for later use (mainly debugging output)
6384 strcpy(line_raw, line);
6386 if (read_continued_line)
6388 // append new line to existing line, if there is enough space
6389 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6390 strcat(previous_line, line_ptr);
6392 strcpy(line, previous_line); // copy storage buffer to line
6394 read_continued_line = FALSE;
6397 // if the last character is '\', continue at next line
6398 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6400 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6401 strcpy(previous_line, line); // copy line to storage buffer
6403 read_continued_line = TRUE;
6409 if (line[0] == '\0')
6412 // extract comment text from comment line
6415 for (line_ptr = line; *line_ptr; line_ptr++)
6416 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6419 strcpy(last_comment, line_ptr);
6424 // extract level title text from line containing level title
6425 if (line[0] == '\'')
6427 strcpy(level_name, &line[1]);
6429 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6430 level_name[strlen(level_name) - 1] = '\0';
6435 // skip lines containing only spaces (or empty lines)
6436 for (line_ptr = line; *line_ptr; line_ptr++)
6437 if (*line_ptr != ' ')
6439 if (*line_ptr == '\0')
6442 // at this point, we have found a line containing part of a playfield
6444 got_valid_playfield_line = TRUE;
6446 if (!reading_playfield)
6448 reading_playfield = TRUE;
6449 invalid_playfield_char = FALSE;
6451 for (x = 0; x < MAX_LEV_FIELDX; x++)
6452 for (y = 0; y < MAX_LEV_FIELDY; y++)
6453 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6458 // start with topmost tile row
6462 // skip playfield line if larger row than allowed
6463 if (y >= MAX_LEV_FIELDY)
6466 // start with leftmost tile column
6469 // read playfield elements from line
6470 for (line_ptr = line; *line_ptr; line_ptr++)
6472 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6474 // stop parsing playfield line if larger column than allowed
6475 if (x >= MAX_LEV_FIELDX)
6478 if (mapped_sb_element == EL_UNDEFINED)
6480 invalid_playfield_char = TRUE;
6485 level->field[x][y] = mapped_sb_element;
6487 // continue with next tile column
6490 level->fieldx = MAX(x, level->fieldx);
6493 if (invalid_playfield_char)
6495 // if first playfield line, treat invalid lines as comment lines
6497 reading_playfield = FALSE;
6502 // continue with next tile row
6510 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6511 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6513 if (!reading_playfield)
6515 level->no_valid_file = TRUE;
6517 Warn("cannot read level '%s' -- using empty level", filename);
6522 if (*level_name != '\0')
6524 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6525 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6527 else if (*last_comment != '\0')
6529 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6530 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6534 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6537 // set all empty fields beyond the border walls to invisible steel wall
6538 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6540 if ((x == 0 || x == level->fieldx - 1 ||
6541 y == 0 || y == level->fieldy - 1) &&
6542 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6543 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6544 level->field, level->fieldx, level->fieldy);
6547 // set special level settings for Sokoban levels
6548 SetLevelSettings_SB(level);
6550 if (load_xsb_to_ces)
6552 // special global settings can now be set in level template
6553 level->use_custom_template = TRUE;
6558 // -------------------------------------------------------------------------
6559 // functions for handling native levels
6560 // -------------------------------------------------------------------------
6562 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6563 struct LevelFileInfo *level_file_info,
6564 boolean level_info_only)
6568 // determine position of requested level inside level package
6569 if (level_file_info->packed)
6570 pos = level_file_info->nr - leveldir_current->first_level;
6572 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6573 level->no_valid_file = TRUE;
6576 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6577 struct LevelFileInfo *level_file_info,
6578 boolean level_info_only)
6580 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6581 level->no_valid_file = TRUE;
6584 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6585 struct LevelFileInfo *level_file_info,
6586 boolean level_info_only)
6590 // determine position of requested level inside level package
6591 if (level_file_info->packed)
6592 pos = level_file_info->nr - leveldir_current->first_level;
6594 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6595 level->no_valid_file = TRUE;
6598 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6599 struct LevelFileInfo *level_file_info,
6600 boolean level_info_only)
6602 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6603 level->no_valid_file = TRUE;
6606 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6608 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6609 CopyNativeLevel_RND_to_BD(level);
6610 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6611 CopyNativeLevel_RND_to_EM(level);
6612 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6613 CopyNativeLevel_RND_to_SP(level);
6614 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6615 CopyNativeLevel_RND_to_MM(level);
6618 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6620 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6621 CopyNativeLevel_BD_to_RND(level);
6622 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6623 CopyNativeLevel_EM_to_RND(level);
6624 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6625 CopyNativeLevel_SP_to_RND(level);
6626 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6627 CopyNativeLevel_MM_to_RND(level);
6630 void SaveNativeLevel(struct LevelInfo *level)
6632 // saving native level files only supported for some game engines
6633 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6634 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6637 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6638 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6639 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6640 char *filename = getLevelFilenameFromBasename(basename);
6642 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6645 boolean success = FALSE;
6647 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6649 CopyNativeLevel_RND_to_BD(level);
6650 // CopyNativeTape_RND_to_BD(level);
6652 success = SaveNativeLevel_BD(filename);
6654 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6656 CopyNativeLevel_RND_to_SP(level);
6657 CopyNativeTape_RND_to_SP(level);
6659 success = SaveNativeLevel_SP(filename);
6663 Request("Native level file saved!", REQ_CONFIRM);
6665 Request("Failed to save native level file!", REQ_CONFIRM);
6669 // ----------------------------------------------------------------------------
6670 // functions for loading generic level
6671 // ----------------------------------------------------------------------------
6673 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6674 struct LevelFileInfo *level_file_info,
6675 boolean level_info_only)
6677 // always start with reliable default values
6678 setLevelInfoToDefaults(level, level_info_only, TRUE);
6680 switch (level_file_info->type)
6682 case LEVEL_FILE_TYPE_RND:
6683 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6686 case LEVEL_FILE_TYPE_BD:
6687 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6688 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6691 case LEVEL_FILE_TYPE_EM:
6692 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6693 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6696 case LEVEL_FILE_TYPE_SP:
6697 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6698 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6701 case LEVEL_FILE_TYPE_MM:
6702 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6703 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6706 case LEVEL_FILE_TYPE_DC:
6707 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6710 case LEVEL_FILE_TYPE_SB:
6711 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6715 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6719 // if level file is invalid, restore level structure to default values
6720 if (level->no_valid_file)
6721 setLevelInfoToDefaults(level, level_info_only, FALSE);
6723 if (check_special_flags("use_native_bd_game_engine"))
6724 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6726 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6727 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6729 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6730 CopyNativeLevel_Native_to_RND(level);
6733 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6735 static struct LevelFileInfo level_file_info;
6737 // always start with reliable default values
6738 setFileInfoToDefaults(&level_file_info);
6740 level_file_info.nr = 0; // unknown level number
6741 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6743 setString(&level_file_info.filename, filename);
6745 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6748 static void LoadLevel_InitVersion(struct LevelInfo *level)
6752 if (leveldir_current == NULL) // only when dumping level
6755 // all engine modifications also valid for levels which use latest engine
6756 if (level->game_version < VERSION_IDENT(3,2,0,5))
6758 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6759 level->time_score_base = 10;
6762 if (leveldir_current->latest_engine)
6764 // ---------- use latest game engine --------------------------------------
6766 /* For all levels which are forced to use the latest game engine version
6767 (normally all but user contributed, private and undefined levels), set
6768 the game engine version to the actual version; this allows for actual
6769 corrections in the game engine to take effect for existing, converted
6770 levels (from "classic" or other existing games) to make the emulation
6771 of the corresponding game more accurate, while (hopefully) not breaking
6772 existing levels created from other players. */
6774 level->game_version = GAME_VERSION_ACTUAL;
6776 /* Set special EM style gems behaviour: EM style gems slip down from
6777 normal, steel and growing wall. As this is a more fundamental change,
6778 it seems better to set the default behaviour to "off" (as it is more
6779 natural) and make it configurable in the level editor (as a property
6780 of gem style elements). Already existing converted levels (neither
6781 private nor contributed levels) are changed to the new behaviour. */
6783 if (level->file_version < FILE_VERSION_2_0)
6784 level->em_slippery_gems = TRUE;
6789 // ---------- use game engine the level was created with --------------------
6791 /* For all levels which are not forced to use the latest game engine
6792 version (normally user contributed, private and undefined levels),
6793 use the version of the game engine the levels were created for.
6795 Since 2.0.1, the game engine version is now directly stored
6796 in the level file (chunk "VERS"), so there is no need anymore
6797 to set the game version from the file version (except for old,
6798 pre-2.0 levels, where the game version is still taken from the
6799 file format version used to store the level -- see above). */
6801 // player was faster than enemies in 1.0.0 and before
6802 if (level->file_version == FILE_VERSION_1_0)
6803 for (i = 0; i < MAX_PLAYERS; i++)
6804 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6806 // default behaviour for EM style gems was "slippery" only in 2.0.1
6807 if (level->game_version == VERSION_IDENT(2,0,1,0))
6808 level->em_slippery_gems = TRUE;
6810 // springs could be pushed over pits before (pre-release version) 2.2.0
6811 if (level->game_version < VERSION_IDENT(2,2,0,0))
6812 level->use_spring_bug = TRUE;
6814 if (level->game_version < VERSION_IDENT(3,2,0,5))
6816 // time orb caused limited time in endless time levels before 3.2.0-5
6817 level->use_time_orb_bug = TRUE;
6819 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6820 level->block_snap_field = FALSE;
6822 // extra time score was same value as time left score before 3.2.0-5
6823 level->extra_time_score = level->score[SC_TIME_BONUS];
6826 if (level->game_version < VERSION_IDENT(3,2,0,7))
6828 // default behaviour for snapping was "not continuous" before 3.2.0-7
6829 level->continuous_snapping = FALSE;
6832 // only few elements were able to actively move into acid before 3.1.0
6833 // trigger settings did not exist before 3.1.0; set to default "any"
6834 if (level->game_version < VERSION_IDENT(3,1,0,0))
6836 // correct "can move into acid" settings (all zero in old levels)
6838 level->can_move_into_acid_bits = 0; // nothing can move into acid
6839 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6841 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6842 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6843 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6844 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6846 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6847 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6849 // correct trigger settings (stored as zero == "none" in old levels)
6851 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6853 int element = EL_CUSTOM_START + i;
6854 struct ElementInfo *ei = &element_info[element];
6856 for (j = 0; j < ei->num_change_pages; j++)
6858 struct ElementChangeInfo *change = &ei->change_page[j];
6860 change->trigger_player = CH_PLAYER_ANY;
6861 change->trigger_page = CH_PAGE_ANY;
6866 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6868 int element = EL_CUSTOM_256;
6869 struct ElementInfo *ei = &element_info[element];
6870 struct ElementChangeInfo *change = &ei->change_page[0];
6872 /* This is needed to fix a problem that was caused by a bugfix in function
6873 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6874 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6875 not replace walkable elements, but instead just placed the player on it,
6876 without placing the Sokoban field under the player). Unfortunately, this
6877 breaks "Snake Bite" style levels when the snake is halfway through a door
6878 that just closes (the snake head is still alive and can be moved in this
6879 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6880 player (without Sokoban element) which then gets killed as designed). */
6882 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6883 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6884 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6885 change->target_element = EL_PLAYER_1;
6888 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6889 if (level->game_version < VERSION_IDENT(3,2,5,0))
6891 /* This is needed to fix a problem that was caused by a bugfix in function
6892 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6893 corrects the behaviour when a custom element changes to another custom
6894 element with a higher element number that has change actions defined.
6895 Normally, only one change per frame is allowed for custom elements.
6896 Therefore, it is checked if a custom element already changed in the
6897 current frame; if it did, subsequent changes are suppressed.
6898 Unfortunately, this is only checked for element changes, but not for
6899 change actions, which are still executed. As the function above loops
6900 through all custom elements from lower to higher, an element change
6901 resulting in a lower CE number won't be checked again, while a target
6902 element with a higher number will also be checked, and potential change
6903 actions will get executed for this CE, too (which is wrong), while
6904 further changes are ignored (which is correct). As this bugfix breaks
6905 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6906 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6907 behaviour for existing levels and tapes that make use of this bug */
6909 level->use_action_after_change_bug = TRUE;
6912 // not centering level after relocating player was default only in 3.2.3
6913 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6914 level->shifted_relocation = TRUE;
6916 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6917 if (level->game_version < VERSION_IDENT(3,2,6,0))
6918 level->em_explodes_by_fire = TRUE;
6920 // levels were solved by the first player entering an exit up to 4.1.0.0
6921 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6922 level->solved_by_one_player = TRUE;
6924 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6925 if (level->game_version < VERSION_IDENT(4,1,1,1))
6926 level->use_life_bugs = TRUE;
6928 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6929 if (level->game_version < VERSION_IDENT(4,1,1,1))
6930 level->sb_objects_needed = FALSE;
6932 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6933 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6934 level->finish_dig_collect = FALSE;
6936 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6937 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6938 level->keep_walkable_ce = TRUE;
6941 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6943 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6946 // check if this level is (not) a Sokoban level
6947 for (y = 0; y < level->fieldy; y++)
6948 for (x = 0; x < level->fieldx; x++)
6949 if (!IS_SB_ELEMENT(Tile[x][y]))
6950 is_sokoban_level = FALSE;
6952 if (is_sokoban_level)
6954 // set special level settings for Sokoban levels
6955 SetLevelSettings_SB(level);
6959 static void LoadLevel_InitSettings(struct LevelInfo *level)
6961 // adjust level settings for (non-native) Sokoban-style levels
6962 LoadLevel_InitSettings_SB(level);
6964 // rename levels with title "nameless level" or if renaming is forced
6965 if (leveldir_current->empty_level_name != NULL &&
6966 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6967 leveldir_current->force_level_name))
6968 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6969 leveldir_current->empty_level_name, level_nr);
6972 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6976 // map elements that have changed in newer versions
6977 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6978 level->game_version);
6979 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6980 for (x = 0; x < 3; x++)
6981 for (y = 0; y < 3; y++)
6982 level->yamyam_content[i].e[x][y] =
6983 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6984 level->game_version);
6988 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6992 // map custom element change events that have changed in newer versions
6993 // (these following values were accidentally changed in version 3.0.1)
6994 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6995 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6997 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6999 int element = EL_CUSTOM_START + i;
7001 // order of checking and copying events to be mapped is important
7002 // (do not change the start and end value -- they are constant)
7003 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7005 if (HAS_CHANGE_EVENT(element, j - 2))
7007 SET_CHANGE_EVENT(element, j - 2, FALSE);
7008 SET_CHANGE_EVENT(element, j, TRUE);
7012 // order of checking and copying events to be mapped is important
7013 // (do not change the start and end value -- they are constant)
7014 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7016 if (HAS_CHANGE_EVENT(element, j - 1))
7018 SET_CHANGE_EVENT(element, j - 1, FALSE);
7019 SET_CHANGE_EVENT(element, j, TRUE);
7025 // initialize "can_change" field for old levels with only one change page
7026 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7030 int element = EL_CUSTOM_START + i;
7032 if (CAN_CHANGE(element))
7033 element_info[element].change->can_change = TRUE;
7037 // correct custom element values (for old levels without these options)
7038 if (level->game_version < VERSION_IDENT(3,1,1,0))
7040 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7042 int element = EL_CUSTOM_START + i;
7043 struct ElementInfo *ei = &element_info[element];
7045 if (ei->access_direction == MV_NO_DIRECTION)
7046 ei->access_direction = MV_ALL_DIRECTIONS;
7050 // correct custom element values (fix invalid values for all versions)
7053 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7055 int element = EL_CUSTOM_START + i;
7056 struct ElementInfo *ei = &element_info[element];
7058 for (j = 0; j < ei->num_change_pages; j++)
7060 struct ElementChangeInfo *change = &ei->change_page[j];
7062 if (change->trigger_player == CH_PLAYER_NONE)
7063 change->trigger_player = CH_PLAYER_ANY;
7065 if (change->trigger_side == CH_SIDE_NONE)
7066 change->trigger_side = CH_SIDE_ANY;
7071 // initialize "can_explode" field for old levels which did not store this
7072 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7073 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7075 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7077 int element = EL_CUSTOM_START + i;
7079 if (EXPLODES_1X1_OLD(element))
7080 element_info[element].explosion_type = EXPLODES_1X1;
7082 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7083 EXPLODES_SMASHED(element) ||
7084 EXPLODES_IMPACT(element)));
7088 // correct previously hard-coded move delay values for maze runner style
7089 if (level->game_version < VERSION_IDENT(3,1,1,0))
7091 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7093 int element = EL_CUSTOM_START + i;
7095 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7097 // previously hard-coded and therefore ignored
7098 element_info[element].move_delay_fixed = 9;
7099 element_info[element].move_delay_random = 0;
7104 // set some other uninitialized values of custom elements in older levels
7105 if (level->game_version < VERSION_IDENT(3,1,0,0))
7107 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7109 int element = EL_CUSTOM_START + i;
7111 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7113 element_info[element].explosion_delay = 17;
7114 element_info[element].ignition_delay = 8;
7118 // set mouse click change events to work for left/middle/right mouse button
7119 if (level->game_version < VERSION_IDENT(4,2,3,0))
7121 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7123 int element = EL_CUSTOM_START + i;
7124 struct ElementInfo *ei = &element_info[element];
7126 for (j = 0; j < ei->num_change_pages; j++)
7128 struct ElementChangeInfo *change = &ei->change_page[j];
7130 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7131 change->has_event[CE_PRESSED_BY_MOUSE] ||
7132 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7133 change->has_event[CE_MOUSE_PRESSED_ON_X])
7134 change->trigger_side = CH_SIDE_ANY;
7140 static void LoadLevel_InitElements(struct LevelInfo *level)
7142 LoadLevel_InitStandardElements(level);
7144 if (level->file_has_custom_elements)
7145 LoadLevel_InitCustomElements(level);
7147 // initialize element properties for level editor etc.
7148 InitElementPropertiesEngine(level->game_version);
7149 InitElementPropertiesGfxElement();
7152 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7156 // map elements that have changed in newer versions
7157 for (y = 0; y < level->fieldy; y++)
7158 for (x = 0; x < level->fieldx; x++)
7159 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7160 level->game_version);
7162 // clear unused playfield data (nicer if level gets resized in editor)
7163 for (x = 0; x < MAX_LEV_FIELDX; x++)
7164 for (y = 0; y < MAX_LEV_FIELDY; y++)
7165 if (x >= level->fieldx || y >= level->fieldy)
7166 level->field[x][y] = EL_EMPTY;
7168 // copy elements to runtime playfield array
7169 for (x = 0; x < MAX_LEV_FIELDX; x++)
7170 for (y = 0; y < MAX_LEV_FIELDY; y++)
7171 Tile[x][y] = level->field[x][y];
7173 // initialize level size variables for faster access
7174 lev_fieldx = level->fieldx;
7175 lev_fieldy = level->fieldy;
7177 // determine border element for this level
7178 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7179 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7184 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7186 struct LevelFileInfo *level_file_info = &level->file_info;
7188 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7189 CopyNativeLevel_RND_to_Native(level);
7192 static void LoadLevelTemplate_LoadAndInit(void)
7194 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7196 LoadLevel_InitVersion(&level_template);
7197 LoadLevel_InitElements(&level_template);
7198 LoadLevel_InitSettings(&level_template);
7200 ActivateLevelTemplate();
7203 void LoadLevelTemplate(int nr)
7205 if (!fileExists(getGlobalLevelTemplateFilename()))
7207 Warn("no level template found for this level");
7212 setLevelFileInfo(&level_template.file_info, nr);
7214 LoadLevelTemplate_LoadAndInit();
7217 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7219 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7221 LoadLevelTemplate_LoadAndInit();
7224 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7226 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7228 if (level.use_custom_template)
7230 if (network_level != NULL)
7231 LoadNetworkLevelTemplate(network_level);
7233 LoadLevelTemplate(-1);
7236 LoadLevel_InitVersion(&level);
7237 LoadLevel_InitElements(&level);
7238 LoadLevel_InitPlayfield(&level);
7239 LoadLevel_InitSettings(&level);
7241 LoadLevel_InitNativeEngines(&level);
7244 void LoadLevel(int nr)
7246 SetLevelSetInfo(leveldir_current->identifier, nr);
7248 setLevelFileInfo(&level.file_info, nr);
7250 LoadLevel_LoadAndInit(NULL);
7253 void LoadLevelInfoOnly(int nr)
7255 setLevelFileInfo(&level.file_info, nr);
7257 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7260 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7262 SetLevelSetInfo(network_level->leveldir_identifier,
7263 network_level->file_info.nr);
7265 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7267 LoadLevel_LoadAndInit(network_level);
7270 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7274 chunk_size += putFileVersion(file, level->file_version);
7275 chunk_size += putFileVersion(file, level->game_version);
7280 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7284 chunk_size += putFile16BitBE(file, level->creation_date.year);
7285 chunk_size += putFile8Bit(file, level->creation_date.month);
7286 chunk_size += putFile8Bit(file, level->creation_date.day);
7291 #if ENABLE_HISTORIC_CHUNKS
7292 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7296 putFile8Bit(file, level->fieldx);
7297 putFile8Bit(file, level->fieldy);
7299 putFile16BitBE(file, level->time);
7300 putFile16BitBE(file, level->gems_needed);
7302 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7303 putFile8Bit(file, level->name[i]);
7305 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7306 putFile8Bit(file, level->score[i]);
7308 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7309 for (y = 0; y < 3; y++)
7310 for (x = 0; x < 3; x++)
7311 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7312 level->yamyam_content[i].e[x][y]));
7313 putFile8Bit(file, level->amoeba_speed);
7314 putFile8Bit(file, level->time_magic_wall);
7315 putFile8Bit(file, level->time_wheel);
7316 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7317 level->amoeba_content));
7318 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7319 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7320 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7321 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7323 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7325 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7326 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7327 putFile32BitBE(file, level->can_move_into_acid_bits);
7328 putFile8Bit(file, level->dont_collide_with_bits);
7330 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7331 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7333 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7334 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7335 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7337 putFile8Bit(file, level->game_engine_type);
7339 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7343 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7348 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7349 chunk_size += putFile8Bit(file, level->name[i]);
7354 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7359 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7360 chunk_size += putFile8Bit(file, level->author[i]);
7365 #if ENABLE_HISTORIC_CHUNKS
7366 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7371 for (y = 0; y < level->fieldy; y++)
7372 for (x = 0; x < level->fieldx; x++)
7373 if (level->encoding_16bit_field)
7374 chunk_size += putFile16BitBE(file, level->field[x][y]);
7376 chunk_size += putFile8Bit(file, level->field[x][y]);
7382 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7387 for (y = 0; y < level->fieldy; y++)
7388 for (x = 0; x < level->fieldx; x++)
7389 chunk_size += putFile16BitBE(file, level->field[x][y]);
7394 #if ENABLE_HISTORIC_CHUNKS
7395 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7399 putFile8Bit(file, EL_YAMYAM);
7400 putFile8Bit(file, level->num_yamyam_contents);
7401 putFile8Bit(file, 0);
7402 putFile8Bit(file, 0);
7404 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7405 for (y = 0; y < 3; y++)
7406 for (x = 0; x < 3; x++)
7407 if (level->encoding_16bit_field)
7408 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7410 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7414 #if ENABLE_HISTORIC_CHUNKS
7415 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7418 int num_contents, content_xsize, content_ysize;
7419 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7421 if (element == EL_YAMYAM)
7423 num_contents = level->num_yamyam_contents;
7427 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7428 for (y = 0; y < 3; y++)
7429 for (x = 0; x < 3; x++)
7430 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7432 else if (element == EL_BD_AMOEBA)
7438 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7439 for (y = 0; y < 3; y++)
7440 for (x = 0; x < 3; x++)
7441 content_array[i][x][y] = EL_EMPTY;
7442 content_array[0][0][0] = level->amoeba_content;
7446 // chunk header already written -- write empty chunk data
7447 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7449 Warn("cannot save content for element '%d'", element);
7454 putFile16BitBE(file, element);
7455 putFile8Bit(file, num_contents);
7456 putFile8Bit(file, content_xsize);
7457 putFile8Bit(file, content_ysize);
7459 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7461 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7462 for (y = 0; y < 3; y++)
7463 for (x = 0; x < 3; x++)
7464 putFile16BitBE(file, content_array[i][x][y]);
7468 #if ENABLE_HISTORIC_CHUNKS
7469 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7471 int envelope_nr = element - EL_ENVELOPE_1;
7472 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7476 chunk_size += putFile16BitBE(file, element);
7477 chunk_size += putFile16BitBE(file, envelope_len);
7478 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7479 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7481 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7482 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7484 for (i = 0; i < envelope_len; i++)
7485 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7491 #if ENABLE_HISTORIC_CHUNKS
7492 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7493 int num_changed_custom_elements)
7497 putFile16BitBE(file, num_changed_custom_elements);
7499 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7501 int element = EL_CUSTOM_START + i;
7503 struct ElementInfo *ei = &element_info[element];
7505 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7507 if (check < num_changed_custom_elements)
7509 putFile16BitBE(file, element);
7510 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7517 if (check != num_changed_custom_elements) // should not happen
7518 Warn("inconsistent number of custom element properties");
7522 #if ENABLE_HISTORIC_CHUNKS
7523 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7524 int num_changed_custom_elements)
7528 putFile16BitBE(file, num_changed_custom_elements);
7530 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7532 int element = EL_CUSTOM_START + i;
7534 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7536 if (check < num_changed_custom_elements)
7538 putFile16BitBE(file, element);
7539 putFile16BitBE(file, element_info[element].change->target_element);
7546 if (check != num_changed_custom_elements) // should not happen
7547 Warn("inconsistent number of custom target elements");
7551 #if ENABLE_HISTORIC_CHUNKS
7552 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7553 int num_changed_custom_elements)
7555 int i, j, x, y, check = 0;
7557 putFile16BitBE(file, num_changed_custom_elements);
7559 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7561 int element = EL_CUSTOM_START + i;
7562 struct ElementInfo *ei = &element_info[element];
7564 if (ei->modified_settings)
7566 if (check < num_changed_custom_elements)
7568 putFile16BitBE(file, element);
7570 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7571 putFile8Bit(file, ei->description[j]);
7573 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7575 // some free bytes for future properties and padding
7576 WriteUnusedBytesToFile(file, 7);
7578 putFile8Bit(file, ei->use_gfx_element);
7579 putFile16BitBE(file, ei->gfx_element_initial);
7581 putFile8Bit(file, ei->collect_score_initial);
7582 putFile8Bit(file, ei->collect_count_initial);
7584 putFile16BitBE(file, ei->push_delay_fixed);
7585 putFile16BitBE(file, ei->push_delay_random);
7586 putFile16BitBE(file, ei->move_delay_fixed);
7587 putFile16BitBE(file, ei->move_delay_random);
7589 putFile16BitBE(file, ei->move_pattern);
7590 putFile8Bit(file, ei->move_direction_initial);
7591 putFile8Bit(file, ei->move_stepsize);
7593 for (y = 0; y < 3; y++)
7594 for (x = 0; x < 3; x++)
7595 putFile16BitBE(file, ei->content.e[x][y]);
7597 putFile32BitBE(file, ei->change->events);
7599 putFile16BitBE(file, ei->change->target_element);
7601 putFile16BitBE(file, ei->change->delay_fixed);
7602 putFile16BitBE(file, ei->change->delay_random);
7603 putFile16BitBE(file, ei->change->delay_frames);
7605 putFile16BitBE(file, ei->change->initial_trigger_element);
7607 putFile8Bit(file, ei->change->explode);
7608 putFile8Bit(file, ei->change->use_target_content);
7609 putFile8Bit(file, ei->change->only_if_complete);
7610 putFile8Bit(file, ei->change->use_random_replace);
7612 putFile8Bit(file, ei->change->random_percentage);
7613 putFile8Bit(file, ei->change->replace_when);
7615 for (y = 0; y < 3; y++)
7616 for (x = 0; x < 3; x++)
7617 putFile16BitBE(file, ei->change->content.e[x][y]);
7619 putFile8Bit(file, ei->slippery_type);
7621 // some free bytes for future properties and padding
7622 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7629 if (check != num_changed_custom_elements) // should not happen
7630 Warn("inconsistent number of custom element properties");
7634 #if ENABLE_HISTORIC_CHUNKS
7635 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7637 struct ElementInfo *ei = &element_info[element];
7640 // ---------- custom element base property values (96 bytes) ----------------
7642 putFile16BitBE(file, element);
7644 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7645 putFile8Bit(file, ei->description[i]);
7647 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7649 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7651 putFile8Bit(file, ei->num_change_pages);
7653 putFile16BitBE(file, ei->ce_value_fixed_initial);
7654 putFile16BitBE(file, ei->ce_value_random_initial);
7655 putFile8Bit(file, ei->use_last_ce_value);
7657 putFile8Bit(file, ei->use_gfx_element);
7658 putFile16BitBE(file, ei->gfx_element_initial);
7660 putFile8Bit(file, ei->collect_score_initial);
7661 putFile8Bit(file, ei->collect_count_initial);
7663 putFile8Bit(file, ei->drop_delay_fixed);
7664 putFile8Bit(file, ei->push_delay_fixed);
7665 putFile8Bit(file, ei->drop_delay_random);
7666 putFile8Bit(file, ei->push_delay_random);
7667 putFile16BitBE(file, ei->move_delay_fixed);
7668 putFile16BitBE(file, ei->move_delay_random);
7670 // bits 0 - 15 of "move_pattern" ...
7671 putFile16BitBE(file, ei->move_pattern & 0xffff);
7672 putFile8Bit(file, ei->move_direction_initial);
7673 putFile8Bit(file, ei->move_stepsize);
7675 putFile8Bit(file, ei->slippery_type);
7677 for (y = 0; y < 3; y++)
7678 for (x = 0; x < 3; x++)
7679 putFile16BitBE(file, ei->content.e[x][y]);
7681 putFile16BitBE(file, ei->move_enter_element);
7682 putFile16BitBE(file, ei->move_leave_element);
7683 putFile8Bit(file, ei->move_leave_type);
7685 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7686 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7688 putFile8Bit(file, ei->access_direction);
7690 putFile8Bit(file, ei->explosion_delay);
7691 putFile8Bit(file, ei->ignition_delay);
7692 putFile8Bit(file, ei->explosion_type);
7694 // some free bytes for future custom property values and padding
7695 WriteUnusedBytesToFile(file, 1);
7697 // ---------- change page property values (48 bytes) ------------------------
7699 for (i = 0; i < ei->num_change_pages; i++)
7701 struct ElementChangeInfo *change = &ei->change_page[i];
7702 unsigned int event_bits;
7704 // bits 0 - 31 of "has_event[]" ...
7706 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7707 if (change->has_event[j])
7708 event_bits |= (1u << j);
7709 putFile32BitBE(file, event_bits);
7711 putFile16BitBE(file, change->target_element);
7713 putFile16BitBE(file, change->delay_fixed);
7714 putFile16BitBE(file, change->delay_random);
7715 putFile16BitBE(file, change->delay_frames);
7717 putFile16BitBE(file, change->initial_trigger_element);
7719 putFile8Bit(file, change->explode);
7720 putFile8Bit(file, change->use_target_content);
7721 putFile8Bit(file, change->only_if_complete);
7722 putFile8Bit(file, change->use_random_replace);
7724 putFile8Bit(file, change->random_percentage);
7725 putFile8Bit(file, change->replace_when);
7727 for (y = 0; y < 3; y++)
7728 for (x = 0; x < 3; x++)
7729 putFile16BitBE(file, change->target_content.e[x][y]);
7731 putFile8Bit(file, change->can_change);
7733 putFile8Bit(file, change->trigger_side);
7735 putFile8Bit(file, change->trigger_player);
7736 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7737 log_2(change->trigger_page)));
7739 putFile8Bit(file, change->has_action);
7740 putFile8Bit(file, change->action_type);
7741 putFile8Bit(file, change->action_mode);
7742 putFile16BitBE(file, change->action_arg);
7744 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7746 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7747 if (change->has_event[j])
7748 event_bits |= (1u << (j - 32));
7749 putFile8Bit(file, event_bits);
7754 #if ENABLE_HISTORIC_CHUNKS
7755 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7757 struct ElementInfo *ei = &element_info[element];
7758 struct ElementGroupInfo *group = ei->group;
7761 putFile16BitBE(file, element);
7763 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7764 putFile8Bit(file, ei->description[i]);
7766 putFile8Bit(file, group->num_elements);
7768 putFile8Bit(file, ei->use_gfx_element);
7769 putFile16BitBE(file, ei->gfx_element_initial);
7771 putFile8Bit(file, group->choice_mode);
7773 // some free bytes for future values and padding
7774 WriteUnusedBytesToFile(file, 3);
7776 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7777 putFile16BitBE(file, group->element[i]);
7781 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7782 boolean write_element)
7784 int save_type = entry->save_type;
7785 int data_type = entry->data_type;
7786 int conf_type = entry->conf_type;
7787 int byte_mask = conf_type & CONF_MASK_BYTES;
7788 int element = entry->element;
7789 int default_value = entry->default_value;
7791 boolean modified = FALSE;
7793 if (byte_mask != CONF_MASK_MULTI_BYTES)
7795 void *value_ptr = entry->value;
7796 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7799 // check if any settings have been modified before saving them
7800 if (value != default_value)
7803 // do not save if explicitly told or if unmodified default settings
7804 if ((save_type == SAVE_CONF_NEVER) ||
7805 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7809 num_bytes += putFile16BitBE(file, element);
7811 num_bytes += putFile8Bit(file, conf_type);
7812 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7813 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7814 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7817 else if (data_type == TYPE_STRING)
7819 char *default_string = entry->default_string;
7820 char *string = (char *)(entry->value);
7821 int string_length = strlen(string);
7824 // check if any settings have been modified before saving them
7825 if (!strEqual(string, default_string))
7828 // do not save if explicitly told or if unmodified default settings
7829 if ((save_type == SAVE_CONF_NEVER) ||
7830 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7834 num_bytes += putFile16BitBE(file, element);
7836 num_bytes += putFile8Bit(file, conf_type);
7837 num_bytes += putFile16BitBE(file, string_length);
7839 for (i = 0; i < string_length; i++)
7840 num_bytes += putFile8Bit(file, string[i]);
7842 else if (data_type == TYPE_ELEMENT_LIST)
7844 int *element_array = (int *)(entry->value);
7845 int num_elements = *(int *)(entry->num_entities);
7848 // check if any settings have been modified before saving them
7849 for (i = 0; i < num_elements; i++)
7850 if (element_array[i] != default_value)
7853 // do not save if explicitly told or if unmodified default settings
7854 if ((save_type == SAVE_CONF_NEVER) ||
7855 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7859 num_bytes += putFile16BitBE(file, element);
7861 num_bytes += putFile8Bit(file, conf_type);
7862 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7864 for (i = 0; i < num_elements; i++)
7865 num_bytes += putFile16BitBE(file, element_array[i]);
7867 else if (data_type == TYPE_CONTENT_LIST)
7869 struct Content *content = (struct Content *)(entry->value);
7870 int num_contents = *(int *)(entry->num_entities);
7873 // check if any settings have been modified before saving them
7874 for (i = 0; i < num_contents; i++)
7875 for (y = 0; y < 3; y++)
7876 for (x = 0; x < 3; x++)
7877 if (content[i].e[x][y] != default_value)
7880 // do not save if explicitly told or if unmodified default settings
7881 if ((save_type == SAVE_CONF_NEVER) ||
7882 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7886 num_bytes += putFile16BitBE(file, element);
7888 num_bytes += putFile8Bit(file, conf_type);
7889 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7891 for (i = 0; i < num_contents; i++)
7892 for (y = 0; y < 3; y++)
7893 for (x = 0; x < 3; x++)
7894 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7900 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7905 li = *level; // copy level data into temporary buffer
7907 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7908 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7913 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7918 li = *level; // copy level data into temporary buffer
7920 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7921 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7926 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7928 int envelope_nr = element - EL_ENVELOPE_1;
7932 chunk_size += putFile16BitBE(file, element);
7934 // copy envelope data into temporary buffer
7935 xx_envelope = level->envelope[envelope_nr];
7937 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7938 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7943 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7945 struct ElementInfo *ei = &element_info[element];
7949 chunk_size += putFile16BitBE(file, element);
7951 xx_ei = *ei; // copy element data into temporary buffer
7953 // set default description string for this specific element
7954 strcpy(xx_default_description, getDefaultElementDescription(ei));
7956 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7957 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7959 for (i = 0; i < ei->num_change_pages; i++)
7961 struct ElementChangeInfo *change = &ei->change_page[i];
7963 xx_current_change_page = i;
7965 xx_change = *change; // copy change data into temporary buffer
7968 setEventBitsFromEventFlags(change);
7970 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7971 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7978 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7980 struct ElementInfo *ei = &element_info[element];
7981 struct ElementGroupInfo *group = ei->group;
7985 chunk_size += putFile16BitBE(file, element);
7987 xx_ei = *ei; // copy element data into temporary buffer
7988 xx_group = *group; // copy group data into temporary buffer
7990 // set default description string for this specific element
7991 strcpy(xx_default_description, getDefaultElementDescription(ei));
7993 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7994 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7999 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8001 struct ElementInfo *ei = &element_info[element];
8005 chunk_size += putFile16BitBE(file, element);
8007 xx_ei = *ei; // copy element data into temporary buffer
8009 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8010 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8015 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8016 boolean save_as_template)
8022 if (!(file = fopen(filename, MODE_WRITE)))
8024 Warn("cannot save level file '%s'", filename);
8029 level->file_version = FILE_VERSION_ACTUAL;
8030 level->game_version = GAME_VERSION_ACTUAL;
8032 level->creation_date = getCurrentDate();
8034 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8035 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8037 chunk_size = SaveLevel_VERS(NULL, level);
8038 putFileChunkBE(file, "VERS", chunk_size);
8039 SaveLevel_VERS(file, level);
8041 chunk_size = SaveLevel_DATE(NULL, level);
8042 putFileChunkBE(file, "DATE", chunk_size);
8043 SaveLevel_DATE(file, level);
8045 chunk_size = SaveLevel_NAME(NULL, level);
8046 putFileChunkBE(file, "NAME", chunk_size);
8047 SaveLevel_NAME(file, level);
8049 chunk_size = SaveLevel_AUTH(NULL, level);
8050 putFileChunkBE(file, "AUTH", chunk_size);
8051 SaveLevel_AUTH(file, level);
8053 chunk_size = SaveLevel_INFO(NULL, level);
8054 putFileChunkBE(file, "INFO", chunk_size);
8055 SaveLevel_INFO(file, level);
8057 chunk_size = SaveLevel_BODY(NULL, level);
8058 putFileChunkBE(file, "BODY", chunk_size);
8059 SaveLevel_BODY(file, level);
8061 chunk_size = SaveLevel_ELEM(NULL, level);
8062 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8064 putFileChunkBE(file, "ELEM", chunk_size);
8065 SaveLevel_ELEM(file, level);
8068 for (i = 0; i < NUM_ENVELOPES; i++)
8070 int element = EL_ENVELOPE_1 + i;
8072 chunk_size = SaveLevel_NOTE(NULL, level, element);
8073 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8075 putFileChunkBE(file, "NOTE", chunk_size);
8076 SaveLevel_NOTE(file, level, element);
8080 // if not using template level, check for non-default custom/group elements
8081 if (!level->use_custom_template || save_as_template)
8083 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8085 int element = EL_CUSTOM_START + i;
8087 chunk_size = SaveLevel_CUSX(NULL, level, element);
8088 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8090 putFileChunkBE(file, "CUSX", chunk_size);
8091 SaveLevel_CUSX(file, level, element);
8095 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8097 int element = EL_GROUP_START + i;
8099 chunk_size = SaveLevel_GRPX(NULL, level, element);
8100 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8102 putFileChunkBE(file, "GRPX", chunk_size);
8103 SaveLevel_GRPX(file, level, element);
8107 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8109 int element = GET_EMPTY_ELEMENT(i);
8111 chunk_size = SaveLevel_EMPX(NULL, level, element);
8112 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8114 putFileChunkBE(file, "EMPX", chunk_size);
8115 SaveLevel_EMPX(file, level, element);
8122 SetFilePermissions(filename, PERMS_PRIVATE);
8125 void SaveLevel(int nr)
8127 char *filename = getDefaultLevelFilename(nr);
8129 SaveLevelFromFilename(&level, filename, FALSE);
8132 void SaveLevelTemplate(void)
8134 char *filename = getLocalLevelTemplateFilename();
8136 SaveLevelFromFilename(&level, filename, TRUE);
8139 boolean SaveLevelChecked(int nr)
8141 char *filename = getDefaultLevelFilename(nr);
8142 boolean new_level = !fileExists(filename);
8143 boolean level_saved = FALSE;
8145 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8150 Request("Level saved!", REQ_CONFIRM);
8158 void DumpLevel(struct LevelInfo *level)
8160 if (level->no_level_file || level->no_valid_file)
8162 Warn("cannot dump -- no valid level file found");
8168 Print("Level xxx (file version %08d, game version %08d)\n",
8169 level->file_version, level->game_version);
8172 Print("Level author: '%s'\n", level->author);
8173 Print("Level title: '%s'\n", level->name);
8175 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8177 Print("Level time: %d seconds\n", level->time);
8178 Print("Gems needed: %d\n", level->gems_needed);
8180 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8181 Print("Time for wheel: %d seconds\n", level->time_wheel);
8182 Print("Time for light: %d seconds\n", level->time_light);
8183 Print("Time for timegate: %d seconds\n", level->time_timegate);
8185 Print("Amoeba speed: %d\n", level->amoeba_speed);
8188 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8189 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8190 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8191 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8192 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8193 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8199 for (i = 0; i < NUM_ENVELOPES; i++)
8201 char *text = level->envelope[i].text;
8202 int text_len = strlen(text);
8203 boolean has_text = FALSE;
8205 for (j = 0; j < text_len; j++)
8206 if (text[j] != ' ' && text[j] != '\n')
8212 Print("Envelope %d:\n'%s'\n", i + 1, text);
8220 void DumpLevels(void)
8222 static LevelDirTree *dumplevel_leveldir = NULL;
8224 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8225 global.dumplevel_leveldir);
8227 if (dumplevel_leveldir == NULL)
8228 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8230 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8231 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8232 Fail("no such level number: %d", global.dumplevel_level_nr);
8234 leveldir_current = dumplevel_leveldir;
8236 LoadLevel(global.dumplevel_level_nr);
8243 // ============================================================================
8244 // tape file functions
8245 // ============================================================================
8247 static void setTapeInfoToDefaults(void)
8251 // always start with reliable default values (empty tape)
8254 // default values (also for pre-1.2 tapes) with only the first player
8255 tape.player_participates[0] = TRUE;
8256 for (i = 1; i < MAX_PLAYERS; i++)
8257 tape.player_participates[i] = FALSE;
8259 // at least one (default: the first) player participates in every tape
8260 tape.num_participating_players = 1;
8262 tape.property_bits = TAPE_PROPERTY_NONE;
8264 tape.level_nr = level_nr;
8266 tape.changed = FALSE;
8267 tape.solved = FALSE;
8269 tape.recording = FALSE;
8270 tape.playing = FALSE;
8271 tape.pausing = FALSE;
8273 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8274 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8276 tape.no_info_chunk = TRUE;
8277 tape.no_valid_file = FALSE;
8280 static int getTapePosSize(struct TapeInfo *tape)
8282 int tape_pos_size = 0;
8284 if (tape->use_key_actions)
8285 tape_pos_size += tape->num_participating_players;
8287 if (tape->use_mouse_actions)
8288 tape_pos_size += 3; // x and y position and mouse button mask
8290 tape_pos_size += 1; // tape action delay value
8292 return tape_pos_size;
8295 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8297 tape->use_key_actions = FALSE;
8298 tape->use_mouse_actions = FALSE;
8300 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8301 tape->use_key_actions = TRUE;
8303 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8304 tape->use_mouse_actions = TRUE;
8307 static int getTapeActionValue(struct TapeInfo *tape)
8309 return (tape->use_key_actions &&
8310 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8311 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8312 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8313 TAPE_ACTIONS_DEFAULT);
8316 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8318 tape->file_version = getFileVersion(file);
8319 tape->game_version = getFileVersion(file);
8324 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8328 tape->random_seed = getFile32BitBE(file);
8329 tape->date = getFile32BitBE(file);
8330 tape->length = getFile32BitBE(file);
8332 // read header fields that are new since version 1.2
8333 if (tape->file_version >= FILE_VERSION_1_2)
8335 byte store_participating_players = getFile8Bit(file);
8338 // since version 1.2, tapes store which players participate in the tape
8339 tape->num_participating_players = 0;
8340 for (i = 0; i < MAX_PLAYERS; i++)
8342 tape->player_participates[i] = FALSE;
8344 if (store_participating_players & (1 << i))
8346 tape->player_participates[i] = TRUE;
8347 tape->num_participating_players++;
8351 setTapeActionFlags(tape, getFile8Bit(file));
8353 tape->property_bits = getFile8Bit(file);
8354 tape->solved = getFile8Bit(file);
8356 engine_version = getFileVersion(file);
8357 if (engine_version > 0)
8358 tape->engine_version = engine_version;
8360 tape->engine_version = tape->game_version;
8366 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8368 tape->scr_fieldx = getFile8Bit(file);
8369 tape->scr_fieldy = getFile8Bit(file);
8374 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8376 char *level_identifier = NULL;
8377 int level_identifier_size;
8380 tape->no_info_chunk = FALSE;
8382 level_identifier_size = getFile16BitBE(file);
8384 level_identifier = checked_malloc(level_identifier_size);
8386 for (i = 0; i < level_identifier_size; i++)
8387 level_identifier[i] = getFile8Bit(file);
8389 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8390 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8392 checked_free(level_identifier);
8394 tape->level_nr = getFile16BitBE(file);
8396 chunk_size = 2 + level_identifier_size + 2;
8401 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8404 int tape_pos_size = getTapePosSize(tape);
8405 int chunk_size_expected = tape_pos_size * tape->length;
8407 if (chunk_size_expected != chunk_size)
8409 ReadUnusedBytesFromFile(file, chunk_size);
8410 return chunk_size_expected;
8413 for (i = 0; i < tape->length; i++)
8415 if (i >= MAX_TAPE_LEN)
8417 Warn("tape truncated -- size exceeds maximum tape size %d",
8420 // tape too large; read and ignore remaining tape data from this chunk
8421 for (;i < tape->length; i++)
8422 ReadUnusedBytesFromFile(file, tape_pos_size);
8427 if (tape->use_key_actions)
8429 for (j = 0; j < MAX_PLAYERS; j++)
8431 tape->pos[i].action[j] = MV_NONE;
8433 if (tape->player_participates[j])
8434 tape->pos[i].action[j] = getFile8Bit(file);
8438 if (tape->use_mouse_actions)
8440 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8441 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8442 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8445 tape->pos[i].delay = getFile8Bit(file);
8447 if (tape->file_version == FILE_VERSION_1_0)
8449 // eliminate possible diagonal moves in old tapes
8450 // this is only for backward compatibility
8452 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8453 byte action = tape->pos[i].action[0];
8454 int k, num_moves = 0;
8456 for (k = 0; k < 4; k++)
8458 if (action & joy_dir[k])
8460 tape->pos[i + num_moves].action[0] = joy_dir[k];
8462 tape->pos[i + num_moves].delay = 0;
8471 tape->length += num_moves;
8474 else if (tape->file_version < FILE_VERSION_2_0)
8476 // convert pre-2.0 tapes to new tape format
8478 if (tape->pos[i].delay > 1)
8481 tape->pos[i + 1] = tape->pos[i];
8482 tape->pos[i + 1].delay = 1;
8485 for (j = 0; j < MAX_PLAYERS; j++)
8486 tape->pos[i].action[j] = MV_NONE;
8487 tape->pos[i].delay--;
8494 if (checkEndOfFile(file))
8498 if (i != tape->length)
8499 chunk_size = tape_pos_size * i;
8504 static void LoadTape_SokobanSolution(char *filename)
8507 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8509 if (!(file = openFile(filename, MODE_READ)))
8511 tape.no_valid_file = TRUE;
8516 while (!checkEndOfFile(file))
8518 unsigned char c = getByteFromFile(file);
8520 if (checkEndOfFile(file))
8527 tape.pos[tape.length].action[0] = MV_UP;
8528 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8534 tape.pos[tape.length].action[0] = MV_DOWN;
8535 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8541 tape.pos[tape.length].action[0] = MV_LEFT;
8542 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8548 tape.pos[tape.length].action[0] = MV_RIGHT;
8549 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8557 // ignore white-space characters
8561 tape.no_valid_file = TRUE;
8563 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8571 if (tape.no_valid_file)
8574 tape.length_frames = GetTapeLengthFrames();
8575 tape.length_seconds = GetTapeLengthSeconds();
8578 void LoadTapeFromFilename(char *filename)
8580 char cookie[MAX_LINE_LEN];
8581 char chunk_name[CHUNK_ID_LEN + 1];
8585 // always start with reliable default values
8586 setTapeInfoToDefaults();
8588 if (strSuffix(filename, ".sln"))
8590 LoadTape_SokobanSolution(filename);
8595 if (!(file = openFile(filename, MODE_READ)))
8597 tape.no_valid_file = TRUE;
8602 getFileChunkBE(file, chunk_name, NULL);
8603 if (strEqual(chunk_name, "RND1"))
8605 getFile32BitBE(file); // not used
8607 getFileChunkBE(file, chunk_name, NULL);
8608 if (!strEqual(chunk_name, "TAPE"))
8610 tape.no_valid_file = TRUE;
8612 Warn("unknown format of tape file '%s'", filename);
8619 else // check for pre-2.0 file format with cookie string
8621 strcpy(cookie, chunk_name);
8622 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8624 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8625 cookie[strlen(cookie) - 1] = '\0';
8627 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8629 tape.no_valid_file = TRUE;
8631 Warn("unknown format of tape file '%s'", filename);
8638 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8640 tape.no_valid_file = TRUE;
8642 Warn("unsupported version of tape file '%s'", filename);
8649 // pre-2.0 tape files have no game version, so use file version here
8650 tape.game_version = tape.file_version;
8653 if (tape.file_version < FILE_VERSION_1_2)
8655 // tape files from versions before 1.2.0 without chunk structure
8656 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8657 LoadTape_BODY(file, 2 * tape.length, &tape);
8665 int (*loader)(File *, int, struct TapeInfo *);
8669 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8670 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8671 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8672 { "INFO", -1, LoadTape_INFO },
8673 { "BODY", -1, LoadTape_BODY },
8677 while (getFileChunkBE(file, chunk_name, &chunk_size))
8681 while (chunk_info[i].name != NULL &&
8682 !strEqual(chunk_name, chunk_info[i].name))
8685 if (chunk_info[i].name == NULL)
8687 Warn("unknown chunk '%s' in tape file '%s'",
8688 chunk_name, filename);
8690 ReadUnusedBytesFromFile(file, chunk_size);
8692 else if (chunk_info[i].size != -1 &&
8693 chunk_info[i].size != chunk_size)
8695 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8696 chunk_size, chunk_name, filename);
8698 ReadUnusedBytesFromFile(file, chunk_size);
8702 // call function to load this tape chunk
8703 int chunk_size_expected =
8704 (chunk_info[i].loader)(file, chunk_size, &tape);
8706 // the size of some chunks cannot be checked before reading other
8707 // chunks first (like "HEAD" and "BODY") that contain some header
8708 // information, so check them here
8709 if (chunk_size_expected != chunk_size)
8711 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8712 chunk_size, chunk_name, filename);
8720 tape.length_frames = GetTapeLengthFrames();
8721 tape.length_seconds = GetTapeLengthSeconds();
8724 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8726 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8728 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8729 tape.engine_version);
8733 void LoadTape(int nr)
8735 char *filename = getTapeFilename(nr);
8737 LoadTapeFromFilename(filename);
8740 void LoadSolutionTape(int nr)
8742 char *filename = getSolutionTapeFilename(nr);
8744 LoadTapeFromFilename(filename);
8746 if (TAPE_IS_EMPTY(tape))
8748 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8749 level.native_bd_level->replay != NULL)
8750 CopyNativeTape_BD_to_RND(&level);
8751 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8752 level.native_sp_level->demo.is_available)
8753 CopyNativeTape_SP_to_RND(&level);
8757 void LoadScoreTape(char *score_tape_basename, int nr)
8759 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8761 LoadTapeFromFilename(filename);
8764 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8766 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8768 LoadTapeFromFilename(filename);
8771 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8773 // chunk required for team mode tapes with non-default screen size
8774 return (tape->num_participating_players > 1 &&
8775 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8776 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8779 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8781 putFileVersion(file, tape->file_version);
8782 putFileVersion(file, tape->game_version);
8785 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8788 byte store_participating_players = 0;
8790 // set bits for participating players for compact storage
8791 for (i = 0; i < MAX_PLAYERS; i++)
8792 if (tape->player_participates[i])
8793 store_participating_players |= (1 << i);
8795 putFile32BitBE(file, tape->random_seed);
8796 putFile32BitBE(file, tape->date);
8797 putFile32BitBE(file, tape->length);
8799 putFile8Bit(file, store_participating_players);
8801 putFile8Bit(file, getTapeActionValue(tape));
8803 putFile8Bit(file, tape->property_bits);
8804 putFile8Bit(file, tape->solved);
8806 putFileVersion(file, tape->engine_version);
8809 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8811 putFile8Bit(file, tape->scr_fieldx);
8812 putFile8Bit(file, tape->scr_fieldy);
8815 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8817 int level_identifier_size = strlen(tape->level_identifier) + 1;
8820 putFile16BitBE(file, level_identifier_size);
8822 for (i = 0; i < level_identifier_size; i++)
8823 putFile8Bit(file, tape->level_identifier[i]);
8825 putFile16BitBE(file, tape->level_nr);
8828 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8832 for (i = 0; i < tape->length; i++)
8834 if (tape->use_key_actions)
8836 for (j = 0; j < MAX_PLAYERS; j++)
8837 if (tape->player_participates[j])
8838 putFile8Bit(file, tape->pos[i].action[j]);
8841 if (tape->use_mouse_actions)
8843 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8844 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8845 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8848 putFile8Bit(file, tape->pos[i].delay);
8852 void SaveTapeToFilename(char *filename)
8856 int info_chunk_size;
8857 int body_chunk_size;
8859 if (!(file = fopen(filename, MODE_WRITE)))
8861 Warn("cannot save level recording file '%s'", filename);
8866 tape_pos_size = getTapePosSize(&tape);
8868 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8869 body_chunk_size = tape_pos_size * tape.length;
8871 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8872 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8874 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8875 SaveTape_VERS(file, &tape);
8877 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8878 SaveTape_HEAD(file, &tape);
8880 if (checkSaveTape_SCRN(&tape))
8882 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8883 SaveTape_SCRN(file, &tape);
8886 putFileChunkBE(file, "INFO", info_chunk_size);
8887 SaveTape_INFO(file, &tape);
8889 putFileChunkBE(file, "BODY", body_chunk_size);
8890 SaveTape_BODY(file, &tape);
8894 SetFilePermissions(filename, PERMS_PRIVATE);
8897 static void SaveTapeExt(char *filename)
8901 tape.file_version = FILE_VERSION_ACTUAL;
8902 tape.game_version = GAME_VERSION_ACTUAL;
8904 tape.num_participating_players = 0;
8906 // count number of participating players
8907 for (i = 0; i < MAX_PLAYERS; i++)
8908 if (tape.player_participates[i])
8909 tape.num_participating_players++;
8911 SaveTapeToFilename(filename);
8913 tape.changed = FALSE;
8916 void SaveTape(int nr)
8918 char *filename = getTapeFilename(nr);
8920 InitTapeDirectory(leveldir_current->subdir);
8922 SaveTapeExt(filename);
8925 void SaveScoreTape(int nr)
8927 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8929 // used instead of "leveldir_current->subdir" (for network games)
8930 InitScoreTapeDirectory(levelset.identifier, nr);
8932 SaveTapeExt(filename);
8935 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8936 unsigned int req_state_added)
8938 char *filename = getTapeFilename(nr);
8939 boolean new_tape = !fileExists(filename);
8940 boolean tape_saved = FALSE;
8942 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8947 Request(msg_saved, REQ_CONFIRM | req_state_added);
8955 boolean SaveTapeChecked(int nr)
8957 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8960 boolean SaveTapeChecked_LevelSolved(int nr)
8962 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8963 "Level solved! Tape saved!", REQ_STAY_OPEN);
8966 void DumpTape(struct TapeInfo *tape)
8968 int tape_frame_counter;
8971 if (tape->no_valid_file)
8973 Warn("cannot dump -- no valid tape file found");
8980 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8981 tape->level_nr, tape->file_version, tape->game_version);
8982 Print(" (effective engine version %08d)\n",
8983 tape->engine_version);
8984 Print("Level series identifier: '%s'\n", tape->level_identifier);
8986 Print("Solution tape: %s\n",
8987 tape->solved ? "yes" :
8988 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8990 Print("Special tape properties: ");
8991 if (tape->property_bits == TAPE_PROPERTY_NONE)
8993 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8994 Print("[em_random_bug]");
8995 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8996 Print("[game_speed]");
8997 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8999 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9000 Print("[single_step]");
9001 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9002 Print("[snapshot]");
9003 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9004 Print("[replayed]");
9005 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9006 Print("[tas_keys]");
9007 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9008 Print("[small_graphics]");
9011 int year2 = tape->date / 10000;
9012 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9013 int month_index_raw = (tape->date / 100) % 100;
9014 int month_index = month_index_raw % 12; // prevent invalid index
9015 int month = month_index + 1;
9016 int day = tape->date % 100;
9018 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9022 tape_frame_counter = 0;
9024 for (i = 0; i < tape->length; i++)
9026 if (i >= MAX_TAPE_LEN)
9031 for (j = 0; j < MAX_PLAYERS; j++)
9033 if (tape->player_participates[j])
9035 int action = tape->pos[i].action[j];
9037 Print("%d:%02x ", j, action);
9038 Print("[%c%c%c%c|%c%c] - ",
9039 (action & JOY_LEFT ? '<' : ' '),
9040 (action & JOY_RIGHT ? '>' : ' '),
9041 (action & JOY_UP ? '^' : ' '),
9042 (action & JOY_DOWN ? 'v' : ' '),
9043 (action & JOY_BUTTON_1 ? '1' : ' '),
9044 (action & JOY_BUTTON_2 ? '2' : ' '));
9048 Print("(%03d) ", tape->pos[i].delay);
9049 Print("[%05d]\n", tape_frame_counter);
9051 tape_frame_counter += tape->pos[i].delay;
9057 void DumpTapes(void)
9059 static LevelDirTree *dumptape_leveldir = NULL;
9061 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9062 global.dumptape_leveldir);
9064 if (dumptape_leveldir == NULL)
9065 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9067 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9068 global.dumptape_level_nr > dumptape_leveldir->last_level)
9069 Fail("no such level number: %d", global.dumptape_level_nr);
9071 leveldir_current = dumptape_leveldir;
9073 if (options.mytapes)
9074 LoadTape(global.dumptape_level_nr);
9076 LoadSolutionTape(global.dumptape_level_nr);
9084 // ============================================================================
9085 // score file functions
9086 // ============================================================================
9088 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9092 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9094 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9095 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9096 scores->entry[i].score = 0;
9097 scores->entry[i].time = 0;
9099 scores->entry[i].id = -1;
9100 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9101 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9102 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9103 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9104 strcpy(scores->entry[i].country_code, "??");
9107 scores->num_entries = 0;
9108 scores->last_added = -1;
9109 scores->last_added_local = -1;
9111 scores->updated = FALSE;
9112 scores->uploaded = FALSE;
9113 scores->tape_downloaded = FALSE;
9114 scores->force_last_added = FALSE;
9116 // The following values are intentionally not reset here:
9120 // - continue_playing
9121 // - continue_on_return
9124 static void setScoreInfoToDefaults(void)
9126 setScoreInfoToDefaultsExt(&scores);
9129 static void setServerScoreInfoToDefaults(void)
9131 setScoreInfoToDefaultsExt(&server_scores);
9134 static void LoadScore_OLD(int nr)
9137 char *filename = getScoreFilename(nr);
9138 char cookie[MAX_LINE_LEN];
9139 char line[MAX_LINE_LEN];
9143 if (!(file = fopen(filename, MODE_READ)))
9146 // check file identifier
9147 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9149 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9150 cookie[strlen(cookie) - 1] = '\0';
9152 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9154 Warn("unknown format of score file '%s'", filename);
9161 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9163 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9164 Warn("fscanf() failed; %s", strerror(errno));
9166 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9169 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9170 line[strlen(line) - 1] = '\0';
9172 for (line_ptr = line; *line_ptr; line_ptr++)
9174 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9176 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9177 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9186 static void ConvertScore_OLD(void)
9188 // only convert score to time for levels that rate playing time over score
9189 if (!level.rate_time_over_score)
9192 // convert old score to playing time for score-less levels (like Supaplex)
9193 int time_final_max = 999;
9196 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9198 int score = scores.entry[i].score;
9200 if (score > 0 && score < time_final_max)
9201 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9205 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9207 scores->file_version = getFileVersion(file);
9208 scores->game_version = getFileVersion(file);
9213 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9215 char *level_identifier = NULL;
9216 int level_identifier_size;
9219 level_identifier_size = getFile16BitBE(file);
9221 level_identifier = checked_malloc(level_identifier_size);
9223 for (i = 0; i < level_identifier_size; i++)
9224 level_identifier[i] = getFile8Bit(file);
9226 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9227 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9229 checked_free(level_identifier);
9231 scores->level_nr = getFile16BitBE(file);
9232 scores->num_entries = getFile16BitBE(file);
9234 chunk_size = 2 + level_identifier_size + 2 + 2;
9239 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9243 for (i = 0; i < scores->num_entries; i++)
9245 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9246 scores->entry[i].name[j] = getFile8Bit(file);
9248 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9251 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9256 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9260 for (i = 0; i < scores->num_entries; i++)
9261 scores->entry[i].score = getFile16BitBE(file);
9263 chunk_size = scores->num_entries * 2;
9268 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9272 for (i = 0; i < scores->num_entries; i++)
9273 scores->entry[i].score = getFile32BitBE(file);
9275 chunk_size = scores->num_entries * 4;
9280 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9284 for (i = 0; i < scores->num_entries; i++)
9285 scores->entry[i].time = getFile32BitBE(file);
9287 chunk_size = scores->num_entries * 4;
9292 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9296 for (i = 0; i < scores->num_entries; i++)
9298 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9299 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9301 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9304 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9309 void LoadScore(int nr)
9311 char *filename = getScoreFilename(nr);
9312 char cookie[MAX_LINE_LEN];
9313 char chunk_name[CHUNK_ID_LEN + 1];
9315 boolean old_score_file_format = FALSE;
9318 // always start with reliable default values
9319 setScoreInfoToDefaults();
9321 if (!(file = openFile(filename, MODE_READ)))
9324 getFileChunkBE(file, chunk_name, NULL);
9325 if (strEqual(chunk_name, "RND1"))
9327 getFile32BitBE(file); // not used
9329 getFileChunkBE(file, chunk_name, NULL);
9330 if (!strEqual(chunk_name, "SCOR"))
9332 Warn("unknown format of score file '%s'", filename);
9339 else // check for old file format with cookie string
9341 strcpy(cookie, chunk_name);
9342 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9344 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9345 cookie[strlen(cookie) - 1] = '\0';
9347 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9349 Warn("unknown format of score file '%s'", filename);
9356 old_score_file_format = TRUE;
9359 if (old_score_file_format)
9361 // score files from versions before 4.2.4.0 without chunk structure
9364 // convert score to time, if possible (mainly for Supaplex levels)
9373 int (*loader)(File *, int, struct ScoreInfo *);
9377 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9378 { "INFO", -1, LoadScore_INFO },
9379 { "NAME", -1, LoadScore_NAME },
9380 { "SCOR", -1, LoadScore_SCOR },
9381 { "SC4R", -1, LoadScore_SC4R },
9382 { "TIME", -1, LoadScore_TIME },
9383 { "TAPE", -1, LoadScore_TAPE },
9388 while (getFileChunkBE(file, chunk_name, &chunk_size))
9392 while (chunk_info[i].name != NULL &&
9393 !strEqual(chunk_name, chunk_info[i].name))
9396 if (chunk_info[i].name == NULL)
9398 Warn("unknown chunk '%s' in score file '%s'",
9399 chunk_name, filename);
9401 ReadUnusedBytesFromFile(file, chunk_size);
9403 else if (chunk_info[i].size != -1 &&
9404 chunk_info[i].size != chunk_size)
9406 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9407 chunk_size, chunk_name, filename);
9409 ReadUnusedBytesFromFile(file, chunk_size);
9413 // call function to load this score chunk
9414 int chunk_size_expected =
9415 (chunk_info[i].loader)(file, chunk_size, &scores);
9417 // the size of some chunks cannot be checked before reading other
9418 // chunks first (like "HEAD" and "BODY") that contain some header
9419 // information, so check them here
9420 if (chunk_size_expected != chunk_size)
9422 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9423 chunk_size, chunk_name, filename);
9432 #if ENABLE_HISTORIC_CHUNKS
9433 void SaveScore_OLD(int nr)
9436 char *filename = getScoreFilename(nr);
9439 // used instead of "leveldir_current->subdir" (for network games)
9440 InitScoreDirectory(levelset.identifier);
9442 if (!(file = fopen(filename, MODE_WRITE)))
9444 Warn("cannot save score for level %d", nr);
9449 fprintf(file, "%s\n\n", SCORE_COOKIE);
9451 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9452 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9456 SetFilePermissions(filename, PERMS_PRIVATE);
9460 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9462 putFileVersion(file, scores->file_version);
9463 putFileVersion(file, scores->game_version);
9466 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9468 int level_identifier_size = strlen(scores->level_identifier) + 1;
9471 putFile16BitBE(file, level_identifier_size);
9473 for (i = 0; i < level_identifier_size; i++)
9474 putFile8Bit(file, scores->level_identifier[i]);
9476 putFile16BitBE(file, scores->level_nr);
9477 putFile16BitBE(file, scores->num_entries);
9480 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9484 for (i = 0; i < scores->num_entries; i++)
9486 int name_size = strlen(scores->entry[i].name);
9488 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9489 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9493 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9497 for (i = 0; i < scores->num_entries; i++)
9498 putFile16BitBE(file, scores->entry[i].score);
9501 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9505 for (i = 0; i < scores->num_entries; i++)
9506 putFile32BitBE(file, scores->entry[i].score);
9509 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9513 for (i = 0; i < scores->num_entries; i++)
9514 putFile32BitBE(file, scores->entry[i].time);
9517 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9521 for (i = 0; i < scores->num_entries; i++)
9523 int size = strlen(scores->entry[i].tape_basename);
9525 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9526 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9530 static void SaveScoreToFilename(char *filename)
9533 int info_chunk_size;
9534 int name_chunk_size;
9535 int scor_chunk_size;
9536 int sc4r_chunk_size;
9537 int time_chunk_size;
9538 int tape_chunk_size;
9539 boolean has_large_score_values;
9542 if (!(file = fopen(filename, MODE_WRITE)))
9544 Warn("cannot save score file '%s'", filename);
9549 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9550 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9551 scor_chunk_size = scores.num_entries * 2;
9552 sc4r_chunk_size = scores.num_entries * 4;
9553 time_chunk_size = scores.num_entries * 4;
9554 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9556 has_large_score_values = FALSE;
9557 for (i = 0; i < scores.num_entries; i++)
9558 if (scores.entry[i].score > 0xffff)
9559 has_large_score_values = TRUE;
9561 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9562 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9564 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9565 SaveScore_VERS(file, &scores);
9567 putFileChunkBE(file, "INFO", info_chunk_size);
9568 SaveScore_INFO(file, &scores);
9570 putFileChunkBE(file, "NAME", name_chunk_size);
9571 SaveScore_NAME(file, &scores);
9573 if (has_large_score_values)
9575 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9576 SaveScore_SC4R(file, &scores);
9580 putFileChunkBE(file, "SCOR", scor_chunk_size);
9581 SaveScore_SCOR(file, &scores);
9584 putFileChunkBE(file, "TIME", time_chunk_size);
9585 SaveScore_TIME(file, &scores);
9587 putFileChunkBE(file, "TAPE", tape_chunk_size);
9588 SaveScore_TAPE(file, &scores);
9592 SetFilePermissions(filename, PERMS_PRIVATE);
9595 void SaveScore(int nr)
9597 char *filename = getScoreFilename(nr);
9600 // used instead of "leveldir_current->subdir" (for network games)
9601 InitScoreDirectory(levelset.identifier);
9603 scores.file_version = FILE_VERSION_ACTUAL;
9604 scores.game_version = GAME_VERSION_ACTUAL;
9606 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9607 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9608 scores.level_nr = level_nr;
9610 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9611 if (scores.entry[i].score == 0 &&
9612 scores.entry[i].time == 0 &&
9613 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9616 scores.num_entries = i;
9618 if (scores.num_entries == 0)
9621 SaveScoreToFilename(filename);
9624 static void LoadServerScoreFromCache(int nr)
9626 struct ScoreEntry score_entry;
9635 { &score_entry.score, FALSE, 0 },
9636 { &score_entry.time, FALSE, 0 },
9637 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9638 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9639 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9640 { &score_entry.id, FALSE, 0 },
9641 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9642 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9643 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9644 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9648 char *filename = getScoreCacheFilename(nr);
9649 SetupFileHash *score_hash = loadSetupFileHash(filename);
9652 server_scores.num_entries = 0;
9654 if (score_hash == NULL)
9657 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9659 score_entry = server_scores.entry[i];
9661 for (j = 0; score_mapping[j].value != NULL; j++)
9665 sprintf(token, "%02d.%d", i, j);
9667 char *value = getHashEntry(score_hash, token);
9672 if (score_mapping[j].is_string)
9674 char *score_value = (char *)score_mapping[j].value;
9675 int value_size = score_mapping[j].string_size;
9677 strncpy(score_value, value, value_size);
9678 score_value[value_size] = '\0';
9682 int *score_value = (int *)score_mapping[j].value;
9684 *score_value = atoi(value);
9687 server_scores.num_entries = i + 1;
9690 server_scores.entry[i] = score_entry;
9693 freeSetupFileHash(score_hash);
9696 void LoadServerScore(int nr, boolean download_score)
9698 if (!setup.use_api_server)
9701 // always start with reliable default values
9702 setServerScoreInfoToDefaults();
9704 // 1st step: load server scores from cache file (which may not exist)
9705 // (this should prevent reading it while the thread is writing to it)
9706 LoadServerScoreFromCache(nr);
9708 if (download_score && runtime.use_api_server)
9710 // 2nd step: download server scores from score server to cache file
9711 // (as thread, as it might time out if the server is not reachable)
9712 ApiGetScoreAsThread(nr);
9716 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9718 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9720 // if score tape not uploaded, ask for uploading missing tapes later
9721 if (!setup.has_remaining_tapes)
9722 setup.ask_for_remaining_tapes = TRUE;
9724 setup.provide_uploading_tapes = TRUE;
9725 setup.has_remaining_tapes = TRUE;
9727 SaveSetup_ServerSetup();
9730 void SaveServerScore(int nr, boolean tape_saved)
9732 if (!runtime.use_api_server)
9734 PrepareScoreTapesForUpload(leveldir_current->subdir);
9739 ApiAddScoreAsThread(nr, tape_saved, NULL);
9742 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9743 char *score_tape_filename)
9745 if (!runtime.use_api_server)
9748 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9751 void LoadLocalAndServerScore(int nr, boolean download_score)
9753 int last_added_local = scores.last_added_local;
9754 boolean force_last_added = scores.force_last_added;
9756 // needed if only showing server scores
9757 setScoreInfoToDefaults();
9759 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9762 // restore last added local score entry (before merging server scores)
9763 scores.last_added = scores.last_added_local = last_added_local;
9765 if (setup.use_api_server &&
9766 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9768 // load server scores from cache file and trigger update from server
9769 LoadServerScore(nr, download_score);
9771 // merge local scores with scores from server
9775 if (force_last_added)
9776 scores.force_last_added = force_last_added;
9780 // ============================================================================
9781 // setup file functions
9782 // ============================================================================
9784 #define TOKEN_STR_PLAYER_PREFIX "player_"
9787 static struct TokenInfo global_setup_tokens[] =
9791 &setup.player_name, "player_name"
9795 &setup.multiple_users, "multiple_users"
9799 &setup.sound, "sound"
9803 &setup.sound_loops, "repeating_sound_loops"
9807 &setup.sound_music, "background_music"
9811 &setup.sound_simple, "simple_sound_effects"
9815 &setup.toons, "toons"
9819 &setup.global_animations, "global_animations"
9823 &setup.scroll_delay, "scroll_delay"
9827 &setup.forced_scroll_delay, "forced_scroll_delay"
9831 &setup.scroll_delay_value, "scroll_delay_value"
9835 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9839 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9843 &setup.fade_screens, "fade_screens"
9847 &setup.autorecord, "automatic_tape_recording"
9851 &setup.autorecord_after_replay, "autorecord_after_replay"
9855 &setup.auto_pause_on_start, "auto_pause_on_start"
9859 &setup.show_titlescreen, "show_titlescreen"
9863 &setup.quick_doors, "quick_doors"
9867 &setup.team_mode, "team_mode"
9871 &setup.handicap, "handicap"
9875 &setup.skip_levels, "skip_levels"
9879 &setup.increment_levels, "increment_levels"
9883 &setup.auto_play_next_level, "auto_play_next_level"
9887 &setup.count_score_after_game, "count_score_after_game"
9891 &setup.show_scores_after_game, "show_scores_after_game"
9895 &setup.time_limit, "time_limit"
9899 &setup.fullscreen, "fullscreen"
9903 &setup.window_scaling_percent, "window_scaling_percent"
9907 &setup.window_scaling_quality, "window_scaling_quality"
9911 &setup.screen_rendering_mode, "screen_rendering_mode"
9915 &setup.vsync_mode, "vsync_mode"
9919 &setup.ask_on_escape, "ask_on_escape"
9923 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9927 &setup.ask_on_game_over, "ask_on_game_over"
9931 &setup.ask_on_quit_game, "ask_on_quit_game"
9935 &setup.ask_on_quit_program, "ask_on_quit_program"
9939 &setup.quick_switch, "quick_player_switch"
9943 &setup.input_on_focus, "input_on_focus"
9947 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9951 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9955 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9959 &setup.game_speed_extended, "game_speed_extended"
9963 &setup.game_frame_delay, "game_frame_delay"
9967 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9971 &setup.bd_skip_hatching, "bd_skip_hatching"
9975 &setup.bd_scroll_delay, "bd_scroll_delay"
9979 &setup.bd_smooth_movements, "bd_smooth_movements"
9983 &setup.sp_show_border_elements, "sp_show_border_elements"
9987 &setup.small_game_graphics, "small_game_graphics"
9991 &setup.show_load_save_buttons, "show_load_save_buttons"
9995 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9999 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10003 &setup.graphics_set, "graphics_set"
10007 &setup.sounds_set, "sounds_set"
10011 &setup.music_set, "music_set"
10015 &setup.override_level_graphics, "override_level_graphics"
10019 &setup.override_level_sounds, "override_level_sounds"
10023 &setup.override_level_music, "override_level_music"
10027 &setup.volume_simple, "volume_simple"
10031 &setup.volume_loops, "volume_loops"
10035 &setup.volume_music, "volume_music"
10039 &setup.network_mode, "network_mode"
10043 &setup.network_player_nr, "network_player"
10047 &setup.network_server_hostname, "network_server_hostname"
10051 &setup.touch.control_type, "touch.control_type"
10055 &setup.touch.move_distance, "touch.move_distance"
10059 &setup.touch.drop_distance, "touch.drop_distance"
10063 &setup.touch.transparency, "touch.transparency"
10067 &setup.touch.draw_outlined, "touch.draw_outlined"
10071 &setup.touch.draw_pressed, "touch.draw_pressed"
10075 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10079 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10083 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10087 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10091 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10095 static struct TokenInfo auto_setup_tokens[] =
10099 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10103 static struct TokenInfo server_setup_tokens[] =
10107 &setup.player_uuid, "player_uuid"
10111 &setup.player_version, "player_version"
10115 &setup.use_api_server, TEST_PREFIX "use_api_server"
10119 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10123 &setup.api_server_password, TEST_PREFIX "api_server_password"
10127 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10131 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10135 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10139 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10143 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10147 static struct TokenInfo editor_setup_tokens[] =
10151 &setup.editor.el_classic, "editor.el_classic"
10155 &setup.editor.el_custom, "editor.el_custom"
10159 &setup.editor.el_user_defined, "editor.el_user_defined"
10163 &setup.editor.el_dynamic, "editor.el_dynamic"
10167 &setup.editor.el_headlines, "editor.el_headlines"
10171 &setup.editor.show_element_token, "editor.show_element_token"
10175 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10179 static struct TokenInfo editor_cascade_setup_tokens[] =
10183 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10187 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10191 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10195 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10199 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10203 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10207 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10211 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10215 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10219 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10223 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10227 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10231 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10235 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10239 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10243 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10247 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10251 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10255 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10259 static struct TokenInfo shortcut_setup_tokens[] =
10263 &setup.shortcut.save_game, "shortcut.save_game"
10267 &setup.shortcut.load_game, "shortcut.load_game"
10271 &setup.shortcut.restart_game, "shortcut.restart_game"
10275 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10279 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10283 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10287 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10291 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10295 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10299 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10303 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10307 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10311 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10315 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10319 &setup.shortcut.tape_record, "shortcut.tape_record"
10323 &setup.shortcut.tape_play, "shortcut.tape_play"
10327 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10331 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10335 &setup.shortcut.sound_music, "shortcut.sound_music"
10339 &setup.shortcut.snap_left, "shortcut.snap_left"
10343 &setup.shortcut.snap_right, "shortcut.snap_right"
10347 &setup.shortcut.snap_up, "shortcut.snap_up"
10351 &setup.shortcut.snap_down, "shortcut.snap_down"
10355 static struct SetupInputInfo setup_input;
10356 static struct TokenInfo player_setup_tokens[] =
10360 &setup_input.use_joystick, ".use_joystick"
10364 &setup_input.joy.device_name, ".joy.device_name"
10368 &setup_input.joy.xleft, ".joy.xleft"
10372 &setup_input.joy.xmiddle, ".joy.xmiddle"
10376 &setup_input.joy.xright, ".joy.xright"
10380 &setup_input.joy.yupper, ".joy.yupper"
10384 &setup_input.joy.ymiddle, ".joy.ymiddle"
10388 &setup_input.joy.ylower, ".joy.ylower"
10392 &setup_input.joy.snap, ".joy.snap_field"
10396 &setup_input.joy.drop, ".joy.place_bomb"
10400 &setup_input.key.left, ".key.move_left"
10404 &setup_input.key.right, ".key.move_right"
10408 &setup_input.key.up, ".key.move_up"
10412 &setup_input.key.down, ".key.move_down"
10416 &setup_input.key.snap, ".key.snap_field"
10420 &setup_input.key.drop, ".key.place_bomb"
10424 static struct TokenInfo system_setup_tokens[] =
10428 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10432 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10436 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10440 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10444 static struct TokenInfo internal_setup_tokens[] =
10448 &setup.internal.program_title, "program_title"
10452 &setup.internal.program_version, "program_version"
10456 &setup.internal.program_author, "program_author"
10460 &setup.internal.program_email, "program_email"
10464 &setup.internal.program_website, "program_website"
10468 &setup.internal.program_copyright, "program_copyright"
10472 &setup.internal.program_company, "program_company"
10476 &setup.internal.program_icon_file, "program_icon_file"
10480 &setup.internal.default_graphics_set, "default_graphics_set"
10484 &setup.internal.default_sounds_set, "default_sounds_set"
10488 &setup.internal.default_music_set, "default_music_set"
10492 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10496 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10500 &setup.internal.fallback_music_file, "fallback_music_file"
10504 &setup.internal.default_level_series, "default_level_series"
10508 &setup.internal.default_window_width, "default_window_width"
10512 &setup.internal.default_window_height, "default_window_height"
10516 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10520 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10524 &setup.internal.create_user_levelset, "create_user_levelset"
10528 &setup.internal.info_screens_from_main, "info_screens_from_main"
10532 &setup.internal.menu_game, "menu_game"
10536 &setup.internal.menu_engines, "menu_engines"
10540 &setup.internal.menu_editor, "menu_editor"
10544 &setup.internal.menu_graphics, "menu_graphics"
10548 &setup.internal.menu_sound, "menu_sound"
10552 &setup.internal.menu_artwork, "menu_artwork"
10556 &setup.internal.menu_input, "menu_input"
10560 &setup.internal.menu_touch, "menu_touch"
10564 &setup.internal.menu_shortcuts, "menu_shortcuts"
10568 &setup.internal.menu_exit, "menu_exit"
10572 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10576 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10580 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10584 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10588 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10592 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10596 &setup.internal.info_title, "info_title"
10600 &setup.internal.info_elements, "info_elements"
10604 &setup.internal.info_music, "info_music"
10608 &setup.internal.info_credits, "info_credits"
10612 &setup.internal.info_program, "info_program"
10616 &setup.internal.info_version, "info_version"
10620 &setup.internal.info_levelset, "info_levelset"
10624 &setup.internal.info_exit, "info_exit"
10628 static struct TokenInfo debug_setup_tokens[] =
10632 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10636 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10640 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10644 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10648 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10652 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10656 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10660 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10664 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10668 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10672 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10676 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10680 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10684 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10688 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10692 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10696 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10700 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10704 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10708 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10712 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10715 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10719 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10723 &setup.debug.xsn_mode, "debug.xsn_mode"
10727 &setup.debug.xsn_percent, "debug.xsn_percent"
10731 static struct TokenInfo options_setup_tokens[] =
10735 &setup.options.verbose, "options.verbose"
10739 &setup.options.debug, "options.debug"
10743 &setup.options.debug_mode, "options.debug_mode"
10747 static void setSetupInfoToDefaults(struct SetupInfo *si)
10751 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10753 si->multiple_users = TRUE;
10756 si->sound_loops = TRUE;
10757 si->sound_music = TRUE;
10758 si->sound_simple = TRUE;
10760 si->global_animations = TRUE;
10761 si->scroll_delay = TRUE;
10762 si->forced_scroll_delay = FALSE;
10763 si->scroll_delay_value = STD_SCROLL_DELAY;
10764 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10765 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10766 si->fade_screens = TRUE;
10767 si->autorecord = TRUE;
10768 si->autorecord_after_replay = TRUE;
10769 si->auto_pause_on_start = FALSE;
10770 si->show_titlescreen = TRUE;
10771 si->quick_doors = FALSE;
10772 si->team_mode = FALSE;
10773 si->handicap = TRUE;
10774 si->skip_levels = TRUE;
10775 si->increment_levels = TRUE;
10776 si->auto_play_next_level = TRUE;
10777 si->count_score_after_game = TRUE;
10778 si->show_scores_after_game = TRUE;
10779 si->time_limit = TRUE;
10780 si->fullscreen = FALSE;
10781 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10782 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10783 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10784 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10785 si->ask_on_escape = TRUE;
10786 si->ask_on_escape_editor = TRUE;
10787 si->ask_on_game_over = TRUE;
10788 si->ask_on_quit_game = TRUE;
10789 si->ask_on_quit_program = TRUE;
10790 si->quick_switch = FALSE;
10791 si->input_on_focus = FALSE;
10792 si->prefer_aga_graphics = TRUE;
10793 si->prefer_lowpass_sounds = FALSE;
10794 si->prefer_extra_panel_items = TRUE;
10795 si->game_speed_extended = FALSE;
10796 si->game_frame_delay = GAME_FRAME_DELAY;
10797 si->bd_skip_uncovering = FALSE;
10798 si->bd_skip_hatching = FALSE;
10799 si->bd_scroll_delay = TRUE;
10800 si->bd_smooth_movements = AUTO;
10801 si->sp_show_border_elements = FALSE;
10802 si->small_game_graphics = FALSE;
10803 si->show_load_save_buttons = FALSE;
10804 si->show_undo_redo_buttons = FALSE;
10805 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10807 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10808 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10809 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10811 si->override_level_graphics = FALSE;
10812 si->override_level_sounds = FALSE;
10813 si->override_level_music = FALSE;
10815 si->volume_simple = 100; // percent
10816 si->volume_loops = 100; // percent
10817 si->volume_music = 100; // percent
10819 si->network_mode = FALSE;
10820 si->network_player_nr = 0; // first player
10821 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10823 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10824 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10825 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10826 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10827 si->touch.draw_outlined = TRUE;
10828 si->touch.draw_pressed = TRUE;
10830 for (i = 0; i < 2; i++)
10832 char *default_grid_button[6][2] =
10838 { "111222", " vv " },
10839 { "111222", " vv " }
10841 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10842 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10843 int min_xsize = MIN(6, grid_xsize);
10844 int min_ysize = MIN(6, grid_ysize);
10845 int startx = grid_xsize - min_xsize;
10846 int starty = grid_ysize - min_ysize;
10849 // virtual buttons grid can only be set to defaults if video is initialized
10850 // (this will be repeated if virtual buttons are not loaded from setup file)
10851 if (video.initialized)
10853 si->touch.grid_xsize[i] = grid_xsize;
10854 si->touch.grid_ysize[i] = grid_ysize;
10858 si->touch.grid_xsize[i] = -1;
10859 si->touch.grid_ysize[i] = -1;
10862 for (x = 0; x < MAX_GRID_XSIZE; x++)
10863 for (y = 0; y < MAX_GRID_YSIZE; y++)
10864 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10866 for (x = 0; x < min_xsize; x++)
10867 for (y = 0; y < min_ysize; y++)
10868 si->touch.grid_button[i][x][starty + y] =
10869 default_grid_button[y][0][x];
10871 for (x = 0; x < min_xsize; x++)
10872 for (y = 0; y < min_ysize; y++)
10873 si->touch.grid_button[i][startx + x][starty + y] =
10874 default_grid_button[y][1][x];
10877 si->touch.grid_initialized = video.initialized;
10879 si->touch.overlay_buttons = FALSE;
10881 si->editor.el_boulderdash = TRUE;
10882 si->editor.el_boulderdash_native = TRUE;
10883 si->editor.el_emerald_mine = TRUE;
10884 si->editor.el_emerald_mine_club = TRUE;
10885 si->editor.el_more = TRUE;
10886 si->editor.el_sokoban = TRUE;
10887 si->editor.el_supaplex = TRUE;
10888 si->editor.el_diamond_caves = TRUE;
10889 si->editor.el_dx_boulderdash = TRUE;
10891 si->editor.el_mirror_magic = TRUE;
10892 si->editor.el_deflektor = TRUE;
10894 si->editor.el_chars = TRUE;
10895 si->editor.el_steel_chars = TRUE;
10897 si->editor.el_classic = TRUE;
10898 si->editor.el_custom = TRUE;
10900 si->editor.el_user_defined = FALSE;
10901 si->editor.el_dynamic = TRUE;
10903 si->editor.el_headlines = TRUE;
10905 si->editor.show_element_token = FALSE;
10907 si->editor.show_read_only_warning = TRUE;
10909 si->editor.use_template_for_new_levels = TRUE;
10911 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10912 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10913 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10914 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10915 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10917 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10918 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10919 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10920 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10921 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10923 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10924 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10925 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10926 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10927 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10928 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10930 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10931 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10932 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10934 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10935 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10936 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10937 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10939 for (i = 0; i < MAX_PLAYERS; i++)
10941 si->input[i].use_joystick = FALSE;
10942 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10943 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10944 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10945 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10946 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10947 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10948 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10949 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10950 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10951 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10952 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10953 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10954 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10955 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10956 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10959 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10960 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10961 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10962 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10964 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10965 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10966 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10967 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10968 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10969 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10970 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10972 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10974 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10975 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10976 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10978 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10979 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10980 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10982 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10983 si->internal.choose_from_top_leveldir = FALSE;
10984 si->internal.show_scaling_in_title = TRUE;
10985 si->internal.create_user_levelset = TRUE;
10986 si->internal.info_screens_from_main = FALSE;
10988 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10989 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10991 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10992 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10993 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10994 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10995 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10996 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10997 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10998 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10999 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11000 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11002 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11003 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11004 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11005 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11006 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11007 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11008 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11009 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11010 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11011 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11013 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11014 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11016 si->debug.show_frames_per_second = FALSE;
11018 si->debug.xsn_mode = AUTO;
11019 si->debug.xsn_percent = 0;
11021 si->options.verbose = FALSE;
11022 si->options.debug = FALSE;
11023 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11025 #if defined(PLATFORM_ANDROID)
11026 si->fullscreen = TRUE;
11027 si->touch.overlay_buttons = TRUE;
11030 setHideSetupEntry(&setup.debug.xsn_mode);
11033 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11035 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11038 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11040 si->player_uuid = NULL; // (will be set later)
11041 si->player_version = 1; // (will be set later)
11043 si->use_api_server = TRUE;
11044 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11045 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11046 si->ask_for_uploading_tapes = TRUE;
11047 si->ask_for_remaining_tapes = FALSE;
11048 si->provide_uploading_tapes = TRUE;
11049 si->ask_for_using_api_server = TRUE;
11050 si->has_remaining_tapes = FALSE;
11053 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11055 si->editor_cascade.el_bd = TRUE;
11056 si->editor_cascade.el_bd_native = TRUE;
11057 si->editor_cascade.el_em = TRUE;
11058 si->editor_cascade.el_emc = TRUE;
11059 si->editor_cascade.el_rnd = TRUE;
11060 si->editor_cascade.el_sb = TRUE;
11061 si->editor_cascade.el_sp = TRUE;
11062 si->editor_cascade.el_dc = TRUE;
11063 si->editor_cascade.el_dx = TRUE;
11065 si->editor_cascade.el_mm = TRUE;
11066 si->editor_cascade.el_df = TRUE;
11068 si->editor_cascade.el_chars = FALSE;
11069 si->editor_cascade.el_steel_chars = FALSE;
11070 si->editor_cascade.el_ce = FALSE;
11071 si->editor_cascade.el_ge = FALSE;
11072 si->editor_cascade.el_es = FALSE;
11073 si->editor_cascade.el_ref = FALSE;
11074 si->editor_cascade.el_user = FALSE;
11075 si->editor_cascade.el_dynamic = FALSE;
11078 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11080 static char *getHideSetupToken(void *setup_value)
11082 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11084 if (setup_value != NULL)
11085 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11087 return hide_setup_token;
11090 void setHideSetupEntry(void *setup_value)
11092 char *hide_setup_token = getHideSetupToken(setup_value);
11094 if (hide_setup_hash == NULL)
11095 hide_setup_hash = newSetupFileHash();
11097 if (setup_value != NULL)
11098 setHashEntry(hide_setup_hash, hide_setup_token, "");
11101 void removeHideSetupEntry(void *setup_value)
11103 char *hide_setup_token = getHideSetupToken(setup_value);
11105 if (setup_value != NULL)
11106 removeHashEntry(hide_setup_hash, hide_setup_token);
11109 boolean hideSetupEntry(void *setup_value)
11111 char *hide_setup_token = getHideSetupToken(setup_value);
11113 return (setup_value != NULL &&
11114 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11117 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11118 struct TokenInfo *token_info,
11119 int token_nr, char *token_text)
11121 char *token_hide_text = getStringCat2(token_text, ".hide");
11122 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11124 // set the value of this setup option in the setup option structure
11125 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11127 // check if this setup option should be hidden in the setup menu
11128 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11129 setHideSetupEntry(token_info[token_nr].value);
11131 free(token_hide_text);
11134 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11135 struct TokenInfo *token_info,
11138 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11139 token_info[token_nr].text);
11142 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11146 if (!setup_file_hash)
11149 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11150 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11152 setup.touch.grid_initialized = TRUE;
11153 for (i = 0; i < 2; i++)
11155 int grid_xsize = setup.touch.grid_xsize[i];
11156 int grid_ysize = setup.touch.grid_ysize[i];
11159 // if virtual buttons are not loaded from setup file, repeat initializing
11160 // virtual buttons grid with default values later when video is initialized
11161 if (grid_xsize == -1 ||
11164 setup.touch.grid_initialized = FALSE;
11169 for (y = 0; y < grid_ysize; y++)
11171 char token_string[MAX_LINE_LEN];
11173 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11175 char *value_string = getHashEntry(setup_file_hash, token_string);
11177 if (value_string == NULL)
11180 for (x = 0; x < grid_xsize; x++)
11182 char c = value_string[x];
11184 setup.touch.grid_button[i][x][y] =
11185 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11190 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11191 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11193 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11194 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11196 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11200 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11202 setup_input = setup.input[pnr];
11203 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11205 char full_token[100];
11207 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11208 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11211 setup.input[pnr] = setup_input;
11214 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11215 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11217 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11218 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11220 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11221 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11223 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11224 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11226 setHideRelatedSetupEntries();
11229 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11233 if (!setup_file_hash)
11236 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11237 setSetupInfo(auto_setup_tokens, i,
11238 getHashEntry(setup_file_hash,
11239 auto_setup_tokens[i].text));
11242 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11246 if (!setup_file_hash)
11249 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11250 setSetupInfo(server_setup_tokens, i,
11251 getHashEntry(setup_file_hash,
11252 server_setup_tokens[i].text));
11255 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11259 if (!setup_file_hash)
11262 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11263 setSetupInfo(editor_cascade_setup_tokens, i,
11264 getHashEntry(setup_file_hash,
11265 editor_cascade_setup_tokens[i].text));
11268 void LoadUserNames(void)
11270 int last_user_nr = user.nr;
11273 if (global.user_names != NULL)
11275 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11276 checked_free(global.user_names[i]);
11278 checked_free(global.user_names);
11281 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11283 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11287 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11289 if (setup_file_hash)
11291 char *player_name = getHashEntry(setup_file_hash, "player_name");
11293 global.user_names[i] = getFixedUserName(player_name);
11295 freeSetupFileHash(setup_file_hash);
11298 if (global.user_names[i] == NULL)
11299 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11302 user.nr = last_user_nr;
11305 void LoadSetupFromFilename(char *filename)
11307 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11309 if (setup_file_hash)
11311 decodeSetupFileHash_Default(setup_file_hash);
11313 freeSetupFileHash(setup_file_hash);
11317 Debug("setup", "using default setup values");
11321 static void LoadSetup_SpecialPostProcessing(void)
11323 char *player_name_new;
11325 // needed to work around problems with fixed length strings
11326 player_name_new = getFixedUserName(setup.player_name);
11327 free(setup.player_name);
11328 setup.player_name = player_name_new;
11330 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11331 if (setup.scroll_delay == FALSE)
11333 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11334 setup.scroll_delay = TRUE; // now always "on"
11337 // make sure that scroll delay value stays inside valid range
11338 setup.scroll_delay_value =
11339 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11342 void LoadSetup_Default(void)
11346 // always start with reliable default values
11347 setSetupInfoToDefaults(&setup);
11349 // try to load setup values from default setup file
11350 filename = getDefaultSetupFilename();
11352 if (fileExists(filename))
11353 LoadSetupFromFilename(filename);
11355 // try to load setup values from platform setup file
11356 filename = getPlatformSetupFilename();
11358 if (fileExists(filename))
11359 LoadSetupFromFilename(filename);
11361 // try to load setup values from user setup file
11362 filename = getSetupFilename();
11364 LoadSetupFromFilename(filename);
11366 LoadSetup_SpecialPostProcessing();
11369 void LoadSetup_AutoSetup(void)
11371 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11372 SetupFileHash *setup_file_hash = NULL;
11374 // always start with reliable default values
11375 setSetupInfoToDefaults_AutoSetup(&setup);
11377 setup_file_hash = loadSetupFileHash(filename);
11379 if (setup_file_hash)
11381 decodeSetupFileHash_AutoSetup(setup_file_hash);
11383 freeSetupFileHash(setup_file_hash);
11389 void LoadSetup_ServerSetup(void)
11391 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11392 SetupFileHash *setup_file_hash = NULL;
11394 // always start with reliable default values
11395 setSetupInfoToDefaults_ServerSetup(&setup);
11397 setup_file_hash = loadSetupFileHash(filename);
11399 if (setup_file_hash)
11401 decodeSetupFileHash_ServerSetup(setup_file_hash);
11403 freeSetupFileHash(setup_file_hash);
11408 if (setup.player_uuid == NULL)
11410 // player UUID does not yet exist in setup file
11411 setup.player_uuid = getStringCopy(getUUID());
11412 setup.player_version = 2;
11414 SaveSetup_ServerSetup();
11418 void LoadSetup_EditorCascade(void)
11420 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11421 SetupFileHash *setup_file_hash = NULL;
11423 // always start with reliable default values
11424 setSetupInfoToDefaults_EditorCascade(&setup);
11426 setup_file_hash = loadSetupFileHash(filename);
11428 if (setup_file_hash)
11430 decodeSetupFileHash_EditorCascade(setup_file_hash);
11432 freeSetupFileHash(setup_file_hash);
11438 void LoadSetup(void)
11440 LoadSetup_Default();
11441 LoadSetup_AutoSetup();
11442 LoadSetup_ServerSetup();
11443 LoadSetup_EditorCascade();
11446 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11447 char *mapping_line)
11449 char mapping_guid[MAX_LINE_LEN];
11450 char *mapping_start, *mapping_end;
11452 // get GUID from game controller mapping line: copy complete line
11453 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11454 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11456 // get GUID from game controller mapping line: cut after GUID part
11457 mapping_start = strchr(mapping_guid, ',');
11458 if (mapping_start != NULL)
11459 *mapping_start = '\0';
11461 // cut newline from game controller mapping line
11462 mapping_end = strchr(mapping_line, '\n');
11463 if (mapping_end != NULL)
11464 *mapping_end = '\0';
11466 // add mapping entry to game controller mappings hash
11467 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11470 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11475 if (!(file = fopen(filename, MODE_READ)))
11477 Warn("cannot read game controller mappings file '%s'", filename);
11482 while (!feof(file))
11484 char line[MAX_LINE_LEN];
11486 if (!fgets(line, MAX_LINE_LEN, file))
11489 addGameControllerMappingToHash(mappings_hash, line);
11495 void SaveSetup_Default(void)
11497 char *filename = getSetupFilename();
11501 InitUserDataDirectory();
11503 if (!(file = fopen(filename, MODE_WRITE)))
11505 Warn("cannot write setup file '%s'", filename);
11510 fprintFileHeader(file, SETUP_FILENAME);
11512 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11514 // just to make things nicer :)
11515 if (global_setup_tokens[i].value == &setup.multiple_users ||
11516 global_setup_tokens[i].value == &setup.sound ||
11517 global_setup_tokens[i].value == &setup.graphics_set ||
11518 global_setup_tokens[i].value == &setup.volume_simple ||
11519 global_setup_tokens[i].value == &setup.network_mode ||
11520 global_setup_tokens[i].value == &setup.touch.control_type ||
11521 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11522 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11523 fprintf(file, "\n");
11525 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11528 for (i = 0; i < 2; i++)
11530 int grid_xsize = setup.touch.grid_xsize[i];
11531 int grid_ysize = setup.touch.grid_ysize[i];
11534 fprintf(file, "\n");
11536 for (y = 0; y < grid_ysize; y++)
11538 char token_string[MAX_LINE_LEN];
11539 char value_string[MAX_LINE_LEN];
11541 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11543 for (x = 0; x < grid_xsize; x++)
11545 char c = setup.touch.grid_button[i][x][y];
11547 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11550 value_string[grid_xsize] = '\0';
11552 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11556 fprintf(file, "\n");
11557 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11558 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11560 fprintf(file, "\n");
11561 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11562 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11564 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11568 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11569 fprintf(file, "\n");
11571 setup_input = setup.input[pnr];
11572 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11573 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11576 fprintf(file, "\n");
11577 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11578 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11580 // (internal setup values not saved to user setup file)
11582 fprintf(file, "\n");
11583 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11584 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11585 setup.debug.xsn_mode != AUTO)
11586 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11588 fprintf(file, "\n");
11589 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11590 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11594 SetFilePermissions(filename, PERMS_PRIVATE);
11597 void SaveSetup_AutoSetup(void)
11599 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11603 InitUserDataDirectory();
11605 if (!(file = fopen(filename, MODE_WRITE)))
11607 Warn("cannot write auto setup file '%s'", filename);
11614 fprintFileHeader(file, AUTOSETUP_FILENAME);
11616 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11617 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11621 SetFilePermissions(filename, PERMS_PRIVATE);
11626 void SaveSetup_ServerSetup(void)
11628 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11632 InitUserDataDirectory();
11634 if (!(file = fopen(filename, MODE_WRITE)))
11636 Warn("cannot write server setup file '%s'", filename);
11643 fprintFileHeader(file, SERVERSETUP_FILENAME);
11645 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11647 // just to make things nicer :)
11648 if (server_setup_tokens[i].value == &setup.use_api_server)
11649 fprintf(file, "\n");
11651 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11656 SetFilePermissions(filename, PERMS_PRIVATE);
11661 void SaveSetup_EditorCascade(void)
11663 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11667 InitUserDataDirectory();
11669 if (!(file = fopen(filename, MODE_WRITE)))
11671 Warn("cannot write editor cascade state file '%s'", filename);
11678 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11680 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11681 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11685 SetFilePermissions(filename, PERMS_PRIVATE);
11690 void SaveSetup(void)
11692 SaveSetup_Default();
11693 SaveSetup_AutoSetup();
11694 SaveSetup_ServerSetup();
11695 SaveSetup_EditorCascade();
11698 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11703 if (!(file = fopen(filename, MODE_WRITE)))
11705 Warn("cannot write game controller mappings file '%s'", filename);
11710 BEGIN_HASH_ITERATION(mappings_hash, itr)
11712 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11714 END_HASH_ITERATION(mappings_hash, itr)
11719 void SaveSetup_AddGameControllerMapping(char *mapping)
11721 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11722 SetupFileHash *mappings_hash = newSetupFileHash();
11724 InitUserDataDirectory();
11726 // load existing personal game controller mappings
11727 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11729 // add new mapping to personal game controller mappings
11730 addGameControllerMappingToHash(mappings_hash, mapping);
11732 // save updated personal game controller mappings
11733 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11735 freeSetupFileHash(mappings_hash);
11739 void LoadCustomElementDescriptions(void)
11741 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11742 SetupFileHash *setup_file_hash;
11745 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11747 if (element_info[i].custom_description != NULL)
11749 free(element_info[i].custom_description);
11750 element_info[i].custom_description = NULL;
11754 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11757 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11759 char *token = getStringCat2(element_info[i].token_name, ".name");
11760 char *value = getHashEntry(setup_file_hash, token);
11763 element_info[i].custom_description = getStringCopy(value);
11768 freeSetupFileHash(setup_file_hash);
11771 static int getElementFromToken(char *token)
11773 char *value = getHashEntry(element_token_hash, token);
11776 return atoi(value);
11778 Warn("unknown element token '%s'", token);
11780 return EL_UNDEFINED;
11783 void FreeGlobalAnimEventInfo(void)
11785 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11787 if (gaei->event_list == NULL)
11792 for (i = 0; i < gaei->num_event_lists; i++)
11794 checked_free(gaei->event_list[i]->event_value);
11795 checked_free(gaei->event_list[i]);
11798 checked_free(gaei->event_list);
11800 gaei->event_list = NULL;
11801 gaei->num_event_lists = 0;
11804 static int AddGlobalAnimEventList(void)
11806 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11807 int list_pos = gaei->num_event_lists++;
11809 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11810 sizeof(struct GlobalAnimEventListInfo *));
11812 gaei->event_list[list_pos] =
11813 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11815 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11817 gaeli->event_value = NULL;
11818 gaeli->num_event_values = 0;
11823 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11825 // do not add empty global animation events
11826 if (event_value == ANIM_EVENT_NONE)
11829 // if list position is undefined, create new list
11830 if (list_pos == ANIM_EVENT_UNDEFINED)
11831 list_pos = AddGlobalAnimEventList();
11833 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11834 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11835 int value_pos = gaeli->num_event_values++;
11837 gaeli->event_value = checked_realloc(gaeli->event_value,
11838 gaeli->num_event_values * sizeof(int *));
11840 gaeli->event_value[value_pos] = event_value;
11845 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11847 if (list_pos == ANIM_EVENT_UNDEFINED)
11848 return ANIM_EVENT_NONE;
11850 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11851 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11853 return gaeli->event_value[value_pos];
11856 int GetGlobalAnimEventValueCount(int list_pos)
11858 if (list_pos == ANIM_EVENT_UNDEFINED)
11861 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11862 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11864 return gaeli->num_event_values;
11867 // This function checks if a string <s> of the format "string1, string2, ..."
11868 // exactly contains a string <s_contained>.
11870 static boolean string_has_parameter(char *s, char *s_contained)
11874 if (s == NULL || s_contained == NULL)
11877 if (strlen(s_contained) > strlen(s))
11880 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11882 char next_char = s[strlen(s_contained)];
11884 // check if next character is delimiter or whitespace
11885 if (next_char == ',' || next_char == '\0' ||
11886 next_char == ' ' || next_char == '\t')
11890 // check if string contains another parameter string after a comma
11891 substring = strchr(s, ',');
11892 if (substring == NULL) // string does not contain a comma
11895 // advance string pointer to next character after the comma
11898 // skip potential whitespaces after the comma
11899 while (*substring == ' ' || *substring == '\t')
11902 return string_has_parameter(substring, s_contained);
11905 static int get_anim_parameter_value_ce(char *s)
11908 char *pattern_1 = "ce_change:custom_";
11909 char *pattern_2 = ".page_";
11910 int pattern_1_len = strlen(pattern_1);
11911 char *matching_char = strstr(s_ptr, pattern_1);
11912 int result = ANIM_EVENT_NONE;
11914 if (matching_char == NULL)
11915 return ANIM_EVENT_NONE;
11917 result = ANIM_EVENT_CE_CHANGE;
11919 s_ptr = matching_char + pattern_1_len;
11921 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11922 if (*s_ptr >= '0' && *s_ptr <= '9')
11924 int gic_ce_nr = (*s_ptr++ - '0');
11926 if (*s_ptr >= '0' && *s_ptr <= '9')
11928 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11930 if (*s_ptr >= '0' && *s_ptr <= '9')
11931 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11934 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11935 return ANIM_EVENT_NONE;
11937 // custom element stored as 0 to 255
11940 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11944 // invalid custom element number specified
11946 return ANIM_EVENT_NONE;
11949 // check for change page number ("page_X" or "page_XX") (optional)
11950 if (strPrefix(s_ptr, pattern_2))
11952 s_ptr += strlen(pattern_2);
11954 if (*s_ptr >= '0' && *s_ptr <= '9')
11956 int gic_page_nr = (*s_ptr++ - '0');
11958 if (*s_ptr >= '0' && *s_ptr <= '9')
11959 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11961 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11962 return ANIM_EVENT_NONE;
11964 // change page stored as 1 to 32 (0 means "all change pages")
11966 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11970 // invalid animation part number specified
11972 return ANIM_EVENT_NONE;
11976 // discard result if next character is neither delimiter nor whitespace
11977 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11978 *s_ptr == ' ' || *s_ptr == '\t'))
11979 return ANIM_EVENT_NONE;
11984 static int get_anim_parameter_value(char *s)
11986 int event_value[] =
11994 char *pattern_1[] =
12002 char *pattern_2 = ".part_";
12003 char *matching_char = NULL;
12005 int pattern_1_len = 0;
12006 int result = ANIM_EVENT_NONE;
12009 result = get_anim_parameter_value_ce(s);
12011 if (result != ANIM_EVENT_NONE)
12014 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12016 matching_char = strstr(s_ptr, pattern_1[i]);
12017 pattern_1_len = strlen(pattern_1[i]);
12018 result = event_value[i];
12020 if (matching_char != NULL)
12024 if (matching_char == NULL)
12025 return ANIM_EVENT_NONE;
12027 s_ptr = matching_char + pattern_1_len;
12029 // check for main animation number ("anim_X" or "anim_XX")
12030 if (*s_ptr >= '0' && *s_ptr <= '9')
12032 int gic_anim_nr = (*s_ptr++ - '0');
12034 if (*s_ptr >= '0' && *s_ptr <= '9')
12035 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12037 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12038 return ANIM_EVENT_NONE;
12040 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12044 // invalid main animation number specified
12046 return ANIM_EVENT_NONE;
12049 // check for animation part number ("part_X" or "part_XX") (optional)
12050 if (strPrefix(s_ptr, pattern_2))
12052 s_ptr += strlen(pattern_2);
12054 if (*s_ptr >= '0' && *s_ptr <= '9')
12056 int gic_part_nr = (*s_ptr++ - '0');
12058 if (*s_ptr >= '0' && *s_ptr <= '9')
12059 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12061 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12062 return ANIM_EVENT_NONE;
12064 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12068 // invalid animation part number specified
12070 return ANIM_EVENT_NONE;
12074 // discard result if next character is neither delimiter nor whitespace
12075 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12076 *s_ptr == ' ' || *s_ptr == '\t'))
12077 return ANIM_EVENT_NONE;
12082 static int get_anim_parameter_values(char *s)
12084 int list_pos = ANIM_EVENT_UNDEFINED;
12085 int event_value = ANIM_EVENT_DEFAULT;
12087 if (string_has_parameter(s, "any"))
12088 event_value |= ANIM_EVENT_ANY;
12090 if (string_has_parameter(s, "click:self") ||
12091 string_has_parameter(s, "click") ||
12092 string_has_parameter(s, "self"))
12093 event_value |= ANIM_EVENT_SELF;
12095 if (string_has_parameter(s, "unclick:any"))
12096 event_value |= ANIM_EVENT_UNCLICK_ANY;
12098 // if animation event found, add it to global animation event list
12099 if (event_value != ANIM_EVENT_NONE)
12100 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12104 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12105 event_value = get_anim_parameter_value(s);
12107 // if animation event found, add it to global animation event list
12108 if (event_value != ANIM_EVENT_NONE)
12109 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12111 // continue with next part of the string, starting with next comma
12112 s = strchr(s + 1, ',');
12118 static int get_anim_action_parameter_value(char *token)
12120 // check most common default case first to massively speed things up
12121 if (strEqual(token, ARG_UNDEFINED))
12122 return ANIM_EVENT_ACTION_NONE;
12124 int result = getImageIDFromToken(token);
12128 char *gfx_token = getStringCat2("gfx.", token);
12130 result = getImageIDFromToken(gfx_token);
12132 checked_free(gfx_token);
12137 Key key = getKeyFromX11KeyName(token);
12139 if (key != KSYM_UNDEFINED)
12140 result = -(int)key;
12147 result = get_hash_from_string(token); // unsigned int => int
12148 result = ABS(result); // may be negative now
12149 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12151 setHashEntry(anim_url_hash, int2str(result, 0), token);
12156 result = ANIM_EVENT_ACTION_NONE;
12161 int get_parameter_value(char *value_raw, char *suffix, int type)
12163 char *value = getStringToLower(value_raw);
12164 int result = 0; // probably a save default value
12166 if (strEqual(suffix, ".direction"))
12168 result = (strEqual(value, "left") ? MV_LEFT :
12169 strEqual(value, "right") ? MV_RIGHT :
12170 strEqual(value, "up") ? MV_UP :
12171 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12173 else if (strEqual(suffix, ".position"))
12175 result = (strEqual(value, "left") ? POS_LEFT :
12176 strEqual(value, "right") ? POS_RIGHT :
12177 strEqual(value, "top") ? POS_TOP :
12178 strEqual(value, "upper") ? POS_UPPER :
12179 strEqual(value, "middle") ? POS_MIDDLE :
12180 strEqual(value, "lower") ? POS_LOWER :
12181 strEqual(value, "bottom") ? POS_BOTTOM :
12182 strEqual(value, "any") ? POS_ANY :
12183 strEqual(value, "ce") ? POS_CE :
12184 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12185 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12187 else if (strEqual(suffix, ".align"))
12189 result = (strEqual(value, "left") ? ALIGN_LEFT :
12190 strEqual(value, "right") ? ALIGN_RIGHT :
12191 strEqual(value, "center") ? ALIGN_CENTER :
12192 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12194 else if (strEqual(suffix, ".valign"))
12196 result = (strEqual(value, "top") ? VALIGN_TOP :
12197 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12198 strEqual(value, "middle") ? VALIGN_MIDDLE :
12199 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12201 else if (strEqual(suffix, ".anim_mode"))
12203 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12204 string_has_parameter(value, "loop") ? ANIM_LOOP :
12205 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12206 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12207 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12208 string_has_parameter(value, "random") ? ANIM_RANDOM :
12209 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12210 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12211 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12212 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12213 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12214 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12215 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12216 string_has_parameter(value, "all") ? ANIM_ALL :
12217 string_has_parameter(value, "tiled") ? ANIM_TILED :
12218 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12221 if (string_has_parameter(value, "once"))
12222 result |= ANIM_ONCE;
12224 if (string_has_parameter(value, "reverse"))
12225 result |= ANIM_REVERSE;
12227 if (string_has_parameter(value, "opaque_player"))
12228 result |= ANIM_OPAQUE_PLAYER;
12230 if (string_has_parameter(value, "static_panel"))
12231 result |= ANIM_STATIC_PANEL;
12233 else if (strEqual(suffix, ".init_event") ||
12234 strEqual(suffix, ".anim_event"))
12236 result = get_anim_parameter_values(value);
12238 else if (strEqual(suffix, ".init_delay_action") ||
12239 strEqual(suffix, ".anim_delay_action") ||
12240 strEqual(suffix, ".post_delay_action") ||
12241 strEqual(suffix, ".init_event_action") ||
12242 strEqual(suffix, ".anim_event_action"))
12244 result = get_anim_action_parameter_value(value_raw);
12246 else if (strEqual(suffix, ".class"))
12248 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12249 get_hash_from_string(value));
12251 else if (strEqual(suffix, ".style"))
12253 result = STYLE_DEFAULT;
12255 if (string_has_parameter(value, "accurate_borders"))
12256 result |= STYLE_ACCURATE_BORDERS;
12258 if (string_has_parameter(value, "inner_corners"))
12259 result |= STYLE_INNER_CORNERS;
12261 if (string_has_parameter(value, "reverse"))
12262 result |= STYLE_REVERSE;
12264 if (string_has_parameter(value, "leftmost_position"))
12265 result |= STYLE_LEFTMOST_POSITION;
12267 if (string_has_parameter(value, "block_clicks"))
12268 result |= STYLE_BLOCK;
12270 if (string_has_parameter(value, "passthrough_clicks"))
12271 result |= STYLE_PASSTHROUGH;
12273 if (string_has_parameter(value, "multiple_actions"))
12274 result |= STYLE_MULTIPLE_ACTIONS;
12276 if (string_has_parameter(value, "consume_ce_event"))
12277 result |= STYLE_CONSUME_CE_EVENT;
12279 else if (strEqual(suffix, ".fade_mode"))
12281 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12282 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12283 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12284 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12285 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12286 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12287 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12288 FADE_MODE_DEFAULT);
12290 else if (strEqual(suffix, ".auto_delay_unit"))
12292 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12293 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12294 AUTO_DELAY_UNIT_DEFAULT);
12296 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12298 result = gfx.get_font_from_token_function(value);
12300 else // generic parameter of type integer or boolean
12302 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12303 type == TYPE_INTEGER ? get_integer_from_string(value) :
12304 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12305 ARG_UNDEFINED_VALUE);
12313 static int get_token_parameter_value(char *token, char *value_raw)
12317 if (token == NULL || value_raw == NULL)
12318 return ARG_UNDEFINED_VALUE;
12320 suffix = strrchr(token, '.');
12321 if (suffix == NULL)
12324 if (strEqual(suffix, ".element"))
12325 return getElementFromToken(value_raw);
12327 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12328 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12331 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12332 boolean ignore_defaults)
12336 for (i = 0; image_config_vars[i].token != NULL; i++)
12338 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12340 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12341 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12345 *image_config_vars[i].value =
12346 get_token_parameter_value(image_config_vars[i].token, value);
12350 void InitMenuDesignSettings_Static(void)
12352 // always start with reliable default values from static default config
12353 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12356 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12360 // the following initializes hierarchical values from static configuration
12362 // special case: initialize "ARG_DEFAULT" values in static default config
12363 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12364 titlescreen_initial_first_default.fade_mode =
12365 title_initial_first_default.fade_mode;
12366 titlescreen_initial_first_default.fade_delay =
12367 title_initial_first_default.fade_delay;
12368 titlescreen_initial_first_default.post_delay =
12369 title_initial_first_default.post_delay;
12370 titlescreen_initial_first_default.auto_delay =
12371 title_initial_first_default.auto_delay;
12372 titlescreen_initial_first_default.auto_delay_unit =
12373 title_initial_first_default.auto_delay_unit;
12374 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12375 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12376 titlescreen_first_default.post_delay = title_first_default.post_delay;
12377 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12378 titlescreen_first_default.auto_delay_unit =
12379 title_first_default.auto_delay_unit;
12380 titlemessage_initial_first_default.fade_mode =
12381 title_initial_first_default.fade_mode;
12382 titlemessage_initial_first_default.fade_delay =
12383 title_initial_first_default.fade_delay;
12384 titlemessage_initial_first_default.post_delay =
12385 title_initial_first_default.post_delay;
12386 titlemessage_initial_first_default.auto_delay =
12387 title_initial_first_default.auto_delay;
12388 titlemessage_initial_first_default.auto_delay_unit =
12389 title_initial_first_default.auto_delay_unit;
12390 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12391 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12392 titlemessage_first_default.post_delay = title_first_default.post_delay;
12393 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12394 titlemessage_first_default.auto_delay_unit =
12395 title_first_default.auto_delay_unit;
12397 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12398 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12399 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12400 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12401 titlescreen_initial_default.auto_delay_unit =
12402 title_initial_default.auto_delay_unit;
12403 titlescreen_default.fade_mode = title_default.fade_mode;
12404 titlescreen_default.fade_delay = title_default.fade_delay;
12405 titlescreen_default.post_delay = title_default.post_delay;
12406 titlescreen_default.auto_delay = title_default.auto_delay;
12407 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12408 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12409 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12410 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12411 titlemessage_initial_default.auto_delay_unit =
12412 title_initial_default.auto_delay_unit;
12413 titlemessage_default.fade_mode = title_default.fade_mode;
12414 titlemessage_default.fade_delay = title_default.fade_delay;
12415 titlemessage_default.post_delay = title_default.post_delay;
12416 titlemessage_default.auto_delay = title_default.auto_delay;
12417 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12419 // special case: initialize "ARG_DEFAULT" values in static default config
12420 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12421 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12423 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12424 titlescreen_first[i] = titlescreen_first_default;
12425 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12426 titlemessage_first[i] = titlemessage_first_default;
12428 titlescreen_initial[i] = titlescreen_initial_default;
12429 titlescreen[i] = titlescreen_default;
12430 titlemessage_initial[i] = titlemessage_initial_default;
12431 titlemessage[i] = titlemessage_default;
12434 // special case: initialize "ARG_DEFAULT" values in static default config
12435 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12436 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12438 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12441 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12442 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12443 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12446 // special case: initialize "ARG_DEFAULT" values in static default config
12447 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12448 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12450 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12451 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12452 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12454 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12457 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12461 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12465 struct XY *dst, *src;
12467 game_buttons_xy[] =
12469 { &game.button.save, &game.button.stop },
12470 { &game.button.pause2, &game.button.pause },
12471 { &game.button.load, &game.button.play },
12472 { &game.button.undo, &game.button.stop },
12473 { &game.button.redo, &game.button.play },
12479 // special case: initialize later added SETUP list size from LEVELS value
12480 if (menu.list_size[GAME_MODE_SETUP] == -1)
12481 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12483 // set default position for snapshot buttons to stop/pause/play buttons
12484 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12485 if ((*game_buttons_xy[i].dst).x == -1 &&
12486 (*game_buttons_xy[i].dst).y == -1)
12487 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12489 // --------------------------------------------------------------------------
12490 // dynamic viewports (including playfield margins, borders and alignments)
12491 // --------------------------------------------------------------------------
12493 // dynamic viewports currently only supported for landscape mode
12494 int display_width = MAX(video.display_width, video.display_height);
12495 int display_height = MIN(video.display_width, video.display_height);
12497 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12499 struct RectWithBorder *vp_window = &viewport.window[i];
12500 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12501 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12502 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12503 boolean dynamic_window_width = (vp_window->min_width != -1);
12504 boolean dynamic_window_height = (vp_window->min_height != -1);
12505 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12506 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12508 // adjust window size if min/max width/height is specified
12510 if (vp_window->min_width != -1)
12512 int window_width = display_width;
12514 // when using static window height, use aspect ratio of display
12515 if (vp_window->min_height == -1)
12516 window_width = vp_window->height * display_width / display_height;
12518 vp_window->width = MAX(vp_window->min_width, window_width);
12521 if (vp_window->min_height != -1)
12523 int window_height = display_height;
12525 // when using static window width, use aspect ratio of display
12526 if (vp_window->min_width == -1)
12527 window_height = vp_window->width * display_height / display_width;
12529 vp_window->height = MAX(vp_window->min_height, window_height);
12532 if (vp_window->max_width != -1)
12533 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12535 if (vp_window->max_height != -1)
12536 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12538 int playfield_width = vp_window->width;
12539 int playfield_height = vp_window->height;
12541 // adjust playfield size and position according to specified margins
12543 playfield_width -= vp_playfield->margin_left;
12544 playfield_width -= vp_playfield->margin_right;
12546 playfield_height -= vp_playfield->margin_top;
12547 playfield_height -= vp_playfield->margin_bottom;
12549 // adjust playfield size if min/max width/height is specified
12551 if (vp_playfield->min_width != -1)
12552 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12554 if (vp_playfield->min_height != -1)
12555 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12557 if (vp_playfield->max_width != -1)
12558 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12560 if (vp_playfield->max_height != -1)
12561 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12563 // adjust playfield position according to specified alignment
12565 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12566 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12567 else if (vp_playfield->align == ALIGN_CENTER)
12568 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12569 else if (vp_playfield->align == ALIGN_RIGHT)
12570 vp_playfield->x += playfield_width - vp_playfield->width;
12572 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12573 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12574 else if (vp_playfield->valign == VALIGN_MIDDLE)
12575 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12576 else if (vp_playfield->valign == VALIGN_BOTTOM)
12577 vp_playfield->y += playfield_height - vp_playfield->height;
12579 vp_playfield->x += vp_playfield->margin_left;
12580 vp_playfield->y += vp_playfield->margin_top;
12582 // adjust individual playfield borders if only default border is specified
12584 if (vp_playfield->border_left == -1)
12585 vp_playfield->border_left = vp_playfield->border_size;
12586 if (vp_playfield->border_right == -1)
12587 vp_playfield->border_right = vp_playfield->border_size;
12588 if (vp_playfield->border_top == -1)
12589 vp_playfield->border_top = vp_playfield->border_size;
12590 if (vp_playfield->border_bottom == -1)
12591 vp_playfield->border_bottom = vp_playfield->border_size;
12593 // set dynamic playfield borders if borders are specified as undefined
12594 // (but only if window size was dynamic and playfield size was static)
12596 if (dynamic_window_width && !dynamic_playfield_width)
12598 if (vp_playfield->border_left == -1)
12600 vp_playfield->border_left = (vp_playfield->x -
12601 vp_playfield->margin_left);
12602 vp_playfield->x -= vp_playfield->border_left;
12603 vp_playfield->width += vp_playfield->border_left;
12606 if (vp_playfield->border_right == -1)
12608 vp_playfield->border_right = (vp_window->width -
12610 vp_playfield->width -
12611 vp_playfield->margin_right);
12612 vp_playfield->width += vp_playfield->border_right;
12616 if (dynamic_window_height && !dynamic_playfield_height)
12618 if (vp_playfield->border_top == -1)
12620 vp_playfield->border_top = (vp_playfield->y -
12621 vp_playfield->margin_top);
12622 vp_playfield->y -= vp_playfield->border_top;
12623 vp_playfield->height += vp_playfield->border_top;
12626 if (vp_playfield->border_bottom == -1)
12628 vp_playfield->border_bottom = (vp_window->height -
12630 vp_playfield->height -
12631 vp_playfield->margin_bottom);
12632 vp_playfield->height += vp_playfield->border_bottom;
12636 // adjust playfield size to be a multiple of a defined alignment tile size
12638 int align_size = vp_playfield->align_size;
12639 int playfield_xtiles = vp_playfield->width / align_size;
12640 int playfield_ytiles = vp_playfield->height / align_size;
12641 int playfield_width_corrected = playfield_xtiles * align_size;
12642 int playfield_height_corrected = playfield_ytiles * align_size;
12643 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12644 i == GFX_SPECIAL_ARG_EDITOR);
12646 if (is_playfield_mode &&
12647 dynamic_playfield_width &&
12648 vp_playfield->width != playfield_width_corrected)
12650 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12652 vp_playfield->width = playfield_width_corrected;
12654 if (vp_playfield->align == ALIGN_LEFT)
12656 vp_playfield->border_left += playfield_xdiff;
12658 else if (vp_playfield->align == ALIGN_RIGHT)
12660 vp_playfield->border_right += playfield_xdiff;
12662 else if (vp_playfield->align == ALIGN_CENTER)
12664 int border_left_diff = playfield_xdiff / 2;
12665 int border_right_diff = playfield_xdiff - border_left_diff;
12667 vp_playfield->border_left += border_left_diff;
12668 vp_playfield->border_right += border_right_diff;
12672 if (is_playfield_mode &&
12673 dynamic_playfield_height &&
12674 vp_playfield->height != playfield_height_corrected)
12676 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12678 vp_playfield->height = playfield_height_corrected;
12680 if (vp_playfield->valign == VALIGN_TOP)
12682 vp_playfield->border_top += playfield_ydiff;
12684 else if (vp_playfield->align == VALIGN_BOTTOM)
12686 vp_playfield->border_right += playfield_ydiff;
12688 else if (vp_playfield->align == VALIGN_MIDDLE)
12690 int border_top_diff = playfield_ydiff / 2;
12691 int border_bottom_diff = playfield_ydiff - border_top_diff;
12693 vp_playfield->border_top += border_top_diff;
12694 vp_playfield->border_bottom += border_bottom_diff;
12698 // adjust door positions according to specified alignment
12700 for (j = 0; j < 2; j++)
12702 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12704 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12705 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12706 else if (vp_door->align == ALIGN_CENTER)
12707 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12708 else if (vp_door->align == ALIGN_RIGHT)
12709 vp_door->x += vp_window->width - vp_door->width;
12711 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12712 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12713 else if (vp_door->valign == VALIGN_MIDDLE)
12714 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12715 else if (vp_door->valign == VALIGN_BOTTOM)
12716 vp_door->y += vp_window->height - vp_door->height;
12721 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12725 struct XYTileSize *dst, *src;
12728 editor_buttons_xy[] =
12731 &editor.button.element_left, &editor.palette.element_left,
12732 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12735 &editor.button.element_middle, &editor.palette.element_middle,
12736 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12739 &editor.button.element_right, &editor.palette.element_right,
12740 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12747 // set default position for element buttons to element graphics
12748 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12750 if ((*editor_buttons_xy[i].dst).x == -1 &&
12751 (*editor_buttons_xy[i].dst).y == -1)
12753 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12755 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12757 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12761 // adjust editor palette rows and columns if specified to be dynamic
12763 if (editor.palette.cols == -1)
12765 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12766 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12767 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12769 editor.palette.cols = (vp_width - sc_width) / bt_width;
12771 if (editor.palette.x == -1)
12773 int palette_width = editor.palette.cols * bt_width + sc_width;
12775 editor.palette.x = (vp_width - palette_width) / 2;
12779 if (editor.palette.rows == -1)
12781 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12782 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12783 int tx_height = getFontHeight(FONT_TEXT_2);
12785 editor.palette.rows = (vp_height - tx_height) / bt_height;
12787 if (editor.palette.y == -1)
12789 int palette_height = editor.palette.rows * bt_height + tx_height;
12791 editor.palette.y = (vp_height - palette_height) / 2;
12796 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12797 boolean initialize)
12799 // special case: check if network and preview player positions are redefined,
12800 // to compare this later against the main menu level preview being redefined
12801 struct TokenIntPtrInfo menu_config_players[] =
12803 { "main.network_players.x", &menu.main.network_players.redefined },
12804 { "main.network_players.y", &menu.main.network_players.redefined },
12805 { "main.preview_players.x", &menu.main.preview_players.redefined },
12806 { "main.preview_players.y", &menu.main.preview_players.redefined },
12807 { "preview.x", &preview.redefined },
12808 { "preview.y", &preview.redefined }
12814 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12815 *menu_config_players[i].value = FALSE;
12819 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12820 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12821 *menu_config_players[i].value = TRUE;
12825 static void InitMenuDesignSettings_PreviewPlayers(void)
12827 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12830 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12832 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12835 static void LoadMenuDesignSettingsFromFilename(char *filename)
12837 static struct TitleFadingInfo tfi;
12838 static struct TitleMessageInfo tmi;
12839 static struct TokenInfo title_tokens[] =
12841 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12842 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12843 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12844 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12845 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12849 static struct TokenInfo titlemessage_tokens[] =
12851 { TYPE_INTEGER, &tmi.x, ".x" },
12852 { TYPE_INTEGER, &tmi.y, ".y" },
12853 { TYPE_INTEGER, &tmi.width, ".width" },
12854 { TYPE_INTEGER, &tmi.height, ".height" },
12855 { TYPE_INTEGER, &tmi.chars, ".chars" },
12856 { TYPE_INTEGER, &tmi.lines, ".lines" },
12857 { TYPE_INTEGER, &tmi.align, ".align" },
12858 { TYPE_INTEGER, &tmi.valign, ".valign" },
12859 { TYPE_INTEGER, &tmi.font, ".font" },
12860 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12861 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12862 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12863 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12864 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12865 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12866 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12867 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12868 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12874 struct TitleFadingInfo *info;
12879 // initialize first titles from "enter screen" definitions, if defined
12880 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12881 { &title_first_default, "menu.enter_screen.TITLE" },
12883 // initialize title screens from "next screen" definitions, if defined
12884 { &title_initial_default, "menu.next_screen.TITLE" },
12885 { &title_default, "menu.next_screen.TITLE" },
12891 struct TitleMessageInfo *array;
12894 titlemessage_arrays[] =
12896 // initialize first titles from "enter screen" definitions, if defined
12897 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12898 { titlescreen_first, "menu.enter_screen.TITLE" },
12899 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12900 { titlemessage_first, "menu.enter_screen.TITLE" },
12902 // initialize titles from "next screen" definitions, if defined
12903 { titlescreen_initial, "menu.next_screen.TITLE" },
12904 { titlescreen, "menu.next_screen.TITLE" },
12905 { titlemessage_initial, "menu.next_screen.TITLE" },
12906 { titlemessage, "menu.next_screen.TITLE" },
12908 // overwrite titles with title definitions, if defined
12909 { titlescreen_initial_first, "[title_initial]" },
12910 { titlescreen_first, "[title]" },
12911 { titlemessage_initial_first, "[title_initial]" },
12912 { titlemessage_first, "[title]" },
12914 { titlescreen_initial, "[title_initial]" },
12915 { titlescreen, "[title]" },
12916 { titlemessage_initial, "[title_initial]" },
12917 { titlemessage, "[title]" },
12919 // overwrite titles with title screen/message definitions, if defined
12920 { titlescreen_initial_first, "[titlescreen_initial]" },
12921 { titlescreen_first, "[titlescreen]" },
12922 { titlemessage_initial_first, "[titlemessage_initial]" },
12923 { titlemessage_first, "[titlemessage]" },
12925 { titlescreen_initial, "[titlescreen_initial]" },
12926 { titlescreen, "[titlescreen]" },
12927 { titlemessage_initial, "[titlemessage_initial]" },
12928 { titlemessage, "[titlemessage]" },
12932 SetupFileHash *setup_file_hash;
12935 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12938 // the following initializes hierarchical values from dynamic configuration
12940 // special case: initialize with default values that may be overwritten
12941 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12942 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12944 struct TokenIntPtrInfo menu_config[] =
12946 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12947 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12948 { "menu.list_size", &menu.list_size[i] }
12951 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12953 char *token = menu_config[j].token;
12954 char *value = getHashEntry(setup_file_hash, token);
12957 *menu_config[j].value = get_integer_from_string(value);
12961 // special case: initialize with default values that may be overwritten
12962 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12963 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12965 struct TokenIntPtrInfo menu_config[] =
12967 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12968 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12969 { "menu.list_size.INFO", &menu.list_size_info[i] },
12970 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12971 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12974 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12976 char *token = menu_config[j].token;
12977 char *value = getHashEntry(setup_file_hash, token);
12980 *menu_config[j].value = get_integer_from_string(value);
12984 // special case: initialize with default values that may be overwritten
12985 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12986 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12988 struct TokenIntPtrInfo menu_config[] =
12990 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12991 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12994 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12996 char *token = menu_config[j].token;
12997 char *value = getHashEntry(setup_file_hash, token);
13000 *menu_config[j].value = get_integer_from_string(value);
13004 // special case: initialize with default values that may be overwritten
13005 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13006 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13008 struct TokenIntPtrInfo menu_config[] =
13010 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13011 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13012 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13013 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13014 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13015 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13016 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13017 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13018 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13019 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13022 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13024 char *token = menu_config[j].token;
13025 char *value = getHashEntry(setup_file_hash, token);
13028 *menu_config[j].value = get_integer_from_string(value);
13032 // special case: initialize with default values that may be overwritten
13033 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13034 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13036 struct TokenIntPtrInfo menu_config[] =
13038 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13039 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13040 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13041 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13042 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13043 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13044 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13045 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13046 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13049 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13051 char *token = menu_config[j].token;
13052 char *value = getHashEntry(setup_file_hash, token);
13055 *menu_config[j].value = get_token_parameter_value(token, value);
13059 // special case: initialize with default values that may be overwritten
13060 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13061 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13065 char *token_prefix;
13066 struct RectWithBorder *struct_ptr;
13070 { "viewport.window", &viewport.window[i] },
13071 { "viewport.playfield", &viewport.playfield[i] },
13072 { "viewport.door_1", &viewport.door_1[i] },
13073 { "viewport.door_2", &viewport.door_2[i] }
13076 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13078 struct TokenIntPtrInfo vp_config[] =
13080 { ".x", &vp_struct[j].struct_ptr->x },
13081 { ".y", &vp_struct[j].struct_ptr->y },
13082 { ".width", &vp_struct[j].struct_ptr->width },
13083 { ".height", &vp_struct[j].struct_ptr->height },
13084 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13085 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13086 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13087 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13088 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13089 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13090 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13091 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13092 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13093 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13094 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13095 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13096 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13097 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13098 { ".align", &vp_struct[j].struct_ptr->align },
13099 { ".valign", &vp_struct[j].struct_ptr->valign }
13102 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13104 char *token = getStringCat2(vp_struct[j].token_prefix,
13105 vp_config[k].token);
13106 char *value = getHashEntry(setup_file_hash, token);
13109 *vp_config[k].value = get_token_parameter_value(token, value);
13116 // special case: initialize with default values that may be overwritten
13117 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13118 for (i = 0; title_info[i].info != NULL; i++)
13120 struct TitleFadingInfo *info = title_info[i].info;
13121 char *base_token = title_info[i].text;
13123 for (j = 0; title_tokens[j].type != -1; j++)
13125 char *token = getStringCat2(base_token, title_tokens[j].text);
13126 char *value = getHashEntry(setup_file_hash, token);
13130 int parameter_value = get_token_parameter_value(token, value);
13134 *(int *)title_tokens[j].value = (int)parameter_value;
13143 // special case: initialize with default values that may be overwritten
13144 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13145 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13147 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13148 char *base_token = titlemessage_arrays[i].text;
13150 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13152 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13153 char *value = getHashEntry(setup_file_hash, token);
13157 int parameter_value = get_token_parameter_value(token, value);
13159 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13163 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13164 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13166 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13176 // read (and overwrite with) values that may be specified in config file
13177 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13179 // special case: check if network and preview player positions are redefined
13180 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13182 freeSetupFileHash(setup_file_hash);
13185 void LoadMenuDesignSettings(void)
13187 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13189 InitMenuDesignSettings_Static();
13190 InitMenuDesignSettings_SpecialPreProcessing();
13191 InitMenuDesignSettings_PreviewPlayers();
13193 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13195 // first look for special settings configured in level series config
13196 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13198 if (fileExists(filename_base))
13199 LoadMenuDesignSettingsFromFilename(filename_base);
13202 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13204 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13205 LoadMenuDesignSettingsFromFilename(filename_local);
13207 InitMenuDesignSettings_SpecialPostProcessing();
13210 void LoadMenuDesignSettings_AfterGraphics(void)
13212 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13215 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13216 boolean ignore_defaults)
13220 for (i = 0; sound_config_vars[i].token != NULL; i++)
13222 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13224 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13225 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13229 *sound_config_vars[i].value =
13230 get_token_parameter_value(sound_config_vars[i].token, value);
13234 void InitSoundSettings_Static(void)
13236 // always start with reliable default values from static default config
13237 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13240 static void LoadSoundSettingsFromFilename(char *filename)
13242 SetupFileHash *setup_file_hash;
13244 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13247 // read (and overwrite with) values that may be specified in config file
13248 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13250 freeSetupFileHash(setup_file_hash);
13253 void LoadSoundSettings(void)
13255 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13257 InitSoundSettings_Static();
13259 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13261 // first look for special settings configured in level series config
13262 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13264 if (fileExists(filename_base))
13265 LoadSoundSettingsFromFilename(filename_base);
13268 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13270 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13271 LoadSoundSettingsFromFilename(filename_local);
13274 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13276 char *filename = getEditorSetupFilename();
13277 SetupFileList *setup_file_list, *list;
13278 SetupFileHash *element_hash;
13279 int num_unknown_tokens = 0;
13282 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13285 element_hash = newSetupFileHash();
13287 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13288 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13290 // determined size may be larger than needed (due to unknown elements)
13292 for (list = setup_file_list; list != NULL; list = list->next)
13295 // add space for up to 3 more elements for padding that may be needed
13296 *num_elements += 3;
13298 // free memory for old list of elements, if needed
13299 checked_free(*elements);
13301 // allocate memory for new list of elements
13302 *elements = checked_malloc(*num_elements * sizeof(int));
13305 for (list = setup_file_list; list != NULL; list = list->next)
13307 char *value = getHashEntry(element_hash, list->token);
13309 if (value == NULL) // try to find obsolete token mapping
13311 char *mapped_token = get_mapped_token(list->token);
13313 if (mapped_token != NULL)
13315 value = getHashEntry(element_hash, mapped_token);
13317 free(mapped_token);
13323 (*elements)[(*num_elements)++] = atoi(value);
13327 if (num_unknown_tokens == 0)
13330 Warn("unknown token(s) found in config file:");
13331 Warn("- config file: '%s'", filename);
13333 num_unknown_tokens++;
13336 Warn("- token: '%s'", list->token);
13340 if (num_unknown_tokens > 0)
13343 while (*num_elements % 4) // pad with empty elements, if needed
13344 (*elements)[(*num_elements)++] = EL_EMPTY;
13346 freeSetupFileList(setup_file_list);
13347 freeSetupFileHash(element_hash);
13350 for (i = 0; i < *num_elements; i++)
13351 Debug("editor", "element '%s' [%d]\n",
13352 element_info[(*elements)[i]].token_name, (*elements)[i]);
13356 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13359 SetupFileHash *setup_file_hash = NULL;
13360 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13361 char *filename_music, *filename_prefix, *filename_info;
13367 token_to_value_ptr[] =
13369 { "title_header", &tmp_music_file_info.title_header },
13370 { "artist_header", &tmp_music_file_info.artist_header },
13371 { "album_header", &tmp_music_file_info.album_header },
13372 { "year_header", &tmp_music_file_info.year_header },
13373 { "played_header", &tmp_music_file_info.played_header },
13375 { "title", &tmp_music_file_info.title },
13376 { "artist", &tmp_music_file_info.artist },
13377 { "album", &tmp_music_file_info.album },
13378 { "year", &tmp_music_file_info.year },
13379 { "played", &tmp_music_file_info.played },
13385 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13386 getCustomMusicFilename(basename));
13388 if (filename_music == NULL)
13391 // ---------- try to replace file extension ----------
13393 filename_prefix = getStringCopy(filename_music);
13394 if (strrchr(filename_prefix, '.') != NULL)
13395 *strrchr(filename_prefix, '.') = '\0';
13396 filename_info = getStringCat2(filename_prefix, ".txt");
13398 if (fileExists(filename_info))
13399 setup_file_hash = loadSetupFileHash(filename_info);
13401 free(filename_prefix);
13402 free(filename_info);
13404 if (setup_file_hash == NULL)
13406 // ---------- try to add file extension ----------
13408 filename_prefix = getStringCopy(filename_music);
13409 filename_info = getStringCat2(filename_prefix, ".txt");
13411 if (fileExists(filename_info))
13412 setup_file_hash = loadSetupFileHash(filename_info);
13414 free(filename_prefix);
13415 free(filename_info);
13418 if (setup_file_hash == NULL)
13421 // ---------- music file info found ----------
13423 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13425 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13427 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13429 *token_to_value_ptr[i].value_ptr =
13430 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13433 tmp_music_file_info.basename = getStringCopy(basename);
13434 tmp_music_file_info.music = music;
13435 tmp_music_file_info.is_sound = is_sound;
13437 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13438 *new_music_file_info = tmp_music_file_info;
13440 return new_music_file_info;
13443 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13445 return get_music_file_info_ext(basename, music, FALSE);
13448 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13450 return get_music_file_info_ext(basename, sound, TRUE);
13453 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13454 char *basename, boolean is_sound)
13456 for (; list != NULL; list = list->next)
13457 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13463 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13465 return music_info_listed_ext(list, basename, FALSE);
13468 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13470 return music_info_listed_ext(list, basename, TRUE);
13473 void LoadMusicInfo(void)
13475 int num_music_noconf = getMusicListSize_NoConf();
13476 int num_music = getMusicListSize();
13477 int num_sounds = getSoundListSize();
13478 struct FileInfo *music, *sound;
13479 struct MusicFileInfo *next, **new;
13483 while (music_file_info != NULL)
13485 next = music_file_info->next;
13487 checked_free(music_file_info->basename);
13489 checked_free(music_file_info->title_header);
13490 checked_free(music_file_info->artist_header);
13491 checked_free(music_file_info->album_header);
13492 checked_free(music_file_info->year_header);
13493 checked_free(music_file_info->played_header);
13495 checked_free(music_file_info->title);
13496 checked_free(music_file_info->artist);
13497 checked_free(music_file_info->album);
13498 checked_free(music_file_info->year);
13499 checked_free(music_file_info->played);
13501 free(music_file_info);
13503 music_file_info = next;
13506 new = &music_file_info;
13508 // get (configured or unconfigured) music file info for all levels
13509 for (i = leveldir_current->first_level;
13510 i <= leveldir_current->last_level; i++)
13514 if (levelset.music[i] != MUS_UNDEFINED)
13516 // get music file info for configured level music
13517 music_nr = levelset.music[i];
13519 else if (num_music_noconf > 0)
13521 // get music file info for unconfigured level music
13522 int level_pos = i - leveldir_current->first_level;
13524 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13531 char *basename = getMusicInfoEntryFilename(music_nr);
13533 if (basename == NULL)
13536 if (!music_info_listed(music_file_info, basename))
13538 *new = get_music_file_info(basename, music_nr);
13541 new = &(*new)->next;
13545 // get music file info for all remaining configured music files
13546 for (i = 0; i < num_music; i++)
13548 music = getMusicListEntry(i);
13550 if (music->filename == NULL)
13553 if (strEqual(music->filename, UNDEFINED_FILENAME))
13556 // a configured file may be not recognized as music
13557 if (!FileIsMusic(music->filename))
13560 if (!music_info_listed(music_file_info, music->filename))
13562 *new = get_music_file_info(music->filename, i);
13565 new = &(*new)->next;
13569 // get sound file info for all configured sound files
13570 for (i = 0; i < num_sounds; i++)
13572 sound = getSoundListEntry(i);
13574 if (sound->filename == NULL)
13577 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13580 // a configured file may be not recognized as sound
13581 if (!FileIsSound(sound->filename))
13584 if (!sound_info_listed(music_file_info, sound->filename))
13586 *new = get_sound_file_info(sound->filename, i);
13588 new = &(*new)->next;
13592 // add pointers to previous list nodes
13594 struct MusicFileInfo *node = music_file_info;
13596 while (node != NULL)
13599 node->next->prev = node;
13605 static void add_helpanim_entry(int element, int action, int direction,
13606 int delay, int *num_list_entries)
13608 struct HelpAnimInfo *new_list_entry;
13609 (*num_list_entries)++;
13612 checked_realloc(helpanim_info,
13613 *num_list_entries * sizeof(struct HelpAnimInfo));
13614 new_list_entry = &helpanim_info[*num_list_entries - 1];
13616 new_list_entry->element = element;
13617 new_list_entry->action = action;
13618 new_list_entry->direction = direction;
13619 new_list_entry->delay = delay;
13622 static void print_unknown_token(char *filename, char *token, int token_nr)
13627 Warn("unknown token(s) found in config file:");
13628 Warn("- config file: '%s'", filename);
13631 Warn("- token: '%s'", token);
13634 static void print_unknown_token_end(int token_nr)
13640 void LoadHelpAnimInfo(void)
13642 char *filename = getHelpAnimFilename();
13643 SetupFileList *setup_file_list = NULL, *list;
13644 SetupFileHash *element_hash, *action_hash, *direction_hash;
13645 int num_list_entries = 0;
13646 int num_unknown_tokens = 0;
13649 if (fileExists(filename))
13650 setup_file_list = loadSetupFileList(filename);
13652 if (setup_file_list == NULL)
13654 // use reliable default values from static configuration
13655 SetupFileList *insert_ptr;
13657 insert_ptr = setup_file_list =
13658 newSetupFileList(helpanim_config[0].token,
13659 helpanim_config[0].value);
13661 for (i = 1; helpanim_config[i].token; i++)
13662 insert_ptr = addListEntry(insert_ptr,
13663 helpanim_config[i].token,
13664 helpanim_config[i].value);
13667 element_hash = newSetupFileHash();
13668 action_hash = newSetupFileHash();
13669 direction_hash = newSetupFileHash();
13671 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13672 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13674 for (i = 0; i < NUM_ACTIONS; i++)
13675 setHashEntry(action_hash, element_action_info[i].suffix,
13676 i_to_a(element_action_info[i].value));
13678 // do not store direction index (bit) here, but direction value!
13679 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13680 setHashEntry(direction_hash, element_direction_info[i].suffix,
13681 i_to_a(1 << element_direction_info[i].value));
13683 for (list = setup_file_list; list != NULL; list = list->next)
13685 char *element_token, *action_token, *direction_token;
13686 char *element_value, *action_value, *direction_value;
13687 int delay = atoi(list->value);
13689 if (strEqual(list->token, "end"))
13691 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13696 /* first try to break element into element/action/direction parts;
13697 if this does not work, also accept combined "element[.act][.dir]"
13698 elements (like "dynamite.active"), which are unique elements */
13700 if (strchr(list->token, '.') == NULL) // token contains no '.'
13702 element_value = getHashEntry(element_hash, list->token);
13703 if (element_value != NULL) // element found
13704 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13705 &num_list_entries);
13708 // no further suffixes found -- this is not an element
13709 print_unknown_token(filename, list->token, num_unknown_tokens++);
13715 // token has format "<prefix>.<something>"
13717 action_token = strchr(list->token, '.'); // suffix may be action ...
13718 direction_token = action_token; // ... or direction
13720 element_token = getStringCopy(list->token);
13721 *strchr(element_token, '.') = '\0';
13723 element_value = getHashEntry(element_hash, element_token);
13725 if (element_value == NULL) // this is no element
13727 element_value = getHashEntry(element_hash, list->token);
13728 if (element_value != NULL) // combined element found
13729 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13730 &num_list_entries);
13732 print_unknown_token(filename, list->token, num_unknown_tokens++);
13734 free(element_token);
13739 action_value = getHashEntry(action_hash, action_token);
13741 if (action_value != NULL) // action found
13743 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13744 &num_list_entries);
13746 free(element_token);
13751 direction_value = getHashEntry(direction_hash, direction_token);
13753 if (direction_value != NULL) // direction found
13755 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13756 &num_list_entries);
13758 free(element_token);
13763 if (strchr(action_token + 1, '.') == NULL)
13765 // no further suffixes found -- this is not an action nor direction
13767 element_value = getHashEntry(element_hash, list->token);
13768 if (element_value != NULL) // combined element found
13769 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13770 &num_list_entries);
13772 print_unknown_token(filename, list->token, num_unknown_tokens++);
13774 free(element_token);
13779 // token has format "<prefix>.<suffix>.<something>"
13781 direction_token = strchr(action_token + 1, '.');
13783 action_token = getStringCopy(action_token);
13784 *strchr(action_token + 1, '.') = '\0';
13786 action_value = getHashEntry(action_hash, action_token);
13788 if (action_value == NULL) // this is no action
13790 element_value = getHashEntry(element_hash, list->token);
13791 if (element_value != NULL) // combined element found
13792 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13793 &num_list_entries);
13795 print_unknown_token(filename, list->token, num_unknown_tokens++);
13797 free(element_token);
13798 free(action_token);
13803 direction_value = getHashEntry(direction_hash, direction_token);
13805 if (direction_value != NULL) // direction found
13807 add_helpanim_entry(atoi(element_value), atoi(action_value),
13808 atoi(direction_value), delay, &num_list_entries);
13810 free(element_token);
13811 free(action_token);
13816 // this is no direction
13818 element_value = getHashEntry(element_hash, list->token);
13819 if (element_value != NULL) // combined element found
13820 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13821 &num_list_entries);
13823 print_unknown_token(filename, list->token, num_unknown_tokens++);
13825 free(element_token);
13826 free(action_token);
13829 print_unknown_token_end(num_unknown_tokens);
13831 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13832 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13834 freeSetupFileList(setup_file_list);
13835 freeSetupFileHash(element_hash);
13836 freeSetupFileHash(action_hash);
13837 freeSetupFileHash(direction_hash);
13840 for (i = 0; i < num_list_entries; i++)
13841 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13842 EL_NAME(helpanim_info[i].element),
13843 helpanim_info[i].element,
13844 helpanim_info[i].action,
13845 helpanim_info[i].direction,
13846 helpanim_info[i].delay);
13850 void LoadHelpTextInfo(void)
13852 char *filename = getHelpTextFilename();
13855 if (helptext_info != NULL)
13857 freeSetupFileHash(helptext_info);
13858 helptext_info = NULL;
13861 if (fileExists(filename))
13862 helptext_info = loadSetupFileHash(filename);
13864 if (helptext_info == NULL)
13866 // use reliable default values from static configuration
13867 helptext_info = newSetupFileHash();
13869 for (i = 0; helptext_config[i].token; i++)
13870 setHashEntry(helptext_info,
13871 helptext_config[i].token,
13872 helptext_config[i].value);
13876 BEGIN_HASH_ITERATION(helptext_info, itr)
13878 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13879 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13881 END_HASH_ITERATION(hash, itr)
13886 // ----------------------------------------------------------------------------
13888 // ----------------------------------------------------------------------------
13890 #define MAX_NUM_CONVERT_LEVELS 1000
13892 void ConvertLevels(void)
13894 static LevelDirTree *convert_leveldir = NULL;
13895 static int convert_level_nr = -1;
13896 static int num_levels_handled = 0;
13897 static int num_levels_converted = 0;
13898 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13901 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13902 global.convert_leveldir);
13904 if (convert_leveldir == NULL)
13905 Fail("no such level identifier: '%s'", global.convert_leveldir);
13907 leveldir_current = convert_leveldir;
13909 if (global.convert_level_nr != -1)
13911 convert_leveldir->first_level = global.convert_level_nr;
13912 convert_leveldir->last_level = global.convert_level_nr;
13915 convert_level_nr = convert_leveldir->first_level;
13917 PrintLine("=", 79);
13918 Print("Converting levels\n");
13919 PrintLine("-", 79);
13920 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13921 Print("Level series name: '%s'\n", convert_leveldir->name);
13922 Print("Level series author: '%s'\n", convert_leveldir->author);
13923 Print("Number of levels: %d\n", convert_leveldir->levels);
13924 PrintLine("=", 79);
13927 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13928 levels_failed[i] = FALSE;
13930 while (convert_level_nr <= convert_leveldir->last_level)
13932 char *level_filename;
13935 level_nr = convert_level_nr++;
13937 Print("Level %03d: ", level_nr);
13939 LoadLevel(level_nr);
13940 if (level.no_level_file || level.no_valid_file)
13942 Print("(no level)\n");
13946 Print("converting level ... ");
13949 // special case: conversion of some EMC levels as requested by ACME
13950 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13953 level_filename = getDefaultLevelFilename(level_nr);
13954 new_level = !fileExists(level_filename);
13958 SaveLevel(level_nr);
13960 num_levels_converted++;
13962 Print("converted.\n");
13966 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13967 levels_failed[level_nr] = TRUE;
13969 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13972 num_levels_handled++;
13976 PrintLine("=", 79);
13977 Print("Number of levels handled: %d\n", num_levels_handled);
13978 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13979 (num_levels_handled ?
13980 num_levels_converted * 100 / num_levels_handled : 0));
13981 PrintLine("-", 79);
13982 Print("Summary (for automatic parsing by scripts):\n");
13983 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13984 convert_leveldir->identifier, num_levels_converted,
13985 num_levels_handled,
13986 (num_levels_handled ?
13987 num_levels_converted * 100 / num_levels_handled : 0));
13989 if (num_levels_handled != num_levels_converted)
13991 Print(", FAILED:");
13992 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13993 if (levels_failed[i])
13998 PrintLine("=", 79);
14000 CloseAllAndExit(0);
14004 // ----------------------------------------------------------------------------
14005 // create and save images for use in level sketches (raw BMP format)
14006 // ----------------------------------------------------------------------------
14008 void CreateLevelSketchImages(void)
14014 InitElementPropertiesGfxElement();
14016 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14017 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14019 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14021 int element = getMappedElement(i);
14022 char basename1[16];
14023 char basename2[16];
14027 sprintf(basename1, "%04d.bmp", i);
14028 sprintf(basename2, "%04ds.bmp", i);
14030 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14031 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14033 DrawSizedElement(0, 0, element, TILESIZE);
14034 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14036 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14037 Fail("cannot save level sketch image file '%s'", filename1);
14039 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14040 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14042 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14043 Fail("cannot save level sketch image file '%s'", filename2);
14048 // create corresponding SQL statements (for normal and small images)
14051 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14052 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14055 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14056 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14058 // optional: create content for forum level sketch demonstration post
14060 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14063 FreeBitmap(bitmap1);
14064 FreeBitmap(bitmap2);
14067 fprintf(stderr, "\n");
14069 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14071 CloseAllAndExit(0);
14075 // ----------------------------------------------------------------------------
14076 // create and save images for element collecting animations (raw BMP format)
14077 // ----------------------------------------------------------------------------
14079 static boolean createCollectImage(int element)
14081 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14084 void CreateCollectElementImages(void)
14088 int anim_frames = num_steps - 1;
14089 int tile_size = TILESIZE;
14090 int anim_width = tile_size * anim_frames;
14091 int anim_height = tile_size;
14092 int num_collect_images = 0;
14093 int pos_collect_images = 0;
14095 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14096 if (createCollectImage(i))
14097 num_collect_images++;
14099 Info("Creating %d element collecting animation images ...",
14100 num_collect_images);
14102 int dst_width = anim_width * 2;
14103 int dst_height = anim_height * num_collect_images / 2;
14104 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14105 char *basename_bmp = "RocksCollect.bmp";
14106 char *basename_png = "RocksCollect.png";
14107 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14108 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14109 int len_filename_bmp = strlen(filename_bmp);
14110 int len_filename_png = strlen(filename_png);
14111 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14112 char cmd_convert[max_command_len];
14114 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14118 // force using RGBA surface for destination bitmap
14119 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14120 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14122 dst_bitmap->surface =
14123 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14127 if (!createCollectImage(i))
14130 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14131 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14132 int graphic = el2img(i);
14133 char *token_name = element_info[i].token_name;
14134 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14135 Bitmap *src_bitmap;
14138 Info("- creating collecting image for '%s' ...", token_name);
14140 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14142 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14143 tile_size, tile_size, 0, 0);
14145 // force using RGBA surface for temporary bitmap (using transparent black)
14146 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14147 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14149 tmp_bitmap->surface =
14150 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14152 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14154 for (j = 0; j < anim_frames; j++)
14156 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14157 int frame_size = frame_size_final * num_steps;
14158 int offset = (tile_size - frame_size_final) / 2;
14159 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14161 while (frame_size > frame_size_final)
14165 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14167 FreeBitmap(frame_bitmap);
14169 frame_bitmap = half_bitmap;
14172 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14173 frame_size_final, frame_size_final,
14174 dst_x + j * tile_size + offset, dst_y + offset);
14176 FreeBitmap(frame_bitmap);
14179 tmp_bitmap->surface_masked = NULL;
14181 FreeBitmap(tmp_bitmap);
14183 pos_collect_images++;
14186 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14187 Fail("cannot save element collecting image file '%s'", filename_bmp);
14189 FreeBitmap(dst_bitmap);
14191 Info("Converting image file from BMP to PNG ...");
14193 if (system(cmd_convert) != 0)
14194 Fail("converting image file failed");
14196 unlink(filename_bmp);
14200 CloseAllAndExit(0);
14204 // ----------------------------------------------------------------------------
14205 // create and save images for custom and group elements (raw BMP format)
14206 // ----------------------------------------------------------------------------
14208 void CreateCustomElementImages(char *directory)
14210 char *src_basename = "RocksCE-template.ilbm";
14211 char *dst_basename = "RocksCE.bmp";
14212 char *src_filename = getPath2(directory, src_basename);
14213 char *dst_filename = getPath2(directory, dst_basename);
14214 Bitmap *src_bitmap;
14216 int yoffset_ce = 0;
14217 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14220 InitVideoDefaults();
14222 ReCreateBitmap(&backbuffer, video.width, video.height);
14224 src_bitmap = LoadImage(src_filename);
14226 bitmap = CreateBitmap(TILEX * 16 * 2,
14227 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14230 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14237 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14238 TILEX * x, TILEY * y + yoffset_ce);
14240 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14242 TILEX * x + TILEX * 16,
14243 TILEY * y + yoffset_ce);
14245 for (j = 2; j >= 0; j--)
14249 BlitBitmap(src_bitmap, bitmap,
14250 TILEX + c * 7, 0, 6, 10,
14251 TILEX * x + 6 + j * 7,
14252 TILEY * y + 11 + yoffset_ce);
14254 BlitBitmap(src_bitmap, bitmap,
14255 TILEX + c * 8, TILEY, 6, 10,
14256 TILEX * 16 + TILEX * x + 6 + j * 8,
14257 TILEY * y + 10 + yoffset_ce);
14263 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14270 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14271 TILEX * x, TILEY * y + yoffset_ge);
14273 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14275 TILEX * x + TILEX * 16,
14276 TILEY * y + yoffset_ge);
14278 for (j = 1; j >= 0; j--)
14282 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14283 TILEX * x + 6 + j * 10,
14284 TILEY * y + 11 + yoffset_ge);
14286 BlitBitmap(src_bitmap, bitmap,
14287 TILEX + c * 8, TILEY + 12, 6, 10,
14288 TILEX * 16 + TILEX * x + 10 + j * 8,
14289 TILEY * y + 10 + yoffset_ge);
14295 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14296 Fail("cannot save CE graphics file '%s'", dst_filename);
14298 FreeBitmap(bitmap);
14300 CloseAllAndExit(0);