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
325 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 // (these values are the same for each player)
330 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
331 &li.block_last_field, FALSE // default case for EM levels
335 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
336 &li.sp_block_last_field, TRUE // default case for SP levels
340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
341 &li.instant_relocation, FALSE
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
346 &li.can_pass_to_walkable, FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
351 &li.block_snap_field, TRUE
355 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
356 &li.continuous_snapping, TRUE
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
361 &li.shifted_relocation, FALSE
365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
366 &li.lazy_relocation, FALSE
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
371 &li.finish_dig_collect, TRUE
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
376 &li.keep_walkable_ce, FALSE
379 // (these values are different for each player)
382 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
383 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
388 &li.initial_player_gravity[0], FALSE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
393 &li.use_start_element[0], FALSE
397 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
398 &li.start_element[0], EL_PLAYER_1
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
403 &li.use_artwork_element[0], FALSE
407 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
408 &li.artwork_element[0], EL_PLAYER_1
412 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
413 &li.use_explosion_element[0], FALSE
417 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
418 &li.explosion_element[0], EL_PLAYER_1
422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
423 &li.use_initial_inventory[0], FALSE
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
428 &li.initial_inventory_size[0], 1
432 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
433 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
434 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
439 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
440 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
445 &li.initial_player_gravity[1], FALSE
449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
450 &li.use_start_element[1], FALSE
454 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
455 &li.start_element[1], EL_PLAYER_2
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
460 &li.use_artwork_element[1], FALSE
464 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
465 &li.artwork_element[1], EL_PLAYER_2
469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
470 &li.use_explosion_element[1], FALSE
474 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
475 &li.explosion_element[1], EL_PLAYER_2
479 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
480 &li.use_initial_inventory[1], FALSE
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
485 &li.initial_inventory_size[1], 1
489 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
490 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
491 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
496 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
497 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
502 &li.initial_player_gravity[2], FALSE
506 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
507 &li.use_start_element[2], FALSE
511 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
512 &li.start_element[2], EL_PLAYER_3
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
517 &li.use_artwork_element[2], FALSE
521 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
522 &li.artwork_element[2], EL_PLAYER_3
526 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
527 &li.use_explosion_element[2], FALSE
531 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
532 &li.explosion_element[2], EL_PLAYER_3
536 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
537 &li.use_initial_inventory[2], FALSE
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
542 &li.initial_inventory_size[2], 1
546 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
547 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
548 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
553 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
554 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
559 &li.initial_player_gravity[3], FALSE
563 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
564 &li.use_start_element[3], FALSE
568 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
569 &li.start_element[3], EL_PLAYER_4
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
574 &li.use_artwork_element[3], FALSE
578 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
579 &li.artwork_element[3], EL_PLAYER_4
583 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
584 &li.use_explosion_element[3], FALSE
588 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
589 &li.explosion_element[3], EL_PLAYER_4
593 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
594 &li.use_initial_inventory[3], FALSE
598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
599 &li.initial_inventory_size[3], 1
603 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
604 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
605 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
608 // (these values are only valid for BD style levels)
611 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
612 &li.bd_diagonal_movements, FALSE
617 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
618 &li.score[SC_DIAMOND_EXTRA], 20
621 // (the following values are related to various game elements)
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_EMERALD], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_DIAMOND], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_BUG], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_SPACESHIP], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_PACMAN], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_NUT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_DYNAMITE], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_KEY], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_PEARL], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_CRYSTAL], 10
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
686 &li.amoeba_content, EL_DIAMOND
690 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
695 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
696 &li.grow_into_diggable, TRUE
701 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
702 &li.yamyam_content, EL_ROCK, NULL,
703 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
708 &li.score[SC_YAMYAM], 10
713 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 &li.score[SC_ROBOT], 10
718 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
724 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
730 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
731 &li.time_magic_wall, 10
736 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
737 &li.game_of_life[0], 2
741 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
742 &li.game_of_life[1], 3
746 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
747 &li.game_of_life[2], 3
751 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
752 &li.game_of_life[3], 3
756 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
757 &li.use_life_bugs, FALSE
762 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
767 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
772 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
777 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
782 EL_TIMEGATE_SWITCH, -1,
783 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
784 &li.time_timegate, 10
788 EL_LIGHT_SWITCH_ACTIVE, -1,
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
794 EL_SHIELD_NORMAL, -1,
795 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
796 &li.shield_normal_time, 10
799 EL_SHIELD_NORMAL, -1,
800 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
801 &li.score[SC_SHIELD], 10
805 EL_SHIELD_DEADLY, -1,
806 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
807 &li.shield_deadly_time, 10
810 EL_SHIELD_DEADLY, -1,
811 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
812 &li.score[SC_SHIELD], 10
817 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
822 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
823 &li.extra_time_score, 10
827 EL_TIME_ORB_FULL, -1,
828 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
829 &li.time_orb_time, 10
832 EL_TIME_ORB_FULL, -1,
833 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
834 &li.use_time_orb_bug, FALSE
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
840 &li.use_spring_bug, FALSE
845 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
846 &li.android_move_time, 10
850 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
851 &li.android_clone_time, 10
854 EL_EMC_ANDROID, SAVE_CONF_NEVER,
855 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
856 &li.android_clone_element[0], EL_EMPTY, NULL,
857 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
861 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
862 &li.android_clone_element[0], EL_EMPTY, NULL,
863 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
868 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
878 EL_EMC_MAGNIFIER, -1,
879 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
880 &li.magnify_score, 10
883 EL_EMC_MAGNIFIER, -1,
884 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
889 EL_EMC_MAGIC_BALL, -1,
890 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
894 EL_EMC_MAGIC_BALL, -1,
895 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
896 &li.ball_random, FALSE
899 EL_EMC_MAGIC_BALL, -1,
900 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
901 &li.ball_active_initial, FALSE
904 EL_EMC_MAGIC_BALL, -1,
905 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
906 &li.ball_content, EL_EMPTY, NULL,
907 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
911 EL_SOKOBAN_FIELD_EMPTY, -1,
912 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
913 &li.sb_fields_needed, TRUE
917 EL_SOKOBAN_OBJECT, -1,
918 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
919 &li.sb_objects_needed, TRUE
924 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
925 &li.mm_laser_red, FALSE
929 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
930 &li.mm_laser_green, FALSE
934 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
935 &li.mm_laser_blue, TRUE
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
941 &li.df_laser_red, TRUE
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
946 &li.df_laser_green, TRUE
950 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
951 &li.df_laser_blue, FALSE
955 EL_MM_FUSE_ACTIVE, -1,
956 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
961 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
967 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
972 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
973 &li.mm_ball_choice_mode, ANIM_RANDOM
977 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
978 &li.mm_ball_content, EL_EMPTY, NULL,
979 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
983 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
984 &li.rotate_mm_ball_content, TRUE
988 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
989 &li.explode_mm_ball, FALSE
993 EL_MM_STEEL_BLOCK, -1,
994 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
995 &li.mm_time_block, 75
999 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1000 &li.score[SC_ELEM_BONUS], 10
1010 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1014 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1015 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1019 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1020 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1025 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1026 &xx_envelope.autowrap, FALSE
1030 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1031 &xx_envelope.centered, FALSE
1036 TYPE_STRING, CONF_VALUE_BYTES(1),
1037 &xx_envelope.text, -1, NULL,
1038 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1039 &xx_default_string_empty[0]
1049 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1053 TYPE_STRING, CONF_VALUE_BYTES(1),
1054 &xx_ei.description[0], -1,
1055 &yy_ei.description[0],
1056 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1057 &xx_default_description[0]
1062 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1063 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1064 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1066 #if ENABLE_RESERVED_CODE
1067 // (reserved for later use)
1070 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1071 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1072 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1078 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1079 &xx_ei.use_gfx_element, FALSE,
1080 &yy_ei.use_gfx_element
1084 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1085 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1086 &yy_ei.gfx_element_initial
1091 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1092 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1093 &yy_ei.access_direction
1098 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1099 &xx_ei.collect_score_initial, 10,
1100 &yy_ei.collect_score_initial
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1105 &xx_ei.collect_count_initial, 1,
1106 &yy_ei.collect_count_initial
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1112 &xx_ei.ce_value_fixed_initial, 0,
1113 &yy_ei.ce_value_fixed_initial
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1118 &xx_ei.ce_value_random_initial, 0,
1119 &yy_ei.ce_value_random_initial
1123 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1124 &xx_ei.use_last_ce_value, FALSE,
1125 &yy_ei.use_last_ce_value
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1131 &xx_ei.push_delay_fixed, 8,
1132 &yy_ei.push_delay_fixed
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1137 &xx_ei.push_delay_random, 8,
1138 &yy_ei.push_delay_random
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1143 &xx_ei.drop_delay_fixed, 0,
1144 &yy_ei.drop_delay_fixed
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1149 &xx_ei.drop_delay_random, 0,
1150 &yy_ei.drop_delay_random
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1155 &xx_ei.move_delay_fixed, 0,
1156 &yy_ei.move_delay_fixed
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1161 &xx_ei.move_delay_random, 0,
1162 &yy_ei.move_delay_random
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1167 &xx_ei.step_delay_fixed, 0,
1168 &yy_ei.step_delay_fixed
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1173 &xx_ei.step_delay_random, 0,
1174 &yy_ei.step_delay_random
1179 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1180 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1185 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1186 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1187 &yy_ei.move_direction_initial
1191 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1192 &xx_ei.move_stepsize, TILEX / 8,
1193 &yy_ei.move_stepsize
1198 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1199 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1200 &yy_ei.move_enter_element
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1205 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1206 &yy_ei.move_leave_element
1210 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1211 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1212 &yy_ei.move_leave_type
1217 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1218 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1219 &yy_ei.slippery_type
1224 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1225 &xx_ei.explosion_type, EXPLODES_3X3,
1226 &yy_ei.explosion_type
1230 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1231 &xx_ei.explosion_delay, 16,
1232 &yy_ei.explosion_delay
1236 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1237 &xx_ei.ignition_delay, 8,
1238 &yy_ei.ignition_delay
1243 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1244 &xx_ei.content, EL_EMPTY_SPACE,
1246 &xx_num_contents, 1, 1
1249 // ---------- "num_change_pages" must be the last entry ---------------------
1252 -1, SAVE_CONF_ALWAYS,
1253 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1254 &xx_ei.num_change_pages, 1,
1255 &yy_ei.num_change_pages
1266 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1268 // ---------- "current_change_page" must be the first entry -----------------
1271 -1, SAVE_CONF_ALWAYS,
1272 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1273 &xx_current_change_page, -1
1276 // ---------- (the remaining entries can be in any order) -------------------
1280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1281 &xx_change.can_change, FALSE
1286 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1287 &xx_event_bits[0], 0
1291 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1292 &xx_event_bits[1], 0
1297 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1298 &xx_change.trigger_player, CH_PLAYER_ANY
1302 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1303 &xx_change.trigger_side, CH_SIDE_ANY
1307 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1308 &xx_change.trigger_page, CH_PAGE_ANY
1313 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1314 &xx_change.target_element, EL_EMPTY_SPACE
1319 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1320 &xx_change.delay_fixed, 0
1324 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1325 &xx_change.delay_random, 0
1329 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1330 &xx_change.delay_frames, FRAMES_PER_SECOND
1335 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1336 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1342 &xx_change.explode, FALSE
1346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1347 &xx_change.use_target_content, FALSE
1351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1352 &xx_change.only_if_complete, FALSE
1356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1357 &xx_change.use_random_replace, FALSE
1361 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1362 &xx_change.random_percentage, 100
1366 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1367 &xx_change.replace_when, CP_WHEN_EMPTY
1372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1373 &xx_change.has_action, FALSE
1377 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1378 &xx_change.action_type, CA_NO_ACTION
1382 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1383 &xx_change.action_mode, CA_MODE_UNDEFINED
1387 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1388 &xx_change.action_arg, CA_ARG_UNDEFINED
1393 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1394 &xx_change.action_element, EL_EMPTY_SPACE
1399 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1400 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1401 &xx_num_contents, 1, 1
1411 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1415 TYPE_STRING, CONF_VALUE_BYTES(1),
1416 &xx_ei.description[0], -1, NULL,
1417 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1418 &xx_default_description[0]
1423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1424 &xx_ei.use_gfx_element, FALSE
1428 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1429 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1434 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1435 &xx_group.choice_mode, ANIM_RANDOM
1440 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1441 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1442 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1452 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1456 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1457 &xx_ei.use_gfx_element, FALSE
1461 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1462 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1472 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1477 &li.block_snap_field, TRUE
1481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1482 &li.continuous_snapping, TRUE
1486 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1487 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1492 &li.use_start_element[0], FALSE
1496 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1497 &li.start_element[0], EL_PLAYER_1
1501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1502 &li.use_artwork_element[0], FALSE
1506 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1507 &li.artwork_element[0], EL_PLAYER_1
1511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1512 &li.use_explosion_element[0], FALSE
1516 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1517 &li.explosion_element[0], EL_PLAYER_1
1532 filetype_id_list[] =
1534 { LEVEL_FILE_TYPE_RND, "RND" },
1535 { LEVEL_FILE_TYPE_BD, "BD" },
1536 { LEVEL_FILE_TYPE_EM, "EM" },
1537 { LEVEL_FILE_TYPE_SP, "SP" },
1538 { LEVEL_FILE_TYPE_DX, "DX" },
1539 { LEVEL_FILE_TYPE_SB, "SB" },
1540 { LEVEL_FILE_TYPE_DC, "DC" },
1541 { LEVEL_FILE_TYPE_MM, "MM" },
1542 { LEVEL_FILE_TYPE_MM, "DF" },
1547 // ============================================================================
1548 // level file functions
1549 // ============================================================================
1551 static boolean check_special_flags(char *flag)
1553 if (strEqual(options.special_flags, flag) ||
1554 strEqual(leveldir_current->special_flags, flag))
1560 static struct DateInfo getCurrentDate(void)
1562 time_t epoch_seconds = time(NULL);
1563 struct tm *now = localtime(&epoch_seconds);
1564 struct DateInfo date;
1566 date.year = now->tm_year + 1900;
1567 date.month = now->tm_mon + 1;
1568 date.day = now->tm_mday;
1570 date.src = DATE_SRC_CLOCK;
1575 static void resetEventFlags(struct ElementChangeInfo *change)
1579 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1580 change->has_event[i] = FALSE;
1583 static void resetEventBits(void)
1587 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1588 xx_event_bits[i] = 0;
1591 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1595 /* important: only change event flag if corresponding event bit is set
1596 (this is because all xx_event_bits[] values are loaded separately,
1597 and all xx_event_bits[] values are set back to zero before loading
1598 another value xx_event_bits[x] (each value representing 32 flags)) */
1600 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1601 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1602 change->has_event[i] = TRUE;
1605 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1609 /* in contrast to the above function setEventFlagsFromEventBits(), it
1610 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1611 depending on the corresponding change->has_event[i] values here, as
1612 all xx_event_bits[] values are reset in resetEventBits() before */
1614 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1615 if (change->has_event[i])
1616 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1619 static char *getDefaultElementDescription(struct ElementInfo *ei)
1621 static char description[MAX_ELEMENT_NAME_LEN + 1];
1622 char *default_description = (ei->custom_description != NULL ?
1623 ei->custom_description :
1624 ei->editor_description);
1627 // always start with reliable default values
1628 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1629 description[i] = '\0';
1631 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1632 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1634 return &description[0];
1637 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1639 char *default_description = getDefaultElementDescription(ei);
1642 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1643 ei->description[i] = default_description[i];
1646 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1650 for (i = 0; conf[i].data_type != -1; i++)
1652 int default_value = conf[i].default_value;
1653 int data_type = conf[i].data_type;
1654 int conf_type = conf[i].conf_type;
1655 int byte_mask = conf_type & CONF_MASK_BYTES;
1657 if (byte_mask == CONF_MASK_MULTI_BYTES)
1659 int default_num_entities = conf[i].default_num_entities;
1660 int max_num_entities = conf[i].max_num_entities;
1662 *(int *)(conf[i].num_entities) = default_num_entities;
1664 if (data_type == TYPE_STRING)
1666 char *default_string = conf[i].default_string;
1667 char *string = (char *)(conf[i].value);
1669 strncpy(string, default_string, max_num_entities);
1671 else if (data_type == TYPE_ELEMENT_LIST)
1673 int *element_array = (int *)(conf[i].value);
1676 for (j = 0; j < max_num_entities; j++)
1677 element_array[j] = default_value;
1679 else if (data_type == TYPE_CONTENT_LIST)
1681 struct Content *content = (struct Content *)(conf[i].value);
1684 for (c = 0; c < max_num_entities; c++)
1685 for (y = 0; y < 3; y++)
1686 for (x = 0; x < 3; x++)
1687 content[c].e[x][y] = default_value;
1690 else // constant size configuration data (1, 2 or 4 bytes)
1692 if (data_type == TYPE_BOOLEAN)
1693 *(boolean *)(conf[i].value) = default_value;
1695 *(int *) (conf[i].value) = default_value;
1700 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1704 for (i = 0; conf[i].data_type != -1; i++)
1706 int data_type = conf[i].data_type;
1707 int conf_type = conf[i].conf_type;
1708 int byte_mask = conf_type & CONF_MASK_BYTES;
1710 if (byte_mask == CONF_MASK_MULTI_BYTES)
1712 int max_num_entities = conf[i].max_num_entities;
1714 if (data_type == TYPE_STRING)
1716 char *string = (char *)(conf[i].value);
1717 char *string_copy = (char *)(conf[i].value_copy);
1719 strncpy(string_copy, string, max_num_entities);
1721 else if (data_type == TYPE_ELEMENT_LIST)
1723 int *element_array = (int *)(conf[i].value);
1724 int *element_array_copy = (int *)(conf[i].value_copy);
1727 for (j = 0; j < max_num_entities; j++)
1728 element_array_copy[j] = element_array[j];
1730 else if (data_type == TYPE_CONTENT_LIST)
1732 struct Content *content = (struct Content *)(conf[i].value);
1733 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1736 for (c = 0; c < max_num_entities; c++)
1737 for (y = 0; y < 3; y++)
1738 for (x = 0; x < 3; x++)
1739 content_copy[c].e[x][y] = content[c].e[x][y];
1742 else // constant size configuration data (1, 2 or 4 bytes)
1744 if (data_type == TYPE_BOOLEAN)
1745 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1747 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1752 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1756 xx_ei = *ei_from; // copy element data into temporary buffer
1757 yy_ei = *ei_to; // copy element data into temporary buffer
1759 copyConfigFromConfigList(chunk_config_CUSX_base);
1764 // ---------- reinitialize and copy change pages ----------
1766 ei_to->num_change_pages = ei_from->num_change_pages;
1767 ei_to->current_change_page = ei_from->current_change_page;
1769 setElementChangePages(ei_to, ei_to->num_change_pages);
1771 for (i = 0; i < ei_to->num_change_pages; i++)
1772 ei_to->change_page[i] = ei_from->change_page[i];
1774 // ---------- copy group element info ----------
1775 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1776 *ei_to->group = *ei_from->group;
1778 // mark this custom element as modified
1779 ei_to->modified_settings = TRUE;
1782 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1784 int change_page_size = sizeof(struct ElementChangeInfo);
1786 ei->num_change_pages = MAX(1, change_pages);
1789 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1791 if (ei->current_change_page >= ei->num_change_pages)
1792 ei->current_change_page = ei->num_change_pages - 1;
1794 ei->change = &ei->change_page[ei->current_change_page];
1797 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1799 xx_change = *change; // copy change data into temporary buffer
1801 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1803 *change = xx_change;
1805 resetEventFlags(change);
1807 change->direct_action = 0;
1808 change->other_action = 0;
1810 change->pre_change_function = NULL;
1811 change->change_function = NULL;
1812 change->post_change_function = NULL;
1815 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1819 li = *level; // copy level data into temporary buffer
1820 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1821 *level = li; // copy temporary buffer back to level data
1823 setLevelInfoToDefaults_BD();
1824 setLevelInfoToDefaults_EM();
1825 setLevelInfoToDefaults_SP();
1826 setLevelInfoToDefaults_MM();
1828 level->native_bd_level = &native_bd_level;
1829 level->native_em_level = &native_em_level;
1830 level->native_sp_level = &native_sp_level;
1831 level->native_mm_level = &native_mm_level;
1833 level->file_version = FILE_VERSION_ACTUAL;
1834 level->game_version = GAME_VERSION_ACTUAL;
1836 level->creation_date = getCurrentDate();
1838 level->encoding_16bit_field = TRUE;
1839 level->encoding_16bit_yamyam = TRUE;
1840 level->encoding_16bit_amoeba = TRUE;
1842 // clear level name and level author string buffers
1843 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1844 level->name[i] = '\0';
1845 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1846 level->author[i] = '\0';
1848 // set level name and level author to default values
1849 strcpy(level->name, NAMELESS_LEVEL_NAME);
1850 strcpy(level->author, ANONYMOUS_NAME);
1852 // set level playfield to playable default level with player and exit
1853 for (x = 0; x < MAX_LEV_FIELDX; x++)
1854 for (y = 0; y < MAX_LEV_FIELDY; y++)
1855 level->field[x][y] = EL_SAND;
1857 level->field[0][0] = EL_PLAYER_1;
1858 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1860 BorderElement = EL_STEELWALL;
1862 // detect custom elements when loading them
1863 level->file_has_custom_elements = FALSE;
1865 // set all bug compatibility flags to "false" => do not emulate this bug
1866 level->use_action_after_change_bug = FALSE;
1868 if (leveldir_current)
1870 // try to determine better author name than 'anonymous'
1871 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1873 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1874 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1878 switch (LEVELCLASS(leveldir_current))
1880 case LEVELCLASS_TUTORIAL:
1881 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1884 case LEVELCLASS_CONTRIB:
1885 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1886 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1889 case LEVELCLASS_PRIVATE:
1890 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1891 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1895 // keep default value
1902 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1904 static boolean clipboard_elements_initialized = FALSE;
1907 InitElementPropertiesStatic();
1909 li = *level; // copy level data into temporary buffer
1910 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1911 *level = li; // copy temporary buffer back to level data
1913 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1916 struct ElementInfo *ei = &element_info[element];
1918 if (element == EL_MM_GRAY_BALL)
1920 struct LevelInfo_MM *level_mm = level->native_mm_level;
1923 for (j = 0; j < level->num_mm_ball_contents; j++)
1924 level->mm_ball_content[j] =
1925 map_element_MM_to_RND(level_mm->ball_content[j]);
1928 // never initialize clipboard elements after the very first time
1929 // (to be able to use clipboard elements between several levels)
1930 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1933 if (IS_ENVELOPE(element))
1935 int envelope_nr = element - EL_ENVELOPE_1;
1937 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1939 level->envelope[envelope_nr] = xx_envelope;
1942 if (IS_CUSTOM_ELEMENT(element) ||
1943 IS_GROUP_ELEMENT(element) ||
1944 IS_INTERNAL_ELEMENT(element))
1946 xx_ei = *ei; // copy element data into temporary buffer
1948 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1953 setElementChangePages(ei, 1);
1954 setElementChangeInfoToDefaults(ei->change);
1956 if (IS_CUSTOM_ELEMENT(element) ||
1957 IS_GROUP_ELEMENT(element))
1959 setElementDescriptionToDefault(ei);
1961 ei->modified_settings = FALSE;
1964 if (IS_CUSTOM_ELEMENT(element) ||
1965 IS_INTERNAL_ELEMENT(element))
1967 // internal values used in level editor
1969 ei->access_type = 0;
1970 ei->access_layer = 0;
1971 ei->access_protected = 0;
1972 ei->walk_to_action = 0;
1973 ei->smash_targets = 0;
1976 ei->can_explode_by_fire = FALSE;
1977 ei->can_explode_smashed = FALSE;
1978 ei->can_explode_impact = FALSE;
1980 ei->current_change_page = 0;
1983 if (IS_GROUP_ELEMENT(element) ||
1984 IS_INTERNAL_ELEMENT(element))
1986 struct ElementGroupInfo *group;
1988 // initialize memory for list of elements in group
1989 if (ei->group == NULL)
1990 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1994 xx_group = *group; // copy group data into temporary buffer
1996 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2001 if (IS_EMPTY_ELEMENT(element) ||
2002 IS_INTERNAL_ELEMENT(element))
2004 xx_ei = *ei; // copy element data into temporary buffer
2006 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2012 clipboard_elements_initialized = TRUE;
2015 static void setLevelInfoToDefaults(struct LevelInfo *level,
2016 boolean level_info_only,
2017 boolean reset_file_status)
2019 setLevelInfoToDefaults_Level(level);
2021 if (!level_info_only)
2022 setLevelInfoToDefaults_Elements(level);
2024 if (reset_file_status)
2026 level->no_valid_file = FALSE;
2027 level->no_level_file = FALSE;
2030 level->changed = FALSE;
2033 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2035 level_file_info->nr = 0;
2036 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2037 level_file_info->packed = FALSE;
2039 setString(&level_file_info->basename, NULL);
2040 setString(&level_file_info->filename, NULL);
2043 int getMappedElement_SB(int, boolean);
2045 static void ActivateLevelTemplate(void)
2049 if (check_special_flags("load_xsb_to_ces"))
2051 // fill smaller playfields with padding "beyond border wall" elements
2052 if (level.fieldx < level_template.fieldx ||
2053 level.fieldy < level_template.fieldy)
2055 short field[level.fieldx][level.fieldy];
2056 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2057 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2058 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2059 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2061 // copy old playfield (which is smaller than the visible area)
2062 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2063 field[x][y] = level.field[x][y];
2065 // fill new, larger playfield with "beyond border wall" elements
2066 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2067 level.field[x][y] = getMappedElement_SB('_', TRUE);
2069 // copy the old playfield to the middle of the new playfield
2070 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2071 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2073 level.fieldx = new_fieldx;
2074 level.fieldy = new_fieldy;
2078 // Currently there is no special action needed to activate the template
2079 // data, because 'element_info' property settings overwrite the original
2080 // level data, while all other variables do not change.
2082 // Exception: 'from_level_template' elements in the original level playfield
2083 // are overwritten with the corresponding elements at the same position in
2084 // playfield from the level template.
2086 for (x = 0; x < level.fieldx; x++)
2087 for (y = 0; y < level.fieldy; y++)
2088 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2089 level.field[x][y] = level_template.field[x][y];
2091 if (check_special_flags("load_xsb_to_ces"))
2093 struct LevelInfo level_backup = level;
2095 // overwrite all individual level settings from template level settings
2096 level = level_template;
2098 // restore level file info
2099 level.file_info = level_backup.file_info;
2101 // restore playfield size
2102 level.fieldx = level_backup.fieldx;
2103 level.fieldy = level_backup.fieldy;
2105 // restore playfield content
2106 for (x = 0; x < level.fieldx; x++)
2107 for (y = 0; y < level.fieldy; y++)
2108 level.field[x][y] = level_backup.field[x][y];
2110 // restore name and author from individual level
2111 strcpy(level.name, level_backup.name);
2112 strcpy(level.author, level_backup.author);
2114 // restore flag "use_custom_template"
2115 level.use_custom_template = level_backup.use_custom_template;
2119 static boolean checkForPackageFromBasename_BD(char *basename)
2121 // check for native BD level file extensions
2122 if (!strSuffixLower(basename, ".bd") &&
2123 !strSuffixLower(basename, ".bdr") &&
2124 !strSuffixLower(basename, ".brc") &&
2125 !strSuffixLower(basename, ".gds"))
2128 // check for standard single-level BD files (like "001.bd")
2129 if (strSuffixLower(basename, ".bd") &&
2130 strlen(basename) == 6 &&
2131 basename[0] >= '0' && basename[0] <= '9' &&
2132 basename[1] >= '0' && basename[1] <= '9' &&
2133 basename[2] >= '0' && basename[2] <= '9')
2136 // this is a level package in native BD file format
2140 static char *getLevelFilenameFromBasename(char *basename)
2142 static char *filename = NULL;
2144 checked_free(filename);
2146 filename = getPath2(getCurrentLevelDir(), basename);
2151 static int getFileTypeFromBasename(char *basename)
2153 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2155 static char *filename = NULL;
2156 struct stat file_status;
2158 // ---------- try to determine file type from filename ----------
2160 // check for typical filename of a Supaplex level package file
2161 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2162 return LEVEL_FILE_TYPE_SP;
2164 // check for typical filename of a Diamond Caves II level package file
2165 if (strSuffixLower(basename, ".dc") ||
2166 strSuffixLower(basename, ".dc2"))
2167 return LEVEL_FILE_TYPE_DC;
2169 // check for typical filename of a Sokoban level package file
2170 if (strSuffixLower(basename, ".xsb") &&
2171 strchr(basename, '%') == NULL)
2172 return LEVEL_FILE_TYPE_SB;
2174 // check for typical filename of a Boulder Dash (GDash) level package file
2175 if (checkForPackageFromBasename_BD(basename))
2176 return LEVEL_FILE_TYPE_BD;
2178 // ---------- try to determine file type from filesize ----------
2180 checked_free(filename);
2181 filename = getPath2(getCurrentLevelDir(), basename);
2183 if (stat(filename, &file_status) == 0)
2185 // check for typical filesize of a Supaplex level package file
2186 if (file_status.st_size == 170496)
2187 return LEVEL_FILE_TYPE_SP;
2190 return LEVEL_FILE_TYPE_UNKNOWN;
2193 static int getFileTypeFromMagicBytes(char *filename, int type)
2197 if ((file = openFile(filename, MODE_READ)))
2199 char chunk_name[CHUNK_ID_LEN + 1];
2201 getFileChunkBE(file, chunk_name, NULL);
2203 if (strEqual(chunk_name, "MMII") ||
2204 strEqual(chunk_name, "MIRR"))
2205 type = LEVEL_FILE_TYPE_MM;
2213 static boolean checkForPackageFromBasename(char *basename)
2215 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2216 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2218 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2221 static char *getSingleLevelBasenameExt(int nr, char *extension)
2223 static char basename[MAX_FILENAME_LEN];
2226 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2228 sprintf(basename, "%03d.%s", nr, extension);
2233 static char *getSingleLevelBasename(int nr)
2235 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2238 static char *getPackedLevelBasename(int type)
2240 static char basename[MAX_FILENAME_LEN];
2241 char *directory = getCurrentLevelDir();
2243 DirectoryEntry *dir_entry;
2245 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2247 if ((dir = openDirectory(directory)) == NULL)
2249 Warn("cannot read current level directory '%s'", directory);
2254 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2256 char *entry_basename = dir_entry->basename;
2257 int entry_type = getFileTypeFromBasename(entry_basename);
2259 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2261 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2264 strcpy(basename, entry_basename);
2271 closeDirectory(dir);
2276 static char *getSingleLevelFilename(int nr)
2278 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2281 #if ENABLE_UNUSED_CODE
2282 static char *getPackedLevelFilename(int type)
2284 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2288 char *getDefaultLevelFilename(int nr)
2290 return getSingleLevelFilename(nr);
2293 #if ENABLE_UNUSED_CODE
2294 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2298 lfi->packed = FALSE;
2300 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2301 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2305 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2306 int type, char *format, ...)
2308 static char basename[MAX_FILENAME_LEN];
2311 va_start(ap, format);
2312 vsprintf(basename, format, ap);
2316 lfi->packed = FALSE;
2318 setString(&lfi->basename, basename);
2319 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2322 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2328 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2329 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2332 static int getFiletypeFromID(char *filetype_id)
2334 char *filetype_id_lower;
2335 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2338 if (filetype_id == NULL)
2339 return LEVEL_FILE_TYPE_UNKNOWN;
2341 filetype_id_lower = getStringToLower(filetype_id);
2343 for (i = 0; filetype_id_list[i].id != NULL; i++)
2345 char *id_lower = getStringToLower(filetype_id_list[i].id);
2347 if (strEqual(filetype_id_lower, id_lower))
2348 filetype = filetype_id_list[i].filetype;
2352 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2356 free(filetype_id_lower);
2361 char *getLocalLevelTemplateFilename(void)
2363 return getDefaultLevelFilename(-1);
2366 char *getGlobalLevelTemplateFilename(void)
2368 // global variable "leveldir_current" must be modified in the loop below
2369 LevelDirTree *leveldir_current_last = leveldir_current;
2370 char *filename = NULL;
2372 // check for template level in path from current to topmost tree node
2374 while (leveldir_current != NULL)
2376 filename = getDefaultLevelFilename(-1);
2378 if (fileExists(filename))
2381 leveldir_current = leveldir_current->node_parent;
2384 // restore global variable "leveldir_current" modified in above loop
2385 leveldir_current = leveldir_current_last;
2390 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2394 // special case: level number is negative => check for level template file
2397 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2398 getSingleLevelBasename(-1));
2400 // replace local level template filename with global template filename
2401 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2403 // no fallback if template file not existing
2407 // special case: check for file name/pattern specified in "levelinfo.conf"
2408 if (leveldir_current->level_filename != NULL)
2410 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2412 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2413 leveldir_current->level_filename, nr);
2415 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2417 if (fileExists(lfi->filename))
2420 else if (leveldir_current->level_filetype != NULL)
2422 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2424 // check for specified native level file with standard file name
2425 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2426 "%03d.%s", nr, LEVELFILE_EXTENSION);
2427 if (fileExists(lfi->filename))
2431 // check for native Rocks'n'Diamonds level file
2432 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2433 "%03d.%s", nr, LEVELFILE_EXTENSION);
2434 if (fileExists(lfi->filename))
2437 // check for native Boulder Dash level file
2438 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2439 if (fileExists(lfi->filename))
2442 // check for Emerald Mine level file (V1)
2443 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2444 'a' + (nr / 10) % 26, '0' + nr % 10);
2445 if (fileExists(lfi->filename))
2447 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2448 'A' + (nr / 10) % 26, '0' + nr % 10);
2449 if (fileExists(lfi->filename))
2452 // check for Emerald Mine level file (V2 to V5)
2453 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2454 if (fileExists(lfi->filename))
2457 // check for Emerald Mine level file (V6 / single mode)
2458 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2459 if (fileExists(lfi->filename))
2461 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2462 if (fileExists(lfi->filename))
2465 // check for Emerald Mine level file (V6 / teamwork mode)
2466 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2467 if (fileExists(lfi->filename))
2469 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2470 if (fileExists(lfi->filename))
2473 // check for various packed level file formats
2474 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2475 if (fileExists(lfi->filename))
2478 // no known level file found -- use default values (and fail later)
2479 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2480 "%03d.%s", nr, LEVELFILE_EXTENSION);
2483 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2485 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2486 lfi->type = getFileTypeFromBasename(lfi->basename);
2488 if (lfi->type == LEVEL_FILE_TYPE_RND)
2489 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2492 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2494 // always start with reliable default values
2495 setFileInfoToDefaults(level_file_info);
2497 level_file_info->nr = nr; // set requested level number
2499 determineLevelFileInfo_Filename(level_file_info);
2500 determineLevelFileInfo_Filetype(level_file_info);
2503 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2504 struct LevelFileInfo *lfi_to)
2506 lfi_to->nr = lfi_from->nr;
2507 lfi_to->type = lfi_from->type;
2508 lfi_to->packed = lfi_from->packed;
2510 setString(&lfi_to->basename, lfi_from->basename);
2511 setString(&lfi_to->filename, lfi_from->filename);
2514 // ----------------------------------------------------------------------------
2515 // functions for loading R'n'D level
2516 // ----------------------------------------------------------------------------
2518 int getMappedElement(int element)
2520 // remap some (historic, now obsolete) elements
2524 case EL_PLAYER_OBSOLETE:
2525 element = EL_PLAYER_1;
2528 case EL_KEY_OBSOLETE:
2532 case EL_EM_KEY_1_FILE_OBSOLETE:
2533 element = EL_EM_KEY_1;
2536 case EL_EM_KEY_2_FILE_OBSOLETE:
2537 element = EL_EM_KEY_2;
2540 case EL_EM_KEY_3_FILE_OBSOLETE:
2541 element = EL_EM_KEY_3;
2544 case EL_EM_KEY_4_FILE_OBSOLETE:
2545 element = EL_EM_KEY_4;
2548 case EL_ENVELOPE_OBSOLETE:
2549 element = EL_ENVELOPE_1;
2557 if (element >= NUM_FILE_ELEMENTS)
2559 Warn("invalid level element %d", element);
2561 element = EL_UNKNOWN;
2569 static int getMappedElementByVersion(int element, int game_version)
2571 // remap some elements due to certain game version
2573 if (game_version <= VERSION_IDENT(2,2,0,0))
2575 // map game font elements
2576 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2577 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2578 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2579 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2582 if (game_version < VERSION_IDENT(3,0,0,0))
2584 // map Supaplex gravity tube elements
2585 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2586 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2587 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2588 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2595 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2597 level->file_version = getFileVersion(file);
2598 level->game_version = getFileVersion(file);
2603 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2605 level->creation_date.year = getFile16BitBE(file);
2606 level->creation_date.month = getFile8Bit(file);
2607 level->creation_date.day = getFile8Bit(file);
2609 level->creation_date.src = DATE_SRC_LEVELFILE;
2614 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2616 int initial_player_stepsize;
2617 int initial_player_gravity;
2620 level->fieldx = getFile8Bit(file);
2621 level->fieldy = getFile8Bit(file);
2623 level->time = getFile16BitBE(file);
2624 level->gems_needed = getFile16BitBE(file);
2626 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2627 level->name[i] = getFile8Bit(file);
2628 level->name[MAX_LEVEL_NAME_LEN] = 0;
2630 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2631 level->score[i] = getFile8Bit(file);
2633 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2634 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2635 for (y = 0; y < 3; y++)
2636 for (x = 0; x < 3; x++)
2637 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2639 level->amoeba_speed = getFile8Bit(file);
2640 level->time_magic_wall = getFile8Bit(file);
2641 level->time_wheel = getFile8Bit(file);
2642 level->amoeba_content = getMappedElement(getFile8Bit(file));
2644 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2647 for (i = 0; i < MAX_PLAYERS; i++)
2648 level->initial_player_stepsize[i] = initial_player_stepsize;
2650 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2652 for (i = 0; i < MAX_PLAYERS; i++)
2653 level->initial_player_gravity[i] = initial_player_gravity;
2655 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2656 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2658 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2660 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2661 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2662 level->can_move_into_acid_bits = getFile32BitBE(file);
2663 level->dont_collide_with_bits = getFile8Bit(file);
2665 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2666 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2668 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2669 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2670 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2672 level->game_engine_type = getFile8Bit(file);
2674 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2679 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2683 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2684 level->name[i] = getFile8Bit(file);
2685 level->name[MAX_LEVEL_NAME_LEN] = 0;
2690 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2694 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2695 level->author[i] = getFile8Bit(file);
2696 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2701 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2704 int chunk_size_expected = level->fieldx * level->fieldy;
2706 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2707 stored with 16-bit encoding (and should be twice as big then).
2708 Even worse, playfield data was stored 16-bit when only yamyam content
2709 contained 16-bit elements and vice versa. */
2711 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2712 chunk_size_expected *= 2;
2714 if (chunk_size_expected != chunk_size)
2716 ReadUnusedBytesFromFile(file, chunk_size);
2717 return chunk_size_expected;
2720 for (y = 0; y < level->fieldy; y++)
2721 for (x = 0; x < level->fieldx; x++)
2722 level->field[x][y] =
2723 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2728 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2731 int header_size = 4;
2732 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2733 int chunk_size_expected = header_size + content_size;
2735 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2736 stored with 16-bit encoding (and should be twice as big then).
2737 Even worse, playfield data was stored 16-bit when only yamyam content
2738 contained 16-bit elements and vice versa. */
2740 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2741 chunk_size_expected += content_size;
2743 if (chunk_size_expected != chunk_size)
2745 ReadUnusedBytesFromFile(file, chunk_size);
2746 return chunk_size_expected;
2750 level->num_yamyam_contents = getFile8Bit(file);
2754 // correct invalid number of content fields -- should never happen
2755 if (level->num_yamyam_contents < 1 ||
2756 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2757 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2759 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2760 for (y = 0; y < 3; y++)
2761 for (x = 0; x < 3; x++)
2762 level->yamyam_content[i].e[x][y] =
2763 getMappedElement(level->encoding_16bit_field ?
2764 getFile16BitBE(file) : getFile8Bit(file));
2768 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2773 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2775 element = getMappedElement(getFile16BitBE(file));
2776 num_contents = getFile8Bit(file);
2778 getFile8Bit(file); // content x size (unused)
2779 getFile8Bit(file); // content y size (unused)
2781 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2783 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2784 for (y = 0; y < 3; y++)
2785 for (x = 0; x < 3; x++)
2786 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2788 // correct invalid number of content fields -- should never happen
2789 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2790 num_contents = STD_ELEMENT_CONTENTS;
2792 if (element == EL_YAMYAM)
2794 level->num_yamyam_contents = num_contents;
2796 for (i = 0; i < num_contents; i++)
2797 for (y = 0; y < 3; y++)
2798 for (x = 0; x < 3; x++)
2799 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2801 else if (element == EL_BD_AMOEBA)
2803 level->amoeba_content = content_array[0][0][0];
2807 Warn("cannot load content for element '%d'", element);
2813 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2819 int chunk_size_expected;
2821 element = getMappedElement(getFile16BitBE(file));
2822 if (!IS_ENVELOPE(element))
2823 element = EL_ENVELOPE_1;
2825 envelope_nr = element - EL_ENVELOPE_1;
2827 envelope_len = getFile16BitBE(file);
2829 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2830 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2832 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2834 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2835 if (chunk_size_expected != chunk_size)
2837 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2838 return chunk_size_expected;
2841 for (i = 0; i < envelope_len; i++)
2842 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2847 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2849 int num_changed_custom_elements = getFile16BitBE(file);
2850 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2853 if (chunk_size_expected != chunk_size)
2855 ReadUnusedBytesFromFile(file, chunk_size - 2);
2856 return chunk_size_expected;
2859 for (i = 0; i < num_changed_custom_elements; i++)
2861 int element = getMappedElement(getFile16BitBE(file));
2862 int properties = getFile32BitBE(file);
2864 if (IS_CUSTOM_ELEMENT(element))
2865 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2867 Warn("invalid custom element number %d", element);
2869 // older game versions that wrote level files with CUS1 chunks used
2870 // different default push delay values (not yet stored in level file)
2871 element_info[element].push_delay_fixed = 2;
2872 element_info[element].push_delay_random = 8;
2875 level->file_has_custom_elements = TRUE;
2880 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2882 int num_changed_custom_elements = getFile16BitBE(file);
2883 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2886 if (chunk_size_expected != chunk_size)
2888 ReadUnusedBytesFromFile(file, chunk_size - 2);
2889 return chunk_size_expected;
2892 for (i = 0; i < num_changed_custom_elements; i++)
2894 int element = getMappedElement(getFile16BitBE(file));
2895 int custom_target_element = getMappedElement(getFile16BitBE(file));
2897 if (IS_CUSTOM_ELEMENT(element))
2898 element_info[element].change->target_element = custom_target_element;
2900 Warn("invalid custom element number %d", element);
2903 level->file_has_custom_elements = TRUE;
2908 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2910 int num_changed_custom_elements = getFile16BitBE(file);
2911 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2914 if (chunk_size_expected != chunk_size)
2916 ReadUnusedBytesFromFile(file, chunk_size - 2);
2917 return chunk_size_expected;
2920 for (i = 0; i < num_changed_custom_elements; i++)
2922 int element = getMappedElement(getFile16BitBE(file));
2923 struct ElementInfo *ei = &element_info[element];
2924 unsigned int event_bits;
2926 if (!IS_CUSTOM_ELEMENT(element))
2928 Warn("invalid custom element number %d", element);
2930 element = EL_INTERNAL_DUMMY;
2933 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2934 ei->description[j] = getFile8Bit(file);
2935 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2937 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2939 // some free bytes for future properties and padding
2940 ReadUnusedBytesFromFile(file, 7);
2942 ei->use_gfx_element = getFile8Bit(file);
2943 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2945 ei->collect_score_initial = getFile8Bit(file);
2946 ei->collect_count_initial = getFile8Bit(file);
2948 ei->push_delay_fixed = getFile16BitBE(file);
2949 ei->push_delay_random = getFile16BitBE(file);
2950 ei->move_delay_fixed = getFile16BitBE(file);
2951 ei->move_delay_random = getFile16BitBE(file);
2953 ei->move_pattern = getFile16BitBE(file);
2954 ei->move_direction_initial = getFile8Bit(file);
2955 ei->move_stepsize = getFile8Bit(file);
2957 for (y = 0; y < 3; y++)
2958 for (x = 0; x < 3; x++)
2959 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2961 // bits 0 - 31 of "has_event[]"
2962 event_bits = getFile32BitBE(file);
2963 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2964 if (event_bits & (1u << j))
2965 ei->change->has_event[j] = TRUE;
2967 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2969 ei->change->delay_fixed = getFile16BitBE(file);
2970 ei->change->delay_random = getFile16BitBE(file);
2971 ei->change->delay_frames = getFile16BitBE(file);
2973 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2975 ei->change->explode = getFile8Bit(file);
2976 ei->change->use_target_content = getFile8Bit(file);
2977 ei->change->only_if_complete = getFile8Bit(file);
2978 ei->change->use_random_replace = getFile8Bit(file);
2980 ei->change->random_percentage = getFile8Bit(file);
2981 ei->change->replace_when = getFile8Bit(file);
2983 for (y = 0; y < 3; y++)
2984 for (x = 0; x < 3; x++)
2985 ei->change->target_content.e[x][y] =
2986 getMappedElement(getFile16BitBE(file));
2988 ei->slippery_type = getFile8Bit(file);
2990 // some free bytes for future properties and padding
2991 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2993 // mark that this custom element has been modified
2994 ei->modified_settings = TRUE;
2997 level->file_has_custom_elements = TRUE;
3002 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3004 struct ElementInfo *ei;
3005 int chunk_size_expected;
3009 // ---------- custom element base property values (96 bytes) ----------------
3011 element = getMappedElement(getFile16BitBE(file));
3013 if (!IS_CUSTOM_ELEMENT(element))
3015 Warn("invalid custom element number %d", element);
3017 ReadUnusedBytesFromFile(file, chunk_size - 2);
3022 ei = &element_info[element];
3024 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3025 ei->description[i] = getFile8Bit(file);
3026 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3028 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3030 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3032 ei->num_change_pages = getFile8Bit(file);
3034 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3035 if (chunk_size_expected != chunk_size)
3037 ReadUnusedBytesFromFile(file, chunk_size - 43);
3038 return chunk_size_expected;
3041 ei->ce_value_fixed_initial = getFile16BitBE(file);
3042 ei->ce_value_random_initial = getFile16BitBE(file);
3043 ei->use_last_ce_value = getFile8Bit(file);
3045 ei->use_gfx_element = getFile8Bit(file);
3046 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3048 ei->collect_score_initial = getFile8Bit(file);
3049 ei->collect_count_initial = getFile8Bit(file);
3051 ei->drop_delay_fixed = getFile8Bit(file);
3052 ei->push_delay_fixed = getFile8Bit(file);
3053 ei->drop_delay_random = getFile8Bit(file);
3054 ei->push_delay_random = getFile8Bit(file);
3055 ei->move_delay_fixed = getFile16BitBE(file);
3056 ei->move_delay_random = getFile16BitBE(file);
3058 // bits 0 - 15 of "move_pattern" ...
3059 ei->move_pattern = getFile16BitBE(file);
3060 ei->move_direction_initial = getFile8Bit(file);
3061 ei->move_stepsize = getFile8Bit(file);
3063 ei->slippery_type = getFile8Bit(file);
3065 for (y = 0; y < 3; y++)
3066 for (x = 0; x < 3; x++)
3067 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3069 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3070 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3071 ei->move_leave_type = getFile8Bit(file);
3073 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3074 ei->move_pattern |= (getFile16BitBE(file) << 16);
3076 ei->access_direction = getFile8Bit(file);
3078 ei->explosion_delay = getFile8Bit(file);
3079 ei->ignition_delay = getFile8Bit(file);
3080 ei->explosion_type = getFile8Bit(file);
3082 // some free bytes for future custom property values and padding
3083 ReadUnusedBytesFromFile(file, 1);
3085 // ---------- change page property values (48 bytes) ------------------------
3087 setElementChangePages(ei, ei->num_change_pages);
3089 for (i = 0; i < ei->num_change_pages; i++)
3091 struct ElementChangeInfo *change = &ei->change_page[i];
3092 unsigned int event_bits;
3094 // always start with reliable default values
3095 setElementChangeInfoToDefaults(change);
3097 // bits 0 - 31 of "has_event[]" ...
3098 event_bits = getFile32BitBE(file);
3099 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3100 if (event_bits & (1u << j))
3101 change->has_event[j] = TRUE;
3103 change->target_element = getMappedElement(getFile16BitBE(file));
3105 change->delay_fixed = getFile16BitBE(file);
3106 change->delay_random = getFile16BitBE(file);
3107 change->delay_frames = getFile16BitBE(file);
3109 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3111 change->explode = getFile8Bit(file);
3112 change->use_target_content = getFile8Bit(file);
3113 change->only_if_complete = getFile8Bit(file);
3114 change->use_random_replace = getFile8Bit(file);
3116 change->random_percentage = getFile8Bit(file);
3117 change->replace_when = getFile8Bit(file);
3119 for (y = 0; y < 3; y++)
3120 for (x = 0; x < 3; x++)
3121 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3123 change->can_change = getFile8Bit(file);
3125 change->trigger_side = getFile8Bit(file);
3127 change->trigger_player = getFile8Bit(file);
3128 change->trigger_page = getFile8Bit(file);
3130 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3131 CH_PAGE_ANY : (1 << change->trigger_page));
3133 change->has_action = getFile8Bit(file);
3134 change->action_type = getFile8Bit(file);
3135 change->action_mode = getFile8Bit(file);
3136 change->action_arg = getFile16BitBE(file);
3138 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3139 event_bits = getFile8Bit(file);
3140 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3141 if (event_bits & (1u << (j - 32)))
3142 change->has_event[j] = TRUE;
3145 // mark this custom element as modified
3146 ei->modified_settings = TRUE;
3148 level->file_has_custom_elements = TRUE;
3153 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3155 struct ElementInfo *ei;
3156 struct ElementGroupInfo *group;
3160 element = getMappedElement(getFile16BitBE(file));
3162 if (!IS_GROUP_ELEMENT(element))
3164 Warn("invalid group element number %d", element);
3166 ReadUnusedBytesFromFile(file, chunk_size - 2);
3171 ei = &element_info[element];
3173 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3174 ei->description[i] = getFile8Bit(file);
3175 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3177 group = element_info[element].group;
3179 group->num_elements = getFile8Bit(file);
3181 ei->use_gfx_element = getFile8Bit(file);
3182 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3184 group->choice_mode = getFile8Bit(file);
3186 // some free bytes for future values and padding
3187 ReadUnusedBytesFromFile(file, 3);
3189 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3190 group->element[i] = getMappedElement(getFile16BitBE(file));
3192 // mark this group element as modified
3193 element_info[element].modified_settings = TRUE;
3195 level->file_has_custom_elements = TRUE;
3200 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3201 int element, int real_element)
3203 int micro_chunk_size = 0;
3204 int conf_type = getFile8Bit(file);
3205 int byte_mask = conf_type & CONF_MASK_BYTES;
3206 boolean element_found = FALSE;
3209 micro_chunk_size += 1;
3211 if (byte_mask == CONF_MASK_MULTI_BYTES)
3213 int num_bytes = getFile16BitBE(file);
3214 byte *buffer = checked_malloc(num_bytes);
3216 ReadBytesFromFile(file, buffer, num_bytes);
3218 for (i = 0; conf[i].data_type != -1; i++)
3220 if (conf[i].element == element &&
3221 conf[i].conf_type == conf_type)
3223 int data_type = conf[i].data_type;
3224 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3225 int max_num_entities = conf[i].max_num_entities;
3227 if (num_entities > max_num_entities)
3229 Warn("truncating number of entities for element %d from %d to %d",
3230 element, num_entities, max_num_entities);
3232 num_entities = max_num_entities;
3235 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3236 data_type == TYPE_CONTENT_LIST))
3238 // for element and content lists, zero entities are not allowed
3239 Warn("found empty list of entities for element %d", element);
3241 // do not set "num_entities" here to prevent reading behind buffer
3243 *(int *)(conf[i].num_entities) = 1; // at least one is required
3247 *(int *)(conf[i].num_entities) = num_entities;
3250 element_found = TRUE;
3252 if (data_type == TYPE_STRING)
3254 char *string = (char *)(conf[i].value);
3257 for (j = 0; j < max_num_entities; j++)
3258 string[j] = (j < num_entities ? buffer[j] : '\0');
3260 else if (data_type == TYPE_ELEMENT_LIST)
3262 int *element_array = (int *)(conf[i].value);
3265 for (j = 0; j < num_entities; j++)
3267 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3269 else if (data_type == TYPE_CONTENT_LIST)
3271 struct Content *content= (struct Content *)(conf[i].value);
3274 for (c = 0; c < num_entities; c++)
3275 for (y = 0; y < 3; y++)
3276 for (x = 0; x < 3; x++)
3277 content[c].e[x][y] =
3278 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3281 element_found = FALSE;
3287 checked_free(buffer);
3289 micro_chunk_size += 2 + num_bytes;
3291 else // constant size configuration data (1, 2 or 4 bytes)
3293 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3294 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3295 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3297 for (i = 0; conf[i].data_type != -1; i++)
3299 if (conf[i].element == element &&
3300 conf[i].conf_type == conf_type)
3302 int data_type = conf[i].data_type;
3304 if (data_type == TYPE_ELEMENT)
3305 value = getMappedElement(value);
3307 if (data_type == TYPE_BOOLEAN)
3308 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3310 *(int *) (conf[i].value) = value;
3312 element_found = TRUE;
3318 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3323 char *error_conf_chunk_bytes =
3324 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3325 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3326 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3327 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3328 int error_element = real_element;
3330 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3331 error_conf_chunk_bytes, error_conf_chunk_token,
3332 error_element, EL_NAME(error_element));
3335 return micro_chunk_size;
3338 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3340 int real_chunk_size = 0;
3342 li = *level; // copy level data into temporary buffer
3344 while (!checkEndOfFile(file))
3346 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3348 if (real_chunk_size >= chunk_size)
3352 *level = li; // copy temporary buffer back to level data
3354 return real_chunk_size;
3357 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3359 int real_chunk_size = 0;
3361 li = *level; // copy level data into temporary buffer
3363 while (!checkEndOfFile(file))
3365 int element = getMappedElement(getFile16BitBE(file));
3367 real_chunk_size += 2;
3368 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3370 if (real_chunk_size >= chunk_size)
3374 *level = li; // copy temporary buffer back to level data
3376 return real_chunk_size;
3379 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3381 int real_chunk_size = 0;
3383 li = *level; // copy level data into temporary buffer
3385 while (!checkEndOfFile(file))
3387 int element = getMappedElement(getFile16BitBE(file));
3389 real_chunk_size += 2;
3390 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3392 if (real_chunk_size >= chunk_size)
3396 *level = li; // copy temporary buffer back to level data
3398 return real_chunk_size;
3401 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3403 int element = getMappedElement(getFile16BitBE(file));
3404 int envelope_nr = element - EL_ENVELOPE_1;
3405 int real_chunk_size = 2;
3407 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3409 while (!checkEndOfFile(file))
3411 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3414 if (real_chunk_size >= chunk_size)
3418 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3420 return real_chunk_size;
3423 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3425 int element = getMappedElement(getFile16BitBE(file));
3426 int real_chunk_size = 2;
3427 struct ElementInfo *ei = &element_info[element];
3430 xx_ei = *ei; // copy element data into temporary buffer
3432 xx_ei.num_change_pages = -1;
3434 while (!checkEndOfFile(file))
3436 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3438 if (xx_ei.num_change_pages != -1)
3441 if (real_chunk_size >= chunk_size)
3447 if (ei->num_change_pages == -1)
3449 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3452 ei->num_change_pages = 1;
3454 setElementChangePages(ei, 1);
3455 setElementChangeInfoToDefaults(ei->change);
3457 return real_chunk_size;
3460 // initialize number of change pages stored for this custom element
3461 setElementChangePages(ei, ei->num_change_pages);
3462 for (i = 0; i < ei->num_change_pages; i++)
3463 setElementChangeInfoToDefaults(&ei->change_page[i]);
3465 // start with reading properties for the first change page
3466 xx_current_change_page = 0;
3468 while (!checkEndOfFile(file))
3470 // level file might contain invalid change page number
3471 if (xx_current_change_page >= ei->num_change_pages)
3474 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3476 xx_change = *change; // copy change data into temporary buffer
3478 resetEventBits(); // reset bits; change page might have changed
3480 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3483 *change = xx_change;
3485 setEventFlagsFromEventBits(change);
3487 if (real_chunk_size >= chunk_size)
3491 level->file_has_custom_elements = TRUE;
3493 return real_chunk_size;
3496 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3498 int element = getMappedElement(getFile16BitBE(file));
3499 int real_chunk_size = 2;
3500 struct ElementInfo *ei = &element_info[element];
3501 struct ElementGroupInfo *group = ei->group;
3506 xx_ei = *ei; // copy element data into temporary buffer
3507 xx_group = *group; // copy group data into temporary buffer
3509 while (!checkEndOfFile(file))
3511 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3514 if (real_chunk_size >= chunk_size)
3521 level->file_has_custom_elements = TRUE;
3523 return real_chunk_size;
3526 static int LoadLevel_EMPX(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];
3532 xx_ei = *ei; // copy element data into temporary buffer
3534 while (!checkEndOfFile(file))
3536 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3539 if (real_chunk_size >= chunk_size)
3545 level->file_has_custom_elements = TRUE;
3547 return real_chunk_size;
3550 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3551 struct LevelFileInfo *level_file_info,
3552 boolean level_info_only)
3554 char *filename = level_file_info->filename;
3555 char cookie[MAX_LINE_LEN];
3556 char chunk_name[CHUNK_ID_LEN + 1];
3560 if (!(file = openFile(filename, MODE_READ)))
3562 level->no_valid_file = TRUE;
3563 level->no_level_file = TRUE;
3565 if (level_info_only)
3568 Warn("cannot read level '%s' -- using empty level", filename);
3570 if (!setup.editor.use_template_for_new_levels)
3573 // if level file not found, try to initialize level data from template
3574 filename = getGlobalLevelTemplateFilename();
3576 if (!(file = openFile(filename, MODE_READ)))
3579 // default: for empty levels, use level template for custom elements
3580 level->use_custom_template = TRUE;
3582 level->no_valid_file = FALSE;
3585 getFileChunkBE(file, chunk_name, NULL);
3586 if (strEqual(chunk_name, "RND1"))
3588 getFile32BitBE(file); // not used
3590 getFileChunkBE(file, chunk_name, NULL);
3591 if (!strEqual(chunk_name, "CAVE"))
3593 level->no_valid_file = TRUE;
3595 Warn("unknown format of level file '%s'", filename);
3602 else // check for pre-2.0 file format with cookie string
3604 strcpy(cookie, chunk_name);
3605 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3607 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3608 cookie[strlen(cookie) - 1] = '\0';
3610 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3612 level->no_valid_file = TRUE;
3614 Warn("unknown format of level file '%s'", filename);
3621 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3623 level->no_valid_file = TRUE;
3625 Warn("unsupported version of level file '%s'", filename);
3632 // pre-2.0 level files have no game version, so use file version here
3633 level->game_version = level->file_version;
3636 if (level->file_version < FILE_VERSION_1_2)
3638 // level files from versions before 1.2.0 without chunk structure
3639 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3640 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3648 int (*loader)(File *, int, struct LevelInfo *);
3652 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3653 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3654 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3655 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3656 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3657 { "INFO", -1, LoadLevel_INFO },
3658 { "BODY", -1, LoadLevel_BODY },
3659 { "CONT", -1, LoadLevel_CONT },
3660 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3661 { "CNT3", -1, LoadLevel_CNT3 },
3662 { "CUS1", -1, LoadLevel_CUS1 },
3663 { "CUS2", -1, LoadLevel_CUS2 },
3664 { "CUS3", -1, LoadLevel_CUS3 },
3665 { "CUS4", -1, LoadLevel_CUS4 },
3666 { "GRP1", -1, LoadLevel_GRP1 },
3667 { "CONF", -1, LoadLevel_CONF },
3668 { "ELEM", -1, LoadLevel_ELEM },
3669 { "NOTE", -1, LoadLevel_NOTE },
3670 { "CUSX", -1, LoadLevel_CUSX },
3671 { "GRPX", -1, LoadLevel_GRPX },
3672 { "EMPX", -1, LoadLevel_EMPX },
3677 while (getFileChunkBE(file, chunk_name, &chunk_size))
3681 while (chunk_info[i].name != NULL &&
3682 !strEqual(chunk_name, chunk_info[i].name))
3685 if (chunk_info[i].name == NULL)
3687 Warn("unknown chunk '%s' in level file '%s'",
3688 chunk_name, filename);
3690 ReadUnusedBytesFromFile(file, chunk_size);
3692 else if (chunk_info[i].size != -1 &&
3693 chunk_info[i].size != chunk_size)
3695 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3696 chunk_size, chunk_name, filename);
3698 ReadUnusedBytesFromFile(file, chunk_size);
3702 // call function to load this level chunk
3703 int chunk_size_expected =
3704 (chunk_info[i].loader)(file, chunk_size, level);
3706 if (chunk_size_expected < 0)
3708 Warn("error reading chunk '%s' in level file '%s'",
3709 chunk_name, filename);
3714 // the size of some chunks cannot be checked before reading other
3715 // chunks first (like "HEAD" and "BODY") that contain some header
3716 // information, so check them here
3717 if (chunk_size_expected != chunk_size)
3719 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3720 chunk_size, chunk_name, filename);
3732 // ----------------------------------------------------------------------------
3733 // functions for loading BD level
3734 // ----------------------------------------------------------------------------
3736 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3738 struct LevelInfo_BD *level_bd = level->native_bd_level;
3739 GdCave *cave = NULL; // will be changed below
3740 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3741 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3744 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3746 // cave and map newly allocated when set to defaults above
3747 cave = level_bd->cave;
3749 for (i = 0; i < 5; i++)
3751 cave->level_time[i] = level->time;
3752 cave->level_diamonds[i] = level->gems_needed;
3753 cave->level_magic_wall_time[i] = level->time_magic_wall;
3755 cave->level_speed[i] = level->bd_cycle_delay_ms;
3756 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3757 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3758 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3760 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3763 cave->diamond_value = level->score[SC_EMERALD];
3764 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3766 cave->scheduling = level->bd_scheduling_type;
3767 cave->pal_timing = level->bd_pal_timing;
3768 cave->intermission = level->bd_intermission;
3769 cave->diagonal_movements = level->bd_diagonal_movements;
3771 strncpy(cave->name, level->name, sizeof(GdString));
3772 cave->name[sizeof(GdString) - 1] = '\0';
3774 for (x = 0; x < cave->w; x++)
3775 for (y = 0; y < cave->h; y++)
3776 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3779 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3781 struct LevelInfo_BD *level_bd = level->native_bd_level;
3782 GdCave *cave = level_bd->cave;
3783 int bd_level_nr = level_bd->level_nr;
3786 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3787 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3789 level->time = cave->level_time[bd_level_nr];
3790 level->gems_needed = cave->level_diamonds[bd_level_nr];
3791 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3793 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3794 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3795 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3796 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3798 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3799 level->score[SC_EMERALD] = cave->diamond_value;
3800 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3802 level->bd_scheduling_type = cave->scheduling;
3803 level->bd_pal_timing = cave->pal_timing;
3804 level->bd_intermission = cave->intermission;
3805 level->bd_diagonal_movements = cave->diagonal_movements;
3807 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3809 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3810 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3812 for (x = 0; x < level->fieldx; x++)
3813 for (y = 0; y < level->fieldy; y++)
3814 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3816 checked_free(cave_name);
3819 static void setTapeInfoToDefaults(void);
3821 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3823 struct LevelInfo_BD *level_bd = level->native_bd_level;
3824 GdCave *cave = level_bd->cave;
3825 GdReplay *replay = level_bd->replay;
3831 // always start with reliable default values
3832 setTapeInfoToDefaults();
3834 tape.level_nr = level_nr; // (currently not used)
3835 tape.random_seed = replay->seed;
3837 TapeSetDateFromIsoDateString(replay->date);
3840 tape.pos[tape.counter].delay = 0;
3842 tape.bd_replay = TRUE;
3844 // all time calculations only used to display approximate tape time
3845 int cave_speed = cave->speed;
3846 int milliseconds_game = 0;
3847 int milliseconds_elapsed = 20;
3849 for (i = 0; i < replay->movements->len; i++)
3851 int replay_action = replay->movements->data[i];
3852 int tape_action = map_action_BD_to_RND(replay_action);
3853 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3854 boolean success = 0;
3858 success = TapeAddAction(action);
3860 milliseconds_game += milliseconds_elapsed;
3862 if (milliseconds_game >= cave_speed)
3864 milliseconds_game -= cave_speed;
3871 tape.pos[tape.counter].delay = 0;
3872 tape.pos[tape.counter].action[0] = 0;
3876 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3882 TapeHaltRecording();
3886 // ----------------------------------------------------------------------------
3887 // functions for loading EM level
3888 // ----------------------------------------------------------------------------
3890 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3892 static int ball_xy[8][2] =
3903 struct LevelInfo_EM *level_em = level->native_em_level;
3904 struct CAVE *cav = level_em->cav;
3907 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3908 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3910 cav->time_seconds = level->time;
3911 cav->gems_needed = level->gems_needed;
3913 cav->emerald_score = level->score[SC_EMERALD];
3914 cav->diamond_score = level->score[SC_DIAMOND];
3915 cav->alien_score = level->score[SC_ROBOT];
3916 cav->tank_score = level->score[SC_SPACESHIP];
3917 cav->bug_score = level->score[SC_BUG];
3918 cav->eater_score = level->score[SC_YAMYAM];
3919 cav->nut_score = level->score[SC_NUT];
3920 cav->dynamite_score = level->score[SC_DYNAMITE];
3921 cav->key_score = level->score[SC_KEY];
3922 cav->exit_score = level->score[SC_TIME_BONUS];
3924 cav->num_eater_arrays = level->num_yamyam_contents;
3926 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3927 for (y = 0; y < 3; y++)
3928 for (x = 0; x < 3; x++)
3929 cav->eater_array[i][y * 3 + x] =
3930 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3932 cav->amoeba_time = level->amoeba_speed;
3933 cav->wonderwall_time = level->time_magic_wall;
3934 cav->wheel_time = level->time_wheel;
3936 cav->android_move_time = level->android_move_time;
3937 cav->android_clone_time = level->android_clone_time;
3938 cav->ball_random = level->ball_random;
3939 cav->ball_active = level->ball_active_initial;
3940 cav->ball_time = level->ball_time;
3941 cav->num_ball_arrays = level->num_ball_contents;
3943 cav->lenses_score = level->lenses_score;
3944 cav->magnify_score = level->magnify_score;
3945 cav->slurp_score = level->slurp_score;
3947 cav->lenses_time = level->lenses_time;
3948 cav->magnify_time = level->magnify_time;
3950 cav->wind_time = 9999;
3951 cav->wind_direction =
3952 map_direction_RND_to_EM(level->wind_direction_initial);
3954 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3955 for (j = 0; j < 8; j++)
3956 cav->ball_array[i][j] =
3957 map_element_RND_to_EM_cave(level->ball_content[i].
3958 e[ball_xy[j][0]][ball_xy[j][1]]);
3960 map_android_clone_elements_RND_to_EM(level);
3962 // first fill the complete playfield with the empty space element
3963 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3964 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3965 cav->cave[x][y] = Cblank;
3967 // then copy the real level contents from level file into the playfield
3968 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3970 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3972 if (level->field[x][y] == EL_AMOEBA_DEAD)
3973 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3975 cav->cave[x][y] = new_element;
3978 for (i = 0; i < MAX_PLAYERS; i++)
3980 cav->player_x[i] = -1;
3981 cav->player_y[i] = -1;
3984 // initialize player positions and delete players from the playfield
3985 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3987 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3989 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3991 cav->player_x[player_nr] = x;
3992 cav->player_y[player_nr] = y;
3994 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3999 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4001 static int ball_xy[8][2] =
4012 struct LevelInfo_EM *level_em = level->native_em_level;
4013 struct CAVE *cav = level_em->cav;
4016 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4017 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4019 level->time = cav->time_seconds;
4020 level->gems_needed = cav->gems_needed;
4022 sprintf(level->name, "Level %d", level->file_info.nr);
4024 level->score[SC_EMERALD] = cav->emerald_score;
4025 level->score[SC_DIAMOND] = cav->diamond_score;
4026 level->score[SC_ROBOT] = cav->alien_score;
4027 level->score[SC_SPACESHIP] = cav->tank_score;
4028 level->score[SC_BUG] = cav->bug_score;
4029 level->score[SC_YAMYAM] = cav->eater_score;
4030 level->score[SC_NUT] = cav->nut_score;
4031 level->score[SC_DYNAMITE] = cav->dynamite_score;
4032 level->score[SC_KEY] = cav->key_score;
4033 level->score[SC_TIME_BONUS] = cav->exit_score;
4035 level->num_yamyam_contents = cav->num_eater_arrays;
4037 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4038 for (y = 0; y < 3; y++)
4039 for (x = 0; x < 3; x++)
4040 level->yamyam_content[i].e[x][y] =
4041 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4043 level->amoeba_speed = cav->amoeba_time;
4044 level->time_magic_wall = cav->wonderwall_time;
4045 level->time_wheel = cav->wheel_time;
4047 level->android_move_time = cav->android_move_time;
4048 level->android_clone_time = cav->android_clone_time;
4049 level->ball_random = cav->ball_random;
4050 level->ball_active_initial = cav->ball_active;
4051 level->ball_time = cav->ball_time;
4052 level->num_ball_contents = cav->num_ball_arrays;
4054 level->lenses_score = cav->lenses_score;
4055 level->magnify_score = cav->magnify_score;
4056 level->slurp_score = cav->slurp_score;
4058 level->lenses_time = cav->lenses_time;
4059 level->magnify_time = cav->magnify_time;
4061 level->wind_direction_initial =
4062 map_direction_EM_to_RND(cav->wind_direction);
4064 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4065 for (j = 0; j < 8; j++)
4066 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4067 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4069 map_android_clone_elements_EM_to_RND(level);
4071 // convert the playfield (some elements need special treatment)
4072 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4074 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4076 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4077 new_element = EL_AMOEBA_DEAD;
4079 level->field[x][y] = new_element;
4082 for (i = 0; i < MAX_PLAYERS; i++)
4084 // in case of all players set to the same field, use the first player
4085 int nr = MAX_PLAYERS - i - 1;
4086 int jx = cav->player_x[nr];
4087 int jy = cav->player_y[nr];
4089 if (jx != -1 && jy != -1)
4090 level->field[jx][jy] = EL_PLAYER_1 + nr;
4093 // time score is counted for each 10 seconds left in Emerald Mine levels
4094 level->time_score_base = 10;
4098 // ----------------------------------------------------------------------------
4099 // functions for loading SP level
4100 // ----------------------------------------------------------------------------
4102 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4104 struct LevelInfo_SP *level_sp = level->native_sp_level;
4105 LevelInfoType *header = &level_sp->header;
4108 level_sp->width = level->fieldx;
4109 level_sp->height = level->fieldy;
4111 for (x = 0; x < level->fieldx; x++)
4112 for (y = 0; y < level->fieldy; y++)
4113 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4115 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4117 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4118 header->LevelTitle[i] = level->name[i];
4119 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4121 header->InfotronsNeeded = level->gems_needed;
4123 header->SpecialPortCount = 0;
4125 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4127 boolean gravity_port_found = FALSE;
4128 boolean gravity_port_valid = FALSE;
4129 int gravity_port_flag;
4130 int gravity_port_base_element;
4131 int element = level->field[x][y];
4133 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4134 element <= EL_SP_GRAVITY_ON_PORT_UP)
4136 gravity_port_found = TRUE;
4137 gravity_port_valid = TRUE;
4138 gravity_port_flag = 1;
4139 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4141 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4142 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4144 gravity_port_found = TRUE;
4145 gravity_port_valid = TRUE;
4146 gravity_port_flag = 0;
4147 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4149 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4150 element <= EL_SP_GRAVITY_PORT_UP)
4152 // change R'n'D style gravity inverting special port to normal port
4153 // (there are no gravity inverting ports in native Supaplex engine)
4155 gravity_port_found = TRUE;
4156 gravity_port_valid = FALSE;
4157 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4160 if (gravity_port_found)
4162 if (gravity_port_valid &&
4163 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4165 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4167 port->PortLocation = (y * level->fieldx + x) * 2;
4168 port->Gravity = gravity_port_flag;
4170 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4172 header->SpecialPortCount++;
4176 // change special gravity port to normal port
4178 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4181 level_sp->playfield[x][y] = element - EL_SP_START;
4186 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4188 struct LevelInfo_SP *level_sp = level->native_sp_level;
4189 LevelInfoType *header = &level_sp->header;
4190 boolean num_invalid_elements = 0;
4193 level->fieldx = level_sp->width;
4194 level->fieldy = level_sp->height;
4196 for (x = 0; x < level->fieldx; x++)
4198 for (y = 0; y < level->fieldy; y++)
4200 int element_old = level_sp->playfield[x][y];
4201 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4203 if (element_new == EL_UNKNOWN)
4205 num_invalid_elements++;
4207 Debug("level:native:SP", "invalid element %d at position %d, %d",
4211 level->field[x][y] = element_new;
4215 if (num_invalid_elements > 0)
4216 Warn("found %d invalid elements%s", num_invalid_elements,
4217 (!options.debug ? " (use '--debug' for more details)" : ""));
4219 for (i = 0; i < MAX_PLAYERS; i++)
4220 level->initial_player_gravity[i] =
4221 (header->InitialGravity == 1 ? TRUE : FALSE);
4223 // skip leading spaces
4224 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4225 if (header->LevelTitle[i] != ' ')
4229 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4230 level->name[j] = header->LevelTitle[i];
4231 level->name[j] = '\0';
4233 // cut trailing spaces
4235 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4236 level->name[j - 1] = '\0';
4238 level->gems_needed = header->InfotronsNeeded;
4240 for (i = 0; i < header->SpecialPortCount; i++)
4242 SpecialPortType *port = &header->SpecialPort[i];
4243 int port_location = port->PortLocation;
4244 int gravity = port->Gravity;
4245 int port_x, port_y, port_element;
4247 port_x = (port_location / 2) % level->fieldx;
4248 port_y = (port_location / 2) / level->fieldx;
4250 if (port_x < 0 || port_x >= level->fieldx ||
4251 port_y < 0 || port_y >= level->fieldy)
4253 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4258 port_element = level->field[port_x][port_y];
4260 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4261 port_element > EL_SP_GRAVITY_PORT_UP)
4263 Warn("no special port at position (%d, %d)", port_x, port_y);
4268 // change previous (wrong) gravity inverting special port to either
4269 // gravity enabling special port or gravity disabling special port
4270 level->field[port_x][port_y] +=
4271 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4272 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4275 // change special gravity ports without database entries to normal ports
4276 for (x = 0; x < level->fieldx; x++)
4277 for (y = 0; y < level->fieldy; y++)
4278 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4279 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4280 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4282 level->time = 0; // no time limit
4283 level->amoeba_speed = 0;
4284 level->time_magic_wall = 0;
4285 level->time_wheel = 0;
4286 level->amoeba_content = EL_EMPTY;
4288 // original Supaplex does not use score values -- rate by playing time
4289 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4290 level->score[i] = 0;
4292 level->rate_time_over_score = TRUE;
4294 // there are no yamyams in supaplex levels
4295 for (i = 0; i < level->num_yamyam_contents; i++)
4296 for (x = 0; x < 3; x++)
4297 for (y = 0; y < 3; y++)
4298 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4301 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4303 struct LevelInfo_SP *level_sp = level->native_sp_level;
4304 struct DemoInfo_SP *demo = &level_sp->demo;
4307 // always start with reliable default values
4308 demo->is_available = FALSE;
4311 if (TAPE_IS_EMPTY(tape))
4314 demo->level_nr = tape.level_nr; // (currently not used)
4316 level_sp->header.DemoRandomSeed = tape.random_seed;
4320 for (i = 0; i < tape.length; i++)
4322 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4323 int demo_repeat = tape.pos[i].delay;
4324 int demo_entries = (demo_repeat + 15) / 16;
4326 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4328 Warn("tape truncated: size exceeds maximum SP demo size %d",
4334 for (j = 0; j < demo_repeat / 16; j++)
4335 demo->data[demo->length++] = 0xf0 | demo_action;
4337 if (demo_repeat % 16)
4338 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4341 demo->is_available = TRUE;
4344 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4346 struct LevelInfo_SP *level_sp = level->native_sp_level;
4347 struct DemoInfo_SP *demo = &level_sp->demo;
4348 char *filename = level->file_info.filename;
4351 // always start with reliable default values
4352 setTapeInfoToDefaults();
4354 if (!demo->is_available)
4357 tape.level_nr = demo->level_nr; // (currently not used)
4358 tape.random_seed = level_sp->header.DemoRandomSeed;
4360 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4363 tape.pos[tape.counter].delay = 0;
4365 for (i = 0; i < demo->length; i++)
4367 int demo_action = demo->data[i] & 0x0f;
4368 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4369 int tape_action = map_key_SP_to_RND(demo_action);
4370 int tape_repeat = demo_repeat + 1;
4371 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4372 boolean success = 0;
4375 for (j = 0; j < tape_repeat; j++)
4376 success = TapeAddAction(action);
4380 Warn("SP demo truncated: size exceeds maximum tape size %d",
4387 TapeHaltRecording();
4391 // ----------------------------------------------------------------------------
4392 // functions for loading MM level
4393 // ----------------------------------------------------------------------------
4395 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4397 struct LevelInfo_MM *level_mm = level->native_mm_level;
4400 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4401 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4403 level_mm->time = level->time;
4404 level_mm->kettles_needed = level->gems_needed;
4405 level_mm->auto_count_kettles = level->auto_count_gems;
4407 level_mm->mm_laser_red = level->mm_laser_red;
4408 level_mm->mm_laser_green = level->mm_laser_green;
4409 level_mm->mm_laser_blue = level->mm_laser_blue;
4411 level_mm->df_laser_red = level->df_laser_red;
4412 level_mm->df_laser_green = level->df_laser_green;
4413 level_mm->df_laser_blue = level->df_laser_blue;
4415 strcpy(level_mm->name, level->name);
4416 strcpy(level_mm->author, level->author);
4418 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4419 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4420 level_mm->score[SC_KEY] = level->score[SC_KEY];
4421 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4422 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4424 level_mm->amoeba_speed = level->amoeba_speed;
4425 level_mm->time_fuse = level->mm_time_fuse;
4426 level_mm->time_bomb = level->mm_time_bomb;
4427 level_mm->time_ball = level->mm_time_ball;
4428 level_mm->time_block = level->mm_time_block;
4430 level_mm->num_ball_contents = level->num_mm_ball_contents;
4431 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4432 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4433 level_mm->explode_ball = level->explode_mm_ball;
4435 for (i = 0; i < level->num_mm_ball_contents; i++)
4436 level_mm->ball_content[i] =
4437 map_element_RND_to_MM(level->mm_ball_content[i]);
4439 for (x = 0; x < level->fieldx; x++)
4440 for (y = 0; y < level->fieldy; y++)
4442 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4445 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4447 struct LevelInfo_MM *level_mm = level->native_mm_level;
4450 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4451 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4453 level->time = level_mm->time;
4454 level->gems_needed = level_mm->kettles_needed;
4455 level->auto_count_gems = level_mm->auto_count_kettles;
4457 level->mm_laser_red = level_mm->mm_laser_red;
4458 level->mm_laser_green = level_mm->mm_laser_green;
4459 level->mm_laser_blue = level_mm->mm_laser_blue;
4461 level->df_laser_red = level_mm->df_laser_red;
4462 level->df_laser_green = level_mm->df_laser_green;
4463 level->df_laser_blue = level_mm->df_laser_blue;
4465 strcpy(level->name, level_mm->name);
4467 // only overwrite author from 'levelinfo.conf' if author defined in level
4468 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4469 strcpy(level->author, level_mm->author);
4471 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4472 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4473 level->score[SC_KEY] = level_mm->score[SC_KEY];
4474 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4475 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4477 level->amoeba_speed = level_mm->amoeba_speed;
4478 level->mm_time_fuse = level_mm->time_fuse;
4479 level->mm_time_bomb = level_mm->time_bomb;
4480 level->mm_time_ball = level_mm->time_ball;
4481 level->mm_time_block = level_mm->time_block;
4483 level->num_mm_ball_contents = level_mm->num_ball_contents;
4484 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4485 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4486 level->explode_mm_ball = level_mm->explode_ball;
4488 for (i = 0; i < level->num_mm_ball_contents; i++)
4489 level->mm_ball_content[i] =
4490 map_element_MM_to_RND(level_mm->ball_content[i]);
4492 for (x = 0; x < level->fieldx; x++)
4493 for (y = 0; y < level->fieldy; y++)
4494 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4498 // ----------------------------------------------------------------------------
4499 // functions for loading DC level
4500 // ----------------------------------------------------------------------------
4502 #define DC_LEVEL_HEADER_SIZE 344
4504 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4507 static int last_data_encoded;
4511 int diff_hi, diff_lo;
4512 int data_hi, data_lo;
4513 unsigned short data_decoded;
4517 last_data_encoded = 0;
4524 diff = data_encoded - last_data_encoded;
4525 diff_hi = diff & ~0xff;
4526 diff_lo = diff & 0xff;
4530 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4531 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4532 data_hi = data_hi & 0xff00;
4534 data_decoded = data_hi | data_lo;
4536 last_data_encoded = data_encoded;
4538 offset1 = (offset1 + 1) % 31;
4539 offset2 = offset2 & 0xff;
4541 return data_decoded;
4544 static int getMappedElement_DC(int element)
4552 // 0x0117 - 0x036e: (?)
4555 // 0x042d - 0x0684: (?)
4571 element = EL_CRYSTAL;
4574 case 0x0e77: // quicksand (boulder)
4575 element = EL_QUICKSAND_FAST_FULL;
4578 case 0x0e99: // slow quicksand (boulder)
4579 element = EL_QUICKSAND_FULL;
4583 element = EL_EM_EXIT_OPEN;
4587 element = EL_EM_EXIT_CLOSED;
4591 element = EL_EM_STEEL_EXIT_OPEN;
4595 element = EL_EM_STEEL_EXIT_CLOSED;
4598 case 0x0f4f: // dynamite (lit 1)
4599 element = EL_EM_DYNAMITE_ACTIVE;
4602 case 0x0f57: // dynamite (lit 2)
4603 element = EL_EM_DYNAMITE_ACTIVE;
4606 case 0x0f5f: // dynamite (lit 3)
4607 element = EL_EM_DYNAMITE_ACTIVE;
4610 case 0x0f67: // dynamite (lit 4)
4611 element = EL_EM_DYNAMITE_ACTIVE;
4618 element = EL_AMOEBA_WET;
4622 element = EL_AMOEBA_DROP;
4626 element = EL_DC_MAGIC_WALL;
4630 element = EL_SPACESHIP_UP;
4634 element = EL_SPACESHIP_DOWN;
4638 element = EL_SPACESHIP_LEFT;
4642 element = EL_SPACESHIP_RIGHT;
4646 element = EL_BUG_UP;
4650 element = EL_BUG_DOWN;
4654 element = EL_BUG_LEFT;
4658 element = EL_BUG_RIGHT;
4662 element = EL_MOLE_UP;
4666 element = EL_MOLE_DOWN;
4670 element = EL_MOLE_LEFT;
4674 element = EL_MOLE_RIGHT;
4682 element = EL_YAMYAM_UP;
4686 element = EL_SWITCHGATE_OPEN;
4690 element = EL_SWITCHGATE_CLOSED;
4694 element = EL_DC_SWITCHGATE_SWITCH_UP;
4698 element = EL_TIMEGATE_CLOSED;
4701 case 0x144c: // conveyor belt switch (green)
4702 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4705 case 0x144f: // conveyor belt switch (red)
4706 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4709 case 0x1452: // conveyor belt switch (blue)
4710 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4714 element = EL_CONVEYOR_BELT_3_MIDDLE;
4718 element = EL_CONVEYOR_BELT_3_LEFT;
4722 element = EL_CONVEYOR_BELT_3_RIGHT;
4726 element = EL_CONVEYOR_BELT_1_MIDDLE;
4730 element = EL_CONVEYOR_BELT_1_LEFT;
4734 element = EL_CONVEYOR_BELT_1_RIGHT;
4738 element = EL_CONVEYOR_BELT_4_MIDDLE;
4742 element = EL_CONVEYOR_BELT_4_LEFT;
4746 element = EL_CONVEYOR_BELT_4_RIGHT;
4750 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4754 element = EL_EXPANDABLE_WALL_VERTICAL;
4758 element = EL_EXPANDABLE_WALL_ANY;
4761 case 0x14ce: // growing steel wall (left/right)
4762 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4765 case 0x14df: // growing steel wall (up/down)
4766 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4769 case 0x14e8: // growing steel wall (up/down/left/right)
4770 element = EL_EXPANDABLE_STEELWALL_ANY;
4774 element = EL_SHIELD_DEADLY;
4778 element = EL_EXTRA_TIME;
4786 element = EL_EMPTY_SPACE;
4789 case 0x1578: // quicksand (empty)
4790 element = EL_QUICKSAND_FAST_EMPTY;
4793 case 0x1579: // slow quicksand (empty)
4794 element = EL_QUICKSAND_EMPTY;
4804 element = EL_EM_DYNAMITE;
4807 case 0x15a1: // key (red)
4808 element = EL_EM_KEY_1;
4811 case 0x15a2: // key (yellow)
4812 element = EL_EM_KEY_2;
4815 case 0x15a3: // key (blue)
4816 element = EL_EM_KEY_4;
4819 case 0x15a4: // key (green)
4820 element = EL_EM_KEY_3;
4823 case 0x15a5: // key (white)
4824 element = EL_DC_KEY_WHITE;
4828 element = EL_WALL_SLIPPERY;
4835 case 0x15a8: // wall (not round)
4839 case 0x15a9: // (blue)
4840 element = EL_CHAR_A;
4843 case 0x15aa: // (blue)
4844 element = EL_CHAR_B;
4847 case 0x15ab: // (blue)
4848 element = EL_CHAR_C;
4851 case 0x15ac: // (blue)
4852 element = EL_CHAR_D;
4855 case 0x15ad: // (blue)
4856 element = EL_CHAR_E;
4859 case 0x15ae: // (blue)
4860 element = EL_CHAR_F;
4863 case 0x15af: // (blue)
4864 element = EL_CHAR_G;
4867 case 0x15b0: // (blue)
4868 element = EL_CHAR_H;
4871 case 0x15b1: // (blue)
4872 element = EL_CHAR_I;
4875 case 0x15b2: // (blue)
4876 element = EL_CHAR_J;
4879 case 0x15b3: // (blue)
4880 element = EL_CHAR_K;
4883 case 0x15b4: // (blue)
4884 element = EL_CHAR_L;
4887 case 0x15b5: // (blue)
4888 element = EL_CHAR_M;
4891 case 0x15b6: // (blue)
4892 element = EL_CHAR_N;
4895 case 0x15b7: // (blue)
4896 element = EL_CHAR_O;
4899 case 0x15b8: // (blue)
4900 element = EL_CHAR_P;
4903 case 0x15b9: // (blue)
4904 element = EL_CHAR_Q;
4907 case 0x15ba: // (blue)
4908 element = EL_CHAR_R;
4911 case 0x15bb: // (blue)
4912 element = EL_CHAR_S;
4915 case 0x15bc: // (blue)
4916 element = EL_CHAR_T;
4919 case 0x15bd: // (blue)
4920 element = EL_CHAR_U;
4923 case 0x15be: // (blue)
4924 element = EL_CHAR_V;
4927 case 0x15bf: // (blue)
4928 element = EL_CHAR_W;
4931 case 0x15c0: // (blue)
4932 element = EL_CHAR_X;
4935 case 0x15c1: // (blue)
4936 element = EL_CHAR_Y;
4939 case 0x15c2: // (blue)
4940 element = EL_CHAR_Z;
4943 case 0x15c3: // (blue)
4944 element = EL_CHAR_AUMLAUT;
4947 case 0x15c4: // (blue)
4948 element = EL_CHAR_OUMLAUT;
4951 case 0x15c5: // (blue)
4952 element = EL_CHAR_UUMLAUT;
4955 case 0x15c6: // (blue)
4956 element = EL_CHAR_0;
4959 case 0x15c7: // (blue)
4960 element = EL_CHAR_1;
4963 case 0x15c8: // (blue)
4964 element = EL_CHAR_2;
4967 case 0x15c9: // (blue)
4968 element = EL_CHAR_3;
4971 case 0x15ca: // (blue)
4972 element = EL_CHAR_4;
4975 case 0x15cb: // (blue)
4976 element = EL_CHAR_5;
4979 case 0x15cc: // (blue)
4980 element = EL_CHAR_6;
4983 case 0x15cd: // (blue)
4984 element = EL_CHAR_7;
4987 case 0x15ce: // (blue)
4988 element = EL_CHAR_8;
4991 case 0x15cf: // (blue)
4992 element = EL_CHAR_9;
4995 case 0x15d0: // (blue)
4996 element = EL_CHAR_PERIOD;
4999 case 0x15d1: // (blue)
5000 element = EL_CHAR_EXCLAM;
5003 case 0x15d2: // (blue)
5004 element = EL_CHAR_COLON;
5007 case 0x15d3: // (blue)
5008 element = EL_CHAR_LESS;
5011 case 0x15d4: // (blue)
5012 element = EL_CHAR_GREATER;
5015 case 0x15d5: // (blue)
5016 element = EL_CHAR_QUESTION;
5019 case 0x15d6: // (blue)
5020 element = EL_CHAR_COPYRIGHT;
5023 case 0x15d7: // (blue)
5024 element = EL_CHAR_UP;
5027 case 0x15d8: // (blue)
5028 element = EL_CHAR_DOWN;
5031 case 0x15d9: // (blue)
5032 element = EL_CHAR_BUTTON;
5035 case 0x15da: // (blue)
5036 element = EL_CHAR_PLUS;
5039 case 0x15db: // (blue)
5040 element = EL_CHAR_MINUS;
5043 case 0x15dc: // (blue)
5044 element = EL_CHAR_APOSTROPHE;
5047 case 0x15dd: // (blue)
5048 element = EL_CHAR_PARENLEFT;
5051 case 0x15de: // (blue)
5052 element = EL_CHAR_PARENRIGHT;
5055 case 0x15df: // (green)
5056 element = EL_CHAR_A;
5059 case 0x15e0: // (green)
5060 element = EL_CHAR_B;
5063 case 0x15e1: // (green)
5064 element = EL_CHAR_C;
5067 case 0x15e2: // (green)
5068 element = EL_CHAR_D;
5071 case 0x15e3: // (green)
5072 element = EL_CHAR_E;
5075 case 0x15e4: // (green)
5076 element = EL_CHAR_F;
5079 case 0x15e5: // (green)
5080 element = EL_CHAR_G;
5083 case 0x15e6: // (green)
5084 element = EL_CHAR_H;
5087 case 0x15e7: // (green)
5088 element = EL_CHAR_I;
5091 case 0x15e8: // (green)
5092 element = EL_CHAR_J;
5095 case 0x15e9: // (green)
5096 element = EL_CHAR_K;
5099 case 0x15ea: // (green)
5100 element = EL_CHAR_L;
5103 case 0x15eb: // (green)
5104 element = EL_CHAR_M;
5107 case 0x15ec: // (green)
5108 element = EL_CHAR_N;
5111 case 0x15ed: // (green)
5112 element = EL_CHAR_O;
5115 case 0x15ee: // (green)
5116 element = EL_CHAR_P;
5119 case 0x15ef: // (green)
5120 element = EL_CHAR_Q;
5123 case 0x15f0: // (green)
5124 element = EL_CHAR_R;
5127 case 0x15f1: // (green)
5128 element = EL_CHAR_S;
5131 case 0x15f2: // (green)
5132 element = EL_CHAR_T;
5135 case 0x15f3: // (green)
5136 element = EL_CHAR_U;
5139 case 0x15f4: // (green)
5140 element = EL_CHAR_V;
5143 case 0x15f5: // (green)
5144 element = EL_CHAR_W;
5147 case 0x15f6: // (green)
5148 element = EL_CHAR_X;
5151 case 0x15f7: // (green)
5152 element = EL_CHAR_Y;
5155 case 0x15f8: // (green)
5156 element = EL_CHAR_Z;
5159 case 0x15f9: // (green)
5160 element = EL_CHAR_AUMLAUT;
5163 case 0x15fa: // (green)
5164 element = EL_CHAR_OUMLAUT;
5167 case 0x15fb: // (green)
5168 element = EL_CHAR_UUMLAUT;
5171 case 0x15fc: // (green)
5172 element = EL_CHAR_0;
5175 case 0x15fd: // (green)
5176 element = EL_CHAR_1;
5179 case 0x15fe: // (green)
5180 element = EL_CHAR_2;
5183 case 0x15ff: // (green)
5184 element = EL_CHAR_3;
5187 case 0x1600: // (green)
5188 element = EL_CHAR_4;
5191 case 0x1601: // (green)
5192 element = EL_CHAR_5;
5195 case 0x1602: // (green)
5196 element = EL_CHAR_6;
5199 case 0x1603: // (green)
5200 element = EL_CHAR_7;
5203 case 0x1604: // (green)
5204 element = EL_CHAR_8;
5207 case 0x1605: // (green)
5208 element = EL_CHAR_9;
5211 case 0x1606: // (green)
5212 element = EL_CHAR_PERIOD;
5215 case 0x1607: // (green)
5216 element = EL_CHAR_EXCLAM;
5219 case 0x1608: // (green)
5220 element = EL_CHAR_COLON;
5223 case 0x1609: // (green)
5224 element = EL_CHAR_LESS;
5227 case 0x160a: // (green)
5228 element = EL_CHAR_GREATER;
5231 case 0x160b: // (green)
5232 element = EL_CHAR_QUESTION;
5235 case 0x160c: // (green)
5236 element = EL_CHAR_COPYRIGHT;
5239 case 0x160d: // (green)
5240 element = EL_CHAR_UP;
5243 case 0x160e: // (green)
5244 element = EL_CHAR_DOWN;
5247 case 0x160f: // (green)
5248 element = EL_CHAR_BUTTON;
5251 case 0x1610: // (green)
5252 element = EL_CHAR_PLUS;
5255 case 0x1611: // (green)
5256 element = EL_CHAR_MINUS;
5259 case 0x1612: // (green)
5260 element = EL_CHAR_APOSTROPHE;
5263 case 0x1613: // (green)
5264 element = EL_CHAR_PARENLEFT;
5267 case 0x1614: // (green)
5268 element = EL_CHAR_PARENRIGHT;
5271 case 0x1615: // (blue steel)
5272 element = EL_STEEL_CHAR_A;
5275 case 0x1616: // (blue steel)
5276 element = EL_STEEL_CHAR_B;
5279 case 0x1617: // (blue steel)
5280 element = EL_STEEL_CHAR_C;
5283 case 0x1618: // (blue steel)
5284 element = EL_STEEL_CHAR_D;
5287 case 0x1619: // (blue steel)
5288 element = EL_STEEL_CHAR_E;
5291 case 0x161a: // (blue steel)
5292 element = EL_STEEL_CHAR_F;
5295 case 0x161b: // (blue steel)
5296 element = EL_STEEL_CHAR_G;
5299 case 0x161c: // (blue steel)
5300 element = EL_STEEL_CHAR_H;
5303 case 0x161d: // (blue steel)
5304 element = EL_STEEL_CHAR_I;
5307 case 0x161e: // (blue steel)
5308 element = EL_STEEL_CHAR_J;
5311 case 0x161f: // (blue steel)
5312 element = EL_STEEL_CHAR_K;
5315 case 0x1620: // (blue steel)
5316 element = EL_STEEL_CHAR_L;
5319 case 0x1621: // (blue steel)
5320 element = EL_STEEL_CHAR_M;
5323 case 0x1622: // (blue steel)
5324 element = EL_STEEL_CHAR_N;
5327 case 0x1623: // (blue steel)
5328 element = EL_STEEL_CHAR_O;
5331 case 0x1624: // (blue steel)
5332 element = EL_STEEL_CHAR_P;
5335 case 0x1625: // (blue steel)
5336 element = EL_STEEL_CHAR_Q;
5339 case 0x1626: // (blue steel)
5340 element = EL_STEEL_CHAR_R;
5343 case 0x1627: // (blue steel)
5344 element = EL_STEEL_CHAR_S;
5347 case 0x1628: // (blue steel)
5348 element = EL_STEEL_CHAR_T;
5351 case 0x1629: // (blue steel)
5352 element = EL_STEEL_CHAR_U;
5355 case 0x162a: // (blue steel)
5356 element = EL_STEEL_CHAR_V;
5359 case 0x162b: // (blue steel)
5360 element = EL_STEEL_CHAR_W;
5363 case 0x162c: // (blue steel)
5364 element = EL_STEEL_CHAR_X;
5367 case 0x162d: // (blue steel)
5368 element = EL_STEEL_CHAR_Y;
5371 case 0x162e: // (blue steel)
5372 element = EL_STEEL_CHAR_Z;
5375 case 0x162f: // (blue steel)
5376 element = EL_STEEL_CHAR_AUMLAUT;
5379 case 0x1630: // (blue steel)
5380 element = EL_STEEL_CHAR_OUMLAUT;
5383 case 0x1631: // (blue steel)
5384 element = EL_STEEL_CHAR_UUMLAUT;
5387 case 0x1632: // (blue steel)
5388 element = EL_STEEL_CHAR_0;
5391 case 0x1633: // (blue steel)
5392 element = EL_STEEL_CHAR_1;
5395 case 0x1634: // (blue steel)
5396 element = EL_STEEL_CHAR_2;
5399 case 0x1635: // (blue steel)
5400 element = EL_STEEL_CHAR_3;
5403 case 0x1636: // (blue steel)
5404 element = EL_STEEL_CHAR_4;
5407 case 0x1637: // (blue steel)
5408 element = EL_STEEL_CHAR_5;
5411 case 0x1638: // (blue steel)
5412 element = EL_STEEL_CHAR_6;
5415 case 0x1639: // (blue steel)
5416 element = EL_STEEL_CHAR_7;
5419 case 0x163a: // (blue steel)
5420 element = EL_STEEL_CHAR_8;
5423 case 0x163b: // (blue steel)
5424 element = EL_STEEL_CHAR_9;
5427 case 0x163c: // (blue steel)
5428 element = EL_STEEL_CHAR_PERIOD;
5431 case 0x163d: // (blue steel)
5432 element = EL_STEEL_CHAR_EXCLAM;
5435 case 0x163e: // (blue steel)
5436 element = EL_STEEL_CHAR_COLON;
5439 case 0x163f: // (blue steel)
5440 element = EL_STEEL_CHAR_LESS;
5443 case 0x1640: // (blue steel)
5444 element = EL_STEEL_CHAR_GREATER;
5447 case 0x1641: // (blue steel)
5448 element = EL_STEEL_CHAR_QUESTION;
5451 case 0x1642: // (blue steel)
5452 element = EL_STEEL_CHAR_COPYRIGHT;
5455 case 0x1643: // (blue steel)
5456 element = EL_STEEL_CHAR_UP;
5459 case 0x1644: // (blue steel)
5460 element = EL_STEEL_CHAR_DOWN;
5463 case 0x1645: // (blue steel)
5464 element = EL_STEEL_CHAR_BUTTON;
5467 case 0x1646: // (blue steel)
5468 element = EL_STEEL_CHAR_PLUS;
5471 case 0x1647: // (blue steel)
5472 element = EL_STEEL_CHAR_MINUS;
5475 case 0x1648: // (blue steel)
5476 element = EL_STEEL_CHAR_APOSTROPHE;
5479 case 0x1649: // (blue steel)
5480 element = EL_STEEL_CHAR_PARENLEFT;
5483 case 0x164a: // (blue steel)
5484 element = EL_STEEL_CHAR_PARENRIGHT;
5487 case 0x164b: // (green steel)
5488 element = EL_STEEL_CHAR_A;
5491 case 0x164c: // (green steel)
5492 element = EL_STEEL_CHAR_B;
5495 case 0x164d: // (green steel)
5496 element = EL_STEEL_CHAR_C;
5499 case 0x164e: // (green steel)
5500 element = EL_STEEL_CHAR_D;
5503 case 0x164f: // (green steel)
5504 element = EL_STEEL_CHAR_E;
5507 case 0x1650: // (green steel)
5508 element = EL_STEEL_CHAR_F;
5511 case 0x1651: // (green steel)
5512 element = EL_STEEL_CHAR_G;
5515 case 0x1652: // (green steel)
5516 element = EL_STEEL_CHAR_H;
5519 case 0x1653: // (green steel)
5520 element = EL_STEEL_CHAR_I;
5523 case 0x1654: // (green steel)
5524 element = EL_STEEL_CHAR_J;
5527 case 0x1655: // (green steel)
5528 element = EL_STEEL_CHAR_K;
5531 case 0x1656: // (green steel)
5532 element = EL_STEEL_CHAR_L;
5535 case 0x1657: // (green steel)
5536 element = EL_STEEL_CHAR_M;
5539 case 0x1658: // (green steel)
5540 element = EL_STEEL_CHAR_N;
5543 case 0x1659: // (green steel)
5544 element = EL_STEEL_CHAR_O;
5547 case 0x165a: // (green steel)
5548 element = EL_STEEL_CHAR_P;
5551 case 0x165b: // (green steel)
5552 element = EL_STEEL_CHAR_Q;
5555 case 0x165c: // (green steel)
5556 element = EL_STEEL_CHAR_R;
5559 case 0x165d: // (green steel)
5560 element = EL_STEEL_CHAR_S;
5563 case 0x165e: // (green steel)
5564 element = EL_STEEL_CHAR_T;
5567 case 0x165f: // (green steel)
5568 element = EL_STEEL_CHAR_U;
5571 case 0x1660: // (green steel)
5572 element = EL_STEEL_CHAR_V;
5575 case 0x1661: // (green steel)
5576 element = EL_STEEL_CHAR_W;
5579 case 0x1662: // (green steel)
5580 element = EL_STEEL_CHAR_X;
5583 case 0x1663: // (green steel)
5584 element = EL_STEEL_CHAR_Y;
5587 case 0x1664: // (green steel)
5588 element = EL_STEEL_CHAR_Z;
5591 case 0x1665: // (green steel)
5592 element = EL_STEEL_CHAR_AUMLAUT;
5595 case 0x1666: // (green steel)
5596 element = EL_STEEL_CHAR_OUMLAUT;
5599 case 0x1667: // (green steel)
5600 element = EL_STEEL_CHAR_UUMLAUT;
5603 case 0x1668: // (green steel)
5604 element = EL_STEEL_CHAR_0;
5607 case 0x1669: // (green steel)
5608 element = EL_STEEL_CHAR_1;
5611 case 0x166a: // (green steel)
5612 element = EL_STEEL_CHAR_2;
5615 case 0x166b: // (green steel)
5616 element = EL_STEEL_CHAR_3;
5619 case 0x166c: // (green steel)
5620 element = EL_STEEL_CHAR_4;
5623 case 0x166d: // (green steel)
5624 element = EL_STEEL_CHAR_5;
5627 case 0x166e: // (green steel)
5628 element = EL_STEEL_CHAR_6;
5631 case 0x166f: // (green steel)
5632 element = EL_STEEL_CHAR_7;
5635 case 0x1670: // (green steel)
5636 element = EL_STEEL_CHAR_8;
5639 case 0x1671: // (green steel)
5640 element = EL_STEEL_CHAR_9;
5643 case 0x1672: // (green steel)
5644 element = EL_STEEL_CHAR_PERIOD;
5647 case 0x1673: // (green steel)
5648 element = EL_STEEL_CHAR_EXCLAM;
5651 case 0x1674: // (green steel)
5652 element = EL_STEEL_CHAR_COLON;
5655 case 0x1675: // (green steel)
5656 element = EL_STEEL_CHAR_LESS;
5659 case 0x1676: // (green steel)
5660 element = EL_STEEL_CHAR_GREATER;
5663 case 0x1677: // (green steel)
5664 element = EL_STEEL_CHAR_QUESTION;
5667 case 0x1678: // (green steel)
5668 element = EL_STEEL_CHAR_COPYRIGHT;
5671 case 0x1679: // (green steel)
5672 element = EL_STEEL_CHAR_UP;
5675 case 0x167a: // (green steel)
5676 element = EL_STEEL_CHAR_DOWN;
5679 case 0x167b: // (green steel)
5680 element = EL_STEEL_CHAR_BUTTON;
5683 case 0x167c: // (green steel)
5684 element = EL_STEEL_CHAR_PLUS;
5687 case 0x167d: // (green steel)
5688 element = EL_STEEL_CHAR_MINUS;
5691 case 0x167e: // (green steel)
5692 element = EL_STEEL_CHAR_APOSTROPHE;
5695 case 0x167f: // (green steel)
5696 element = EL_STEEL_CHAR_PARENLEFT;
5699 case 0x1680: // (green steel)
5700 element = EL_STEEL_CHAR_PARENRIGHT;
5703 case 0x1681: // gate (red)
5704 element = EL_EM_GATE_1;
5707 case 0x1682: // secret gate (red)
5708 element = EL_EM_GATE_1_GRAY;
5711 case 0x1683: // gate (yellow)
5712 element = EL_EM_GATE_2;
5715 case 0x1684: // secret gate (yellow)
5716 element = EL_EM_GATE_2_GRAY;
5719 case 0x1685: // gate (blue)
5720 element = EL_EM_GATE_4;
5723 case 0x1686: // secret gate (blue)
5724 element = EL_EM_GATE_4_GRAY;
5727 case 0x1687: // gate (green)
5728 element = EL_EM_GATE_3;
5731 case 0x1688: // secret gate (green)
5732 element = EL_EM_GATE_3_GRAY;
5735 case 0x1689: // gate (white)
5736 element = EL_DC_GATE_WHITE;
5739 case 0x168a: // secret gate (white)
5740 element = EL_DC_GATE_WHITE_GRAY;
5743 case 0x168b: // secret gate (no key)
5744 element = EL_DC_GATE_FAKE_GRAY;
5748 element = EL_ROBOT_WHEEL;
5752 element = EL_DC_TIMEGATE_SWITCH;
5756 element = EL_ACID_POOL_BOTTOM;
5760 element = EL_ACID_POOL_TOPLEFT;
5764 element = EL_ACID_POOL_TOPRIGHT;
5768 element = EL_ACID_POOL_BOTTOMLEFT;
5772 element = EL_ACID_POOL_BOTTOMRIGHT;
5776 element = EL_STEELWALL;
5780 element = EL_STEELWALL_SLIPPERY;
5783 case 0x1695: // steel wall (not round)
5784 element = EL_STEELWALL;
5787 case 0x1696: // steel wall (left)
5788 element = EL_DC_STEELWALL_1_LEFT;
5791 case 0x1697: // steel wall (bottom)
5792 element = EL_DC_STEELWALL_1_BOTTOM;
5795 case 0x1698: // steel wall (right)
5796 element = EL_DC_STEELWALL_1_RIGHT;
5799 case 0x1699: // steel wall (top)
5800 element = EL_DC_STEELWALL_1_TOP;
5803 case 0x169a: // steel wall (left/bottom)
5804 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5807 case 0x169b: // steel wall (right/bottom)
5808 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5811 case 0x169c: // steel wall (right/top)
5812 element = EL_DC_STEELWALL_1_TOPRIGHT;
5815 case 0x169d: // steel wall (left/top)
5816 element = EL_DC_STEELWALL_1_TOPLEFT;
5819 case 0x169e: // steel wall (right/bottom small)
5820 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5823 case 0x169f: // steel wall (left/bottom small)
5824 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5827 case 0x16a0: // steel wall (right/top small)
5828 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5831 case 0x16a1: // steel wall (left/top small)
5832 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5835 case 0x16a2: // steel wall (left/right)
5836 element = EL_DC_STEELWALL_1_VERTICAL;
5839 case 0x16a3: // steel wall (top/bottom)
5840 element = EL_DC_STEELWALL_1_HORIZONTAL;
5843 case 0x16a4: // steel wall 2 (left end)
5844 element = EL_DC_STEELWALL_2_LEFT;
5847 case 0x16a5: // steel wall 2 (right end)
5848 element = EL_DC_STEELWALL_2_RIGHT;
5851 case 0x16a6: // steel wall 2 (top end)
5852 element = EL_DC_STEELWALL_2_TOP;
5855 case 0x16a7: // steel wall 2 (bottom end)
5856 element = EL_DC_STEELWALL_2_BOTTOM;
5859 case 0x16a8: // steel wall 2 (left/right)
5860 element = EL_DC_STEELWALL_2_HORIZONTAL;
5863 case 0x16a9: // steel wall 2 (up/down)
5864 element = EL_DC_STEELWALL_2_VERTICAL;
5867 case 0x16aa: // steel wall 2 (mid)
5868 element = EL_DC_STEELWALL_2_MIDDLE;
5872 element = EL_SIGN_EXCLAMATION;
5876 element = EL_SIGN_RADIOACTIVITY;
5880 element = EL_SIGN_STOP;
5884 element = EL_SIGN_WHEELCHAIR;
5888 element = EL_SIGN_PARKING;
5892 element = EL_SIGN_NO_ENTRY;
5896 element = EL_SIGN_HEART;
5900 element = EL_SIGN_GIVE_WAY;
5904 element = EL_SIGN_ENTRY_FORBIDDEN;
5908 element = EL_SIGN_EMERGENCY_EXIT;
5912 element = EL_SIGN_YIN_YANG;
5916 element = EL_WALL_EMERALD;
5920 element = EL_WALL_DIAMOND;
5924 element = EL_WALL_PEARL;
5928 element = EL_WALL_CRYSTAL;
5932 element = EL_INVISIBLE_WALL;
5936 element = EL_INVISIBLE_STEELWALL;
5940 // EL_INVISIBLE_SAND
5943 element = EL_LIGHT_SWITCH;
5947 element = EL_ENVELOPE_1;
5951 if (element >= 0x0117 && element <= 0x036e) // (?)
5952 element = EL_DIAMOND;
5953 else if (element >= 0x042d && element <= 0x0684) // (?)
5954 element = EL_EMERALD;
5955 else if (element >= 0x157c && element <= 0x158b)
5957 else if (element >= 0x1590 && element <= 0x159f)
5958 element = EL_DC_LANDMINE;
5959 else if (element >= 0x16bc && element <= 0x16cb)
5960 element = EL_INVISIBLE_SAND;
5963 Warn("unknown Diamond Caves element 0x%04x", element);
5965 element = EL_UNKNOWN;
5970 return getMappedElement(element);
5973 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5975 byte header[DC_LEVEL_HEADER_SIZE];
5977 int envelope_header_pos = 62;
5978 int envelope_content_pos = 94;
5979 int level_name_pos = 251;
5980 int level_author_pos = 292;
5981 int envelope_header_len;
5982 int envelope_content_len;
5984 int level_author_len;
5986 int num_yamyam_contents;
5989 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5991 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5993 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5995 header[i * 2 + 0] = header_word >> 8;
5996 header[i * 2 + 1] = header_word & 0xff;
5999 // read some values from level header to check level decoding integrity
6000 fieldx = header[6] | (header[7] << 8);
6001 fieldy = header[8] | (header[9] << 8);
6002 num_yamyam_contents = header[60] | (header[61] << 8);
6004 // do some simple sanity checks to ensure that level was correctly decoded
6005 if (fieldx < 1 || fieldx > 256 ||
6006 fieldy < 1 || fieldy > 256 ||
6007 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6009 level->no_valid_file = TRUE;
6011 Warn("cannot decode level from stream -- using empty level");
6016 // maximum envelope header size is 31 bytes
6017 envelope_header_len = header[envelope_header_pos];
6018 // maximum envelope content size is 110 (156?) bytes
6019 envelope_content_len = header[envelope_content_pos];
6021 // maximum level title size is 40 bytes
6022 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6023 // maximum level author size is 30 (51?) bytes
6024 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6028 for (i = 0; i < envelope_header_len; i++)
6029 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6030 level->envelope[0].text[envelope_size++] =
6031 header[envelope_header_pos + 1 + i];
6033 if (envelope_header_len > 0 && envelope_content_len > 0)
6035 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6036 level->envelope[0].text[envelope_size++] = '\n';
6037 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6038 level->envelope[0].text[envelope_size++] = '\n';
6041 for (i = 0; i < envelope_content_len; i++)
6042 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6043 level->envelope[0].text[envelope_size++] =
6044 header[envelope_content_pos + 1 + i];
6046 level->envelope[0].text[envelope_size] = '\0';
6048 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6049 level->envelope[0].ysize = 10;
6050 level->envelope[0].autowrap = TRUE;
6051 level->envelope[0].centered = TRUE;
6053 for (i = 0; i < level_name_len; i++)
6054 level->name[i] = header[level_name_pos + 1 + i];
6055 level->name[level_name_len] = '\0';
6057 for (i = 0; i < level_author_len; i++)
6058 level->author[i] = header[level_author_pos + 1 + i];
6059 level->author[level_author_len] = '\0';
6061 num_yamyam_contents = header[60] | (header[61] << 8);
6062 level->num_yamyam_contents =
6063 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6065 for (i = 0; i < num_yamyam_contents; i++)
6067 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6069 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6070 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6072 if (i < MAX_ELEMENT_CONTENTS)
6073 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6077 fieldx = header[6] | (header[7] << 8);
6078 fieldy = header[8] | (header[9] << 8);
6079 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6080 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6082 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6084 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6085 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6087 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6088 level->field[x][y] = getMappedElement_DC(element_dc);
6091 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6092 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6093 level->field[x][y] = EL_PLAYER_1;
6095 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6096 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6097 level->field[x][y] = EL_PLAYER_2;
6099 level->gems_needed = header[18] | (header[19] << 8);
6101 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6102 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6103 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6104 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6105 level->score[SC_NUT] = header[28] | (header[29] << 8);
6106 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6107 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6108 level->score[SC_BUG] = header[34] | (header[35] << 8);
6109 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6110 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6111 level->score[SC_KEY] = header[40] | (header[41] << 8);
6112 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6114 level->time = header[44] | (header[45] << 8);
6116 level->amoeba_speed = header[46] | (header[47] << 8);
6117 level->time_light = header[48] | (header[49] << 8);
6118 level->time_timegate = header[50] | (header[51] << 8);
6119 level->time_wheel = header[52] | (header[53] << 8);
6120 level->time_magic_wall = header[54] | (header[55] << 8);
6121 level->extra_time = header[56] | (header[57] << 8);
6122 level->shield_normal_time = header[58] | (header[59] << 8);
6124 // shield and extra time elements do not have a score
6125 level->score[SC_SHIELD] = 0;
6126 level->extra_time_score = 0;
6128 // set time for normal and deadly shields to the same value
6129 level->shield_deadly_time = level->shield_normal_time;
6131 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6132 // can slip down from flat walls, like normal walls and steel walls
6133 level->em_slippery_gems = TRUE;
6135 // time score is counted for each 10 seconds left in Diamond Caves levels
6136 level->time_score_base = 10;
6139 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6140 struct LevelFileInfo *level_file_info,
6141 boolean level_info_only)
6143 char *filename = level_file_info->filename;
6145 int num_magic_bytes = 8;
6146 char magic_bytes[num_magic_bytes + 1];
6147 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6149 if (!(file = openFile(filename, MODE_READ)))
6151 level->no_valid_file = TRUE;
6153 if (!level_info_only)
6154 Warn("cannot read level '%s' -- using empty level", filename);
6159 // fseek(file, 0x0000, SEEK_SET);
6161 if (level_file_info->packed)
6163 // read "magic bytes" from start of file
6164 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6165 magic_bytes[0] = '\0';
6167 // check "magic bytes" for correct file format
6168 if (!strPrefix(magic_bytes, "DC2"))
6170 level->no_valid_file = TRUE;
6172 Warn("unknown DC level file '%s' -- using empty level", filename);
6177 if (strPrefix(magic_bytes, "DC2Win95") ||
6178 strPrefix(magic_bytes, "DC2Win98"))
6180 int position_first_level = 0x00fa;
6181 int extra_bytes = 4;
6184 // advance file stream to first level inside the level package
6185 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6187 // each block of level data is followed by block of non-level data
6188 num_levels_to_skip *= 2;
6190 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6191 while (num_levels_to_skip >= 0)
6193 // advance file stream to next level inside the level package
6194 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6196 level->no_valid_file = TRUE;
6198 Warn("cannot fseek in file '%s' -- using empty level", filename);
6203 // skip apparently unused extra bytes following each level
6204 ReadUnusedBytesFromFile(file, extra_bytes);
6206 // read size of next level in level package
6207 skip_bytes = getFile32BitLE(file);
6209 num_levels_to_skip--;
6214 level->no_valid_file = TRUE;
6216 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6222 LoadLevelFromFileStream_DC(file, level);
6228 // ----------------------------------------------------------------------------
6229 // functions for loading SB level
6230 // ----------------------------------------------------------------------------
6232 int getMappedElement_SB(int element_ascii, boolean use_ces)
6240 sb_element_mapping[] =
6242 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6243 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6244 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6245 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6246 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6247 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6248 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6249 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6256 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6257 if (element_ascii == sb_element_mapping[i].ascii)
6258 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6260 return EL_UNDEFINED;
6263 static void SetLevelSettings_SB(struct LevelInfo *level)
6267 level->use_step_counter = TRUE;
6270 level->score[SC_TIME_BONUS] = 0;
6271 level->time_score_base = 1;
6272 level->rate_time_over_score = TRUE;
6275 level->auto_exit_sokoban = TRUE;
6278 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6279 struct LevelFileInfo *level_file_info,
6280 boolean level_info_only)
6282 char *filename = level_file_info->filename;
6283 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6284 char last_comment[MAX_LINE_LEN];
6285 char level_name[MAX_LINE_LEN];
6288 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6289 boolean read_continued_line = FALSE;
6290 boolean reading_playfield = FALSE;
6291 boolean got_valid_playfield_line = FALSE;
6292 boolean invalid_playfield_char = FALSE;
6293 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6294 int file_level_nr = 0;
6295 int x = 0, y = 0; // initialized to make compilers happy
6297 last_comment[0] = '\0';
6298 level_name[0] = '\0';
6300 if (!(file = openFile(filename, MODE_READ)))
6302 level->no_valid_file = TRUE;
6304 if (!level_info_only)
6305 Warn("cannot read level '%s' -- using empty level", filename);
6310 while (!checkEndOfFile(file))
6312 // level successfully read, but next level may follow here
6313 if (!got_valid_playfield_line && reading_playfield)
6315 // read playfield from single level file -- skip remaining file
6316 if (!level_file_info->packed)
6319 if (file_level_nr >= num_levels_to_skip)
6324 last_comment[0] = '\0';
6325 level_name[0] = '\0';
6327 reading_playfield = FALSE;
6330 got_valid_playfield_line = FALSE;
6332 // read next line of input file
6333 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6336 // cut trailing line break (this can be newline and/or carriage return)
6337 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6338 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6341 // copy raw input line for later use (mainly debugging output)
6342 strcpy(line_raw, line);
6344 if (read_continued_line)
6346 // append new line to existing line, if there is enough space
6347 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6348 strcat(previous_line, line_ptr);
6350 strcpy(line, previous_line); // copy storage buffer to line
6352 read_continued_line = FALSE;
6355 // if the last character is '\', continue at next line
6356 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6358 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6359 strcpy(previous_line, line); // copy line to storage buffer
6361 read_continued_line = TRUE;
6367 if (line[0] == '\0')
6370 // extract comment text from comment line
6373 for (line_ptr = line; *line_ptr; line_ptr++)
6374 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6377 strcpy(last_comment, line_ptr);
6382 // extract level title text from line containing level title
6383 if (line[0] == '\'')
6385 strcpy(level_name, &line[1]);
6387 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6388 level_name[strlen(level_name) - 1] = '\0';
6393 // skip lines containing only spaces (or empty lines)
6394 for (line_ptr = line; *line_ptr; line_ptr++)
6395 if (*line_ptr != ' ')
6397 if (*line_ptr == '\0')
6400 // at this point, we have found a line containing part of a playfield
6402 got_valid_playfield_line = TRUE;
6404 if (!reading_playfield)
6406 reading_playfield = TRUE;
6407 invalid_playfield_char = FALSE;
6409 for (x = 0; x < MAX_LEV_FIELDX; x++)
6410 for (y = 0; y < MAX_LEV_FIELDY; y++)
6411 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6416 // start with topmost tile row
6420 // skip playfield line if larger row than allowed
6421 if (y >= MAX_LEV_FIELDY)
6424 // start with leftmost tile column
6427 // read playfield elements from line
6428 for (line_ptr = line; *line_ptr; line_ptr++)
6430 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6432 // stop parsing playfield line if larger column than allowed
6433 if (x >= MAX_LEV_FIELDX)
6436 if (mapped_sb_element == EL_UNDEFINED)
6438 invalid_playfield_char = TRUE;
6443 level->field[x][y] = mapped_sb_element;
6445 // continue with next tile column
6448 level->fieldx = MAX(x, level->fieldx);
6451 if (invalid_playfield_char)
6453 // if first playfield line, treat invalid lines as comment lines
6455 reading_playfield = FALSE;
6460 // continue with next tile row
6468 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6469 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6471 if (!reading_playfield)
6473 level->no_valid_file = TRUE;
6475 Warn("cannot read level '%s' -- using empty level", filename);
6480 if (*level_name != '\0')
6482 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6483 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6485 else if (*last_comment != '\0')
6487 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6488 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6492 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6495 // set all empty fields beyond the border walls to invisible steel wall
6496 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6498 if ((x == 0 || x == level->fieldx - 1 ||
6499 y == 0 || y == level->fieldy - 1) &&
6500 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6501 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6502 level->field, level->fieldx, level->fieldy);
6505 // set special level settings for Sokoban levels
6506 SetLevelSettings_SB(level);
6508 if (load_xsb_to_ces)
6510 // special global settings can now be set in level template
6511 level->use_custom_template = TRUE;
6516 // -------------------------------------------------------------------------
6517 // functions for handling native levels
6518 // -------------------------------------------------------------------------
6520 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6521 struct LevelFileInfo *level_file_info,
6522 boolean level_info_only)
6526 // determine position of requested level inside level package
6527 if (level_file_info->packed)
6528 pos = level_file_info->nr - leveldir_current->first_level;
6530 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6531 level->no_valid_file = TRUE;
6534 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6535 struct LevelFileInfo *level_file_info,
6536 boolean level_info_only)
6538 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6539 level->no_valid_file = TRUE;
6542 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6543 struct LevelFileInfo *level_file_info,
6544 boolean level_info_only)
6548 // determine position of requested level inside level package
6549 if (level_file_info->packed)
6550 pos = level_file_info->nr - leveldir_current->first_level;
6552 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6553 level->no_valid_file = TRUE;
6556 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6557 struct LevelFileInfo *level_file_info,
6558 boolean level_info_only)
6560 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6561 level->no_valid_file = TRUE;
6564 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6566 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6567 CopyNativeLevel_RND_to_BD(level);
6568 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6569 CopyNativeLevel_RND_to_EM(level);
6570 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6571 CopyNativeLevel_RND_to_SP(level);
6572 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6573 CopyNativeLevel_RND_to_MM(level);
6576 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6578 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6579 CopyNativeLevel_BD_to_RND(level);
6580 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6581 CopyNativeLevel_EM_to_RND(level);
6582 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6583 CopyNativeLevel_SP_to_RND(level);
6584 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6585 CopyNativeLevel_MM_to_RND(level);
6588 void SaveNativeLevel(struct LevelInfo *level)
6590 // saving native level files only supported for some game engines
6591 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6592 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6595 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6596 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6597 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6598 char *filename = getLevelFilenameFromBasename(basename);
6600 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6603 boolean success = FALSE;
6605 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6607 CopyNativeLevel_RND_to_BD(level);
6608 // CopyNativeTape_RND_to_BD(level);
6610 success = SaveNativeLevel_BD(filename);
6612 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6614 CopyNativeLevel_RND_to_SP(level);
6615 CopyNativeTape_RND_to_SP(level);
6617 success = SaveNativeLevel_SP(filename);
6621 Request("Native level file saved!", REQ_CONFIRM);
6623 Request("Failed to save native level file!", REQ_CONFIRM);
6627 // ----------------------------------------------------------------------------
6628 // functions for loading generic level
6629 // ----------------------------------------------------------------------------
6631 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6632 struct LevelFileInfo *level_file_info,
6633 boolean level_info_only)
6635 // always start with reliable default values
6636 setLevelInfoToDefaults(level, level_info_only, TRUE);
6638 switch (level_file_info->type)
6640 case LEVEL_FILE_TYPE_RND:
6641 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6644 case LEVEL_FILE_TYPE_BD:
6645 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6646 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6649 case LEVEL_FILE_TYPE_EM:
6650 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6651 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6654 case LEVEL_FILE_TYPE_SP:
6655 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6656 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6659 case LEVEL_FILE_TYPE_MM:
6660 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6661 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6664 case LEVEL_FILE_TYPE_DC:
6665 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6668 case LEVEL_FILE_TYPE_SB:
6669 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6673 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6677 // if level file is invalid, restore level structure to default values
6678 if (level->no_valid_file)
6679 setLevelInfoToDefaults(level, level_info_only, FALSE);
6681 if (check_special_flags("use_native_bd_game_engine"))
6682 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6684 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6685 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6687 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6688 CopyNativeLevel_Native_to_RND(level);
6691 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6693 static struct LevelFileInfo level_file_info;
6695 // always start with reliable default values
6696 setFileInfoToDefaults(&level_file_info);
6698 level_file_info.nr = 0; // unknown level number
6699 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6701 setString(&level_file_info.filename, filename);
6703 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6706 static void LoadLevel_InitVersion(struct LevelInfo *level)
6710 if (leveldir_current == NULL) // only when dumping level
6713 // all engine modifications also valid for levels which use latest engine
6714 if (level->game_version < VERSION_IDENT(3,2,0,5))
6716 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6717 level->time_score_base = 10;
6720 if (leveldir_current->latest_engine)
6722 // ---------- use latest game engine --------------------------------------
6724 /* For all levels which are forced to use the latest game engine version
6725 (normally all but user contributed, private and undefined levels), set
6726 the game engine version to the actual version; this allows for actual
6727 corrections in the game engine to take effect for existing, converted
6728 levels (from "classic" or other existing games) to make the emulation
6729 of the corresponding game more accurate, while (hopefully) not breaking
6730 existing levels created from other players. */
6732 level->game_version = GAME_VERSION_ACTUAL;
6734 /* Set special EM style gems behaviour: EM style gems slip down from
6735 normal, steel and growing wall. As this is a more fundamental change,
6736 it seems better to set the default behaviour to "off" (as it is more
6737 natural) and make it configurable in the level editor (as a property
6738 of gem style elements). Already existing converted levels (neither
6739 private nor contributed levels) are changed to the new behaviour. */
6741 if (level->file_version < FILE_VERSION_2_0)
6742 level->em_slippery_gems = TRUE;
6747 // ---------- use game engine the level was created with --------------------
6749 /* For all levels which are not forced to use the latest game engine
6750 version (normally user contributed, private and undefined levels),
6751 use the version of the game engine the levels were created for.
6753 Since 2.0.1, the game engine version is now directly stored
6754 in the level file (chunk "VERS"), so there is no need anymore
6755 to set the game version from the file version (except for old,
6756 pre-2.0 levels, where the game version is still taken from the
6757 file format version used to store the level -- see above). */
6759 // player was faster than enemies in 1.0.0 and before
6760 if (level->file_version == FILE_VERSION_1_0)
6761 for (i = 0; i < MAX_PLAYERS; i++)
6762 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6764 // default behaviour for EM style gems was "slippery" only in 2.0.1
6765 if (level->game_version == VERSION_IDENT(2,0,1,0))
6766 level->em_slippery_gems = TRUE;
6768 // springs could be pushed over pits before (pre-release version) 2.2.0
6769 if (level->game_version < VERSION_IDENT(2,2,0,0))
6770 level->use_spring_bug = TRUE;
6772 if (level->game_version < VERSION_IDENT(3,2,0,5))
6774 // time orb caused limited time in endless time levels before 3.2.0-5
6775 level->use_time_orb_bug = TRUE;
6777 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6778 level->block_snap_field = FALSE;
6780 // extra time score was same value as time left score before 3.2.0-5
6781 level->extra_time_score = level->score[SC_TIME_BONUS];
6784 if (level->game_version < VERSION_IDENT(3,2,0,7))
6786 // default behaviour for snapping was "not continuous" before 3.2.0-7
6787 level->continuous_snapping = FALSE;
6790 // only few elements were able to actively move into acid before 3.1.0
6791 // trigger settings did not exist before 3.1.0; set to default "any"
6792 if (level->game_version < VERSION_IDENT(3,1,0,0))
6794 // correct "can move into acid" settings (all zero in old levels)
6796 level->can_move_into_acid_bits = 0; // nothing can move into acid
6797 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6799 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6800 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6801 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6802 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6804 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6805 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6807 // correct trigger settings (stored as zero == "none" in old levels)
6809 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6811 int element = EL_CUSTOM_START + i;
6812 struct ElementInfo *ei = &element_info[element];
6814 for (j = 0; j < ei->num_change_pages; j++)
6816 struct ElementChangeInfo *change = &ei->change_page[j];
6818 change->trigger_player = CH_PLAYER_ANY;
6819 change->trigger_page = CH_PAGE_ANY;
6824 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6826 int element = EL_CUSTOM_256;
6827 struct ElementInfo *ei = &element_info[element];
6828 struct ElementChangeInfo *change = &ei->change_page[0];
6830 /* This is needed to fix a problem that was caused by a bugfix in function
6831 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6832 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6833 not replace walkable elements, but instead just placed the player on it,
6834 without placing the Sokoban field under the player). Unfortunately, this
6835 breaks "Snake Bite" style levels when the snake is halfway through a door
6836 that just closes (the snake head is still alive and can be moved in this
6837 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6838 player (without Sokoban element) which then gets killed as designed). */
6840 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6841 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6842 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6843 change->target_element = EL_PLAYER_1;
6846 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6847 if (level->game_version < VERSION_IDENT(3,2,5,0))
6849 /* This is needed to fix a problem that was caused by a bugfix in function
6850 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6851 corrects the behaviour when a custom element changes to another custom
6852 element with a higher element number that has change actions defined.
6853 Normally, only one change per frame is allowed for custom elements.
6854 Therefore, it is checked if a custom element already changed in the
6855 current frame; if it did, subsequent changes are suppressed.
6856 Unfortunately, this is only checked for element changes, but not for
6857 change actions, which are still executed. As the function above loops
6858 through all custom elements from lower to higher, an element change
6859 resulting in a lower CE number won't be checked again, while a target
6860 element with a higher number will also be checked, and potential change
6861 actions will get executed for this CE, too (which is wrong), while
6862 further changes are ignored (which is correct). As this bugfix breaks
6863 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6864 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6865 behaviour for existing levels and tapes that make use of this bug */
6867 level->use_action_after_change_bug = TRUE;
6870 // not centering level after relocating player was default only in 3.2.3
6871 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6872 level->shifted_relocation = TRUE;
6874 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6875 if (level->game_version < VERSION_IDENT(3,2,6,0))
6876 level->em_explodes_by_fire = TRUE;
6878 // levels were solved by the first player entering an exit up to 4.1.0.0
6879 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6880 level->solved_by_one_player = TRUE;
6882 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6883 if (level->game_version < VERSION_IDENT(4,1,1,1))
6884 level->use_life_bugs = TRUE;
6886 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6887 if (level->game_version < VERSION_IDENT(4,1,1,1))
6888 level->sb_objects_needed = FALSE;
6890 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6891 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6892 level->finish_dig_collect = FALSE;
6894 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6895 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6896 level->keep_walkable_ce = TRUE;
6899 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6901 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6904 // check if this level is (not) a Sokoban level
6905 for (y = 0; y < level->fieldy; y++)
6906 for (x = 0; x < level->fieldx; x++)
6907 if (!IS_SB_ELEMENT(Tile[x][y]))
6908 is_sokoban_level = FALSE;
6910 if (is_sokoban_level)
6912 // set special level settings for Sokoban levels
6913 SetLevelSettings_SB(level);
6917 static void LoadLevel_InitSettings(struct LevelInfo *level)
6919 // adjust level settings for (non-native) Sokoban-style levels
6920 LoadLevel_InitSettings_SB(level);
6922 // rename levels with title "nameless level" or if renaming is forced
6923 if (leveldir_current->empty_level_name != NULL &&
6924 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6925 leveldir_current->force_level_name))
6926 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6927 leveldir_current->empty_level_name, level_nr);
6930 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6934 // map elements that have changed in newer versions
6935 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6936 level->game_version);
6937 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6938 for (x = 0; x < 3; x++)
6939 for (y = 0; y < 3; y++)
6940 level->yamyam_content[i].e[x][y] =
6941 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6942 level->game_version);
6946 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6950 // map custom element change events that have changed in newer versions
6951 // (these following values were accidentally changed in version 3.0.1)
6952 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6953 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6955 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6957 int element = EL_CUSTOM_START + i;
6959 // order of checking and copying events to be mapped is important
6960 // (do not change the start and end value -- they are constant)
6961 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6963 if (HAS_CHANGE_EVENT(element, j - 2))
6965 SET_CHANGE_EVENT(element, j - 2, FALSE);
6966 SET_CHANGE_EVENT(element, j, TRUE);
6970 // order of checking and copying events to be mapped is important
6971 // (do not change the start and end value -- they are constant)
6972 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6974 if (HAS_CHANGE_EVENT(element, j - 1))
6976 SET_CHANGE_EVENT(element, j - 1, FALSE);
6977 SET_CHANGE_EVENT(element, j, TRUE);
6983 // initialize "can_change" field for old levels with only one change page
6984 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6986 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6988 int element = EL_CUSTOM_START + i;
6990 if (CAN_CHANGE(element))
6991 element_info[element].change->can_change = TRUE;
6995 // correct custom element values (for old levels without these options)
6996 if (level->game_version < VERSION_IDENT(3,1,1,0))
6998 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7000 int element = EL_CUSTOM_START + i;
7001 struct ElementInfo *ei = &element_info[element];
7003 if (ei->access_direction == MV_NO_DIRECTION)
7004 ei->access_direction = MV_ALL_DIRECTIONS;
7008 // correct custom element values (fix invalid values for all versions)
7011 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7013 int element = EL_CUSTOM_START + i;
7014 struct ElementInfo *ei = &element_info[element];
7016 for (j = 0; j < ei->num_change_pages; j++)
7018 struct ElementChangeInfo *change = &ei->change_page[j];
7020 if (change->trigger_player == CH_PLAYER_NONE)
7021 change->trigger_player = CH_PLAYER_ANY;
7023 if (change->trigger_side == CH_SIDE_NONE)
7024 change->trigger_side = CH_SIDE_ANY;
7029 // initialize "can_explode" field for old levels which did not store this
7030 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7031 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7033 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7035 int element = EL_CUSTOM_START + i;
7037 if (EXPLODES_1X1_OLD(element))
7038 element_info[element].explosion_type = EXPLODES_1X1;
7040 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7041 EXPLODES_SMASHED(element) ||
7042 EXPLODES_IMPACT(element)));
7046 // correct previously hard-coded move delay values for maze runner style
7047 if (level->game_version < VERSION_IDENT(3,1,1,0))
7049 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7051 int element = EL_CUSTOM_START + i;
7053 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7055 // previously hard-coded and therefore ignored
7056 element_info[element].move_delay_fixed = 9;
7057 element_info[element].move_delay_random = 0;
7062 // set some other uninitialized values of custom elements in older levels
7063 if (level->game_version < VERSION_IDENT(3,1,0,0))
7065 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7067 int element = EL_CUSTOM_START + i;
7069 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7071 element_info[element].explosion_delay = 17;
7072 element_info[element].ignition_delay = 8;
7076 // set mouse click change events to work for left/middle/right mouse button
7077 if (level->game_version < VERSION_IDENT(4,2,3,0))
7079 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7081 int element = EL_CUSTOM_START + i;
7082 struct ElementInfo *ei = &element_info[element];
7084 for (j = 0; j < ei->num_change_pages; j++)
7086 struct ElementChangeInfo *change = &ei->change_page[j];
7088 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7089 change->has_event[CE_PRESSED_BY_MOUSE] ||
7090 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7091 change->has_event[CE_MOUSE_PRESSED_ON_X])
7092 change->trigger_side = CH_SIDE_ANY;
7098 static void LoadLevel_InitElements(struct LevelInfo *level)
7100 LoadLevel_InitStandardElements(level);
7102 if (level->file_has_custom_elements)
7103 LoadLevel_InitCustomElements(level);
7105 // initialize element properties for level editor etc.
7106 InitElementPropertiesEngine(level->game_version);
7107 InitElementPropertiesGfxElement();
7110 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7114 // map elements that have changed in newer versions
7115 for (y = 0; y < level->fieldy; y++)
7116 for (x = 0; x < level->fieldx; x++)
7117 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7118 level->game_version);
7120 // clear unused playfield data (nicer if level gets resized in editor)
7121 for (x = 0; x < MAX_LEV_FIELDX; x++)
7122 for (y = 0; y < MAX_LEV_FIELDY; y++)
7123 if (x >= level->fieldx || y >= level->fieldy)
7124 level->field[x][y] = EL_EMPTY;
7126 // copy elements to runtime playfield array
7127 for (x = 0; x < MAX_LEV_FIELDX; x++)
7128 for (y = 0; y < MAX_LEV_FIELDY; y++)
7129 Tile[x][y] = level->field[x][y];
7131 // initialize level size variables for faster access
7132 lev_fieldx = level->fieldx;
7133 lev_fieldy = level->fieldy;
7135 // determine border element for this level
7136 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7137 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7142 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7144 struct LevelFileInfo *level_file_info = &level->file_info;
7146 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7147 CopyNativeLevel_RND_to_Native(level);
7150 static void LoadLevelTemplate_LoadAndInit(void)
7152 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7154 LoadLevel_InitVersion(&level_template);
7155 LoadLevel_InitElements(&level_template);
7156 LoadLevel_InitSettings(&level_template);
7158 ActivateLevelTemplate();
7161 void LoadLevelTemplate(int nr)
7163 if (!fileExists(getGlobalLevelTemplateFilename()))
7165 Warn("no level template found for this level");
7170 setLevelFileInfo(&level_template.file_info, nr);
7172 LoadLevelTemplate_LoadAndInit();
7175 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7177 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7179 LoadLevelTemplate_LoadAndInit();
7182 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7184 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7186 if (level.use_custom_template)
7188 if (network_level != NULL)
7189 LoadNetworkLevelTemplate(network_level);
7191 LoadLevelTemplate(-1);
7194 LoadLevel_InitVersion(&level);
7195 LoadLevel_InitElements(&level);
7196 LoadLevel_InitPlayfield(&level);
7197 LoadLevel_InitSettings(&level);
7199 LoadLevel_InitNativeEngines(&level);
7202 void LoadLevel(int nr)
7204 SetLevelSetInfo(leveldir_current->identifier, nr);
7206 setLevelFileInfo(&level.file_info, nr);
7208 LoadLevel_LoadAndInit(NULL);
7211 void LoadLevelInfoOnly(int nr)
7213 setLevelFileInfo(&level.file_info, nr);
7215 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7218 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7220 SetLevelSetInfo(network_level->leveldir_identifier,
7221 network_level->file_info.nr);
7223 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7225 LoadLevel_LoadAndInit(network_level);
7228 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7232 chunk_size += putFileVersion(file, level->file_version);
7233 chunk_size += putFileVersion(file, level->game_version);
7238 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7242 chunk_size += putFile16BitBE(file, level->creation_date.year);
7243 chunk_size += putFile8Bit(file, level->creation_date.month);
7244 chunk_size += putFile8Bit(file, level->creation_date.day);
7249 #if ENABLE_HISTORIC_CHUNKS
7250 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7254 putFile8Bit(file, level->fieldx);
7255 putFile8Bit(file, level->fieldy);
7257 putFile16BitBE(file, level->time);
7258 putFile16BitBE(file, level->gems_needed);
7260 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7261 putFile8Bit(file, level->name[i]);
7263 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7264 putFile8Bit(file, level->score[i]);
7266 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7267 for (y = 0; y < 3; y++)
7268 for (x = 0; x < 3; x++)
7269 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7270 level->yamyam_content[i].e[x][y]));
7271 putFile8Bit(file, level->amoeba_speed);
7272 putFile8Bit(file, level->time_magic_wall);
7273 putFile8Bit(file, level->time_wheel);
7274 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7275 level->amoeba_content));
7276 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7277 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7278 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7279 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7281 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7283 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7284 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7285 putFile32BitBE(file, level->can_move_into_acid_bits);
7286 putFile8Bit(file, level->dont_collide_with_bits);
7288 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7289 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7291 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7292 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7293 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7295 putFile8Bit(file, level->game_engine_type);
7297 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7301 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7306 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7307 chunk_size += putFile8Bit(file, level->name[i]);
7312 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7317 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7318 chunk_size += putFile8Bit(file, level->author[i]);
7323 #if ENABLE_HISTORIC_CHUNKS
7324 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7329 for (y = 0; y < level->fieldy; y++)
7330 for (x = 0; x < level->fieldx; x++)
7331 if (level->encoding_16bit_field)
7332 chunk_size += putFile16BitBE(file, level->field[x][y]);
7334 chunk_size += putFile8Bit(file, level->field[x][y]);
7340 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7345 for (y = 0; y < level->fieldy; y++)
7346 for (x = 0; x < level->fieldx; x++)
7347 chunk_size += putFile16BitBE(file, level->field[x][y]);
7352 #if ENABLE_HISTORIC_CHUNKS
7353 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7357 putFile8Bit(file, EL_YAMYAM);
7358 putFile8Bit(file, level->num_yamyam_contents);
7359 putFile8Bit(file, 0);
7360 putFile8Bit(file, 0);
7362 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7363 for (y = 0; y < 3; y++)
7364 for (x = 0; x < 3; x++)
7365 if (level->encoding_16bit_field)
7366 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7368 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7372 #if ENABLE_HISTORIC_CHUNKS
7373 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7376 int num_contents, content_xsize, content_ysize;
7377 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7379 if (element == EL_YAMYAM)
7381 num_contents = level->num_yamyam_contents;
7385 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7386 for (y = 0; y < 3; y++)
7387 for (x = 0; x < 3; x++)
7388 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7390 else if (element == EL_BD_AMOEBA)
7396 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7397 for (y = 0; y < 3; y++)
7398 for (x = 0; x < 3; x++)
7399 content_array[i][x][y] = EL_EMPTY;
7400 content_array[0][0][0] = level->amoeba_content;
7404 // chunk header already written -- write empty chunk data
7405 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7407 Warn("cannot save content for element '%d'", element);
7412 putFile16BitBE(file, element);
7413 putFile8Bit(file, num_contents);
7414 putFile8Bit(file, content_xsize);
7415 putFile8Bit(file, content_ysize);
7417 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7419 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7420 for (y = 0; y < 3; y++)
7421 for (x = 0; x < 3; x++)
7422 putFile16BitBE(file, content_array[i][x][y]);
7426 #if ENABLE_HISTORIC_CHUNKS
7427 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7429 int envelope_nr = element - EL_ENVELOPE_1;
7430 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7434 chunk_size += putFile16BitBE(file, element);
7435 chunk_size += putFile16BitBE(file, envelope_len);
7436 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7437 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7439 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7440 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7442 for (i = 0; i < envelope_len; i++)
7443 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7449 #if ENABLE_HISTORIC_CHUNKS
7450 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7451 int num_changed_custom_elements)
7455 putFile16BitBE(file, num_changed_custom_elements);
7457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7459 int element = EL_CUSTOM_START + i;
7461 struct ElementInfo *ei = &element_info[element];
7463 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7465 if (check < num_changed_custom_elements)
7467 putFile16BitBE(file, element);
7468 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7475 if (check != num_changed_custom_elements) // should not happen
7476 Warn("inconsistent number of custom element properties");
7480 #if ENABLE_HISTORIC_CHUNKS
7481 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7482 int num_changed_custom_elements)
7486 putFile16BitBE(file, num_changed_custom_elements);
7488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7490 int element = EL_CUSTOM_START + i;
7492 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7494 if (check < num_changed_custom_elements)
7496 putFile16BitBE(file, element);
7497 putFile16BitBE(file, element_info[element].change->target_element);
7504 if (check != num_changed_custom_elements) // should not happen
7505 Warn("inconsistent number of custom target elements");
7509 #if ENABLE_HISTORIC_CHUNKS
7510 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7511 int num_changed_custom_elements)
7513 int i, j, x, y, check = 0;
7515 putFile16BitBE(file, num_changed_custom_elements);
7517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7519 int element = EL_CUSTOM_START + i;
7520 struct ElementInfo *ei = &element_info[element];
7522 if (ei->modified_settings)
7524 if (check < num_changed_custom_elements)
7526 putFile16BitBE(file, element);
7528 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7529 putFile8Bit(file, ei->description[j]);
7531 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7533 // some free bytes for future properties and padding
7534 WriteUnusedBytesToFile(file, 7);
7536 putFile8Bit(file, ei->use_gfx_element);
7537 putFile16BitBE(file, ei->gfx_element_initial);
7539 putFile8Bit(file, ei->collect_score_initial);
7540 putFile8Bit(file, ei->collect_count_initial);
7542 putFile16BitBE(file, ei->push_delay_fixed);
7543 putFile16BitBE(file, ei->push_delay_random);
7544 putFile16BitBE(file, ei->move_delay_fixed);
7545 putFile16BitBE(file, ei->move_delay_random);
7547 putFile16BitBE(file, ei->move_pattern);
7548 putFile8Bit(file, ei->move_direction_initial);
7549 putFile8Bit(file, ei->move_stepsize);
7551 for (y = 0; y < 3; y++)
7552 for (x = 0; x < 3; x++)
7553 putFile16BitBE(file, ei->content.e[x][y]);
7555 putFile32BitBE(file, ei->change->events);
7557 putFile16BitBE(file, ei->change->target_element);
7559 putFile16BitBE(file, ei->change->delay_fixed);
7560 putFile16BitBE(file, ei->change->delay_random);
7561 putFile16BitBE(file, ei->change->delay_frames);
7563 putFile16BitBE(file, ei->change->initial_trigger_element);
7565 putFile8Bit(file, ei->change->explode);
7566 putFile8Bit(file, ei->change->use_target_content);
7567 putFile8Bit(file, ei->change->only_if_complete);
7568 putFile8Bit(file, ei->change->use_random_replace);
7570 putFile8Bit(file, ei->change->random_percentage);
7571 putFile8Bit(file, ei->change->replace_when);
7573 for (y = 0; y < 3; y++)
7574 for (x = 0; x < 3; x++)
7575 putFile16BitBE(file, ei->change->content.e[x][y]);
7577 putFile8Bit(file, ei->slippery_type);
7579 // some free bytes for future properties and padding
7580 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7587 if (check != num_changed_custom_elements) // should not happen
7588 Warn("inconsistent number of custom element properties");
7592 #if ENABLE_HISTORIC_CHUNKS
7593 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7595 struct ElementInfo *ei = &element_info[element];
7598 // ---------- custom element base property values (96 bytes) ----------------
7600 putFile16BitBE(file, element);
7602 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7603 putFile8Bit(file, ei->description[i]);
7605 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7607 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7609 putFile8Bit(file, ei->num_change_pages);
7611 putFile16BitBE(file, ei->ce_value_fixed_initial);
7612 putFile16BitBE(file, ei->ce_value_random_initial);
7613 putFile8Bit(file, ei->use_last_ce_value);
7615 putFile8Bit(file, ei->use_gfx_element);
7616 putFile16BitBE(file, ei->gfx_element_initial);
7618 putFile8Bit(file, ei->collect_score_initial);
7619 putFile8Bit(file, ei->collect_count_initial);
7621 putFile8Bit(file, ei->drop_delay_fixed);
7622 putFile8Bit(file, ei->push_delay_fixed);
7623 putFile8Bit(file, ei->drop_delay_random);
7624 putFile8Bit(file, ei->push_delay_random);
7625 putFile16BitBE(file, ei->move_delay_fixed);
7626 putFile16BitBE(file, ei->move_delay_random);
7628 // bits 0 - 15 of "move_pattern" ...
7629 putFile16BitBE(file, ei->move_pattern & 0xffff);
7630 putFile8Bit(file, ei->move_direction_initial);
7631 putFile8Bit(file, ei->move_stepsize);
7633 putFile8Bit(file, ei->slippery_type);
7635 for (y = 0; y < 3; y++)
7636 for (x = 0; x < 3; x++)
7637 putFile16BitBE(file, ei->content.e[x][y]);
7639 putFile16BitBE(file, ei->move_enter_element);
7640 putFile16BitBE(file, ei->move_leave_element);
7641 putFile8Bit(file, ei->move_leave_type);
7643 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7644 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7646 putFile8Bit(file, ei->access_direction);
7648 putFile8Bit(file, ei->explosion_delay);
7649 putFile8Bit(file, ei->ignition_delay);
7650 putFile8Bit(file, ei->explosion_type);
7652 // some free bytes for future custom property values and padding
7653 WriteUnusedBytesToFile(file, 1);
7655 // ---------- change page property values (48 bytes) ------------------------
7657 for (i = 0; i < ei->num_change_pages; i++)
7659 struct ElementChangeInfo *change = &ei->change_page[i];
7660 unsigned int event_bits;
7662 // bits 0 - 31 of "has_event[]" ...
7664 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7665 if (change->has_event[j])
7666 event_bits |= (1u << j);
7667 putFile32BitBE(file, event_bits);
7669 putFile16BitBE(file, change->target_element);
7671 putFile16BitBE(file, change->delay_fixed);
7672 putFile16BitBE(file, change->delay_random);
7673 putFile16BitBE(file, change->delay_frames);
7675 putFile16BitBE(file, change->initial_trigger_element);
7677 putFile8Bit(file, change->explode);
7678 putFile8Bit(file, change->use_target_content);
7679 putFile8Bit(file, change->only_if_complete);
7680 putFile8Bit(file, change->use_random_replace);
7682 putFile8Bit(file, change->random_percentage);
7683 putFile8Bit(file, change->replace_when);
7685 for (y = 0; y < 3; y++)
7686 for (x = 0; x < 3; x++)
7687 putFile16BitBE(file, change->target_content.e[x][y]);
7689 putFile8Bit(file, change->can_change);
7691 putFile8Bit(file, change->trigger_side);
7693 putFile8Bit(file, change->trigger_player);
7694 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7695 log_2(change->trigger_page)));
7697 putFile8Bit(file, change->has_action);
7698 putFile8Bit(file, change->action_type);
7699 putFile8Bit(file, change->action_mode);
7700 putFile16BitBE(file, change->action_arg);
7702 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7704 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7705 if (change->has_event[j])
7706 event_bits |= (1u << (j - 32));
7707 putFile8Bit(file, event_bits);
7712 #if ENABLE_HISTORIC_CHUNKS
7713 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7715 struct ElementInfo *ei = &element_info[element];
7716 struct ElementGroupInfo *group = ei->group;
7719 putFile16BitBE(file, element);
7721 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7722 putFile8Bit(file, ei->description[i]);
7724 putFile8Bit(file, group->num_elements);
7726 putFile8Bit(file, ei->use_gfx_element);
7727 putFile16BitBE(file, ei->gfx_element_initial);
7729 putFile8Bit(file, group->choice_mode);
7731 // some free bytes for future values and padding
7732 WriteUnusedBytesToFile(file, 3);
7734 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7735 putFile16BitBE(file, group->element[i]);
7739 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7740 boolean write_element)
7742 int save_type = entry->save_type;
7743 int data_type = entry->data_type;
7744 int conf_type = entry->conf_type;
7745 int byte_mask = conf_type & CONF_MASK_BYTES;
7746 int element = entry->element;
7747 int default_value = entry->default_value;
7749 boolean modified = FALSE;
7751 if (byte_mask != CONF_MASK_MULTI_BYTES)
7753 void *value_ptr = entry->value;
7754 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7757 // check if any settings have been modified before saving them
7758 if (value != default_value)
7761 // do not save if explicitly told or if unmodified default settings
7762 if ((save_type == SAVE_CONF_NEVER) ||
7763 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7767 num_bytes += putFile16BitBE(file, element);
7769 num_bytes += putFile8Bit(file, conf_type);
7770 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7771 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7772 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7775 else if (data_type == TYPE_STRING)
7777 char *default_string = entry->default_string;
7778 char *string = (char *)(entry->value);
7779 int string_length = strlen(string);
7782 // check if any settings have been modified before saving them
7783 if (!strEqual(string, default_string))
7786 // do not save if explicitly told or if unmodified default settings
7787 if ((save_type == SAVE_CONF_NEVER) ||
7788 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7792 num_bytes += putFile16BitBE(file, element);
7794 num_bytes += putFile8Bit(file, conf_type);
7795 num_bytes += putFile16BitBE(file, string_length);
7797 for (i = 0; i < string_length; i++)
7798 num_bytes += putFile8Bit(file, string[i]);
7800 else if (data_type == TYPE_ELEMENT_LIST)
7802 int *element_array = (int *)(entry->value);
7803 int num_elements = *(int *)(entry->num_entities);
7806 // check if any settings have been modified before saving them
7807 for (i = 0; i < num_elements; i++)
7808 if (element_array[i] != default_value)
7811 // do not save if explicitly told or if unmodified default settings
7812 if ((save_type == SAVE_CONF_NEVER) ||
7813 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7817 num_bytes += putFile16BitBE(file, element);
7819 num_bytes += putFile8Bit(file, conf_type);
7820 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7822 for (i = 0; i < num_elements; i++)
7823 num_bytes += putFile16BitBE(file, element_array[i]);
7825 else if (data_type == TYPE_CONTENT_LIST)
7827 struct Content *content = (struct Content *)(entry->value);
7828 int num_contents = *(int *)(entry->num_entities);
7831 // check if any settings have been modified before saving them
7832 for (i = 0; i < num_contents; i++)
7833 for (y = 0; y < 3; y++)
7834 for (x = 0; x < 3; x++)
7835 if (content[i].e[x][y] != default_value)
7838 // do not save if explicitly told or if unmodified default settings
7839 if ((save_type == SAVE_CONF_NEVER) ||
7840 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7844 num_bytes += putFile16BitBE(file, element);
7846 num_bytes += putFile8Bit(file, conf_type);
7847 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7849 for (i = 0; i < num_contents; i++)
7850 for (y = 0; y < 3; y++)
7851 for (x = 0; x < 3; x++)
7852 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7858 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7863 li = *level; // copy level data into temporary buffer
7865 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7866 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7871 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7876 li = *level; // copy level data into temporary buffer
7878 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7879 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7884 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7886 int envelope_nr = element - EL_ENVELOPE_1;
7890 chunk_size += putFile16BitBE(file, element);
7892 // copy envelope data into temporary buffer
7893 xx_envelope = level->envelope[envelope_nr];
7895 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7896 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7901 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7903 struct ElementInfo *ei = &element_info[element];
7907 chunk_size += putFile16BitBE(file, element);
7909 xx_ei = *ei; // copy element data into temporary buffer
7911 // set default description string for this specific element
7912 strcpy(xx_default_description, getDefaultElementDescription(ei));
7914 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7915 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7917 for (i = 0; i < ei->num_change_pages; i++)
7919 struct ElementChangeInfo *change = &ei->change_page[i];
7921 xx_current_change_page = i;
7923 xx_change = *change; // copy change data into temporary buffer
7926 setEventBitsFromEventFlags(change);
7928 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7929 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7936 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7938 struct ElementInfo *ei = &element_info[element];
7939 struct ElementGroupInfo *group = ei->group;
7943 chunk_size += putFile16BitBE(file, element);
7945 xx_ei = *ei; // copy element data into temporary buffer
7946 xx_group = *group; // copy group data into temporary buffer
7948 // set default description string for this specific element
7949 strcpy(xx_default_description, getDefaultElementDescription(ei));
7951 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7952 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7957 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7959 struct ElementInfo *ei = &element_info[element];
7963 chunk_size += putFile16BitBE(file, element);
7965 xx_ei = *ei; // copy element data into temporary buffer
7967 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7968 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7973 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7974 boolean save_as_template)
7980 if (!(file = fopen(filename, MODE_WRITE)))
7982 Warn("cannot save level file '%s'", filename);
7987 level->file_version = FILE_VERSION_ACTUAL;
7988 level->game_version = GAME_VERSION_ACTUAL;
7990 level->creation_date = getCurrentDate();
7992 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7993 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7995 chunk_size = SaveLevel_VERS(NULL, level);
7996 putFileChunkBE(file, "VERS", chunk_size);
7997 SaveLevel_VERS(file, level);
7999 chunk_size = SaveLevel_DATE(NULL, level);
8000 putFileChunkBE(file, "DATE", chunk_size);
8001 SaveLevel_DATE(file, level);
8003 chunk_size = SaveLevel_NAME(NULL, level);
8004 putFileChunkBE(file, "NAME", chunk_size);
8005 SaveLevel_NAME(file, level);
8007 chunk_size = SaveLevel_AUTH(NULL, level);
8008 putFileChunkBE(file, "AUTH", chunk_size);
8009 SaveLevel_AUTH(file, level);
8011 chunk_size = SaveLevel_INFO(NULL, level);
8012 putFileChunkBE(file, "INFO", chunk_size);
8013 SaveLevel_INFO(file, level);
8015 chunk_size = SaveLevel_BODY(NULL, level);
8016 putFileChunkBE(file, "BODY", chunk_size);
8017 SaveLevel_BODY(file, level);
8019 chunk_size = SaveLevel_ELEM(NULL, level);
8020 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8022 putFileChunkBE(file, "ELEM", chunk_size);
8023 SaveLevel_ELEM(file, level);
8026 for (i = 0; i < NUM_ENVELOPES; i++)
8028 int element = EL_ENVELOPE_1 + i;
8030 chunk_size = SaveLevel_NOTE(NULL, level, element);
8031 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8033 putFileChunkBE(file, "NOTE", chunk_size);
8034 SaveLevel_NOTE(file, level, element);
8038 // if not using template level, check for non-default custom/group elements
8039 if (!level->use_custom_template || save_as_template)
8041 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8043 int element = EL_CUSTOM_START + i;
8045 chunk_size = SaveLevel_CUSX(NULL, level, element);
8046 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8048 putFileChunkBE(file, "CUSX", chunk_size);
8049 SaveLevel_CUSX(file, level, element);
8053 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8055 int element = EL_GROUP_START + i;
8057 chunk_size = SaveLevel_GRPX(NULL, level, element);
8058 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8060 putFileChunkBE(file, "GRPX", chunk_size);
8061 SaveLevel_GRPX(file, level, element);
8065 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8067 int element = GET_EMPTY_ELEMENT(i);
8069 chunk_size = SaveLevel_EMPX(NULL, level, element);
8070 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8072 putFileChunkBE(file, "EMPX", chunk_size);
8073 SaveLevel_EMPX(file, level, element);
8080 SetFilePermissions(filename, PERMS_PRIVATE);
8083 void SaveLevel(int nr)
8085 char *filename = getDefaultLevelFilename(nr);
8087 SaveLevelFromFilename(&level, filename, FALSE);
8090 void SaveLevelTemplate(void)
8092 char *filename = getLocalLevelTemplateFilename();
8094 SaveLevelFromFilename(&level, filename, TRUE);
8097 boolean SaveLevelChecked(int nr)
8099 char *filename = getDefaultLevelFilename(nr);
8100 boolean new_level = !fileExists(filename);
8101 boolean level_saved = FALSE;
8103 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8108 Request("Level saved!", REQ_CONFIRM);
8116 void DumpLevel(struct LevelInfo *level)
8118 if (level->no_level_file || level->no_valid_file)
8120 Warn("cannot dump -- no valid level file found");
8126 Print("Level xxx (file version %08d, game version %08d)\n",
8127 level->file_version, level->game_version);
8130 Print("Level author: '%s'\n", level->author);
8131 Print("Level title: '%s'\n", level->name);
8133 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8135 Print("Level time: %d seconds\n", level->time);
8136 Print("Gems needed: %d\n", level->gems_needed);
8138 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8139 Print("Time for wheel: %d seconds\n", level->time_wheel);
8140 Print("Time for light: %d seconds\n", level->time_light);
8141 Print("Time for timegate: %d seconds\n", level->time_timegate);
8143 Print("Amoeba speed: %d\n", level->amoeba_speed);
8146 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8147 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8148 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8149 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8150 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8151 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8157 for (i = 0; i < NUM_ENVELOPES; i++)
8159 char *text = level->envelope[i].text;
8160 int text_len = strlen(text);
8161 boolean has_text = FALSE;
8163 for (j = 0; j < text_len; j++)
8164 if (text[j] != ' ' && text[j] != '\n')
8170 Print("Envelope %d:\n'%s'\n", i + 1, text);
8178 void DumpLevels(void)
8180 static LevelDirTree *dumplevel_leveldir = NULL;
8182 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8183 global.dumplevel_leveldir);
8185 if (dumplevel_leveldir == NULL)
8186 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8188 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8189 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8190 Fail("no such level number: %d", global.dumplevel_level_nr);
8192 leveldir_current = dumplevel_leveldir;
8194 LoadLevel(global.dumplevel_level_nr);
8201 // ============================================================================
8202 // tape file functions
8203 // ============================================================================
8205 static void setTapeInfoToDefaults(void)
8209 // always start with reliable default values (empty tape)
8212 // default values (also for pre-1.2 tapes) with only the first player
8213 tape.player_participates[0] = TRUE;
8214 for (i = 1; i < MAX_PLAYERS; i++)
8215 tape.player_participates[i] = FALSE;
8217 // at least one (default: the first) player participates in every tape
8218 tape.num_participating_players = 1;
8220 tape.property_bits = TAPE_PROPERTY_NONE;
8222 tape.level_nr = level_nr;
8224 tape.changed = FALSE;
8225 tape.solved = FALSE;
8227 tape.recording = FALSE;
8228 tape.playing = FALSE;
8229 tape.pausing = FALSE;
8231 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8232 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8234 tape.no_info_chunk = TRUE;
8235 tape.no_valid_file = FALSE;
8238 static int getTapePosSize(struct TapeInfo *tape)
8240 int tape_pos_size = 0;
8242 if (tape->use_key_actions)
8243 tape_pos_size += tape->num_participating_players;
8245 if (tape->use_mouse_actions)
8246 tape_pos_size += 3; // x and y position and mouse button mask
8248 tape_pos_size += 1; // tape action delay value
8250 return tape_pos_size;
8253 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8255 tape->use_key_actions = FALSE;
8256 tape->use_mouse_actions = FALSE;
8258 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8259 tape->use_key_actions = TRUE;
8261 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8262 tape->use_mouse_actions = TRUE;
8265 static int getTapeActionValue(struct TapeInfo *tape)
8267 return (tape->use_key_actions &&
8268 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8269 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8270 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8271 TAPE_ACTIONS_DEFAULT);
8274 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8276 tape->file_version = getFileVersion(file);
8277 tape->game_version = getFileVersion(file);
8282 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8286 tape->random_seed = getFile32BitBE(file);
8287 tape->date = getFile32BitBE(file);
8288 tape->length = getFile32BitBE(file);
8290 // read header fields that are new since version 1.2
8291 if (tape->file_version >= FILE_VERSION_1_2)
8293 byte store_participating_players = getFile8Bit(file);
8296 // since version 1.2, tapes store which players participate in the tape
8297 tape->num_participating_players = 0;
8298 for (i = 0; i < MAX_PLAYERS; i++)
8300 tape->player_participates[i] = FALSE;
8302 if (store_participating_players & (1 << i))
8304 tape->player_participates[i] = TRUE;
8305 tape->num_participating_players++;
8309 setTapeActionFlags(tape, getFile8Bit(file));
8311 tape->property_bits = getFile8Bit(file);
8312 tape->solved = getFile8Bit(file);
8314 engine_version = getFileVersion(file);
8315 if (engine_version > 0)
8316 tape->engine_version = engine_version;
8318 tape->engine_version = tape->game_version;
8324 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8326 tape->scr_fieldx = getFile8Bit(file);
8327 tape->scr_fieldy = getFile8Bit(file);
8332 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8334 char *level_identifier = NULL;
8335 int level_identifier_size;
8338 tape->no_info_chunk = FALSE;
8340 level_identifier_size = getFile16BitBE(file);
8342 level_identifier = checked_malloc(level_identifier_size);
8344 for (i = 0; i < level_identifier_size; i++)
8345 level_identifier[i] = getFile8Bit(file);
8347 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8348 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8350 checked_free(level_identifier);
8352 tape->level_nr = getFile16BitBE(file);
8354 chunk_size = 2 + level_identifier_size + 2;
8359 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8362 int tape_pos_size = getTapePosSize(tape);
8363 int chunk_size_expected = tape_pos_size * tape->length;
8365 if (chunk_size_expected != chunk_size)
8367 ReadUnusedBytesFromFile(file, chunk_size);
8368 return chunk_size_expected;
8371 for (i = 0; i < tape->length; i++)
8373 if (i >= MAX_TAPE_LEN)
8375 Warn("tape truncated -- size exceeds maximum tape size %d",
8378 // tape too large; read and ignore remaining tape data from this chunk
8379 for (;i < tape->length; i++)
8380 ReadUnusedBytesFromFile(file, tape_pos_size);
8385 if (tape->use_key_actions)
8387 for (j = 0; j < MAX_PLAYERS; j++)
8389 tape->pos[i].action[j] = MV_NONE;
8391 if (tape->player_participates[j])
8392 tape->pos[i].action[j] = getFile8Bit(file);
8396 if (tape->use_mouse_actions)
8398 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8399 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8400 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8403 tape->pos[i].delay = getFile8Bit(file);
8405 if (tape->file_version == FILE_VERSION_1_0)
8407 // eliminate possible diagonal moves in old tapes
8408 // this is only for backward compatibility
8410 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8411 byte action = tape->pos[i].action[0];
8412 int k, num_moves = 0;
8414 for (k = 0; k < 4; k++)
8416 if (action & joy_dir[k])
8418 tape->pos[i + num_moves].action[0] = joy_dir[k];
8420 tape->pos[i + num_moves].delay = 0;
8429 tape->length += num_moves;
8432 else if (tape->file_version < FILE_VERSION_2_0)
8434 // convert pre-2.0 tapes to new tape format
8436 if (tape->pos[i].delay > 1)
8439 tape->pos[i + 1] = tape->pos[i];
8440 tape->pos[i + 1].delay = 1;
8443 for (j = 0; j < MAX_PLAYERS; j++)
8444 tape->pos[i].action[j] = MV_NONE;
8445 tape->pos[i].delay--;
8452 if (checkEndOfFile(file))
8456 if (i != tape->length)
8457 chunk_size = tape_pos_size * i;
8462 static void LoadTape_SokobanSolution(char *filename)
8465 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8467 if (!(file = openFile(filename, MODE_READ)))
8469 tape.no_valid_file = TRUE;
8474 while (!checkEndOfFile(file))
8476 unsigned char c = getByteFromFile(file);
8478 if (checkEndOfFile(file))
8485 tape.pos[tape.length].action[0] = MV_UP;
8486 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8492 tape.pos[tape.length].action[0] = MV_DOWN;
8493 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8499 tape.pos[tape.length].action[0] = MV_LEFT;
8500 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8506 tape.pos[tape.length].action[0] = MV_RIGHT;
8507 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8515 // ignore white-space characters
8519 tape.no_valid_file = TRUE;
8521 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8529 if (tape.no_valid_file)
8532 tape.length_frames = GetTapeLengthFrames();
8533 tape.length_seconds = GetTapeLengthSeconds();
8536 void LoadTapeFromFilename(char *filename)
8538 char cookie[MAX_LINE_LEN];
8539 char chunk_name[CHUNK_ID_LEN + 1];
8543 // always start with reliable default values
8544 setTapeInfoToDefaults();
8546 if (strSuffix(filename, ".sln"))
8548 LoadTape_SokobanSolution(filename);
8553 if (!(file = openFile(filename, MODE_READ)))
8555 tape.no_valid_file = TRUE;
8560 getFileChunkBE(file, chunk_name, NULL);
8561 if (strEqual(chunk_name, "RND1"))
8563 getFile32BitBE(file); // not used
8565 getFileChunkBE(file, chunk_name, NULL);
8566 if (!strEqual(chunk_name, "TAPE"))
8568 tape.no_valid_file = TRUE;
8570 Warn("unknown format of tape file '%s'", filename);
8577 else // check for pre-2.0 file format with cookie string
8579 strcpy(cookie, chunk_name);
8580 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8582 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8583 cookie[strlen(cookie) - 1] = '\0';
8585 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8587 tape.no_valid_file = TRUE;
8589 Warn("unknown format of tape file '%s'", filename);
8596 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8598 tape.no_valid_file = TRUE;
8600 Warn("unsupported version of tape file '%s'", filename);
8607 // pre-2.0 tape files have no game version, so use file version here
8608 tape.game_version = tape.file_version;
8611 if (tape.file_version < FILE_VERSION_1_2)
8613 // tape files from versions before 1.2.0 without chunk structure
8614 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8615 LoadTape_BODY(file, 2 * tape.length, &tape);
8623 int (*loader)(File *, int, struct TapeInfo *);
8627 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8628 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8629 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8630 { "INFO", -1, LoadTape_INFO },
8631 { "BODY", -1, LoadTape_BODY },
8635 while (getFileChunkBE(file, chunk_name, &chunk_size))
8639 while (chunk_info[i].name != NULL &&
8640 !strEqual(chunk_name, chunk_info[i].name))
8643 if (chunk_info[i].name == NULL)
8645 Warn("unknown chunk '%s' in tape file '%s'",
8646 chunk_name, filename);
8648 ReadUnusedBytesFromFile(file, chunk_size);
8650 else if (chunk_info[i].size != -1 &&
8651 chunk_info[i].size != chunk_size)
8653 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8654 chunk_size, chunk_name, filename);
8656 ReadUnusedBytesFromFile(file, chunk_size);
8660 // call function to load this tape chunk
8661 int chunk_size_expected =
8662 (chunk_info[i].loader)(file, chunk_size, &tape);
8664 // the size of some chunks cannot be checked before reading other
8665 // chunks first (like "HEAD" and "BODY") that contain some header
8666 // information, so check them here
8667 if (chunk_size_expected != chunk_size)
8669 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8670 chunk_size, chunk_name, filename);
8678 tape.length_frames = GetTapeLengthFrames();
8679 tape.length_seconds = GetTapeLengthSeconds();
8682 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8684 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8686 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8687 tape.engine_version);
8691 void LoadTape(int nr)
8693 char *filename = getTapeFilename(nr);
8695 LoadTapeFromFilename(filename);
8698 void LoadSolutionTape(int nr)
8700 char *filename = getSolutionTapeFilename(nr);
8702 LoadTapeFromFilename(filename);
8704 if (TAPE_IS_EMPTY(tape))
8706 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8707 level.native_bd_level->replay != NULL)
8708 CopyNativeTape_BD_to_RND(&level);
8709 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8710 level.native_sp_level->demo.is_available)
8711 CopyNativeTape_SP_to_RND(&level);
8715 void LoadScoreTape(char *score_tape_basename, int nr)
8717 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8719 LoadTapeFromFilename(filename);
8722 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8724 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8726 LoadTapeFromFilename(filename);
8729 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8731 // chunk required for team mode tapes with non-default screen size
8732 return (tape->num_participating_players > 1 &&
8733 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8734 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8737 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8739 putFileVersion(file, tape->file_version);
8740 putFileVersion(file, tape->game_version);
8743 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8746 byte store_participating_players = 0;
8748 // set bits for participating players for compact storage
8749 for (i = 0; i < MAX_PLAYERS; i++)
8750 if (tape->player_participates[i])
8751 store_participating_players |= (1 << i);
8753 putFile32BitBE(file, tape->random_seed);
8754 putFile32BitBE(file, tape->date);
8755 putFile32BitBE(file, tape->length);
8757 putFile8Bit(file, store_participating_players);
8759 putFile8Bit(file, getTapeActionValue(tape));
8761 putFile8Bit(file, tape->property_bits);
8762 putFile8Bit(file, tape->solved);
8764 putFileVersion(file, tape->engine_version);
8767 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8769 putFile8Bit(file, tape->scr_fieldx);
8770 putFile8Bit(file, tape->scr_fieldy);
8773 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8775 int level_identifier_size = strlen(tape->level_identifier) + 1;
8778 putFile16BitBE(file, level_identifier_size);
8780 for (i = 0; i < level_identifier_size; i++)
8781 putFile8Bit(file, tape->level_identifier[i]);
8783 putFile16BitBE(file, tape->level_nr);
8786 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8790 for (i = 0; i < tape->length; i++)
8792 if (tape->use_key_actions)
8794 for (j = 0; j < MAX_PLAYERS; j++)
8795 if (tape->player_participates[j])
8796 putFile8Bit(file, tape->pos[i].action[j]);
8799 if (tape->use_mouse_actions)
8801 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8802 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8803 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8806 putFile8Bit(file, tape->pos[i].delay);
8810 void SaveTapeToFilename(char *filename)
8814 int info_chunk_size;
8815 int body_chunk_size;
8817 if (!(file = fopen(filename, MODE_WRITE)))
8819 Warn("cannot save level recording file '%s'", filename);
8824 tape_pos_size = getTapePosSize(&tape);
8826 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8827 body_chunk_size = tape_pos_size * tape.length;
8829 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8830 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8832 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8833 SaveTape_VERS(file, &tape);
8835 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8836 SaveTape_HEAD(file, &tape);
8838 if (checkSaveTape_SCRN(&tape))
8840 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8841 SaveTape_SCRN(file, &tape);
8844 putFileChunkBE(file, "INFO", info_chunk_size);
8845 SaveTape_INFO(file, &tape);
8847 putFileChunkBE(file, "BODY", body_chunk_size);
8848 SaveTape_BODY(file, &tape);
8852 SetFilePermissions(filename, PERMS_PRIVATE);
8855 static void SaveTapeExt(char *filename)
8859 tape.file_version = FILE_VERSION_ACTUAL;
8860 tape.game_version = GAME_VERSION_ACTUAL;
8862 tape.num_participating_players = 0;
8864 // count number of participating players
8865 for (i = 0; i < MAX_PLAYERS; i++)
8866 if (tape.player_participates[i])
8867 tape.num_participating_players++;
8869 SaveTapeToFilename(filename);
8871 tape.changed = FALSE;
8874 void SaveTape(int nr)
8876 char *filename = getTapeFilename(nr);
8878 InitTapeDirectory(leveldir_current->subdir);
8880 SaveTapeExt(filename);
8883 void SaveScoreTape(int nr)
8885 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8887 // used instead of "leveldir_current->subdir" (for network games)
8888 InitScoreTapeDirectory(levelset.identifier, nr);
8890 SaveTapeExt(filename);
8893 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8894 unsigned int req_state_added)
8896 char *filename = getTapeFilename(nr);
8897 boolean new_tape = !fileExists(filename);
8898 boolean tape_saved = FALSE;
8900 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8905 Request(msg_saved, REQ_CONFIRM | req_state_added);
8913 boolean SaveTapeChecked(int nr)
8915 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8918 boolean SaveTapeChecked_LevelSolved(int nr)
8920 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8921 "Level solved! Tape saved!", REQ_STAY_OPEN);
8924 void DumpTape(struct TapeInfo *tape)
8926 int tape_frame_counter;
8929 if (tape->no_valid_file)
8931 Warn("cannot dump -- no valid tape file found");
8938 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8939 tape->level_nr, tape->file_version, tape->game_version);
8940 Print(" (effective engine version %08d)\n",
8941 tape->engine_version);
8942 Print("Level series identifier: '%s'\n", tape->level_identifier);
8944 Print("Solution tape: %s\n",
8945 tape->solved ? "yes" :
8946 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8948 Print("Special tape properties: ");
8949 if (tape->property_bits == TAPE_PROPERTY_NONE)
8951 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8952 Print("[em_random_bug]");
8953 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8954 Print("[game_speed]");
8955 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8957 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8958 Print("[single_step]");
8959 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8960 Print("[snapshot]");
8961 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8962 Print("[replayed]");
8963 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8964 Print("[tas_keys]");
8965 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8966 Print("[small_graphics]");
8969 int year2 = tape->date / 10000;
8970 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8971 int month_index_raw = (tape->date / 100) % 100;
8972 int month_index = month_index_raw % 12; // prevent invalid index
8973 int month = month_index + 1;
8974 int day = tape->date % 100;
8976 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8980 tape_frame_counter = 0;
8982 for (i = 0; i < tape->length; i++)
8984 if (i >= MAX_TAPE_LEN)
8989 for (j = 0; j < MAX_PLAYERS; j++)
8991 if (tape->player_participates[j])
8993 int action = tape->pos[i].action[j];
8995 Print("%d:%02x ", j, action);
8996 Print("[%c%c%c%c|%c%c] - ",
8997 (action & JOY_LEFT ? '<' : ' '),
8998 (action & JOY_RIGHT ? '>' : ' '),
8999 (action & JOY_UP ? '^' : ' '),
9000 (action & JOY_DOWN ? 'v' : ' '),
9001 (action & JOY_BUTTON_1 ? '1' : ' '),
9002 (action & JOY_BUTTON_2 ? '2' : ' '));
9006 Print("(%03d) ", tape->pos[i].delay);
9007 Print("[%05d]\n", tape_frame_counter);
9009 tape_frame_counter += tape->pos[i].delay;
9015 void DumpTapes(void)
9017 static LevelDirTree *dumptape_leveldir = NULL;
9019 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9020 global.dumptape_leveldir);
9022 if (dumptape_leveldir == NULL)
9023 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9025 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9026 global.dumptape_level_nr > dumptape_leveldir->last_level)
9027 Fail("no such level number: %d", global.dumptape_level_nr);
9029 leveldir_current = dumptape_leveldir;
9031 if (options.mytapes)
9032 LoadTape(global.dumptape_level_nr);
9034 LoadSolutionTape(global.dumptape_level_nr);
9042 // ============================================================================
9043 // score file functions
9044 // ============================================================================
9046 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9050 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9052 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9053 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9054 scores->entry[i].score = 0;
9055 scores->entry[i].time = 0;
9057 scores->entry[i].id = -1;
9058 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9059 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9060 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9061 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9062 strcpy(scores->entry[i].country_code, "??");
9065 scores->num_entries = 0;
9066 scores->last_added = -1;
9067 scores->last_added_local = -1;
9069 scores->updated = FALSE;
9070 scores->uploaded = FALSE;
9071 scores->tape_downloaded = FALSE;
9072 scores->force_last_added = FALSE;
9074 // The following values are intentionally not reset here:
9078 // - continue_playing
9079 // - continue_on_return
9082 static void setScoreInfoToDefaults(void)
9084 setScoreInfoToDefaultsExt(&scores);
9087 static void setServerScoreInfoToDefaults(void)
9089 setScoreInfoToDefaultsExt(&server_scores);
9092 static void LoadScore_OLD(int nr)
9095 char *filename = getScoreFilename(nr);
9096 char cookie[MAX_LINE_LEN];
9097 char line[MAX_LINE_LEN];
9101 if (!(file = fopen(filename, MODE_READ)))
9104 // check file identifier
9105 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9107 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9108 cookie[strlen(cookie) - 1] = '\0';
9110 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9112 Warn("unknown format of score file '%s'", filename);
9119 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9121 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9122 Warn("fscanf() failed; %s", strerror(errno));
9124 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9127 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9128 line[strlen(line) - 1] = '\0';
9130 for (line_ptr = line; *line_ptr; line_ptr++)
9132 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9134 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9135 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9144 static void ConvertScore_OLD(void)
9146 // only convert score to time for levels that rate playing time over score
9147 if (!level.rate_time_over_score)
9150 // convert old score to playing time for score-less levels (like Supaplex)
9151 int time_final_max = 999;
9154 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9156 int score = scores.entry[i].score;
9158 if (score > 0 && score < time_final_max)
9159 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9163 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9165 scores->file_version = getFileVersion(file);
9166 scores->game_version = getFileVersion(file);
9171 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9173 char *level_identifier = NULL;
9174 int level_identifier_size;
9177 level_identifier_size = getFile16BitBE(file);
9179 level_identifier = checked_malloc(level_identifier_size);
9181 for (i = 0; i < level_identifier_size; i++)
9182 level_identifier[i] = getFile8Bit(file);
9184 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9185 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9187 checked_free(level_identifier);
9189 scores->level_nr = getFile16BitBE(file);
9190 scores->num_entries = getFile16BitBE(file);
9192 chunk_size = 2 + level_identifier_size + 2 + 2;
9197 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9201 for (i = 0; i < scores->num_entries; i++)
9203 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9204 scores->entry[i].name[j] = getFile8Bit(file);
9206 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9209 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9214 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9218 for (i = 0; i < scores->num_entries; i++)
9219 scores->entry[i].score = getFile16BitBE(file);
9221 chunk_size = scores->num_entries * 2;
9226 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9230 for (i = 0; i < scores->num_entries; i++)
9231 scores->entry[i].score = getFile32BitBE(file);
9233 chunk_size = scores->num_entries * 4;
9238 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9242 for (i = 0; i < scores->num_entries; i++)
9243 scores->entry[i].time = getFile32BitBE(file);
9245 chunk_size = scores->num_entries * 4;
9250 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9254 for (i = 0; i < scores->num_entries; i++)
9256 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9257 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9259 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9262 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9267 void LoadScore(int nr)
9269 char *filename = getScoreFilename(nr);
9270 char cookie[MAX_LINE_LEN];
9271 char chunk_name[CHUNK_ID_LEN + 1];
9273 boolean old_score_file_format = FALSE;
9276 // always start with reliable default values
9277 setScoreInfoToDefaults();
9279 if (!(file = openFile(filename, MODE_READ)))
9282 getFileChunkBE(file, chunk_name, NULL);
9283 if (strEqual(chunk_name, "RND1"))
9285 getFile32BitBE(file); // not used
9287 getFileChunkBE(file, chunk_name, NULL);
9288 if (!strEqual(chunk_name, "SCOR"))
9290 Warn("unknown format of score file '%s'", filename);
9297 else // check for old file format with cookie string
9299 strcpy(cookie, chunk_name);
9300 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9302 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9303 cookie[strlen(cookie) - 1] = '\0';
9305 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9307 Warn("unknown format of score file '%s'", filename);
9314 old_score_file_format = TRUE;
9317 if (old_score_file_format)
9319 // score files from versions before 4.2.4.0 without chunk structure
9322 // convert score to time, if possible (mainly for Supaplex levels)
9331 int (*loader)(File *, int, struct ScoreInfo *);
9335 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9336 { "INFO", -1, LoadScore_INFO },
9337 { "NAME", -1, LoadScore_NAME },
9338 { "SCOR", -1, LoadScore_SCOR },
9339 { "SC4R", -1, LoadScore_SC4R },
9340 { "TIME", -1, LoadScore_TIME },
9341 { "TAPE", -1, LoadScore_TAPE },
9346 while (getFileChunkBE(file, chunk_name, &chunk_size))
9350 while (chunk_info[i].name != NULL &&
9351 !strEqual(chunk_name, chunk_info[i].name))
9354 if (chunk_info[i].name == NULL)
9356 Warn("unknown chunk '%s' in score file '%s'",
9357 chunk_name, filename);
9359 ReadUnusedBytesFromFile(file, chunk_size);
9361 else if (chunk_info[i].size != -1 &&
9362 chunk_info[i].size != chunk_size)
9364 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9365 chunk_size, chunk_name, filename);
9367 ReadUnusedBytesFromFile(file, chunk_size);
9371 // call function to load this score chunk
9372 int chunk_size_expected =
9373 (chunk_info[i].loader)(file, chunk_size, &scores);
9375 // the size of some chunks cannot be checked before reading other
9376 // chunks first (like "HEAD" and "BODY") that contain some header
9377 // information, so check them here
9378 if (chunk_size_expected != chunk_size)
9380 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9381 chunk_size, chunk_name, filename);
9390 #if ENABLE_HISTORIC_CHUNKS
9391 void SaveScore_OLD(int nr)
9394 char *filename = getScoreFilename(nr);
9397 // used instead of "leveldir_current->subdir" (for network games)
9398 InitScoreDirectory(levelset.identifier);
9400 if (!(file = fopen(filename, MODE_WRITE)))
9402 Warn("cannot save score for level %d", nr);
9407 fprintf(file, "%s\n\n", SCORE_COOKIE);
9409 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9410 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9414 SetFilePermissions(filename, PERMS_PRIVATE);
9418 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9420 putFileVersion(file, scores->file_version);
9421 putFileVersion(file, scores->game_version);
9424 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9426 int level_identifier_size = strlen(scores->level_identifier) + 1;
9429 putFile16BitBE(file, level_identifier_size);
9431 for (i = 0; i < level_identifier_size; i++)
9432 putFile8Bit(file, scores->level_identifier[i]);
9434 putFile16BitBE(file, scores->level_nr);
9435 putFile16BitBE(file, scores->num_entries);
9438 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9442 for (i = 0; i < scores->num_entries; i++)
9444 int name_size = strlen(scores->entry[i].name);
9446 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9447 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9451 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9455 for (i = 0; i < scores->num_entries; i++)
9456 putFile16BitBE(file, scores->entry[i].score);
9459 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9463 for (i = 0; i < scores->num_entries; i++)
9464 putFile32BitBE(file, scores->entry[i].score);
9467 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9471 for (i = 0; i < scores->num_entries; i++)
9472 putFile32BitBE(file, scores->entry[i].time);
9475 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9479 for (i = 0; i < scores->num_entries; i++)
9481 int size = strlen(scores->entry[i].tape_basename);
9483 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9484 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9488 static void SaveScoreToFilename(char *filename)
9491 int info_chunk_size;
9492 int name_chunk_size;
9493 int scor_chunk_size;
9494 int sc4r_chunk_size;
9495 int time_chunk_size;
9496 int tape_chunk_size;
9497 boolean has_large_score_values;
9500 if (!(file = fopen(filename, MODE_WRITE)))
9502 Warn("cannot save score file '%s'", filename);
9507 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9508 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9509 scor_chunk_size = scores.num_entries * 2;
9510 sc4r_chunk_size = scores.num_entries * 4;
9511 time_chunk_size = scores.num_entries * 4;
9512 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9514 has_large_score_values = FALSE;
9515 for (i = 0; i < scores.num_entries; i++)
9516 if (scores.entry[i].score > 0xffff)
9517 has_large_score_values = TRUE;
9519 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9520 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9522 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9523 SaveScore_VERS(file, &scores);
9525 putFileChunkBE(file, "INFO", info_chunk_size);
9526 SaveScore_INFO(file, &scores);
9528 putFileChunkBE(file, "NAME", name_chunk_size);
9529 SaveScore_NAME(file, &scores);
9531 if (has_large_score_values)
9533 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9534 SaveScore_SC4R(file, &scores);
9538 putFileChunkBE(file, "SCOR", scor_chunk_size);
9539 SaveScore_SCOR(file, &scores);
9542 putFileChunkBE(file, "TIME", time_chunk_size);
9543 SaveScore_TIME(file, &scores);
9545 putFileChunkBE(file, "TAPE", tape_chunk_size);
9546 SaveScore_TAPE(file, &scores);
9550 SetFilePermissions(filename, PERMS_PRIVATE);
9553 void SaveScore(int nr)
9555 char *filename = getScoreFilename(nr);
9558 // used instead of "leveldir_current->subdir" (for network games)
9559 InitScoreDirectory(levelset.identifier);
9561 scores.file_version = FILE_VERSION_ACTUAL;
9562 scores.game_version = GAME_VERSION_ACTUAL;
9564 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9565 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9566 scores.level_nr = level_nr;
9568 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9569 if (scores.entry[i].score == 0 &&
9570 scores.entry[i].time == 0 &&
9571 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9574 scores.num_entries = i;
9576 if (scores.num_entries == 0)
9579 SaveScoreToFilename(filename);
9582 static void LoadServerScoreFromCache(int nr)
9584 struct ScoreEntry score_entry;
9593 { &score_entry.score, FALSE, 0 },
9594 { &score_entry.time, FALSE, 0 },
9595 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9596 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9597 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9598 { &score_entry.id, FALSE, 0 },
9599 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9600 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9601 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9602 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9606 char *filename = getScoreCacheFilename(nr);
9607 SetupFileHash *score_hash = loadSetupFileHash(filename);
9610 server_scores.num_entries = 0;
9612 if (score_hash == NULL)
9615 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9617 score_entry = server_scores.entry[i];
9619 for (j = 0; score_mapping[j].value != NULL; j++)
9623 sprintf(token, "%02d.%d", i, j);
9625 char *value = getHashEntry(score_hash, token);
9630 if (score_mapping[j].is_string)
9632 char *score_value = (char *)score_mapping[j].value;
9633 int value_size = score_mapping[j].string_size;
9635 strncpy(score_value, value, value_size);
9636 score_value[value_size] = '\0';
9640 int *score_value = (int *)score_mapping[j].value;
9642 *score_value = atoi(value);
9645 server_scores.num_entries = i + 1;
9648 server_scores.entry[i] = score_entry;
9651 freeSetupFileHash(score_hash);
9654 void LoadServerScore(int nr, boolean download_score)
9656 if (!setup.use_api_server)
9659 // always start with reliable default values
9660 setServerScoreInfoToDefaults();
9662 // 1st step: load server scores from cache file (which may not exist)
9663 // (this should prevent reading it while the thread is writing to it)
9664 LoadServerScoreFromCache(nr);
9666 if (download_score && runtime.use_api_server)
9668 // 2nd step: download server scores from score server to cache file
9669 // (as thread, as it might time out if the server is not reachable)
9670 ApiGetScoreAsThread(nr);
9674 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9676 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9678 // if score tape not uploaded, ask for uploading missing tapes later
9679 if (!setup.has_remaining_tapes)
9680 setup.ask_for_remaining_tapes = TRUE;
9682 setup.provide_uploading_tapes = TRUE;
9683 setup.has_remaining_tapes = TRUE;
9685 SaveSetup_ServerSetup();
9688 void SaveServerScore(int nr, boolean tape_saved)
9690 if (!runtime.use_api_server)
9692 PrepareScoreTapesForUpload(leveldir_current->subdir);
9697 ApiAddScoreAsThread(nr, tape_saved, NULL);
9700 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9701 char *score_tape_filename)
9703 if (!runtime.use_api_server)
9706 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9709 void LoadLocalAndServerScore(int nr, boolean download_score)
9711 int last_added_local = scores.last_added_local;
9712 boolean force_last_added = scores.force_last_added;
9714 // needed if only showing server scores
9715 setScoreInfoToDefaults();
9717 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9720 // restore last added local score entry (before merging server scores)
9721 scores.last_added = scores.last_added_local = last_added_local;
9723 if (setup.use_api_server &&
9724 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9726 // load server scores from cache file and trigger update from server
9727 LoadServerScore(nr, download_score);
9729 // merge local scores with scores from server
9733 if (force_last_added)
9734 scores.force_last_added = force_last_added;
9738 // ============================================================================
9739 // setup file functions
9740 // ============================================================================
9742 #define TOKEN_STR_PLAYER_PREFIX "player_"
9745 static struct TokenInfo global_setup_tokens[] =
9749 &setup.player_name, "player_name"
9753 &setup.multiple_users, "multiple_users"
9757 &setup.sound, "sound"
9761 &setup.sound_loops, "repeating_sound_loops"
9765 &setup.sound_music, "background_music"
9769 &setup.sound_simple, "simple_sound_effects"
9773 &setup.toons, "toons"
9777 &setup.global_animations, "global_animations"
9781 &setup.scroll_delay, "scroll_delay"
9785 &setup.forced_scroll_delay, "forced_scroll_delay"
9789 &setup.scroll_delay_value, "scroll_delay_value"
9793 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9797 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9801 &setup.fade_screens, "fade_screens"
9805 &setup.autorecord, "automatic_tape_recording"
9809 &setup.autorecord_after_replay, "autorecord_after_replay"
9813 &setup.auto_pause_on_start, "auto_pause_on_start"
9817 &setup.show_titlescreen, "show_titlescreen"
9821 &setup.quick_doors, "quick_doors"
9825 &setup.team_mode, "team_mode"
9829 &setup.handicap, "handicap"
9833 &setup.skip_levels, "skip_levels"
9837 &setup.increment_levels, "increment_levels"
9841 &setup.auto_play_next_level, "auto_play_next_level"
9845 &setup.count_score_after_game, "count_score_after_game"
9849 &setup.show_scores_after_game, "show_scores_after_game"
9853 &setup.time_limit, "time_limit"
9857 &setup.fullscreen, "fullscreen"
9861 &setup.window_scaling_percent, "window_scaling_percent"
9865 &setup.window_scaling_quality, "window_scaling_quality"
9869 &setup.screen_rendering_mode, "screen_rendering_mode"
9873 &setup.vsync_mode, "vsync_mode"
9877 &setup.ask_on_escape, "ask_on_escape"
9881 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9885 &setup.ask_on_game_over, "ask_on_game_over"
9889 &setup.ask_on_quit_game, "ask_on_quit_game"
9893 &setup.ask_on_quit_program, "ask_on_quit_program"
9897 &setup.quick_switch, "quick_player_switch"
9901 &setup.input_on_focus, "input_on_focus"
9905 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9909 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9913 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9917 &setup.game_speed_extended, "game_speed_extended"
9921 &setup.game_frame_delay, "game_frame_delay"
9925 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9929 &setup.bd_skip_hatching, "bd_skip_hatching"
9933 &setup.bd_scroll_delay, "bd_scroll_delay"
9937 &setup.bd_smooth_movements, "bd_smooth_movements"
9941 &setup.sp_show_border_elements, "sp_show_border_elements"
9945 &setup.small_game_graphics, "small_game_graphics"
9949 &setup.show_load_save_buttons, "show_load_save_buttons"
9953 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9957 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9961 &setup.graphics_set, "graphics_set"
9965 &setup.sounds_set, "sounds_set"
9969 &setup.music_set, "music_set"
9973 &setup.override_level_graphics, "override_level_graphics"
9977 &setup.override_level_sounds, "override_level_sounds"
9981 &setup.override_level_music, "override_level_music"
9985 &setup.volume_simple, "volume_simple"
9989 &setup.volume_loops, "volume_loops"
9993 &setup.volume_music, "volume_music"
9997 &setup.network_mode, "network_mode"
10001 &setup.network_player_nr, "network_player"
10005 &setup.network_server_hostname, "network_server_hostname"
10009 &setup.touch.control_type, "touch.control_type"
10013 &setup.touch.move_distance, "touch.move_distance"
10017 &setup.touch.drop_distance, "touch.drop_distance"
10021 &setup.touch.transparency, "touch.transparency"
10025 &setup.touch.draw_outlined, "touch.draw_outlined"
10029 &setup.touch.draw_pressed, "touch.draw_pressed"
10033 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10037 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10041 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10045 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10049 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10053 static struct TokenInfo auto_setup_tokens[] =
10057 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10061 static struct TokenInfo server_setup_tokens[] =
10065 &setup.player_uuid, "player_uuid"
10069 &setup.player_version, "player_version"
10073 &setup.use_api_server, TEST_PREFIX "use_api_server"
10077 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10081 &setup.api_server_password, TEST_PREFIX "api_server_password"
10085 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10089 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10093 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10097 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10101 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10105 static struct TokenInfo editor_setup_tokens[] =
10109 &setup.editor.el_classic, "editor.el_classic"
10113 &setup.editor.el_custom, "editor.el_custom"
10117 &setup.editor.el_user_defined, "editor.el_user_defined"
10121 &setup.editor.el_dynamic, "editor.el_dynamic"
10125 &setup.editor.el_headlines, "editor.el_headlines"
10129 &setup.editor.show_element_token, "editor.show_element_token"
10133 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10137 static struct TokenInfo editor_cascade_setup_tokens[] =
10141 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10145 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10149 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10153 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10157 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10161 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10165 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10169 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10173 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10177 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10181 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10185 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10189 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10193 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10197 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10201 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10205 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10209 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10213 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10217 static struct TokenInfo shortcut_setup_tokens[] =
10221 &setup.shortcut.save_game, "shortcut.save_game"
10225 &setup.shortcut.load_game, "shortcut.load_game"
10229 &setup.shortcut.restart_game, "shortcut.restart_game"
10233 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10237 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10241 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10245 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10249 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10253 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10257 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10261 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10265 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10269 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10273 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10277 &setup.shortcut.tape_record, "shortcut.tape_record"
10281 &setup.shortcut.tape_play, "shortcut.tape_play"
10285 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10289 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10293 &setup.shortcut.sound_music, "shortcut.sound_music"
10297 &setup.shortcut.snap_left, "shortcut.snap_left"
10301 &setup.shortcut.snap_right, "shortcut.snap_right"
10305 &setup.shortcut.snap_up, "shortcut.snap_up"
10309 &setup.shortcut.snap_down, "shortcut.snap_down"
10313 static struct SetupInputInfo setup_input;
10314 static struct TokenInfo player_setup_tokens[] =
10318 &setup_input.use_joystick, ".use_joystick"
10322 &setup_input.joy.device_name, ".joy.device_name"
10326 &setup_input.joy.xleft, ".joy.xleft"
10330 &setup_input.joy.xmiddle, ".joy.xmiddle"
10334 &setup_input.joy.xright, ".joy.xright"
10338 &setup_input.joy.yupper, ".joy.yupper"
10342 &setup_input.joy.ymiddle, ".joy.ymiddle"
10346 &setup_input.joy.ylower, ".joy.ylower"
10350 &setup_input.joy.snap, ".joy.snap_field"
10354 &setup_input.joy.drop, ".joy.place_bomb"
10358 &setup_input.key.left, ".key.move_left"
10362 &setup_input.key.right, ".key.move_right"
10366 &setup_input.key.up, ".key.move_up"
10370 &setup_input.key.down, ".key.move_down"
10374 &setup_input.key.snap, ".key.snap_field"
10378 &setup_input.key.drop, ".key.place_bomb"
10382 static struct TokenInfo system_setup_tokens[] =
10386 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10390 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10394 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10398 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10402 static struct TokenInfo internal_setup_tokens[] =
10406 &setup.internal.program_title, "program_title"
10410 &setup.internal.program_version, "program_version"
10414 &setup.internal.program_author, "program_author"
10418 &setup.internal.program_email, "program_email"
10422 &setup.internal.program_website, "program_website"
10426 &setup.internal.program_copyright, "program_copyright"
10430 &setup.internal.program_company, "program_company"
10434 &setup.internal.program_icon_file, "program_icon_file"
10438 &setup.internal.default_graphics_set, "default_graphics_set"
10442 &setup.internal.default_sounds_set, "default_sounds_set"
10446 &setup.internal.default_music_set, "default_music_set"
10450 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10454 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10458 &setup.internal.fallback_music_file, "fallback_music_file"
10462 &setup.internal.default_level_series, "default_level_series"
10466 &setup.internal.default_window_width, "default_window_width"
10470 &setup.internal.default_window_height, "default_window_height"
10474 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10478 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10482 &setup.internal.create_user_levelset, "create_user_levelset"
10486 &setup.internal.info_screens_from_main, "info_screens_from_main"
10490 &setup.internal.menu_game, "menu_game"
10494 &setup.internal.menu_engines, "menu_engines"
10498 &setup.internal.menu_editor, "menu_editor"
10502 &setup.internal.menu_graphics, "menu_graphics"
10506 &setup.internal.menu_sound, "menu_sound"
10510 &setup.internal.menu_artwork, "menu_artwork"
10514 &setup.internal.menu_input, "menu_input"
10518 &setup.internal.menu_touch, "menu_touch"
10522 &setup.internal.menu_shortcuts, "menu_shortcuts"
10526 &setup.internal.menu_exit, "menu_exit"
10530 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10534 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10538 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10542 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10546 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10550 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10554 &setup.internal.info_title, "info_title"
10558 &setup.internal.info_elements, "info_elements"
10562 &setup.internal.info_music, "info_music"
10566 &setup.internal.info_credits, "info_credits"
10570 &setup.internal.info_program, "info_program"
10574 &setup.internal.info_version, "info_version"
10578 &setup.internal.info_levelset, "info_levelset"
10582 &setup.internal.info_exit, "info_exit"
10586 static struct TokenInfo debug_setup_tokens[] =
10590 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10594 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10598 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10602 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10606 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10610 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10614 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10618 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10622 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10626 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10630 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10634 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10638 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10642 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10646 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10650 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10654 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10658 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10662 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10666 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10670 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10673 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10677 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10681 &setup.debug.xsn_mode, "debug.xsn_mode"
10685 &setup.debug.xsn_percent, "debug.xsn_percent"
10689 static struct TokenInfo options_setup_tokens[] =
10693 &setup.options.verbose, "options.verbose"
10697 &setup.options.debug, "options.debug"
10701 &setup.options.debug_mode, "options.debug_mode"
10705 static void setSetupInfoToDefaults(struct SetupInfo *si)
10709 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10711 si->multiple_users = TRUE;
10714 si->sound_loops = TRUE;
10715 si->sound_music = TRUE;
10716 si->sound_simple = TRUE;
10718 si->global_animations = TRUE;
10719 si->scroll_delay = TRUE;
10720 si->forced_scroll_delay = FALSE;
10721 si->scroll_delay_value = STD_SCROLL_DELAY;
10722 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10723 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10724 si->fade_screens = TRUE;
10725 si->autorecord = TRUE;
10726 si->autorecord_after_replay = TRUE;
10727 si->auto_pause_on_start = FALSE;
10728 si->show_titlescreen = TRUE;
10729 si->quick_doors = FALSE;
10730 si->team_mode = FALSE;
10731 si->handicap = TRUE;
10732 si->skip_levels = TRUE;
10733 si->increment_levels = TRUE;
10734 si->auto_play_next_level = TRUE;
10735 si->count_score_after_game = TRUE;
10736 si->show_scores_after_game = TRUE;
10737 si->time_limit = TRUE;
10738 si->fullscreen = FALSE;
10739 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10740 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10741 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10742 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10743 si->ask_on_escape = TRUE;
10744 si->ask_on_escape_editor = TRUE;
10745 si->ask_on_game_over = TRUE;
10746 si->ask_on_quit_game = TRUE;
10747 si->ask_on_quit_program = TRUE;
10748 si->quick_switch = FALSE;
10749 si->input_on_focus = FALSE;
10750 si->prefer_aga_graphics = TRUE;
10751 si->prefer_lowpass_sounds = FALSE;
10752 si->prefer_extra_panel_items = TRUE;
10753 si->game_speed_extended = FALSE;
10754 si->game_frame_delay = GAME_FRAME_DELAY;
10755 si->bd_skip_uncovering = FALSE;
10756 si->bd_skip_hatching = FALSE;
10757 si->bd_scroll_delay = TRUE;
10758 si->bd_smooth_movements = AUTO;
10759 si->sp_show_border_elements = FALSE;
10760 si->small_game_graphics = FALSE;
10761 si->show_load_save_buttons = FALSE;
10762 si->show_undo_redo_buttons = FALSE;
10763 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10765 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10766 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10767 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10769 si->override_level_graphics = FALSE;
10770 si->override_level_sounds = FALSE;
10771 si->override_level_music = FALSE;
10773 si->volume_simple = 100; // percent
10774 si->volume_loops = 100; // percent
10775 si->volume_music = 100; // percent
10777 si->network_mode = FALSE;
10778 si->network_player_nr = 0; // first player
10779 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10781 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10782 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10783 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10784 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10785 si->touch.draw_outlined = TRUE;
10786 si->touch.draw_pressed = TRUE;
10788 for (i = 0; i < 2; i++)
10790 char *default_grid_button[6][2] =
10796 { "111222", " vv " },
10797 { "111222", " vv " }
10799 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10800 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10801 int min_xsize = MIN(6, grid_xsize);
10802 int min_ysize = MIN(6, grid_ysize);
10803 int startx = grid_xsize - min_xsize;
10804 int starty = grid_ysize - min_ysize;
10807 // virtual buttons grid can only be set to defaults if video is initialized
10808 // (this will be repeated if virtual buttons are not loaded from setup file)
10809 if (video.initialized)
10811 si->touch.grid_xsize[i] = grid_xsize;
10812 si->touch.grid_ysize[i] = grid_ysize;
10816 si->touch.grid_xsize[i] = -1;
10817 si->touch.grid_ysize[i] = -1;
10820 for (x = 0; x < MAX_GRID_XSIZE; x++)
10821 for (y = 0; y < MAX_GRID_YSIZE; y++)
10822 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10824 for (x = 0; x < min_xsize; x++)
10825 for (y = 0; y < min_ysize; y++)
10826 si->touch.grid_button[i][x][starty + y] =
10827 default_grid_button[y][0][x];
10829 for (x = 0; x < min_xsize; x++)
10830 for (y = 0; y < min_ysize; y++)
10831 si->touch.grid_button[i][startx + x][starty + y] =
10832 default_grid_button[y][1][x];
10835 si->touch.grid_initialized = video.initialized;
10837 si->touch.overlay_buttons = FALSE;
10839 si->editor.el_boulderdash = TRUE;
10840 si->editor.el_boulderdash_native = TRUE;
10841 si->editor.el_emerald_mine = TRUE;
10842 si->editor.el_emerald_mine_club = TRUE;
10843 si->editor.el_more = TRUE;
10844 si->editor.el_sokoban = TRUE;
10845 si->editor.el_supaplex = TRUE;
10846 si->editor.el_diamond_caves = TRUE;
10847 si->editor.el_dx_boulderdash = TRUE;
10849 si->editor.el_mirror_magic = TRUE;
10850 si->editor.el_deflektor = TRUE;
10852 si->editor.el_chars = TRUE;
10853 si->editor.el_steel_chars = TRUE;
10855 si->editor.el_classic = TRUE;
10856 si->editor.el_custom = TRUE;
10858 si->editor.el_user_defined = FALSE;
10859 si->editor.el_dynamic = TRUE;
10861 si->editor.el_headlines = TRUE;
10863 si->editor.show_element_token = FALSE;
10865 si->editor.show_read_only_warning = TRUE;
10867 si->editor.use_template_for_new_levels = TRUE;
10869 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10870 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10871 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10872 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10873 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10875 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10876 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10877 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10878 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10879 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10881 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10882 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10883 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10884 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10885 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10886 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10888 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10889 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10890 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10892 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10893 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10894 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10895 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10897 for (i = 0; i < MAX_PLAYERS; i++)
10899 si->input[i].use_joystick = FALSE;
10900 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10901 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10902 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10903 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10904 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10905 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10906 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10907 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10908 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10909 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10910 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10911 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10912 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10913 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10914 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10917 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10918 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10919 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10920 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10922 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10923 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10924 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10925 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10926 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10927 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10928 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10930 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10932 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10933 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10934 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10936 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10937 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10938 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10940 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10941 si->internal.choose_from_top_leveldir = FALSE;
10942 si->internal.show_scaling_in_title = TRUE;
10943 si->internal.create_user_levelset = TRUE;
10944 si->internal.info_screens_from_main = FALSE;
10946 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10947 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10949 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10950 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10951 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10952 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10953 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10954 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10955 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10956 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10957 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10958 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10960 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10961 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10962 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10963 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10964 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10965 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10966 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10967 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10968 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10969 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10971 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10972 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10974 si->debug.show_frames_per_second = FALSE;
10976 si->debug.xsn_mode = AUTO;
10977 si->debug.xsn_percent = 0;
10979 si->options.verbose = FALSE;
10980 si->options.debug = FALSE;
10981 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10983 #if defined(PLATFORM_ANDROID)
10984 si->fullscreen = TRUE;
10985 si->touch.overlay_buttons = TRUE;
10988 setHideSetupEntry(&setup.debug.xsn_mode);
10991 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10993 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10996 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10998 si->player_uuid = NULL; // (will be set later)
10999 si->player_version = 1; // (will be set later)
11001 si->use_api_server = TRUE;
11002 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11003 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11004 si->ask_for_uploading_tapes = TRUE;
11005 si->ask_for_remaining_tapes = FALSE;
11006 si->provide_uploading_tapes = TRUE;
11007 si->ask_for_using_api_server = TRUE;
11008 si->has_remaining_tapes = FALSE;
11011 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11013 si->editor_cascade.el_bd = TRUE;
11014 si->editor_cascade.el_bd_native = TRUE;
11015 si->editor_cascade.el_em = TRUE;
11016 si->editor_cascade.el_emc = TRUE;
11017 si->editor_cascade.el_rnd = TRUE;
11018 si->editor_cascade.el_sb = TRUE;
11019 si->editor_cascade.el_sp = TRUE;
11020 si->editor_cascade.el_dc = TRUE;
11021 si->editor_cascade.el_dx = TRUE;
11023 si->editor_cascade.el_mm = TRUE;
11024 si->editor_cascade.el_df = TRUE;
11026 si->editor_cascade.el_chars = FALSE;
11027 si->editor_cascade.el_steel_chars = FALSE;
11028 si->editor_cascade.el_ce = FALSE;
11029 si->editor_cascade.el_ge = FALSE;
11030 si->editor_cascade.el_es = FALSE;
11031 si->editor_cascade.el_ref = FALSE;
11032 si->editor_cascade.el_user = FALSE;
11033 si->editor_cascade.el_dynamic = FALSE;
11036 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11038 static char *getHideSetupToken(void *setup_value)
11040 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11042 if (setup_value != NULL)
11043 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11045 return hide_setup_token;
11048 void setHideSetupEntry(void *setup_value)
11050 char *hide_setup_token = getHideSetupToken(setup_value);
11052 if (hide_setup_hash == NULL)
11053 hide_setup_hash = newSetupFileHash();
11055 if (setup_value != NULL)
11056 setHashEntry(hide_setup_hash, hide_setup_token, "");
11059 void removeHideSetupEntry(void *setup_value)
11061 char *hide_setup_token = getHideSetupToken(setup_value);
11063 if (setup_value != NULL)
11064 removeHashEntry(hide_setup_hash, hide_setup_token);
11067 boolean hideSetupEntry(void *setup_value)
11069 char *hide_setup_token = getHideSetupToken(setup_value);
11071 return (setup_value != NULL &&
11072 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11075 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11076 struct TokenInfo *token_info,
11077 int token_nr, char *token_text)
11079 char *token_hide_text = getStringCat2(token_text, ".hide");
11080 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11082 // set the value of this setup option in the setup option structure
11083 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11085 // check if this setup option should be hidden in the setup menu
11086 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11087 setHideSetupEntry(token_info[token_nr].value);
11089 free(token_hide_text);
11092 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11093 struct TokenInfo *token_info,
11096 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11097 token_info[token_nr].text);
11100 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11104 if (!setup_file_hash)
11107 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11108 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11110 setup.touch.grid_initialized = TRUE;
11111 for (i = 0; i < 2; i++)
11113 int grid_xsize = setup.touch.grid_xsize[i];
11114 int grid_ysize = setup.touch.grid_ysize[i];
11117 // if virtual buttons are not loaded from setup file, repeat initializing
11118 // virtual buttons grid with default values later when video is initialized
11119 if (grid_xsize == -1 ||
11122 setup.touch.grid_initialized = FALSE;
11127 for (y = 0; y < grid_ysize; y++)
11129 char token_string[MAX_LINE_LEN];
11131 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11133 char *value_string = getHashEntry(setup_file_hash, token_string);
11135 if (value_string == NULL)
11138 for (x = 0; x < grid_xsize; x++)
11140 char c = value_string[x];
11142 setup.touch.grid_button[i][x][y] =
11143 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11148 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11149 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11151 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11152 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11154 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11158 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11160 setup_input = setup.input[pnr];
11161 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11163 char full_token[100];
11165 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11166 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11169 setup.input[pnr] = setup_input;
11172 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11173 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11175 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11176 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11178 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11179 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11181 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11182 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11184 setHideRelatedSetupEntries();
11187 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11191 if (!setup_file_hash)
11194 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11195 setSetupInfo(auto_setup_tokens, i,
11196 getHashEntry(setup_file_hash,
11197 auto_setup_tokens[i].text));
11200 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11204 if (!setup_file_hash)
11207 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11208 setSetupInfo(server_setup_tokens, i,
11209 getHashEntry(setup_file_hash,
11210 server_setup_tokens[i].text));
11213 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11217 if (!setup_file_hash)
11220 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11221 setSetupInfo(editor_cascade_setup_tokens, i,
11222 getHashEntry(setup_file_hash,
11223 editor_cascade_setup_tokens[i].text));
11226 void LoadUserNames(void)
11228 int last_user_nr = user.nr;
11231 if (global.user_names != NULL)
11233 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11234 checked_free(global.user_names[i]);
11236 checked_free(global.user_names);
11239 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11241 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11245 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11247 if (setup_file_hash)
11249 char *player_name = getHashEntry(setup_file_hash, "player_name");
11251 global.user_names[i] = getFixedUserName(player_name);
11253 freeSetupFileHash(setup_file_hash);
11256 if (global.user_names[i] == NULL)
11257 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11260 user.nr = last_user_nr;
11263 void LoadSetupFromFilename(char *filename)
11265 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11267 if (setup_file_hash)
11269 decodeSetupFileHash_Default(setup_file_hash);
11271 freeSetupFileHash(setup_file_hash);
11275 Debug("setup", "using default setup values");
11279 static void LoadSetup_SpecialPostProcessing(void)
11281 char *player_name_new;
11283 // needed to work around problems with fixed length strings
11284 player_name_new = getFixedUserName(setup.player_name);
11285 free(setup.player_name);
11286 setup.player_name = player_name_new;
11288 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11289 if (setup.scroll_delay == FALSE)
11291 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11292 setup.scroll_delay = TRUE; // now always "on"
11295 // make sure that scroll delay value stays inside valid range
11296 setup.scroll_delay_value =
11297 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11300 void LoadSetup_Default(void)
11304 // always start with reliable default values
11305 setSetupInfoToDefaults(&setup);
11307 // try to load setup values from default setup file
11308 filename = getDefaultSetupFilename();
11310 if (fileExists(filename))
11311 LoadSetupFromFilename(filename);
11313 // try to load setup values from platform setup file
11314 filename = getPlatformSetupFilename();
11316 if (fileExists(filename))
11317 LoadSetupFromFilename(filename);
11319 // try to load setup values from user setup file
11320 filename = getSetupFilename();
11322 LoadSetupFromFilename(filename);
11324 LoadSetup_SpecialPostProcessing();
11327 void LoadSetup_AutoSetup(void)
11329 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11330 SetupFileHash *setup_file_hash = NULL;
11332 // always start with reliable default values
11333 setSetupInfoToDefaults_AutoSetup(&setup);
11335 setup_file_hash = loadSetupFileHash(filename);
11337 if (setup_file_hash)
11339 decodeSetupFileHash_AutoSetup(setup_file_hash);
11341 freeSetupFileHash(setup_file_hash);
11347 void LoadSetup_ServerSetup(void)
11349 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11350 SetupFileHash *setup_file_hash = NULL;
11352 // always start with reliable default values
11353 setSetupInfoToDefaults_ServerSetup(&setup);
11355 setup_file_hash = loadSetupFileHash(filename);
11357 if (setup_file_hash)
11359 decodeSetupFileHash_ServerSetup(setup_file_hash);
11361 freeSetupFileHash(setup_file_hash);
11366 if (setup.player_uuid == NULL)
11368 // player UUID does not yet exist in setup file
11369 setup.player_uuid = getStringCopy(getUUID());
11370 setup.player_version = 2;
11372 SaveSetup_ServerSetup();
11376 void LoadSetup_EditorCascade(void)
11378 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11379 SetupFileHash *setup_file_hash = NULL;
11381 // always start with reliable default values
11382 setSetupInfoToDefaults_EditorCascade(&setup);
11384 setup_file_hash = loadSetupFileHash(filename);
11386 if (setup_file_hash)
11388 decodeSetupFileHash_EditorCascade(setup_file_hash);
11390 freeSetupFileHash(setup_file_hash);
11396 void LoadSetup(void)
11398 LoadSetup_Default();
11399 LoadSetup_AutoSetup();
11400 LoadSetup_ServerSetup();
11401 LoadSetup_EditorCascade();
11404 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11405 char *mapping_line)
11407 char mapping_guid[MAX_LINE_LEN];
11408 char *mapping_start, *mapping_end;
11410 // get GUID from game controller mapping line: copy complete line
11411 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11412 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11414 // get GUID from game controller mapping line: cut after GUID part
11415 mapping_start = strchr(mapping_guid, ',');
11416 if (mapping_start != NULL)
11417 *mapping_start = '\0';
11419 // cut newline from game controller mapping line
11420 mapping_end = strchr(mapping_line, '\n');
11421 if (mapping_end != NULL)
11422 *mapping_end = '\0';
11424 // add mapping entry to game controller mappings hash
11425 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11428 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11433 if (!(file = fopen(filename, MODE_READ)))
11435 Warn("cannot read game controller mappings file '%s'", filename);
11440 while (!feof(file))
11442 char line[MAX_LINE_LEN];
11444 if (!fgets(line, MAX_LINE_LEN, file))
11447 addGameControllerMappingToHash(mappings_hash, line);
11453 void SaveSetup_Default(void)
11455 char *filename = getSetupFilename();
11459 InitUserDataDirectory();
11461 if (!(file = fopen(filename, MODE_WRITE)))
11463 Warn("cannot write setup file '%s'", filename);
11468 fprintFileHeader(file, SETUP_FILENAME);
11470 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11472 // just to make things nicer :)
11473 if (global_setup_tokens[i].value == &setup.multiple_users ||
11474 global_setup_tokens[i].value == &setup.sound ||
11475 global_setup_tokens[i].value == &setup.graphics_set ||
11476 global_setup_tokens[i].value == &setup.volume_simple ||
11477 global_setup_tokens[i].value == &setup.network_mode ||
11478 global_setup_tokens[i].value == &setup.touch.control_type ||
11479 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11480 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11481 fprintf(file, "\n");
11483 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11486 for (i = 0; i < 2; i++)
11488 int grid_xsize = setup.touch.grid_xsize[i];
11489 int grid_ysize = setup.touch.grid_ysize[i];
11492 fprintf(file, "\n");
11494 for (y = 0; y < grid_ysize; y++)
11496 char token_string[MAX_LINE_LEN];
11497 char value_string[MAX_LINE_LEN];
11499 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11501 for (x = 0; x < grid_xsize; x++)
11503 char c = setup.touch.grid_button[i][x][y];
11505 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11508 value_string[grid_xsize] = '\0';
11510 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11514 fprintf(file, "\n");
11515 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11516 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11518 fprintf(file, "\n");
11519 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11520 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11522 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11526 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11527 fprintf(file, "\n");
11529 setup_input = setup.input[pnr];
11530 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11531 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11534 fprintf(file, "\n");
11535 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11536 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11538 // (internal setup values not saved to user setup file)
11540 fprintf(file, "\n");
11541 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11542 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11543 setup.debug.xsn_mode != AUTO)
11544 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11546 fprintf(file, "\n");
11547 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11548 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11552 SetFilePermissions(filename, PERMS_PRIVATE);
11555 void SaveSetup_AutoSetup(void)
11557 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11561 InitUserDataDirectory();
11563 if (!(file = fopen(filename, MODE_WRITE)))
11565 Warn("cannot write auto setup file '%s'", filename);
11572 fprintFileHeader(file, AUTOSETUP_FILENAME);
11574 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11575 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11579 SetFilePermissions(filename, PERMS_PRIVATE);
11584 void SaveSetup_ServerSetup(void)
11586 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11590 InitUserDataDirectory();
11592 if (!(file = fopen(filename, MODE_WRITE)))
11594 Warn("cannot write server setup file '%s'", filename);
11601 fprintFileHeader(file, SERVERSETUP_FILENAME);
11603 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11605 // just to make things nicer :)
11606 if (server_setup_tokens[i].value == &setup.use_api_server)
11607 fprintf(file, "\n");
11609 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11614 SetFilePermissions(filename, PERMS_PRIVATE);
11619 void SaveSetup_EditorCascade(void)
11621 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11625 InitUserDataDirectory();
11627 if (!(file = fopen(filename, MODE_WRITE)))
11629 Warn("cannot write editor cascade state file '%s'", filename);
11636 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11638 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11639 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11643 SetFilePermissions(filename, PERMS_PRIVATE);
11648 void SaveSetup(void)
11650 SaveSetup_Default();
11651 SaveSetup_AutoSetup();
11652 SaveSetup_ServerSetup();
11653 SaveSetup_EditorCascade();
11656 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11661 if (!(file = fopen(filename, MODE_WRITE)))
11663 Warn("cannot write game controller mappings file '%s'", filename);
11668 BEGIN_HASH_ITERATION(mappings_hash, itr)
11670 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11672 END_HASH_ITERATION(mappings_hash, itr)
11677 void SaveSetup_AddGameControllerMapping(char *mapping)
11679 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11680 SetupFileHash *mappings_hash = newSetupFileHash();
11682 InitUserDataDirectory();
11684 // load existing personal game controller mappings
11685 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11687 // add new mapping to personal game controller mappings
11688 addGameControllerMappingToHash(mappings_hash, mapping);
11690 // save updated personal game controller mappings
11691 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11693 freeSetupFileHash(mappings_hash);
11697 void LoadCustomElementDescriptions(void)
11699 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11700 SetupFileHash *setup_file_hash;
11703 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11705 if (element_info[i].custom_description != NULL)
11707 free(element_info[i].custom_description);
11708 element_info[i].custom_description = NULL;
11712 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11715 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11717 char *token = getStringCat2(element_info[i].token_name, ".name");
11718 char *value = getHashEntry(setup_file_hash, token);
11721 element_info[i].custom_description = getStringCopy(value);
11726 freeSetupFileHash(setup_file_hash);
11729 static int getElementFromToken(char *token)
11731 char *value = getHashEntry(element_token_hash, token);
11734 return atoi(value);
11736 Warn("unknown element token '%s'", token);
11738 return EL_UNDEFINED;
11741 void FreeGlobalAnimEventInfo(void)
11743 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11745 if (gaei->event_list == NULL)
11750 for (i = 0; i < gaei->num_event_lists; i++)
11752 checked_free(gaei->event_list[i]->event_value);
11753 checked_free(gaei->event_list[i]);
11756 checked_free(gaei->event_list);
11758 gaei->event_list = NULL;
11759 gaei->num_event_lists = 0;
11762 static int AddGlobalAnimEventList(void)
11764 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11765 int list_pos = gaei->num_event_lists++;
11767 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11768 sizeof(struct GlobalAnimEventListInfo *));
11770 gaei->event_list[list_pos] =
11771 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11773 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11775 gaeli->event_value = NULL;
11776 gaeli->num_event_values = 0;
11781 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11783 // do not add empty global animation events
11784 if (event_value == ANIM_EVENT_NONE)
11787 // if list position is undefined, create new list
11788 if (list_pos == ANIM_EVENT_UNDEFINED)
11789 list_pos = AddGlobalAnimEventList();
11791 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11792 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11793 int value_pos = gaeli->num_event_values++;
11795 gaeli->event_value = checked_realloc(gaeli->event_value,
11796 gaeli->num_event_values * sizeof(int *));
11798 gaeli->event_value[value_pos] = event_value;
11803 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11805 if (list_pos == ANIM_EVENT_UNDEFINED)
11806 return ANIM_EVENT_NONE;
11808 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11809 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11811 return gaeli->event_value[value_pos];
11814 int GetGlobalAnimEventValueCount(int list_pos)
11816 if (list_pos == ANIM_EVENT_UNDEFINED)
11819 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11820 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11822 return gaeli->num_event_values;
11825 // This function checks if a string <s> of the format "string1, string2, ..."
11826 // exactly contains a string <s_contained>.
11828 static boolean string_has_parameter(char *s, char *s_contained)
11832 if (s == NULL || s_contained == NULL)
11835 if (strlen(s_contained) > strlen(s))
11838 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11840 char next_char = s[strlen(s_contained)];
11842 // check if next character is delimiter or whitespace
11843 if (next_char == ',' || next_char == '\0' ||
11844 next_char == ' ' || next_char == '\t')
11848 // check if string contains another parameter string after a comma
11849 substring = strchr(s, ',');
11850 if (substring == NULL) // string does not contain a comma
11853 // advance string pointer to next character after the comma
11856 // skip potential whitespaces after the comma
11857 while (*substring == ' ' || *substring == '\t')
11860 return string_has_parameter(substring, s_contained);
11863 static int get_anim_parameter_value_ce(char *s)
11866 char *pattern_1 = "ce_change:custom_";
11867 char *pattern_2 = ".page_";
11868 int pattern_1_len = strlen(pattern_1);
11869 char *matching_char = strstr(s_ptr, pattern_1);
11870 int result = ANIM_EVENT_NONE;
11872 if (matching_char == NULL)
11873 return ANIM_EVENT_NONE;
11875 result = ANIM_EVENT_CE_CHANGE;
11877 s_ptr = matching_char + pattern_1_len;
11879 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11880 if (*s_ptr >= '0' && *s_ptr <= '9')
11882 int gic_ce_nr = (*s_ptr++ - '0');
11884 if (*s_ptr >= '0' && *s_ptr <= '9')
11886 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11888 if (*s_ptr >= '0' && *s_ptr <= '9')
11889 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11892 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11893 return ANIM_EVENT_NONE;
11895 // custom element stored as 0 to 255
11898 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11902 // invalid custom element number specified
11904 return ANIM_EVENT_NONE;
11907 // check for change page number ("page_X" or "page_XX") (optional)
11908 if (strPrefix(s_ptr, pattern_2))
11910 s_ptr += strlen(pattern_2);
11912 if (*s_ptr >= '0' && *s_ptr <= '9')
11914 int gic_page_nr = (*s_ptr++ - '0');
11916 if (*s_ptr >= '0' && *s_ptr <= '9')
11917 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11919 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11920 return ANIM_EVENT_NONE;
11922 // change page stored as 1 to 32 (0 means "all change pages")
11924 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11928 // invalid animation part number specified
11930 return ANIM_EVENT_NONE;
11934 // discard result if next character is neither delimiter nor whitespace
11935 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11936 *s_ptr == ' ' || *s_ptr == '\t'))
11937 return ANIM_EVENT_NONE;
11942 static int get_anim_parameter_value(char *s)
11944 int event_value[] =
11952 char *pattern_1[] =
11960 char *pattern_2 = ".part_";
11961 char *matching_char = NULL;
11963 int pattern_1_len = 0;
11964 int result = ANIM_EVENT_NONE;
11967 result = get_anim_parameter_value_ce(s);
11969 if (result != ANIM_EVENT_NONE)
11972 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11974 matching_char = strstr(s_ptr, pattern_1[i]);
11975 pattern_1_len = strlen(pattern_1[i]);
11976 result = event_value[i];
11978 if (matching_char != NULL)
11982 if (matching_char == NULL)
11983 return ANIM_EVENT_NONE;
11985 s_ptr = matching_char + pattern_1_len;
11987 // check for main animation number ("anim_X" or "anim_XX")
11988 if (*s_ptr >= '0' && *s_ptr <= '9')
11990 int gic_anim_nr = (*s_ptr++ - '0');
11992 if (*s_ptr >= '0' && *s_ptr <= '9')
11993 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11995 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11996 return ANIM_EVENT_NONE;
11998 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12002 // invalid main animation number specified
12004 return ANIM_EVENT_NONE;
12007 // check for animation part number ("part_X" or "part_XX") (optional)
12008 if (strPrefix(s_ptr, pattern_2))
12010 s_ptr += strlen(pattern_2);
12012 if (*s_ptr >= '0' && *s_ptr <= '9')
12014 int gic_part_nr = (*s_ptr++ - '0');
12016 if (*s_ptr >= '0' && *s_ptr <= '9')
12017 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12019 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12020 return ANIM_EVENT_NONE;
12022 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12026 // invalid animation part number specified
12028 return ANIM_EVENT_NONE;
12032 // discard result if next character is neither delimiter nor whitespace
12033 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12034 *s_ptr == ' ' || *s_ptr == '\t'))
12035 return ANIM_EVENT_NONE;
12040 static int get_anim_parameter_values(char *s)
12042 int list_pos = ANIM_EVENT_UNDEFINED;
12043 int event_value = ANIM_EVENT_DEFAULT;
12045 if (string_has_parameter(s, "any"))
12046 event_value |= ANIM_EVENT_ANY;
12048 if (string_has_parameter(s, "click:self") ||
12049 string_has_parameter(s, "click") ||
12050 string_has_parameter(s, "self"))
12051 event_value |= ANIM_EVENT_SELF;
12053 if (string_has_parameter(s, "unclick:any"))
12054 event_value |= ANIM_EVENT_UNCLICK_ANY;
12056 // if animation event found, add it to global animation event list
12057 if (event_value != ANIM_EVENT_NONE)
12058 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12062 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12063 event_value = get_anim_parameter_value(s);
12065 // if animation event found, add it to global animation event list
12066 if (event_value != ANIM_EVENT_NONE)
12067 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12069 // continue with next part of the string, starting with next comma
12070 s = strchr(s + 1, ',');
12076 static int get_anim_action_parameter_value(char *token)
12078 // check most common default case first to massively speed things up
12079 if (strEqual(token, ARG_UNDEFINED))
12080 return ANIM_EVENT_ACTION_NONE;
12082 int result = getImageIDFromToken(token);
12086 char *gfx_token = getStringCat2("gfx.", token);
12088 result = getImageIDFromToken(gfx_token);
12090 checked_free(gfx_token);
12095 Key key = getKeyFromX11KeyName(token);
12097 if (key != KSYM_UNDEFINED)
12098 result = -(int)key;
12105 result = get_hash_from_string(token); // unsigned int => int
12106 result = ABS(result); // may be negative now
12107 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12109 setHashEntry(anim_url_hash, int2str(result, 0), token);
12114 result = ANIM_EVENT_ACTION_NONE;
12119 int get_parameter_value(char *value_raw, char *suffix, int type)
12121 char *value = getStringToLower(value_raw);
12122 int result = 0; // probably a save default value
12124 if (strEqual(suffix, ".direction"))
12126 result = (strEqual(value, "left") ? MV_LEFT :
12127 strEqual(value, "right") ? MV_RIGHT :
12128 strEqual(value, "up") ? MV_UP :
12129 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12131 else if (strEqual(suffix, ".position"))
12133 result = (strEqual(value, "left") ? POS_LEFT :
12134 strEqual(value, "right") ? POS_RIGHT :
12135 strEqual(value, "top") ? POS_TOP :
12136 strEqual(value, "upper") ? POS_UPPER :
12137 strEqual(value, "middle") ? POS_MIDDLE :
12138 strEqual(value, "lower") ? POS_LOWER :
12139 strEqual(value, "bottom") ? POS_BOTTOM :
12140 strEqual(value, "any") ? POS_ANY :
12141 strEqual(value, "ce") ? POS_CE :
12142 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12143 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12145 else if (strEqual(suffix, ".align"))
12147 result = (strEqual(value, "left") ? ALIGN_LEFT :
12148 strEqual(value, "right") ? ALIGN_RIGHT :
12149 strEqual(value, "center") ? ALIGN_CENTER :
12150 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12152 else if (strEqual(suffix, ".valign"))
12154 result = (strEqual(value, "top") ? VALIGN_TOP :
12155 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12156 strEqual(value, "middle") ? VALIGN_MIDDLE :
12157 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12159 else if (strEqual(suffix, ".anim_mode"))
12161 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12162 string_has_parameter(value, "loop") ? ANIM_LOOP :
12163 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12164 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12165 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12166 string_has_parameter(value, "random") ? ANIM_RANDOM :
12167 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12168 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12169 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12170 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12171 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12172 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12173 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12174 string_has_parameter(value, "all") ? ANIM_ALL :
12175 string_has_parameter(value, "tiled") ? ANIM_TILED :
12176 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12179 if (string_has_parameter(value, "once"))
12180 result |= ANIM_ONCE;
12182 if (string_has_parameter(value, "reverse"))
12183 result |= ANIM_REVERSE;
12185 if (string_has_parameter(value, "opaque_player"))
12186 result |= ANIM_OPAQUE_PLAYER;
12188 if (string_has_parameter(value, "static_panel"))
12189 result |= ANIM_STATIC_PANEL;
12191 else if (strEqual(suffix, ".init_event") ||
12192 strEqual(suffix, ".anim_event"))
12194 result = get_anim_parameter_values(value);
12196 else if (strEqual(suffix, ".init_delay_action") ||
12197 strEqual(suffix, ".anim_delay_action") ||
12198 strEqual(suffix, ".post_delay_action") ||
12199 strEqual(suffix, ".init_event_action") ||
12200 strEqual(suffix, ".anim_event_action"))
12202 result = get_anim_action_parameter_value(value_raw);
12204 else if (strEqual(suffix, ".class"))
12206 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12207 get_hash_from_string(value));
12209 else if (strEqual(suffix, ".style"))
12211 result = STYLE_DEFAULT;
12213 if (string_has_parameter(value, "accurate_borders"))
12214 result |= STYLE_ACCURATE_BORDERS;
12216 if (string_has_parameter(value, "inner_corners"))
12217 result |= STYLE_INNER_CORNERS;
12219 if (string_has_parameter(value, "reverse"))
12220 result |= STYLE_REVERSE;
12222 if (string_has_parameter(value, "leftmost_position"))
12223 result |= STYLE_LEFTMOST_POSITION;
12225 if (string_has_parameter(value, "block_clicks"))
12226 result |= STYLE_BLOCK;
12228 if (string_has_parameter(value, "passthrough_clicks"))
12229 result |= STYLE_PASSTHROUGH;
12231 if (string_has_parameter(value, "multiple_actions"))
12232 result |= STYLE_MULTIPLE_ACTIONS;
12234 if (string_has_parameter(value, "consume_ce_event"))
12235 result |= STYLE_CONSUME_CE_EVENT;
12237 else if (strEqual(suffix, ".fade_mode"))
12239 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12240 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12241 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12242 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12243 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12244 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12245 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12246 FADE_MODE_DEFAULT);
12248 else if (strEqual(suffix, ".auto_delay_unit"))
12250 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12251 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12252 AUTO_DELAY_UNIT_DEFAULT);
12254 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12256 result = gfx.get_font_from_token_function(value);
12258 else // generic parameter of type integer or boolean
12260 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12261 type == TYPE_INTEGER ? get_integer_from_string(value) :
12262 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12263 ARG_UNDEFINED_VALUE);
12271 static int get_token_parameter_value(char *token, char *value_raw)
12275 if (token == NULL || value_raw == NULL)
12276 return ARG_UNDEFINED_VALUE;
12278 suffix = strrchr(token, '.');
12279 if (suffix == NULL)
12282 if (strEqual(suffix, ".element"))
12283 return getElementFromToken(value_raw);
12285 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12286 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12289 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12290 boolean ignore_defaults)
12294 for (i = 0; image_config_vars[i].token != NULL; i++)
12296 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12298 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12299 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12303 *image_config_vars[i].value =
12304 get_token_parameter_value(image_config_vars[i].token, value);
12308 void InitMenuDesignSettings_Static(void)
12310 // always start with reliable default values from static default config
12311 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12314 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12318 // the following initializes hierarchical values from static configuration
12320 // special case: initialize "ARG_DEFAULT" values in static default config
12321 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12322 titlescreen_initial_first_default.fade_mode =
12323 title_initial_first_default.fade_mode;
12324 titlescreen_initial_first_default.fade_delay =
12325 title_initial_first_default.fade_delay;
12326 titlescreen_initial_first_default.post_delay =
12327 title_initial_first_default.post_delay;
12328 titlescreen_initial_first_default.auto_delay =
12329 title_initial_first_default.auto_delay;
12330 titlescreen_initial_first_default.auto_delay_unit =
12331 title_initial_first_default.auto_delay_unit;
12332 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12333 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12334 titlescreen_first_default.post_delay = title_first_default.post_delay;
12335 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12336 titlescreen_first_default.auto_delay_unit =
12337 title_first_default.auto_delay_unit;
12338 titlemessage_initial_first_default.fade_mode =
12339 title_initial_first_default.fade_mode;
12340 titlemessage_initial_first_default.fade_delay =
12341 title_initial_first_default.fade_delay;
12342 titlemessage_initial_first_default.post_delay =
12343 title_initial_first_default.post_delay;
12344 titlemessage_initial_first_default.auto_delay =
12345 title_initial_first_default.auto_delay;
12346 titlemessage_initial_first_default.auto_delay_unit =
12347 title_initial_first_default.auto_delay_unit;
12348 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12349 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12350 titlemessage_first_default.post_delay = title_first_default.post_delay;
12351 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12352 titlemessage_first_default.auto_delay_unit =
12353 title_first_default.auto_delay_unit;
12355 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12356 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12357 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12358 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12359 titlescreen_initial_default.auto_delay_unit =
12360 title_initial_default.auto_delay_unit;
12361 titlescreen_default.fade_mode = title_default.fade_mode;
12362 titlescreen_default.fade_delay = title_default.fade_delay;
12363 titlescreen_default.post_delay = title_default.post_delay;
12364 titlescreen_default.auto_delay = title_default.auto_delay;
12365 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12366 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12367 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12368 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12369 titlemessage_initial_default.auto_delay_unit =
12370 title_initial_default.auto_delay_unit;
12371 titlemessage_default.fade_mode = title_default.fade_mode;
12372 titlemessage_default.fade_delay = title_default.fade_delay;
12373 titlemessage_default.post_delay = title_default.post_delay;
12374 titlemessage_default.auto_delay = title_default.auto_delay;
12375 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12377 // special case: initialize "ARG_DEFAULT" values in static default config
12378 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12379 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12381 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12382 titlescreen_first[i] = titlescreen_first_default;
12383 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12384 titlemessage_first[i] = titlemessage_first_default;
12386 titlescreen_initial[i] = titlescreen_initial_default;
12387 titlescreen[i] = titlescreen_default;
12388 titlemessage_initial[i] = titlemessage_initial_default;
12389 titlemessage[i] = titlemessage_default;
12392 // special case: initialize "ARG_DEFAULT" values in static default config
12393 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12394 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12396 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12399 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12400 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12401 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12404 // special case: initialize "ARG_DEFAULT" values in static default config
12405 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12406 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12408 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12409 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12410 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12412 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12415 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12419 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12423 struct XY *dst, *src;
12425 game_buttons_xy[] =
12427 { &game.button.save, &game.button.stop },
12428 { &game.button.pause2, &game.button.pause },
12429 { &game.button.load, &game.button.play },
12430 { &game.button.undo, &game.button.stop },
12431 { &game.button.redo, &game.button.play },
12437 // special case: initialize later added SETUP list size from LEVELS value
12438 if (menu.list_size[GAME_MODE_SETUP] == -1)
12439 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12441 // set default position for snapshot buttons to stop/pause/play buttons
12442 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12443 if ((*game_buttons_xy[i].dst).x == -1 &&
12444 (*game_buttons_xy[i].dst).y == -1)
12445 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12447 // --------------------------------------------------------------------------
12448 // dynamic viewports (including playfield margins, borders and alignments)
12449 // --------------------------------------------------------------------------
12451 // dynamic viewports currently only supported for landscape mode
12452 int display_width = MAX(video.display_width, video.display_height);
12453 int display_height = MIN(video.display_width, video.display_height);
12455 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12457 struct RectWithBorder *vp_window = &viewport.window[i];
12458 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12459 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12460 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12461 boolean dynamic_window_width = (vp_window->min_width != -1);
12462 boolean dynamic_window_height = (vp_window->min_height != -1);
12463 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12464 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12466 // adjust window size if min/max width/height is specified
12468 if (vp_window->min_width != -1)
12470 int window_width = display_width;
12472 // when using static window height, use aspect ratio of display
12473 if (vp_window->min_height == -1)
12474 window_width = vp_window->height * display_width / display_height;
12476 vp_window->width = MAX(vp_window->min_width, window_width);
12479 if (vp_window->min_height != -1)
12481 int window_height = display_height;
12483 // when using static window width, use aspect ratio of display
12484 if (vp_window->min_width == -1)
12485 window_height = vp_window->width * display_height / display_width;
12487 vp_window->height = MAX(vp_window->min_height, window_height);
12490 if (vp_window->max_width != -1)
12491 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12493 if (vp_window->max_height != -1)
12494 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12496 int playfield_width = vp_window->width;
12497 int playfield_height = vp_window->height;
12499 // adjust playfield size and position according to specified margins
12501 playfield_width -= vp_playfield->margin_left;
12502 playfield_width -= vp_playfield->margin_right;
12504 playfield_height -= vp_playfield->margin_top;
12505 playfield_height -= vp_playfield->margin_bottom;
12507 // adjust playfield size if min/max width/height is specified
12509 if (vp_playfield->min_width != -1)
12510 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12512 if (vp_playfield->min_height != -1)
12513 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12515 if (vp_playfield->max_width != -1)
12516 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12518 if (vp_playfield->max_height != -1)
12519 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12521 // adjust playfield position according to specified alignment
12523 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12524 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12525 else if (vp_playfield->align == ALIGN_CENTER)
12526 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12527 else if (vp_playfield->align == ALIGN_RIGHT)
12528 vp_playfield->x += playfield_width - vp_playfield->width;
12530 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12531 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12532 else if (vp_playfield->valign == VALIGN_MIDDLE)
12533 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12534 else if (vp_playfield->valign == VALIGN_BOTTOM)
12535 vp_playfield->y += playfield_height - vp_playfield->height;
12537 vp_playfield->x += vp_playfield->margin_left;
12538 vp_playfield->y += vp_playfield->margin_top;
12540 // adjust individual playfield borders if only default border is specified
12542 if (vp_playfield->border_left == -1)
12543 vp_playfield->border_left = vp_playfield->border_size;
12544 if (vp_playfield->border_right == -1)
12545 vp_playfield->border_right = vp_playfield->border_size;
12546 if (vp_playfield->border_top == -1)
12547 vp_playfield->border_top = vp_playfield->border_size;
12548 if (vp_playfield->border_bottom == -1)
12549 vp_playfield->border_bottom = vp_playfield->border_size;
12551 // set dynamic playfield borders if borders are specified as undefined
12552 // (but only if window size was dynamic and playfield size was static)
12554 if (dynamic_window_width && !dynamic_playfield_width)
12556 if (vp_playfield->border_left == -1)
12558 vp_playfield->border_left = (vp_playfield->x -
12559 vp_playfield->margin_left);
12560 vp_playfield->x -= vp_playfield->border_left;
12561 vp_playfield->width += vp_playfield->border_left;
12564 if (vp_playfield->border_right == -1)
12566 vp_playfield->border_right = (vp_window->width -
12568 vp_playfield->width -
12569 vp_playfield->margin_right);
12570 vp_playfield->width += vp_playfield->border_right;
12574 if (dynamic_window_height && !dynamic_playfield_height)
12576 if (vp_playfield->border_top == -1)
12578 vp_playfield->border_top = (vp_playfield->y -
12579 vp_playfield->margin_top);
12580 vp_playfield->y -= vp_playfield->border_top;
12581 vp_playfield->height += vp_playfield->border_top;
12584 if (vp_playfield->border_bottom == -1)
12586 vp_playfield->border_bottom = (vp_window->height -
12588 vp_playfield->height -
12589 vp_playfield->margin_bottom);
12590 vp_playfield->height += vp_playfield->border_bottom;
12594 // adjust playfield size to be a multiple of a defined alignment tile size
12596 int align_size = vp_playfield->align_size;
12597 int playfield_xtiles = vp_playfield->width / align_size;
12598 int playfield_ytiles = vp_playfield->height / align_size;
12599 int playfield_width_corrected = playfield_xtiles * align_size;
12600 int playfield_height_corrected = playfield_ytiles * align_size;
12601 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12602 i == GFX_SPECIAL_ARG_EDITOR);
12604 if (is_playfield_mode &&
12605 dynamic_playfield_width &&
12606 vp_playfield->width != playfield_width_corrected)
12608 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12610 vp_playfield->width = playfield_width_corrected;
12612 if (vp_playfield->align == ALIGN_LEFT)
12614 vp_playfield->border_left += playfield_xdiff;
12616 else if (vp_playfield->align == ALIGN_RIGHT)
12618 vp_playfield->border_right += playfield_xdiff;
12620 else if (vp_playfield->align == ALIGN_CENTER)
12622 int border_left_diff = playfield_xdiff / 2;
12623 int border_right_diff = playfield_xdiff - border_left_diff;
12625 vp_playfield->border_left += border_left_diff;
12626 vp_playfield->border_right += border_right_diff;
12630 if (is_playfield_mode &&
12631 dynamic_playfield_height &&
12632 vp_playfield->height != playfield_height_corrected)
12634 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12636 vp_playfield->height = playfield_height_corrected;
12638 if (vp_playfield->valign == VALIGN_TOP)
12640 vp_playfield->border_top += playfield_ydiff;
12642 else if (vp_playfield->align == VALIGN_BOTTOM)
12644 vp_playfield->border_right += playfield_ydiff;
12646 else if (vp_playfield->align == VALIGN_MIDDLE)
12648 int border_top_diff = playfield_ydiff / 2;
12649 int border_bottom_diff = playfield_ydiff - border_top_diff;
12651 vp_playfield->border_top += border_top_diff;
12652 vp_playfield->border_bottom += border_bottom_diff;
12656 // adjust door positions according to specified alignment
12658 for (j = 0; j < 2; j++)
12660 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12662 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12663 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12664 else if (vp_door->align == ALIGN_CENTER)
12665 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12666 else if (vp_door->align == ALIGN_RIGHT)
12667 vp_door->x += vp_window->width - vp_door->width;
12669 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12670 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12671 else if (vp_door->valign == VALIGN_MIDDLE)
12672 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12673 else if (vp_door->valign == VALIGN_BOTTOM)
12674 vp_door->y += vp_window->height - vp_door->height;
12679 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12683 struct XYTileSize *dst, *src;
12686 editor_buttons_xy[] =
12689 &editor.button.element_left, &editor.palette.element_left,
12690 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12693 &editor.button.element_middle, &editor.palette.element_middle,
12694 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12697 &editor.button.element_right, &editor.palette.element_right,
12698 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12705 // set default position for element buttons to element graphics
12706 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12708 if ((*editor_buttons_xy[i].dst).x == -1 &&
12709 (*editor_buttons_xy[i].dst).y == -1)
12711 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12713 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12715 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12719 // adjust editor palette rows and columns if specified to be dynamic
12721 if (editor.palette.cols == -1)
12723 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12724 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12725 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12727 editor.palette.cols = (vp_width - sc_width) / bt_width;
12729 if (editor.palette.x == -1)
12731 int palette_width = editor.palette.cols * bt_width + sc_width;
12733 editor.palette.x = (vp_width - palette_width) / 2;
12737 if (editor.palette.rows == -1)
12739 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12740 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12741 int tx_height = getFontHeight(FONT_TEXT_2);
12743 editor.palette.rows = (vp_height - tx_height) / bt_height;
12745 if (editor.palette.y == -1)
12747 int palette_height = editor.palette.rows * bt_height + tx_height;
12749 editor.palette.y = (vp_height - palette_height) / 2;
12754 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12755 boolean initialize)
12757 // special case: check if network and preview player positions are redefined,
12758 // to compare this later against the main menu level preview being redefined
12759 struct TokenIntPtrInfo menu_config_players[] =
12761 { "main.network_players.x", &menu.main.network_players.redefined },
12762 { "main.network_players.y", &menu.main.network_players.redefined },
12763 { "main.preview_players.x", &menu.main.preview_players.redefined },
12764 { "main.preview_players.y", &menu.main.preview_players.redefined },
12765 { "preview.x", &preview.redefined },
12766 { "preview.y", &preview.redefined }
12772 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12773 *menu_config_players[i].value = FALSE;
12777 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12778 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12779 *menu_config_players[i].value = TRUE;
12783 static void InitMenuDesignSettings_PreviewPlayers(void)
12785 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12788 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12790 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12793 static void LoadMenuDesignSettingsFromFilename(char *filename)
12795 static struct TitleFadingInfo tfi;
12796 static struct TitleMessageInfo tmi;
12797 static struct TokenInfo title_tokens[] =
12799 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12800 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12801 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12802 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12803 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12807 static struct TokenInfo titlemessage_tokens[] =
12809 { TYPE_INTEGER, &tmi.x, ".x" },
12810 { TYPE_INTEGER, &tmi.y, ".y" },
12811 { TYPE_INTEGER, &tmi.width, ".width" },
12812 { TYPE_INTEGER, &tmi.height, ".height" },
12813 { TYPE_INTEGER, &tmi.chars, ".chars" },
12814 { TYPE_INTEGER, &tmi.lines, ".lines" },
12815 { TYPE_INTEGER, &tmi.align, ".align" },
12816 { TYPE_INTEGER, &tmi.valign, ".valign" },
12817 { TYPE_INTEGER, &tmi.font, ".font" },
12818 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12819 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12820 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12821 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12822 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12823 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12824 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12825 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12826 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12832 struct TitleFadingInfo *info;
12837 // initialize first titles from "enter screen" definitions, if defined
12838 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12839 { &title_first_default, "menu.enter_screen.TITLE" },
12841 // initialize title screens from "next screen" definitions, if defined
12842 { &title_initial_default, "menu.next_screen.TITLE" },
12843 { &title_default, "menu.next_screen.TITLE" },
12849 struct TitleMessageInfo *array;
12852 titlemessage_arrays[] =
12854 // initialize first titles from "enter screen" definitions, if defined
12855 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12856 { titlescreen_first, "menu.enter_screen.TITLE" },
12857 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12858 { titlemessage_first, "menu.enter_screen.TITLE" },
12860 // initialize titles from "next screen" definitions, if defined
12861 { titlescreen_initial, "menu.next_screen.TITLE" },
12862 { titlescreen, "menu.next_screen.TITLE" },
12863 { titlemessage_initial, "menu.next_screen.TITLE" },
12864 { titlemessage, "menu.next_screen.TITLE" },
12866 // overwrite titles with title definitions, if defined
12867 { titlescreen_initial_first, "[title_initial]" },
12868 { titlescreen_first, "[title]" },
12869 { titlemessage_initial_first, "[title_initial]" },
12870 { titlemessage_first, "[title]" },
12872 { titlescreen_initial, "[title_initial]" },
12873 { titlescreen, "[title]" },
12874 { titlemessage_initial, "[title_initial]" },
12875 { titlemessage, "[title]" },
12877 // overwrite titles with title screen/message definitions, if defined
12878 { titlescreen_initial_first, "[titlescreen_initial]" },
12879 { titlescreen_first, "[titlescreen]" },
12880 { titlemessage_initial_first, "[titlemessage_initial]" },
12881 { titlemessage_first, "[titlemessage]" },
12883 { titlescreen_initial, "[titlescreen_initial]" },
12884 { titlescreen, "[titlescreen]" },
12885 { titlemessage_initial, "[titlemessage_initial]" },
12886 { titlemessage, "[titlemessage]" },
12890 SetupFileHash *setup_file_hash;
12893 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12896 // the following initializes hierarchical values from dynamic configuration
12898 // special case: initialize with default values that may be overwritten
12899 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12900 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12902 struct TokenIntPtrInfo menu_config[] =
12904 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12905 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12906 { "menu.list_size", &menu.list_size[i] }
12909 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12911 char *token = menu_config[j].token;
12912 char *value = getHashEntry(setup_file_hash, token);
12915 *menu_config[j].value = get_integer_from_string(value);
12919 // special case: initialize with default values that may be overwritten
12920 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12921 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12923 struct TokenIntPtrInfo menu_config[] =
12925 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12926 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12927 { "menu.list_size.INFO", &menu.list_size_info[i] },
12928 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12929 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12932 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12934 char *token = menu_config[j].token;
12935 char *value = getHashEntry(setup_file_hash, token);
12938 *menu_config[j].value = get_integer_from_string(value);
12942 // special case: initialize with default values that may be overwritten
12943 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12944 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12946 struct TokenIntPtrInfo menu_config[] =
12948 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12949 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12952 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12954 char *token = menu_config[j].token;
12955 char *value = getHashEntry(setup_file_hash, token);
12958 *menu_config[j].value = get_integer_from_string(value);
12962 // special case: initialize with default values that may be overwritten
12963 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12964 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12966 struct TokenIntPtrInfo menu_config[] =
12968 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12969 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12970 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12971 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12972 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12973 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12974 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12975 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12976 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12977 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12980 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12982 char *token = menu_config[j].token;
12983 char *value = getHashEntry(setup_file_hash, token);
12986 *menu_config[j].value = get_integer_from_string(value);
12990 // special case: initialize with default values that may be overwritten
12991 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12992 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12994 struct TokenIntPtrInfo menu_config[] =
12996 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12997 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12998 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12999 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13000 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13001 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13002 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13003 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13004 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13007 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13009 char *token = menu_config[j].token;
13010 char *value = getHashEntry(setup_file_hash, token);
13013 *menu_config[j].value = get_token_parameter_value(token, value);
13017 // special case: initialize with default values that may be overwritten
13018 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13019 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13023 char *token_prefix;
13024 struct RectWithBorder *struct_ptr;
13028 { "viewport.window", &viewport.window[i] },
13029 { "viewport.playfield", &viewport.playfield[i] },
13030 { "viewport.door_1", &viewport.door_1[i] },
13031 { "viewport.door_2", &viewport.door_2[i] }
13034 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13036 struct TokenIntPtrInfo vp_config[] =
13038 { ".x", &vp_struct[j].struct_ptr->x },
13039 { ".y", &vp_struct[j].struct_ptr->y },
13040 { ".width", &vp_struct[j].struct_ptr->width },
13041 { ".height", &vp_struct[j].struct_ptr->height },
13042 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13043 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13044 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13045 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13046 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13047 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13048 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13049 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13050 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13051 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13052 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13053 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13054 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13055 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13056 { ".align", &vp_struct[j].struct_ptr->align },
13057 { ".valign", &vp_struct[j].struct_ptr->valign }
13060 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13062 char *token = getStringCat2(vp_struct[j].token_prefix,
13063 vp_config[k].token);
13064 char *value = getHashEntry(setup_file_hash, token);
13067 *vp_config[k].value = get_token_parameter_value(token, value);
13074 // special case: initialize with default values that may be overwritten
13075 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13076 for (i = 0; title_info[i].info != NULL; i++)
13078 struct TitleFadingInfo *info = title_info[i].info;
13079 char *base_token = title_info[i].text;
13081 for (j = 0; title_tokens[j].type != -1; j++)
13083 char *token = getStringCat2(base_token, title_tokens[j].text);
13084 char *value = getHashEntry(setup_file_hash, token);
13088 int parameter_value = get_token_parameter_value(token, value);
13092 *(int *)title_tokens[j].value = (int)parameter_value;
13101 // special case: initialize with default values that may be overwritten
13102 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13103 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13105 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13106 char *base_token = titlemessage_arrays[i].text;
13108 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13110 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13111 char *value = getHashEntry(setup_file_hash, token);
13115 int parameter_value = get_token_parameter_value(token, value);
13117 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13121 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13122 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13124 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13134 // read (and overwrite with) values that may be specified in config file
13135 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13137 // special case: check if network and preview player positions are redefined
13138 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13140 freeSetupFileHash(setup_file_hash);
13143 void LoadMenuDesignSettings(void)
13145 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13147 InitMenuDesignSettings_Static();
13148 InitMenuDesignSettings_SpecialPreProcessing();
13149 InitMenuDesignSettings_PreviewPlayers();
13151 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13153 // first look for special settings configured in level series config
13154 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13156 if (fileExists(filename_base))
13157 LoadMenuDesignSettingsFromFilename(filename_base);
13160 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13162 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13163 LoadMenuDesignSettingsFromFilename(filename_local);
13165 InitMenuDesignSettings_SpecialPostProcessing();
13168 void LoadMenuDesignSettings_AfterGraphics(void)
13170 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13173 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13174 boolean ignore_defaults)
13178 for (i = 0; sound_config_vars[i].token != NULL; i++)
13180 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13182 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13183 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13187 *sound_config_vars[i].value =
13188 get_token_parameter_value(sound_config_vars[i].token, value);
13192 void InitSoundSettings_Static(void)
13194 // always start with reliable default values from static default config
13195 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13198 static void LoadSoundSettingsFromFilename(char *filename)
13200 SetupFileHash *setup_file_hash;
13202 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13205 // read (and overwrite with) values that may be specified in config file
13206 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13208 freeSetupFileHash(setup_file_hash);
13211 void LoadSoundSettings(void)
13213 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13215 InitSoundSettings_Static();
13217 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13219 // first look for special settings configured in level series config
13220 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13222 if (fileExists(filename_base))
13223 LoadSoundSettingsFromFilename(filename_base);
13226 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13228 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13229 LoadSoundSettingsFromFilename(filename_local);
13232 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13234 char *filename = getEditorSetupFilename();
13235 SetupFileList *setup_file_list, *list;
13236 SetupFileHash *element_hash;
13237 int num_unknown_tokens = 0;
13240 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13243 element_hash = newSetupFileHash();
13245 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13246 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13248 // determined size may be larger than needed (due to unknown elements)
13250 for (list = setup_file_list; list != NULL; list = list->next)
13253 // add space for up to 3 more elements for padding that may be needed
13254 *num_elements += 3;
13256 // free memory for old list of elements, if needed
13257 checked_free(*elements);
13259 // allocate memory for new list of elements
13260 *elements = checked_malloc(*num_elements * sizeof(int));
13263 for (list = setup_file_list; list != NULL; list = list->next)
13265 char *value = getHashEntry(element_hash, list->token);
13267 if (value == NULL) // try to find obsolete token mapping
13269 char *mapped_token = get_mapped_token(list->token);
13271 if (mapped_token != NULL)
13273 value = getHashEntry(element_hash, mapped_token);
13275 free(mapped_token);
13281 (*elements)[(*num_elements)++] = atoi(value);
13285 if (num_unknown_tokens == 0)
13288 Warn("unknown token(s) found in config file:");
13289 Warn("- config file: '%s'", filename);
13291 num_unknown_tokens++;
13294 Warn("- token: '%s'", list->token);
13298 if (num_unknown_tokens > 0)
13301 while (*num_elements % 4) // pad with empty elements, if needed
13302 (*elements)[(*num_elements)++] = EL_EMPTY;
13304 freeSetupFileList(setup_file_list);
13305 freeSetupFileHash(element_hash);
13308 for (i = 0; i < *num_elements; i++)
13309 Debug("editor", "element '%s' [%d]\n",
13310 element_info[(*elements)[i]].token_name, (*elements)[i]);
13314 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13317 SetupFileHash *setup_file_hash = NULL;
13318 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13319 char *filename_music, *filename_prefix, *filename_info;
13325 token_to_value_ptr[] =
13327 { "title_header", &tmp_music_file_info.title_header },
13328 { "artist_header", &tmp_music_file_info.artist_header },
13329 { "album_header", &tmp_music_file_info.album_header },
13330 { "year_header", &tmp_music_file_info.year_header },
13331 { "played_header", &tmp_music_file_info.played_header },
13333 { "title", &tmp_music_file_info.title },
13334 { "artist", &tmp_music_file_info.artist },
13335 { "album", &tmp_music_file_info.album },
13336 { "year", &tmp_music_file_info.year },
13337 { "played", &tmp_music_file_info.played },
13343 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13344 getCustomMusicFilename(basename));
13346 if (filename_music == NULL)
13349 // ---------- try to replace file extension ----------
13351 filename_prefix = getStringCopy(filename_music);
13352 if (strrchr(filename_prefix, '.') != NULL)
13353 *strrchr(filename_prefix, '.') = '\0';
13354 filename_info = getStringCat2(filename_prefix, ".txt");
13356 if (fileExists(filename_info))
13357 setup_file_hash = loadSetupFileHash(filename_info);
13359 free(filename_prefix);
13360 free(filename_info);
13362 if (setup_file_hash == NULL)
13364 // ---------- try to add file extension ----------
13366 filename_prefix = getStringCopy(filename_music);
13367 filename_info = getStringCat2(filename_prefix, ".txt");
13369 if (fileExists(filename_info))
13370 setup_file_hash = loadSetupFileHash(filename_info);
13372 free(filename_prefix);
13373 free(filename_info);
13376 if (setup_file_hash == NULL)
13379 // ---------- music file info found ----------
13381 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13383 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13385 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13387 *token_to_value_ptr[i].value_ptr =
13388 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13391 tmp_music_file_info.basename = getStringCopy(basename);
13392 tmp_music_file_info.music = music;
13393 tmp_music_file_info.is_sound = is_sound;
13395 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13396 *new_music_file_info = tmp_music_file_info;
13398 return new_music_file_info;
13401 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13403 return get_music_file_info_ext(basename, music, FALSE);
13406 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13408 return get_music_file_info_ext(basename, sound, TRUE);
13411 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13412 char *basename, boolean is_sound)
13414 for (; list != NULL; list = list->next)
13415 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13421 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13423 return music_info_listed_ext(list, basename, FALSE);
13426 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13428 return music_info_listed_ext(list, basename, TRUE);
13431 void LoadMusicInfo(void)
13433 int num_music_noconf = getMusicListSize_NoConf();
13434 int num_music = getMusicListSize();
13435 int num_sounds = getSoundListSize();
13436 struct FileInfo *music, *sound;
13437 struct MusicFileInfo *next, **new;
13441 while (music_file_info != NULL)
13443 next = music_file_info->next;
13445 checked_free(music_file_info->basename);
13447 checked_free(music_file_info->title_header);
13448 checked_free(music_file_info->artist_header);
13449 checked_free(music_file_info->album_header);
13450 checked_free(music_file_info->year_header);
13451 checked_free(music_file_info->played_header);
13453 checked_free(music_file_info->title);
13454 checked_free(music_file_info->artist);
13455 checked_free(music_file_info->album);
13456 checked_free(music_file_info->year);
13457 checked_free(music_file_info->played);
13459 free(music_file_info);
13461 music_file_info = next;
13464 new = &music_file_info;
13466 // get (configured or unconfigured) music file info for all levels
13467 for (i = leveldir_current->first_level;
13468 i <= leveldir_current->last_level; i++)
13472 if (levelset.music[i] != MUS_UNDEFINED)
13474 // get music file info for configured level music
13475 music_nr = levelset.music[i];
13477 else if (num_music_noconf > 0)
13479 // get music file info for unconfigured level music
13480 int level_pos = i - leveldir_current->first_level;
13482 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13489 char *basename = getMusicInfoEntryFilename(music_nr);
13491 if (basename == NULL)
13494 if (!music_info_listed(music_file_info, basename))
13496 *new = get_music_file_info(basename, music_nr);
13499 new = &(*new)->next;
13503 // get music file info for all remaining configured music files
13504 for (i = 0; i < num_music; i++)
13506 music = getMusicListEntry(i);
13508 if (music->filename == NULL)
13511 if (strEqual(music->filename, UNDEFINED_FILENAME))
13514 // a configured file may be not recognized as music
13515 if (!FileIsMusic(music->filename))
13518 if (!music_info_listed(music_file_info, music->filename))
13520 *new = get_music_file_info(music->filename, i);
13523 new = &(*new)->next;
13527 // get sound file info for all configured sound files
13528 for (i = 0; i < num_sounds; i++)
13530 sound = getSoundListEntry(i);
13532 if (sound->filename == NULL)
13535 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13538 // a configured file may be not recognized as sound
13539 if (!FileIsSound(sound->filename))
13542 if (!sound_info_listed(music_file_info, sound->filename))
13544 *new = get_sound_file_info(sound->filename, i);
13546 new = &(*new)->next;
13550 // add pointers to previous list nodes
13552 struct MusicFileInfo *node = music_file_info;
13554 while (node != NULL)
13557 node->next->prev = node;
13563 static void add_helpanim_entry(int element, int action, int direction,
13564 int delay, int *num_list_entries)
13566 struct HelpAnimInfo *new_list_entry;
13567 (*num_list_entries)++;
13570 checked_realloc(helpanim_info,
13571 *num_list_entries * sizeof(struct HelpAnimInfo));
13572 new_list_entry = &helpanim_info[*num_list_entries - 1];
13574 new_list_entry->element = element;
13575 new_list_entry->action = action;
13576 new_list_entry->direction = direction;
13577 new_list_entry->delay = delay;
13580 static void print_unknown_token(char *filename, char *token, int token_nr)
13585 Warn("unknown token(s) found in config file:");
13586 Warn("- config file: '%s'", filename);
13589 Warn("- token: '%s'", token);
13592 static void print_unknown_token_end(int token_nr)
13598 void LoadHelpAnimInfo(void)
13600 char *filename = getHelpAnimFilename();
13601 SetupFileList *setup_file_list = NULL, *list;
13602 SetupFileHash *element_hash, *action_hash, *direction_hash;
13603 int num_list_entries = 0;
13604 int num_unknown_tokens = 0;
13607 if (fileExists(filename))
13608 setup_file_list = loadSetupFileList(filename);
13610 if (setup_file_list == NULL)
13612 // use reliable default values from static configuration
13613 SetupFileList *insert_ptr;
13615 insert_ptr = setup_file_list =
13616 newSetupFileList(helpanim_config[0].token,
13617 helpanim_config[0].value);
13619 for (i = 1; helpanim_config[i].token; i++)
13620 insert_ptr = addListEntry(insert_ptr,
13621 helpanim_config[i].token,
13622 helpanim_config[i].value);
13625 element_hash = newSetupFileHash();
13626 action_hash = newSetupFileHash();
13627 direction_hash = newSetupFileHash();
13629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13630 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13632 for (i = 0; i < NUM_ACTIONS; i++)
13633 setHashEntry(action_hash, element_action_info[i].suffix,
13634 i_to_a(element_action_info[i].value));
13636 // do not store direction index (bit) here, but direction value!
13637 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13638 setHashEntry(direction_hash, element_direction_info[i].suffix,
13639 i_to_a(1 << element_direction_info[i].value));
13641 for (list = setup_file_list; list != NULL; list = list->next)
13643 char *element_token, *action_token, *direction_token;
13644 char *element_value, *action_value, *direction_value;
13645 int delay = atoi(list->value);
13647 if (strEqual(list->token, "end"))
13649 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13654 /* first try to break element into element/action/direction parts;
13655 if this does not work, also accept combined "element[.act][.dir]"
13656 elements (like "dynamite.active"), which are unique elements */
13658 if (strchr(list->token, '.') == NULL) // token contains no '.'
13660 element_value = getHashEntry(element_hash, list->token);
13661 if (element_value != NULL) // element found
13662 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13663 &num_list_entries);
13666 // no further suffixes found -- this is not an element
13667 print_unknown_token(filename, list->token, num_unknown_tokens++);
13673 // token has format "<prefix>.<something>"
13675 action_token = strchr(list->token, '.'); // suffix may be action ...
13676 direction_token = action_token; // ... or direction
13678 element_token = getStringCopy(list->token);
13679 *strchr(element_token, '.') = '\0';
13681 element_value = getHashEntry(element_hash, element_token);
13683 if (element_value == NULL) // this is no element
13685 element_value = getHashEntry(element_hash, list->token);
13686 if (element_value != NULL) // combined element found
13687 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13688 &num_list_entries);
13690 print_unknown_token(filename, list->token, num_unknown_tokens++);
13692 free(element_token);
13697 action_value = getHashEntry(action_hash, action_token);
13699 if (action_value != NULL) // action found
13701 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13702 &num_list_entries);
13704 free(element_token);
13709 direction_value = getHashEntry(direction_hash, direction_token);
13711 if (direction_value != NULL) // direction found
13713 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13714 &num_list_entries);
13716 free(element_token);
13721 if (strchr(action_token + 1, '.') == NULL)
13723 // no further suffixes found -- this is not an action nor direction
13725 element_value = getHashEntry(element_hash, list->token);
13726 if (element_value != NULL) // combined element found
13727 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13728 &num_list_entries);
13730 print_unknown_token(filename, list->token, num_unknown_tokens++);
13732 free(element_token);
13737 // token has format "<prefix>.<suffix>.<something>"
13739 direction_token = strchr(action_token + 1, '.');
13741 action_token = getStringCopy(action_token);
13742 *strchr(action_token + 1, '.') = '\0';
13744 action_value = getHashEntry(action_hash, action_token);
13746 if (action_value == NULL) // this is no action
13748 element_value = getHashEntry(element_hash, list->token);
13749 if (element_value != NULL) // combined element found
13750 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13751 &num_list_entries);
13753 print_unknown_token(filename, list->token, num_unknown_tokens++);
13755 free(element_token);
13756 free(action_token);
13761 direction_value = getHashEntry(direction_hash, direction_token);
13763 if (direction_value != NULL) // direction found
13765 add_helpanim_entry(atoi(element_value), atoi(action_value),
13766 atoi(direction_value), delay, &num_list_entries);
13768 free(element_token);
13769 free(action_token);
13774 // this is no direction
13776 element_value = getHashEntry(element_hash, list->token);
13777 if (element_value != NULL) // combined element found
13778 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13779 &num_list_entries);
13781 print_unknown_token(filename, list->token, num_unknown_tokens++);
13783 free(element_token);
13784 free(action_token);
13787 print_unknown_token_end(num_unknown_tokens);
13789 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13790 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13792 freeSetupFileList(setup_file_list);
13793 freeSetupFileHash(element_hash);
13794 freeSetupFileHash(action_hash);
13795 freeSetupFileHash(direction_hash);
13798 for (i = 0; i < num_list_entries; i++)
13799 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13800 EL_NAME(helpanim_info[i].element),
13801 helpanim_info[i].element,
13802 helpanim_info[i].action,
13803 helpanim_info[i].direction,
13804 helpanim_info[i].delay);
13808 void LoadHelpTextInfo(void)
13810 char *filename = getHelpTextFilename();
13813 if (helptext_info != NULL)
13815 freeSetupFileHash(helptext_info);
13816 helptext_info = NULL;
13819 if (fileExists(filename))
13820 helptext_info = loadSetupFileHash(filename);
13822 if (helptext_info == NULL)
13824 // use reliable default values from static configuration
13825 helptext_info = newSetupFileHash();
13827 for (i = 0; helptext_config[i].token; i++)
13828 setHashEntry(helptext_info,
13829 helptext_config[i].token,
13830 helptext_config[i].value);
13834 BEGIN_HASH_ITERATION(helptext_info, itr)
13836 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13837 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13839 END_HASH_ITERATION(hash, itr)
13844 // ----------------------------------------------------------------------------
13846 // ----------------------------------------------------------------------------
13848 #define MAX_NUM_CONVERT_LEVELS 1000
13850 void ConvertLevels(void)
13852 static LevelDirTree *convert_leveldir = NULL;
13853 static int convert_level_nr = -1;
13854 static int num_levels_handled = 0;
13855 static int num_levels_converted = 0;
13856 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13859 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13860 global.convert_leveldir);
13862 if (convert_leveldir == NULL)
13863 Fail("no such level identifier: '%s'", global.convert_leveldir);
13865 leveldir_current = convert_leveldir;
13867 if (global.convert_level_nr != -1)
13869 convert_leveldir->first_level = global.convert_level_nr;
13870 convert_leveldir->last_level = global.convert_level_nr;
13873 convert_level_nr = convert_leveldir->first_level;
13875 PrintLine("=", 79);
13876 Print("Converting levels\n");
13877 PrintLine("-", 79);
13878 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13879 Print("Level series name: '%s'\n", convert_leveldir->name);
13880 Print("Level series author: '%s'\n", convert_leveldir->author);
13881 Print("Number of levels: %d\n", convert_leveldir->levels);
13882 PrintLine("=", 79);
13885 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13886 levels_failed[i] = FALSE;
13888 while (convert_level_nr <= convert_leveldir->last_level)
13890 char *level_filename;
13893 level_nr = convert_level_nr++;
13895 Print("Level %03d: ", level_nr);
13897 LoadLevel(level_nr);
13898 if (level.no_level_file || level.no_valid_file)
13900 Print("(no level)\n");
13904 Print("converting level ... ");
13907 // special case: conversion of some EMC levels as requested by ACME
13908 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13911 level_filename = getDefaultLevelFilename(level_nr);
13912 new_level = !fileExists(level_filename);
13916 SaveLevel(level_nr);
13918 num_levels_converted++;
13920 Print("converted.\n");
13924 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13925 levels_failed[level_nr] = TRUE;
13927 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13930 num_levels_handled++;
13934 PrintLine("=", 79);
13935 Print("Number of levels handled: %d\n", num_levels_handled);
13936 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13937 (num_levels_handled ?
13938 num_levels_converted * 100 / num_levels_handled : 0));
13939 PrintLine("-", 79);
13940 Print("Summary (for automatic parsing by scripts):\n");
13941 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13942 convert_leveldir->identifier, num_levels_converted,
13943 num_levels_handled,
13944 (num_levels_handled ?
13945 num_levels_converted * 100 / num_levels_handled : 0));
13947 if (num_levels_handled != num_levels_converted)
13949 Print(", FAILED:");
13950 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13951 if (levels_failed[i])
13956 PrintLine("=", 79);
13958 CloseAllAndExit(0);
13962 // ----------------------------------------------------------------------------
13963 // create and save images for use in level sketches (raw BMP format)
13964 // ----------------------------------------------------------------------------
13966 void CreateLevelSketchImages(void)
13972 InitElementPropertiesGfxElement();
13974 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13975 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13977 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13979 int element = getMappedElement(i);
13980 char basename1[16];
13981 char basename2[16];
13985 sprintf(basename1, "%04d.bmp", i);
13986 sprintf(basename2, "%04ds.bmp", i);
13988 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13989 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13991 DrawSizedElement(0, 0, element, TILESIZE);
13992 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13994 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13995 Fail("cannot save level sketch image file '%s'", filename1);
13997 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13998 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14000 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14001 Fail("cannot save level sketch image file '%s'", filename2);
14006 // create corresponding SQL statements (for normal and small images)
14009 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14010 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14013 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14014 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14016 // optional: create content for forum level sketch demonstration post
14018 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14021 FreeBitmap(bitmap1);
14022 FreeBitmap(bitmap2);
14025 fprintf(stderr, "\n");
14027 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14029 CloseAllAndExit(0);
14033 // ----------------------------------------------------------------------------
14034 // create and save images for element collecting animations (raw BMP format)
14035 // ----------------------------------------------------------------------------
14037 static boolean createCollectImage(int element)
14039 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14042 void CreateCollectElementImages(void)
14046 int anim_frames = num_steps - 1;
14047 int tile_size = TILESIZE;
14048 int anim_width = tile_size * anim_frames;
14049 int anim_height = tile_size;
14050 int num_collect_images = 0;
14051 int pos_collect_images = 0;
14053 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14054 if (createCollectImage(i))
14055 num_collect_images++;
14057 Info("Creating %d element collecting animation images ...",
14058 num_collect_images);
14060 int dst_width = anim_width * 2;
14061 int dst_height = anim_height * num_collect_images / 2;
14062 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14063 char *basename_bmp = "RocksCollect.bmp";
14064 char *basename_png = "RocksCollect.png";
14065 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14066 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14067 int len_filename_bmp = strlen(filename_bmp);
14068 int len_filename_png = strlen(filename_png);
14069 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14070 char cmd_convert[max_command_len];
14072 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14076 // force using RGBA surface for destination bitmap
14077 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14078 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14080 dst_bitmap->surface =
14081 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14083 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14085 if (!createCollectImage(i))
14088 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14089 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14090 int graphic = el2img(i);
14091 char *token_name = element_info[i].token_name;
14092 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14093 Bitmap *src_bitmap;
14096 Info("- creating collecting image for '%s' ...", token_name);
14098 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14100 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14101 tile_size, tile_size, 0, 0);
14103 // force using RGBA surface for temporary bitmap (using transparent black)
14104 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14105 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14107 tmp_bitmap->surface =
14108 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14110 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14112 for (j = 0; j < anim_frames; j++)
14114 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14115 int frame_size = frame_size_final * num_steps;
14116 int offset = (tile_size - frame_size_final) / 2;
14117 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14119 while (frame_size > frame_size_final)
14123 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14125 FreeBitmap(frame_bitmap);
14127 frame_bitmap = half_bitmap;
14130 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14131 frame_size_final, frame_size_final,
14132 dst_x + j * tile_size + offset, dst_y + offset);
14134 FreeBitmap(frame_bitmap);
14137 tmp_bitmap->surface_masked = NULL;
14139 FreeBitmap(tmp_bitmap);
14141 pos_collect_images++;
14144 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14145 Fail("cannot save element collecting image file '%s'", filename_bmp);
14147 FreeBitmap(dst_bitmap);
14149 Info("Converting image file from BMP to PNG ...");
14151 if (system(cmd_convert) != 0)
14152 Fail("converting image file failed");
14154 unlink(filename_bmp);
14158 CloseAllAndExit(0);
14162 // ----------------------------------------------------------------------------
14163 // create and save images for custom and group elements (raw BMP format)
14164 // ----------------------------------------------------------------------------
14166 void CreateCustomElementImages(char *directory)
14168 char *src_basename = "RocksCE-template.ilbm";
14169 char *dst_basename = "RocksCE.bmp";
14170 char *src_filename = getPath2(directory, src_basename);
14171 char *dst_filename = getPath2(directory, dst_basename);
14172 Bitmap *src_bitmap;
14174 int yoffset_ce = 0;
14175 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14178 InitVideoDefaults();
14180 ReCreateBitmap(&backbuffer, video.width, video.height);
14182 src_bitmap = LoadImage(src_filename);
14184 bitmap = CreateBitmap(TILEX * 16 * 2,
14185 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14188 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14195 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14196 TILEX * x, TILEY * y + yoffset_ce);
14198 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14200 TILEX * x + TILEX * 16,
14201 TILEY * y + yoffset_ce);
14203 for (j = 2; j >= 0; j--)
14207 BlitBitmap(src_bitmap, bitmap,
14208 TILEX + c * 7, 0, 6, 10,
14209 TILEX * x + 6 + j * 7,
14210 TILEY * y + 11 + yoffset_ce);
14212 BlitBitmap(src_bitmap, bitmap,
14213 TILEX + c * 8, TILEY, 6, 10,
14214 TILEX * 16 + TILEX * x + 6 + j * 8,
14215 TILEY * y + 10 + yoffset_ce);
14221 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14228 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14229 TILEX * x, TILEY * y + yoffset_ge);
14231 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14233 TILEX * x + TILEX * 16,
14234 TILEY * y + yoffset_ge);
14236 for (j = 1; j >= 0; j--)
14240 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14241 TILEX * x + 6 + j * 10,
14242 TILEY * y + 11 + yoffset_ge);
14244 BlitBitmap(src_bitmap, bitmap,
14245 TILEX + c * 8, TILEY + 12, 6, 10,
14246 TILEX * 16 + TILEX * x + 10 + j * 8,
14247 TILEY * y + 10 + yoffset_ge);
14253 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14254 Fail("cannot save CE graphics file '%s'", dst_filename);
14256 FreeBitmap(bitmap);
14258 CloseAllAndExit(0);