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
289 static struct LevelFileConfigInfo chunk_config_ELEM[] =
291 // (these values are the same for each player)
294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
295 &li.block_last_field, FALSE // default case for EM levels
299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
300 &li.sp_block_last_field, TRUE // default case for SP levels
304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
305 &li.instant_relocation, FALSE
309 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
310 &li.can_pass_to_walkable, FALSE
314 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
315 &li.block_snap_field, TRUE
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
320 &li.continuous_snapping, TRUE
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
325 &li.shifted_relocation, FALSE
329 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
330 &li.lazy_relocation, FALSE
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
335 &li.finish_dig_collect, TRUE
339 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
340 &li.keep_walkable_ce, FALSE
343 // (these values are different for each player)
346 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
347 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
352 &li.initial_player_gravity[0], FALSE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
357 &li.use_start_element[0], FALSE
361 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
362 &li.start_element[0], EL_PLAYER_1
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
367 &li.use_artwork_element[0], FALSE
371 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
372 &li.artwork_element[0], EL_PLAYER_1
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
377 &li.use_explosion_element[0], FALSE
381 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
382 &li.explosion_element[0], EL_PLAYER_1
386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
387 &li.use_initial_inventory[0], FALSE
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
392 &li.initial_inventory_size[0], 1
396 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
397 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
398 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
403 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
404 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
409 &li.initial_player_gravity[1], FALSE
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
414 &li.use_start_element[1], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
419 &li.start_element[1], EL_PLAYER_2
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
424 &li.use_artwork_element[1], FALSE
428 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
429 &li.artwork_element[1], EL_PLAYER_2
433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
434 &li.use_explosion_element[1], FALSE
438 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
439 &li.explosion_element[1], EL_PLAYER_2
443 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
444 &li.use_initial_inventory[1], FALSE
448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
449 &li.initial_inventory_size[1], 1
453 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
454 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
455 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
460 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
461 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
466 &li.initial_player_gravity[2], FALSE
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
471 &li.use_start_element[2], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
476 &li.start_element[2], EL_PLAYER_3
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
481 &li.use_artwork_element[2], FALSE
485 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
486 &li.artwork_element[2], EL_PLAYER_3
490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
491 &li.use_explosion_element[2], FALSE
495 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
496 &li.explosion_element[2], EL_PLAYER_3
500 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
501 &li.use_initial_inventory[2], FALSE
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
506 &li.initial_inventory_size[2], 1
510 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
511 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
512 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
517 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
518 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
523 &li.initial_player_gravity[3], FALSE
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
528 &li.use_start_element[3], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
533 &li.start_element[3], EL_PLAYER_4
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
538 &li.use_artwork_element[3], FALSE
542 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
543 &li.artwork_element[3], EL_PLAYER_4
547 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
548 &li.use_explosion_element[3], FALSE
552 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
553 &li.explosion_element[3], EL_PLAYER_4
557 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
558 &li.use_initial_inventory[3], FALSE
562 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
563 &li.initial_inventory_size[3], 1
567 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
568 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
569 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
572 // (these values are only valid for BD style levels)
575 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
576 &li.bd_diagonal_movements, FALSE
581 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
582 &li.score[SC_DIAMOND_EXTRA], 20
585 // (the following values are related to various game elements)
589 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
590 &li.score[SC_EMERALD], 10
595 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
596 &li.score[SC_DIAMOND], 10
601 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
602 &li.score[SC_BUG], 10
607 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
608 &li.score[SC_SPACESHIP], 10
613 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
614 &li.score[SC_PACMAN], 10
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
620 &li.score[SC_NUT], 10
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_DYNAMITE], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_KEY], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_PEARL], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_CRYSTAL], 10
649 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
650 &li.amoeba_content, EL_DIAMOND
654 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
659 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
660 &li.grow_into_diggable, TRUE
665 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
666 &li.yamyam_content, EL_ROCK, NULL,
667 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
671 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 &li.score[SC_YAMYAM], 10
677 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
678 &li.score[SC_ROBOT], 10
682 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
695 &li.time_magic_wall, 10
700 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
701 &li.game_of_life[0], 2
705 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
706 &li.game_of_life[1], 3
710 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
711 &li.game_of_life[2], 3
715 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
716 &li.game_of_life[3], 3
720 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
721 &li.use_life_bugs, FALSE
726 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
731 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
736 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
741 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
746 EL_TIMEGATE_SWITCH, -1,
747 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
748 &li.time_timegate, 10
752 EL_LIGHT_SWITCH_ACTIVE, -1,
753 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
758 EL_SHIELD_NORMAL, -1,
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 &li.shield_normal_time, 10
763 EL_SHIELD_NORMAL, -1,
764 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
765 &li.score[SC_SHIELD], 10
769 EL_SHIELD_DEADLY, -1,
770 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
771 &li.shield_deadly_time, 10
774 EL_SHIELD_DEADLY, -1,
775 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
776 &li.score[SC_SHIELD], 10
781 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
787 &li.extra_time_score, 10
791 EL_TIME_ORB_FULL, -1,
792 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
793 &li.time_orb_time, 10
796 EL_TIME_ORB_FULL, -1,
797 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
798 &li.use_time_orb_bug, FALSE
803 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
804 &li.use_spring_bug, FALSE
809 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
810 &li.android_move_time, 10
814 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
815 &li.android_clone_time, 10
818 EL_EMC_ANDROID, SAVE_CONF_NEVER,
819 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
820 &li.android_clone_element[0], EL_EMPTY, NULL,
821 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
825 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
826 &li.android_clone_element[0], EL_EMPTY, NULL,
827 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
832 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
842 EL_EMC_MAGNIFIER, -1,
843 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
844 &li.magnify_score, 10
847 EL_EMC_MAGNIFIER, -1,
848 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
853 EL_EMC_MAGIC_BALL, -1,
854 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
858 EL_EMC_MAGIC_BALL, -1,
859 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
860 &li.ball_random, FALSE
863 EL_EMC_MAGIC_BALL, -1,
864 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
865 &li.ball_active_initial, FALSE
868 EL_EMC_MAGIC_BALL, -1,
869 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
870 &li.ball_content, EL_EMPTY, NULL,
871 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
875 EL_SOKOBAN_FIELD_EMPTY, -1,
876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
877 &li.sb_fields_needed, TRUE
881 EL_SOKOBAN_OBJECT, -1,
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
883 &li.sb_objects_needed, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
889 &li.mm_laser_red, FALSE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
894 &li.mm_laser_green, FALSE
898 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
899 &li.mm_laser_blue, TRUE
904 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
905 &li.df_laser_red, TRUE
909 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
910 &li.df_laser_green, TRUE
914 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
915 &li.df_laser_blue, FALSE
919 EL_MM_FUSE_ACTIVE, -1,
920 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
931 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
936 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
937 &li.mm_ball_choice_mode, ANIM_RANDOM
941 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
942 &li.mm_ball_content, EL_EMPTY, NULL,
943 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
948 &li.rotate_mm_ball_content, TRUE
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
953 &li.explode_mm_ball, FALSE
957 EL_MM_STEEL_BLOCK, -1,
958 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
959 &li.mm_time_block, 75
963 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
964 &li.score[SC_ELEM_BONUS], 10
974 static struct LevelFileConfigInfo chunk_config_NOTE[] =
978 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
979 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
983 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
984 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
989 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
990 &xx_envelope.autowrap, FALSE
994 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
995 &xx_envelope.centered, FALSE
1000 TYPE_STRING, CONF_VALUE_BYTES(1),
1001 &xx_envelope.text, -1, NULL,
1002 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1003 &xx_default_string_empty[0]
1013 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1017 TYPE_STRING, CONF_VALUE_BYTES(1),
1018 &xx_ei.description[0], -1,
1019 &yy_ei.description[0],
1020 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1021 &xx_default_description[0]
1026 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1027 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1028 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1030 #if ENABLE_RESERVED_CODE
1031 // (reserved for later use)
1034 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1035 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1036 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1042 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1043 &xx_ei.use_gfx_element, FALSE,
1044 &yy_ei.use_gfx_element
1048 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1049 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1050 &yy_ei.gfx_element_initial
1055 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1056 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1057 &yy_ei.access_direction
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1063 &xx_ei.collect_score_initial, 10,
1064 &yy_ei.collect_score_initial
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1069 &xx_ei.collect_count_initial, 1,
1070 &yy_ei.collect_count_initial
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1076 &xx_ei.ce_value_fixed_initial, 0,
1077 &yy_ei.ce_value_fixed_initial
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1082 &xx_ei.ce_value_random_initial, 0,
1083 &yy_ei.ce_value_random_initial
1087 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1088 &xx_ei.use_last_ce_value, FALSE,
1089 &yy_ei.use_last_ce_value
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1095 &xx_ei.push_delay_fixed, 8,
1096 &yy_ei.push_delay_fixed
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1101 &xx_ei.push_delay_random, 8,
1102 &yy_ei.push_delay_random
1106 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1107 &xx_ei.drop_delay_fixed, 0,
1108 &yy_ei.drop_delay_fixed
1112 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1113 &xx_ei.drop_delay_random, 0,
1114 &yy_ei.drop_delay_random
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1119 &xx_ei.move_delay_fixed, 0,
1120 &yy_ei.move_delay_fixed
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1125 &xx_ei.move_delay_random, 0,
1126 &yy_ei.move_delay_random
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1131 &xx_ei.step_delay_fixed, 0,
1132 &yy_ei.step_delay_fixed
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1137 &xx_ei.step_delay_random, 0,
1138 &yy_ei.step_delay_random
1143 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1144 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1149 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1150 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1151 &yy_ei.move_direction_initial
1155 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1156 &xx_ei.move_stepsize, TILEX / 8,
1157 &yy_ei.move_stepsize
1162 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1163 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1164 &yy_ei.move_enter_element
1168 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1169 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1170 &yy_ei.move_leave_element
1174 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1175 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1176 &yy_ei.move_leave_type
1181 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1182 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1183 &yy_ei.slippery_type
1188 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1189 &xx_ei.explosion_type, EXPLODES_3X3,
1190 &yy_ei.explosion_type
1194 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1195 &xx_ei.explosion_delay, 16,
1196 &yy_ei.explosion_delay
1200 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1201 &xx_ei.ignition_delay, 8,
1202 &yy_ei.ignition_delay
1207 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1208 &xx_ei.content, EL_EMPTY_SPACE,
1210 &xx_num_contents, 1, 1
1213 // ---------- "num_change_pages" must be the last entry ---------------------
1216 -1, SAVE_CONF_ALWAYS,
1217 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1218 &xx_ei.num_change_pages, 1,
1219 &yy_ei.num_change_pages
1230 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1232 // ---------- "current_change_page" must be the first entry -----------------
1235 -1, SAVE_CONF_ALWAYS,
1236 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1237 &xx_current_change_page, -1
1240 // ---------- (the remaining entries can be in any order) -------------------
1244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1245 &xx_change.can_change, FALSE
1250 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1251 &xx_event_bits[0], 0
1255 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1256 &xx_event_bits[1], 0
1261 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1262 &xx_change.trigger_player, CH_PLAYER_ANY
1266 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1267 &xx_change.trigger_side, CH_SIDE_ANY
1271 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1272 &xx_change.trigger_page, CH_PAGE_ANY
1277 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1278 &xx_change.target_element, EL_EMPTY_SPACE
1283 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1284 &xx_change.delay_fixed, 0
1288 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1289 &xx_change.delay_random, 0
1293 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1294 &xx_change.delay_frames, FRAMES_PER_SECOND
1299 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1300 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1306 &xx_change.explode, FALSE
1310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1311 &xx_change.use_target_content, FALSE
1315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1316 &xx_change.only_if_complete, FALSE
1320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1321 &xx_change.use_random_replace, FALSE
1325 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1326 &xx_change.random_percentage, 100
1330 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1331 &xx_change.replace_when, CP_WHEN_EMPTY
1336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1337 &xx_change.has_action, FALSE
1341 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1342 &xx_change.action_type, CA_NO_ACTION
1346 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1347 &xx_change.action_mode, CA_MODE_UNDEFINED
1351 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1352 &xx_change.action_arg, CA_ARG_UNDEFINED
1357 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1358 &xx_change.action_element, EL_EMPTY_SPACE
1363 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1364 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1365 &xx_num_contents, 1, 1
1375 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1379 TYPE_STRING, CONF_VALUE_BYTES(1),
1380 &xx_ei.description[0], -1, NULL,
1381 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1382 &xx_default_description[0]
1387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1388 &xx_ei.use_gfx_element, FALSE
1392 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1393 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1398 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1399 &xx_group.choice_mode, ANIM_RANDOM
1404 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1405 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1406 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1416 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1421 &xx_ei.use_gfx_element, FALSE
1425 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1426 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1436 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1441 &li.block_snap_field, TRUE
1445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1446 &li.continuous_snapping, TRUE
1450 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1451 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1456 &li.use_start_element[0], FALSE
1460 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1461 &li.start_element[0], EL_PLAYER_1
1465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1466 &li.use_artwork_element[0], FALSE
1470 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1471 &li.artwork_element[0], EL_PLAYER_1
1475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1476 &li.use_explosion_element[0], FALSE
1480 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1481 &li.explosion_element[0], EL_PLAYER_1
1496 filetype_id_list[] =
1498 { LEVEL_FILE_TYPE_RND, "RND" },
1499 { LEVEL_FILE_TYPE_BD, "BD" },
1500 { LEVEL_FILE_TYPE_EM, "EM" },
1501 { LEVEL_FILE_TYPE_SP, "SP" },
1502 { LEVEL_FILE_TYPE_DX, "DX" },
1503 { LEVEL_FILE_TYPE_SB, "SB" },
1504 { LEVEL_FILE_TYPE_DC, "DC" },
1505 { LEVEL_FILE_TYPE_MM, "MM" },
1506 { LEVEL_FILE_TYPE_MM, "DF" },
1511 // ============================================================================
1512 // level file functions
1513 // ============================================================================
1515 static boolean check_special_flags(char *flag)
1517 if (strEqual(options.special_flags, flag) ||
1518 strEqual(leveldir_current->special_flags, flag))
1524 static struct DateInfo getCurrentDate(void)
1526 time_t epoch_seconds = time(NULL);
1527 struct tm *now = localtime(&epoch_seconds);
1528 struct DateInfo date;
1530 date.year = now->tm_year + 1900;
1531 date.month = now->tm_mon + 1;
1532 date.day = now->tm_mday;
1534 date.src = DATE_SRC_CLOCK;
1539 static void resetEventFlags(struct ElementChangeInfo *change)
1543 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1544 change->has_event[i] = FALSE;
1547 static void resetEventBits(void)
1551 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1552 xx_event_bits[i] = 0;
1555 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1559 /* important: only change event flag if corresponding event bit is set
1560 (this is because all xx_event_bits[] values are loaded separately,
1561 and all xx_event_bits[] values are set back to zero before loading
1562 another value xx_event_bits[x] (each value representing 32 flags)) */
1564 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1565 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1566 change->has_event[i] = TRUE;
1569 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1573 /* in contrast to the above function setEventFlagsFromEventBits(), it
1574 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1575 depending on the corresponding change->has_event[i] values here, as
1576 all xx_event_bits[] values are reset in resetEventBits() before */
1578 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1579 if (change->has_event[i])
1580 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1583 static char *getDefaultElementDescription(struct ElementInfo *ei)
1585 static char description[MAX_ELEMENT_NAME_LEN + 1];
1586 char *default_description = (ei->custom_description != NULL ?
1587 ei->custom_description :
1588 ei->editor_description);
1591 // always start with reliable default values
1592 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1593 description[i] = '\0';
1595 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1596 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1598 return &description[0];
1601 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1603 char *default_description = getDefaultElementDescription(ei);
1606 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1607 ei->description[i] = default_description[i];
1610 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1614 for (i = 0; conf[i].data_type != -1; i++)
1616 int default_value = conf[i].default_value;
1617 int data_type = conf[i].data_type;
1618 int conf_type = conf[i].conf_type;
1619 int byte_mask = conf_type & CONF_MASK_BYTES;
1621 if (byte_mask == CONF_MASK_MULTI_BYTES)
1623 int default_num_entities = conf[i].default_num_entities;
1624 int max_num_entities = conf[i].max_num_entities;
1626 *(int *)(conf[i].num_entities) = default_num_entities;
1628 if (data_type == TYPE_STRING)
1630 char *default_string = conf[i].default_string;
1631 char *string = (char *)(conf[i].value);
1633 strncpy(string, default_string, max_num_entities);
1635 else if (data_type == TYPE_ELEMENT_LIST)
1637 int *element_array = (int *)(conf[i].value);
1640 for (j = 0; j < max_num_entities; j++)
1641 element_array[j] = default_value;
1643 else if (data_type == TYPE_CONTENT_LIST)
1645 struct Content *content = (struct Content *)(conf[i].value);
1648 for (c = 0; c < max_num_entities; c++)
1649 for (y = 0; y < 3; y++)
1650 for (x = 0; x < 3; x++)
1651 content[c].e[x][y] = default_value;
1654 else // constant size configuration data (1, 2 or 4 bytes)
1656 if (data_type == TYPE_BOOLEAN)
1657 *(boolean *)(conf[i].value) = default_value;
1659 *(int *) (conf[i].value) = default_value;
1664 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1668 for (i = 0; conf[i].data_type != -1; i++)
1670 int data_type = conf[i].data_type;
1671 int conf_type = conf[i].conf_type;
1672 int byte_mask = conf_type & CONF_MASK_BYTES;
1674 if (byte_mask == CONF_MASK_MULTI_BYTES)
1676 int max_num_entities = conf[i].max_num_entities;
1678 if (data_type == TYPE_STRING)
1680 char *string = (char *)(conf[i].value);
1681 char *string_copy = (char *)(conf[i].value_copy);
1683 strncpy(string_copy, string, max_num_entities);
1685 else if (data_type == TYPE_ELEMENT_LIST)
1687 int *element_array = (int *)(conf[i].value);
1688 int *element_array_copy = (int *)(conf[i].value_copy);
1691 for (j = 0; j < max_num_entities; j++)
1692 element_array_copy[j] = element_array[j];
1694 else if (data_type == TYPE_CONTENT_LIST)
1696 struct Content *content = (struct Content *)(conf[i].value);
1697 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1700 for (c = 0; c < max_num_entities; c++)
1701 for (y = 0; y < 3; y++)
1702 for (x = 0; x < 3; x++)
1703 content_copy[c].e[x][y] = content[c].e[x][y];
1706 else // constant size configuration data (1, 2 or 4 bytes)
1708 if (data_type == TYPE_BOOLEAN)
1709 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1711 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1716 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1720 xx_ei = *ei_from; // copy element data into temporary buffer
1721 yy_ei = *ei_to; // copy element data into temporary buffer
1723 copyConfigFromConfigList(chunk_config_CUSX_base);
1728 // ---------- reinitialize and copy change pages ----------
1730 ei_to->num_change_pages = ei_from->num_change_pages;
1731 ei_to->current_change_page = ei_from->current_change_page;
1733 setElementChangePages(ei_to, ei_to->num_change_pages);
1735 for (i = 0; i < ei_to->num_change_pages; i++)
1736 ei_to->change_page[i] = ei_from->change_page[i];
1738 // ---------- copy group element info ----------
1739 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1740 *ei_to->group = *ei_from->group;
1742 // mark this custom element as modified
1743 ei_to->modified_settings = TRUE;
1746 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1748 int change_page_size = sizeof(struct ElementChangeInfo);
1750 ei->num_change_pages = MAX(1, change_pages);
1753 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1755 if (ei->current_change_page >= ei->num_change_pages)
1756 ei->current_change_page = ei->num_change_pages - 1;
1758 ei->change = &ei->change_page[ei->current_change_page];
1761 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1763 xx_change = *change; // copy change data into temporary buffer
1765 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1767 *change = xx_change;
1769 resetEventFlags(change);
1771 change->direct_action = 0;
1772 change->other_action = 0;
1774 change->pre_change_function = NULL;
1775 change->change_function = NULL;
1776 change->post_change_function = NULL;
1779 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1783 li = *level; // copy level data into temporary buffer
1784 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1785 *level = li; // copy temporary buffer back to level data
1787 setLevelInfoToDefaults_BD();
1788 setLevelInfoToDefaults_EM();
1789 setLevelInfoToDefaults_SP();
1790 setLevelInfoToDefaults_MM();
1792 level->native_bd_level = &native_bd_level;
1793 level->native_em_level = &native_em_level;
1794 level->native_sp_level = &native_sp_level;
1795 level->native_mm_level = &native_mm_level;
1797 level->file_version = FILE_VERSION_ACTUAL;
1798 level->game_version = GAME_VERSION_ACTUAL;
1800 level->creation_date = getCurrentDate();
1802 level->encoding_16bit_field = TRUE;
1803 level->encoding_16bit_yamyam = TRUE;
1804 level->encoding_16bit_amoeba = TRUE;
1806 // clear level name and level author string buffers
1807 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1808 level->name[i] = '\0';
1809 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1810 level->author[i] = '\0';
1812 // set level name and level author to default values
1813 strcpy(level->name, NAMELESS_LEVEL_NAME);
1814 strcpy(level->author, ANONYMOUS_NAME);
1816 // set level playfield to playable default level with player and exit
1817 for (x = 0; x < MAX_LEV_FIELDX; x++)
1818 for (y = 0; y < MAX_LEV_FIELDY; y++)
1819 level->field[x][y] = EL_SAND;
1821 level->field[0][0] = EL_PLAYER_1;
1822 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1824 BorderElement = EL_STEELWALL;
1826 // detect custom elements when loading them
1827 level->file_has_custom_elements = FALSE;
1829 // set all bug compatibility flags to "false" => do not emulate this bug
1830 level->use_action_after_change_bug = FALSE;
1832 if (leveldir_current)
1834 // try to determine better author name than 'anonymous'
1835 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1837 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1838 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1842 switch (LEVELCLASS(leveldir_current))
1844 case LEVELCLASS_TUTORIAL:
1845 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1848 case LEVELCLASS_CONTRIB:
1849 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1850 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1853 case LEVELCLASS_PRIVATE:
1854 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1855 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1859 // keep default value
1866 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1868 static boolean clipboard_elements_initialized = FALSE;
1871 InitElementPropertiesStatic();
1873 li = *level; // copy level data into temporary buffer
1874 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1875 *level = li; // copy temporary buffer back to level data
1877 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1880 struct ElementInfo *ei = &element_info[element];
1882 if (element == EL_MM_GRAY_BALL)
1884 struct LevelInfo_MM *level_mm = level->native_mm_level;
1887 for (j = 0; j < level->num_mm_ball_contents; j++)
1888 level->mm_ball_content[j] =
1889 map_element_MM_to_RND(level_mm->ball_content[j]);
1892 // never initialize clipboard elements after the very first time
1893 // (to be able to use clipboard elements between several levels)
1894 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1897 if (IS_ENVELOPE(element))
1899 int envelope_nr = element - EL_ENVELOPE_1;
1901 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1903 level->envelope[envelope_nr] = xx_envelope;
1906 if (IS_CUSTOM_ELEMENT(element) ||
1907 IS_GROUP_ELEMENT(element) ||
1908 IS_INTERNAL_ELEMENT(element))
1910 xx_ei = *ei; // copy element data into temporary buffer
1912 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1917 setElementChangePages(ei, 1);
1918 setElementChangeInfoToDefaults(ei->change);
1920 if (IS_CUSTOM_ELEMENT(element) ||
1921 IS_GROUP_ELEMENT(element))
1923 setElementDescriptionToDefault(ei);
1925 ei->modified_settings = FALSE;
1928 if (IS_CUSTOM_ELEMENT(element) ||
1929 IS_INTERNAL_ELEMENT(element))
1931 // internal values used in level editor
1933 ei->access_type = 0;
1934 ei->access_layer = 0;
1935 ei->access_protected = 0;
1936 ei->walk_to_action = 0;
1937 ei->smash_targets = 0;
1940 ei->can_explode_by_fire = FALSE;
1941 ei->can_explode_smashed = FALSE;
1942 ei->can_explode_impact = FALSE;
1944 ei->current_change_page = 0;
1947 if (IS_GROUP_ELEMENT(element) ||
1948 IS_INTERNAL_ELEMENT(element))
1950 struct ElementGroupInfo *group;
1952 // initialize memory for list of elements in group
1953 if (ei->group == NULL)
1954 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1958 xx_group = *group; // copy group data into temporary buffer
1960 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1965 if (IS_EMPTY_ELEMENT(element) ||
1966 IS_INTERNAL_ELEMENT(element))
1968 xx_ei = *ei; // copy element data into temporary buffer
1970 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1976 clipboard_elements_initialized = TRUE;
1979 static void setLevelInfoToDefaults(struct LevelInfo *level,
1980 boolean level_info_only,
1981 boolean reset_file_status)
1983 setLevelInfoToDefaults_Level(level);
1985 if (!level_info_only)
1986 setLevelInfoToDefaults_Elements(level);
1988 if (reset_file_status)
1990 level->no_valid_file = FALSE;
1991 level->no_level_file = FALSE;
1994 level->changed = FALSE;
1997 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1999 level_file_info->nr = 0;
2000 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2001 level_file_info->packed = FALSE;
2003 setString(&level_file_info->basename, NULL);
2004 setString(&level_file_info->filename, NULL);
2007 int getMappedElement_SB(int, boolean);
2009 static void ActivateLevelTemplate(void)
2013 if (check_special_flags("load_xsb_to_ces"))
2015 // fill smaller playfields with padding "beyond border wall" elements
2016 if (level.fieldx < level_template.fieldx ||
2017 level.fieldy < level_template.fieldy)
2019 short field[level.fieldx][level.fieldy];
2020 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2021 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2022 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2023 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2025 // copy old playfield (which is smaller than the visible area)
2026 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2027 field[x][y] = level.field[x][y];
2029 // fill new, larger playfield with "beyond border wall" elements
2030 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2031 level.field[x][y] = getMappedElement_SB('_', TRUE);
2033 // copy the old playfield to the middle of the new playfield
2034 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2035 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2037 level.fieldx = new_fieldx;
2038 level.fieldy = new_fieldy;
2042 // Currently there is no special action needed to activate the template
2043 // data, because 'element_info' property settings overwrite the original
2044 // level data, while all other variables do not change.
2046 // Exception: 'from_level_template' elements in the original level playfield
2047 // are overwritten with the corresponding elements at the same position in
2048 // playfield from the level template.
2050 for (x = 0; x < level.fieldx; x++)
2051 for (y = 0; y < level.fieldy; y++)
2052 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2053 level.field[x][y] = level_template.field[x][y];
2055 if (check_special_flags("load_xsb_to_ces"))
2057 struct LevelInfo level_backup = level;
2059 // overwrite all individual level settings from template level settings
2060 level = level_template;
2062 // restore level file info
2063 level.file_info = level_backup.file_info;
2065 // restore playfield size
2066 level.fieldx = level_backup.fieldx;
2067 level.fieldy = level_backup.fieldy;
2069 // restore playfield content
2070 for (x = 0; x < level.fieldx; x++)
2071 for (y = 0; y < level.fieldy; y++)
2072 level.field[x][y] = level_backup.field[x][y];
2074 // restore name and author from individual level
2075 strcpy(level.name, level_backup.name);
2076 strcpy(level.author, level_backup.author);
2078 // restore flag "use_custom_template"
2079 level.use_custom_template = level_backup.use_custom_template;
2083 static boolean checkForPackageFromBasename_BD(char *basename)
2085 // check for native BD level file extensions
2086 if (!strSuffixLower(basename, ".bd") &&
2087 !strSuffixLower(basename, ".bdr") &&
2088 !strSuffixLower(basename, ".brc") &&
2089 !strSuffixLower(basename, ".gds"))
2092 // check for standard single-level BD files (like "001.bd")
2093 if (strSuffixLower(basename, ".bd") &&
2094 strlen(basename) == 6 &&
2095 basename[0] >= '0' && basename[0] <= '9' &&
2096 basename[1] >= '0' && basename[1] <= '9' &&
2097 basename[2] >= '0' && basename[2] <= '9')
2100 // this is a level package in native BD file format
2104 static char *getLevelFilenameFromBasename(char *basename)
2106 static char *filename = NULL;
2108 checked_free(filename);
2110 filename = getPath2(getCurrentLevelDir(), basename);
2115 static int getFileTypeFromBasename(char *basename)
2117 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2119 static char *filename = NULL;
2120 struct stat file_status;
2122 // ---------- try to determine file type from filename ----------
2124 // check for typical filename of a Supaplex level package file
2125 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2126 return LEVEL_FILE_TYPE_SP;
2128 // check for typical filename of a Diamond Caves II level package file
2129 if (strSuffixLower(basename, ".dc") ||
2130 strSuffixLower(basename, ".dc2"))
2131 return LEVEL_FILE_TYPE_DC;
2133 // check for typical filename of a Sokoban level package file
2134 if (strSuffixLower(basename, ".xsb") &&
2135 strchr(basename, '%') == NULL)
2136 return LEVEL_FILE_TYPE_SB;
2138 // check for typical filename of a Boulder Dash (GDash) level package file
2139 if (checkForPackageFromBasename_BD(basename))
2140 return LEVEL_FILE_TYPE_BD;
2142 // ---------- try to determine file type from filesize ----------
2144 checked_free(filename);
2145 filename = getPath2(getCurrentLevelDir(), basename);
2147 if (stat(filename, &file_status) == 0)
2149 // check for typical filesize of a Supaplex level package file
2150 if (file_status.st_size == 170496)
2151 return LEVEL_FILE_TYPE_SP;
2154 return LEVEL_FILE_TYPE_UNKNOWN;
2157 static int getFileTypeFromMagicBytes(char *filename, int type)
2161 if ((file = openFile(filename, MODE_READ)))
2163 char chunk_name[CHUNK_ID_LEN + 1];
2165 getFileChunkBE(file, chunk_name, NULL);
2167 if (strEqual(chunk_name, "MMII") ||
2168 strEqual(chunk_name, "MIRR"))
2169 type = LEVEL_FILE_TYPE_MM;
2177 static boolean checkForPackageFromBasename(char *basename)
2179 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2180 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2182 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2185 static char *getSingleLevelBasenameExt(int nr, char *extension)
2187 static char basename[MAX_FILENAME_LEN];
2190 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2192 sprintf(basename, "%03d.%s", nr, extension);
2197 static char *getSingleLevelBasename(int nr)
2199 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2202 static char *getPackedLevelBasename(int type)
2204 static char basename[MAX_FILENAME_LEN];
2205 char *directory = getCurrentLevelDir();
2207 DirectoryEntry *dir_entry;
2209 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2211 if ((dir = openDirectory(directory)) == NULL)
2213 Warn("cannot read current level directory '%s'", directory);
2218 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2220 char *entry_basename = dir_entry->basename;
2221 int entry_type = getFileTypeFromBasename(entry_basename);
2223 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2225 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2228 strcpy(basename, entry_basename);
2235 closeDirectory(dir);
2240 static char *getSingleLevelFilename(int nr)
2242 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2245 #if ENABLE_UNUSED_CODE
2246 static char *getPackedLevelFilename(int type)
2248 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2252 char *getDefaultLevelFilename(int nr)
2254 return getSingleLevelFilename(nr);
2257 #if ENABLE_UNUSED_CODE
2258 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2262 lfi->packed = FALSE;
2264 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2265 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2269 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2270 int type, char *format, ...)
2272 static char basename[MAX_FILENAME_LEN];
2275 va_start(ap, format);
2276 vsprintf(basename, format, ap);
2280 lfi->packed = FALSE;
2282 setString(&lfi->basename, basename);
2283 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2286 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2292 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2293 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2296 static int getFiletypeFromID(char *filetype_id)
2298 char *filetype_id_lower;
2299 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2302 if (filetype_id == NULL)
2303 return LEVEL_FILE_TYPE_UNKNOWN;
2305 filetype_id_lower = getStringToLower(filetype_id);
2307 for (i = 0; filetype_id_list[i].id != NULL; i++)
2309 char *id_lower = getStringToLower(filetype_id_list[i].id);
2311 if (strEqual(filetype_id_lower, id_lower))
2312 filetype = filetype_id_list[i].filetype;
2316 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2320 free(filetype_id_lower);
2325 char *getLocalLevelTemplateFilename(void)
2327 return getDefaultLevelFilename(-1);
2330 char *getGlobalLevelTemplateFilename(void)
2332 // global variable "leveldir_current" must be modified in the loop below
2333 LevelDirTree *leveldir_current_last = leveldir_current;
2334 char *filename = NULL;
2336 // check for template level in path from current to topmost tree node
2338 while (leveldir_current != NULL)
2340 filename = getDefaultLevelFilename(-1);
2342 if (fileExists(filename))
2345 leveldir_current = leveldir_current->node_parent;
2348 // restore global variable "leveldir_current" modified in above loop
2349 leveldir_current = leveldir_current_last;
2354 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2358 // special case: level number is negative => check for level template file
2361 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2362 getSingleLevelBasename(-1));
2364 // replace local level template filename with global template filename
2365 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2367 // no fallback if template file not existing
2371 // special case: check for file name/pattern specified in "levelinfo.conf"
2372 if (leveldir_current->level_filename != NULL)
2374 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2376 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2377 leveldir_current->level_filename, nr);
2379 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2381 if (fileExists(lfi->filename))
2384 else if (leveldir_current->level_filetype != NULL)
2386 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2388 // check for specified native level file with standard file name
2389 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2390 "%03d.%s", nr, LEVELFILE_EXTENSION);
2391 if (fileExists(lfi->filename))
2395 // check for native Rocks'n'Diamonds level file
2396 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2397 "%03d.%s", nr, LEVELFILE_EXTENSION);
2398 if (fileExists(lfi->filename))
2401 // check for native Boulder Dash level file
2402 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2403 if (fileExists(lfi->filename))
2406 // check for Emerald Mine level file (V1)
2407 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2408 'a' + (nr / 10) % 26, '0' + nr % 10);
2409 if (fileExists(lfi->filename))
2411 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2412 'A' + (nr / 10) % 26, '0' + nr % 10);
2413 if (fileExists(lfi->filename))
2416 // check for Emerald Mine level file (V2 to V5)
2417 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2418 if (fileExists(lfi->filename))
2421 // check for Emerald Mine level file (V6 / single mode)
2422 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2423 if (fileExists(lfi->filename))
2425 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2426 if (fileExists(lfi->filename))
2429 // check for Emerald Mine level file (V6 / teamwork mode)
2430 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2431 if (fileExists(lfi->filename))
2433 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2434 if (fileExists(lfi->filename))
2437 // check for various packed level file formats
2438 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2439 if (fileExists(lfi->filename))
2442 // no known level file found -- use default values (and fail later)
2443 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2444 "%03d.%s", nr, LEVELFILE_EXTENSION);
2447 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2449 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2450 lfi->type = getFileTypeFromBasename(lfi->basename);
2452 if (lfi->type == LEVEL_FILE_TYPE_RND)
2453 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2456 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2458 // always start with reliable default values
2459 setFileInfoToDefaults(level_file_info);
2461 level_file_info->nr = nr; // set requested level number
2463 determineLevelFileInfo_Filename(level_file_info);
2464 determineLevelFileInfo_Filetype(level_file_info);
2467 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2468 struct LevelFileInfo *lfi_to)
2470 lfi_to->nr = lfi_from->nr;
2471 lfi_to->type = lfi_from->type;
2472 lfi_to->packed = lfi_from->packed;
2474 setString(&lfi_to->basename, lfi_from->basename);
2475 setString(&lfi_to->filename, lfi_from->filename);
2478 // ----------------------------------------------------------------------------
2479 // functions for loading R'n'D level
2480 // ----------------------------------------------------------------------------
2482 int getMappedElement(int element)
2484 // remap some (historic, now obsolete) elements
2488 case EL_PLAYER_OBSOLETE:
2489 element = EL_PLAYER_1;
2492 case EL_KEY_OBSOLETE:
2496 case EL_EM_KEY_1_FILE_OBSOLETE:
2497 element = EL_EM_KEY_1;
2500 case EL_EM_KEY_2_FILE_OBSOLETE:
2501 element = EL_EM_KEY_2;
2504 case EL_EM_KEY_3_FILE_OBSOLETE:
2505 element = EL_EM_KEY_3;
2508 case EL_EM_KEY_4_FILE_OBSOLETE:
2509 element = EL_EM_KEY_4;
2512 case EL_ENVELOPE_OBSOLETE:
2513 element = EL_ENVELOPE_1;
2521 if (element >= NUM_FILE_ELEMENTS)
2523 Warn("invalid level element %d", element);
2525 element = EL_UNKNOWN;
2533 static int getMappedElementByVersion(int element, int game_version)
2535 // remap some elements due to certain game version
2537 if (game_version <= VERSION_IDENT(2,2,0,0))
2539 // map game font elements
2540 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2541 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2542 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2543 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2546 if (game_version < VERSION_IDENT(3,0,0,0))
2548 // map Supaplex gravity tube elements
2549 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2550 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2551 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2552 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2559 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2561 level->file_version = getFileVersion(file);
2562 level->game_version = getFileVersion(file);
2567 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2569 level->creation_date.year = getFile16BitBE(file);
2570 level->creation_date.month = getFile8Bit(file);
2571 level->creation_date.day = getFile8Bit(file);
2573 level->creation_date.src = DATE_SRC_LEVELFILE;
2578 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2580 int initial_player_stepsize;
2581 int initial_player_gravity;
2584 level->fieldx = getFile8Bit(file);
2585 level->fieldy = getFile8Bit(file);
2587 level->time = getFile16BitBE(file);
2588 level->gems_needed = getFile16BitBE(file);
2590 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2591 level->name[i] = getFile8Bit(file);
2592 level->name[MAX_LEVEL_NAME_LEN] = 0;
2594 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2595 level->score[i] = getFile8Bit(file);
2597 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2598 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2599 for (y = 0; y < 3; y++)
2600 for (x = 0; x < 3; x++)
2601 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2603 level->amoeba_speed = getFile8Bit(file);
2604 level->time_magic_wall = getFile8Bit(file);
2605 level->time_wheel = getFile8Bit(file);
2606 level->amoeba_content = getMappedElement(getFile8Bit(file));
2608 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2611 for (i = 0; i < MAX_PLAYERS; i++)
2612 level->initial_player_stepsize[i] = initial_player_stepsize;
2614 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2616 for (i = 0; i < MAX_PLAYERS; i++)
2617 level->initial_player_gravity[i] = initial_player_gravity;
2619 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2620 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2622 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2624 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2625 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2626 level->can_move_into_acid_bits = getFile32BitBE(file);
2627 level->dont_collide_with_bits = getFile8Bit(file);
2629 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2630 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2632 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2633 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2634 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2636 level->game_engine_type = getFile8Bit(file);
2638 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2643 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2647 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2648 level->name[i] = getFile8Bit(file);
2649 level->name[MAX_LEVEL_NAME_LEN] = 0;
2654 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2658 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2659 level->author[i] = getFile8Bit(file);
2660 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2665 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2668 int chunk_size_expected = level->fieldx * level->fieldy;
2670 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2671 stored with 16-bit encoding (and should be twice as big then).
2672 Even worse, playfield data was stored 16-bit when only yamyam content
2673 contained 16-bit elements and vice versa. */
2675 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2676 chunk_size_expected *= 2;
2678 if (chunk_size_expected != chunk_size)
2680 ReadUnusedBytesFromFile(file, chunk_size);
2681 return chunk_size_expected;
2684 for (y = 0; y < level->fieldy; y++)
2685 for (x = 0; x < level->fieldx; x++)
2686 level->field[x][y] =
2687 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2692 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2695 int header_size = 4;
2696 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2697 int chunk_size_expected = header_size + content_size;
2699 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2700 stored with 16-bit encoding (and should be twice as big then).
2701 Even worse, playfield data was stored 16-bit when only yamyam content
2702 contained 16-bit elements and vice versa. */
2704 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2705 chunk_size_expected += content_size;
2707 if (chunk_size_expected != chunk_size)
2709 ReadUnusedBytesFromFile(file, chunk_size);
2710 return chunk_size_expected;
2714 level->num_yamyam_contents = getFile8Bit(file);
2718 // correct invalid number of content fields -- should never happen
2719 if (level->num_yamyam_contents < 1 ||
2720 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2721 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2723 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2724 for (y = 0; y < 3; y++)
2725 for (x = 0; x < 3; x++)
2726 level->yamyam_content[i].e[x][y] =
2727 getMappedElement(level->encoding_16bit_field ?
2728 getFile16BitBE(file) : getFile8Bit(file));
2732 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2737 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2739 element = getMappedElement(getFile16BitBE(file));
2740 num_contents = getFile8Bit(file);
2742 getFile8Bit(file); // content x size (unused)
2743 getFile8Bit(file); // content y size (unused)
2745 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2747 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2748 for (y = 0; y < 3; y++)
2749 for (x = 0; x < 3; x++)
2750 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2752 // correct invalid number of content fields -- should never happen
2753 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2754 num_contents = STD_ELEMENT_CONTENTS;
2756 if (element == EL_YAMYAM)
2758 level->num_yamyam_contents = num_contents;
2760 for (i = 0; i < num_contents; i++)
2761 for (y = 0; y < 3; y++)
2762 for (x = 0; x < 3; x++)
2763 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2765 else if (element == EL_BD_AMOEBA)
2767 level->amoeba_content = content_array[0][0][0];
2771 Warn("cannot load content for element '%d'", element);
2777 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2783 int chunk_size_expected;
2785 element = getMappedElement(getFile16BitBE(file));
2786 if (!IS_ENVELOPE(element))
2787 element = EL_ENVELOPE_1;
2789 envelope_nr = element - EL_ENVELOPE_1;
2791 envelope_len = getFile16BitBE(file);
2793 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2794 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2796 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2798 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2799 if (chunk_size_expected != chunk_size)
2801 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2802 return chunk_size_expected;
2805 for (i = 0; i < envelope_len; i++)
2806 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2811 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2813 int num_changed_custom_elements = getFile16BitBE(file);
2814 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2817 if (chunk_size_expected != chunk_size)
2819 ReadUnusedBytesFromFile(file, chunk_size - 2);
2820 return chunk_size_expected;
2823 for (i = 0; i < num_changed_custom_elements; i++)
2825 int element = getMappedElement(getFile16BitBE(file));
2826 int properties = getFile32BitBE(file);
2828 if (IS_CUSTOM_ELEMENT(element))
2829 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2831 Warn("invalid custom element number %d", element);
2833 // older game versions that wrote level files with CUS1 chunks used
2834 // different default push delay values (not yet stored in level file)
2835 element_info[element].push_delay_fixed = 2;
2836 element_info[element].push_delay_random = 8;
2839 level->file_has_custom_elements = TRUE;
2844 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2846 int num_changed_custom_elements = getFile16BitBE(file);
2847 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2850 if (chunk_size_expected != chunk_size)
2852 ReadUnusedBytesFromFile(file, chunk_size - 2);
2853 return chunk_size_expected;
2856 for (i = 0; i < num_changed_custom_elements; i++)
2858 int element = getMappedElement(getFile16BitBE(file));
2859 int custom_target_element = getMappedElement(getFile16BitBE(file));
2861 if (IS_CUSTOM_ELEMENT(element))
2862 element_info[element].change->target_element = custom_target_element;
2864 Warn("invalid custom element number %d", element);
2867 level->file_has_custom_elements = TRUE;
2872 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2874 int num_changed_custom_elements = getFile16BitBE(file);
2875 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2878 if (chunk_size_expected != chunk_size)
2880 ReadUnusedBytesFromFile(file, chunk_size - 2);
2881 return chunk_size_expected;
2884 for (i = 0; i < num_changed_custom_elements; i++)
2886 int element = getMappedElement(getFile16BitBE(file));
2887 struct ElementInfo *ei = &element_info[element];
2888 unsigned int event_bits;
2890 if (!IS_CUSTOM_ELEMENT(element))
2892 Warn("invalid custom element number %d", element);
2894 element = EL_INTERNAL_DUMMY;
2897 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2898 ei->description[j] = getFile8Bit(file);
2899 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2901 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2903 // some free bytes for future properties and padding
2904 ReadUnusedBytesFromFile(file, 7);
2906 ei->use_gfx_element = getFile8Bit(file);
2907 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2909 ei->collect_score_initial = getFile8Bit(file);
2910 ei->collect_count_initial = getFile8Bit(file);
2912 ei->push_delay_fixed = getFile16BitBE(file);
2913 ei->push_delay_random = getFile16BitBE(file);
2914 ei->move_delay_fixed = getFile16BitBE(file);
2915 ei->move_delay_random = getFile16BitBE(file);
2917 ei->move_pattern = getFile16BitBE(file);
2918 ei->move_direction_initial = getFile8Bit(file);
2919 ei->move_stepsize = getFile8Bit(file);
2921 for (y = 0; y < 3; y++)
2922 for (x = 0; x < 3; x++)
2923 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2925 // bits 0 - 31 of "has_event[]"
2926 event_bits = getFile32BitBE(file);
2927 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2928 if (event_bits & (1u << j))
2929 ei->change->has_event[j] = TRUE;
2931 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2933 ei->change->delay_fixed = getFile16BitBE(file);
2934 ei->change->delay_random = getFile16BitBE(file);
2935 ei->change->delay_frames = getFile16BitBE(file);
2937 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2939 ei->change->explode = getFile8Bit(file);
2940 ei->change->use_target_content = getFile8Bit(file);
2941 ei->change->only_if_complete = getFile8Bit(file);
2942 ei->change->use_random_replace = getFile8Bit(file);
2944 ei->change->random_percentage = getFile8Bit(file);
2945 ei->change->replace_when = getFile8Bit(file);
2947 for (y = 0; y < 3; y++)
2948 for (x = 0; x < 3; x++)
2949 ei->change->target_content.e[x][y] =
2950 getMappedElement(getFile16BitBE(file));
2952 ei->slippery_type = getFile8Bit(file);
2954 // some free bytes for future properties and padding
2955 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2957 // mark that this custom element has been modified
2958 ei->modified_settings = TRUE;
2961 level->file_has_custom_elements = TRUE;
2966 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2968 struct ElementInfo *ei;
2969 int chunk_size_expected;
2973 // ---------- custom element base property values (96 bytes) ----------------
2975 element = getMappedElement(getFile16BitBE(file));
2977 if (!IS_CUSTOM_ELEMENT(element))
2979 Warn("invalid custom element number %d", element);
2981 ReadUnusedBytesFromFile(file, chunk_size - 2);
2986 ei = &element_info[element];
2988 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2989 ei->description[i] = getFile8Bit(file);
2990 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2992 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2994 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2996 ei->num_change_pages = getFile8Bit(file);
2998 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2999 if (chunk_size_expected != chunk_size)
3001 ReadUnusedBytesFromFile(file, chunk_size - 43);
3002 return chunk_size_expected;
3005 ei->ce_value_fixed_initial = getFile16BitBE(file);
3006 ei->ce_value_random_initial = getFile16BitBE(file);
3007 ei->use_last_ce_value = getFile8Bit(file);
3009 ei->use_gfx_element = getFile8Bit(file);
3010 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3012 ei->collect_score_initial = getFile8Bit(file);
3013 ei->collect_count_initial = getFile8Bit(file);
3015 ei->drop_delay_fixed = getFile8Bit(file);
3016 ei->push_delay_fixed = getFile8Bit(file);
3017 ei->drop_delay_random = getFile8Bit(file);
3018 ei->push_delay_random = getFile8Bit(file);
3019 ei->move_delay_fixed = getFile16BitBE(file);
3020 ei->move_delay_random = getFile16BitBE(file);
3022 // bits 0 - 15 of "move_pattern" ...
3023 ei->move_pattern = getFile16BitBE(file);
3024 ei->move_direction_initial = getFile8Bit(file);
3025 ei->move_stepsize = getFile8Bit(file);
3027 ei->slippery_type = getFile8Bit(file);
3029 for (y = 0; y < 3; y++)
3030 for (x = 0; x < 3; x++)
3031 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3033 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3034 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3035 ei->move_leave_type = getFile8Bit(file);
3037 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3038 ei->move_pattern |= (getFile16BitBE(file) << 16);
3040 ei->access_direction = getFile8Bit(file);
3042 ei->explosion_delay = getFile8Bit(file);
3043 ei->ignition_delay = getFile8Bit(file);
3044 ei->explosion_type = getFile8Bit(file);
3046 // some free bytes for future custom property values and padding
3047 ReadUnusedBytesFromFile(file, 1);
3049 // ---------- change page property values (48 bytes) ------------------------
3051 setElementChangePages(ei, ei->num_change_pages);
3053 for (i = 0; i < ei->num_change_pages; i++)
3055 struct ElementChangeInfo *change = &ei->change_page[i];
3056 unsigned int event_bits;
3058 // always start with reliable default values
3059 setElementChangeInfoToDefaults(change);
3061 // bits 0 - 31 of "has_event[]" ...
3062 event_bits = getFile32BitBE(file);
3063 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3064 if (event_bits & (1u << j))
3065 change->has_event[j] = TRUE;
3067 change->target_element = getMappedElement(getFile16BitBE(file));
3069 change->delay_fixed = getFile16BitBE(file);
3070 change->delay_random = getFile16BitBE(file);
3071 change->delay_frames = getFile16BitBE(file);
3073 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3075 change->explode = getFile8Bit(file);
3076 change->use_target_content = getFile8Bit(file);
3077 change->only_if_complete = getFile8Bit(file);
3078 change->use_random_replace = getFile8Bit(file);
3080 change->random_percentage = getFile8Bit(file);
3081 change->replace_when = getFile8Bit(file);
3083 for (y = 0; y < 3; y++)
3084 for (x = 0; x < 3; x++)
3085 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3087 change->can_change = getFile8Bit(file);
3089 change->trigger_side = getFile8Bit(file);
3091 change->trigger_player = getFile8Bit(file);
3092 change->trigger_page = getFile8Bit(file);
3094 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3095 CH_PAGE_ANY : (1 << change->trigger_page));
3097 change->has_action = getFile8Bit(file);
3098 change->action_type = getFile8Bit(file);
3099 change->action_mode = getFile8Bit(file);
3100 change->action_arg = getFile16BitBE(file);
3102 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3103 event_bits = getFile8Bit(file);
3104 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3105 if (event_bits & (1u << (j - 32)))
3106 change->has_event[j] = TRUE;
3109 // mark this custom element as modified
3110 ei->modified_settings = TRUE;
3112 level->file_has_custom_elements = TRUE;
3117 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3119 struct ElementInfo *ei;
3120 struct ElementGroupInfo *group;
3124 element = getMappedElement(getFile16BitBE(file));
3126 if (!IS_GROUP_ELEMENT(element))
3128 Warn("invalid group element number %d", element);
3130 ReadUnusedBytesFromFile(file, chunk_size - 2);
3135 ei = &element_info[element];
3137 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3138 ei->description[i] = getFile8Bit(file);
3139 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3141 group = element_info[element].group;
3143 group->num_elements = getFile8Bit(file);
3145 ei->use_gfx_element = getFile8Bit(file);
3146 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3148 group->choice_mode = getFile8Bit(file);
3150 // some free bytes for future values and padding
3151 ReadUnusedBytesFromFile(file, 3);
3153 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3154 group->element[i] = getMappedElement(getFile16BitBE(file));
3156 // mark this group element as modified
3157 element_info[element].modified_settings = TRUE;
3159 level->file_has_custom_elements = TRUE;
3164 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3165 int element, int real_element)
3167 int micro_chunk_size = 0;
3168 int conf_type = getFile8Bit(file);
3169 int byte_mask = conf_type & CONF_MASK_BYTES;
3170 boolean element_found = FALSE;
3173 micro_chunk_size += 1;
3175 if (byte_mask == CONF_MASK_MULTI_BYTES)
3177 int num_bytes = getFile16BitBE(file);
3178 byte *buffer = checked_malloc(num_bytes);
3180 ReadBytesFromFile(file, buffer, num_bytes);
3182 for (i = 0; conf[i].data_type != -1; i++)
3184 if (conf[i].element == element &&
3185 conf[i].conf_type == conf_type)
3187 int data_type = conf[i].data_type;
3188 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3189 int max_num_entities = conf[i].max_num_entities;
3191 if (num_entities > max_num_entities)
3193 Warn("truncating number of entities for element %d from %d to %d",
3194 element, num_entities, max_num_entities);
3196 num_entities = max_num_entities;
3199 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3200 data_type == TYPE_CONTENT_LIST))
3202 // for element and content lists, zero entities are not allowed
3203 Warn("found empty list of entities for element %d", element);
3205 // do not set "num_entities" here to prevent reading behind buffer
3207 *(int *)(conf[i].num_entities) = 1; // at least one is required
3211 *(int *)(conf[i].num_entities) = num_entities;
3214 element_found = TRUE;
3216 if (data_type == TYPE_STRING)
3218 char *string = (char *)(conf[i].value);
3221 for (j = 0; j < max_num_entities; j++)
3222 string[j] = (j < num_entities ? buffer[j] : '\0');
3224 else if (data_type == TYPE_ELEMENT_LIST)
3226 int *element_array = (int *)(conf[i].value);
3229 for (j = 0; j < num_entities; j++)
3231 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3233 else if (data_type == TYPE_CONTENT_LIST)
3235 struct Content *content= (struct Content *)(conf[i].value);
3238 for (c = 0; c < num_entities; c++)
3239 for (y = 0; y < 3; y++)
3240 for (x = 0; x < 3; x++)
3241 content[c].e[x][y] =
3242 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3245 element_found = FALSE;
3251 checked_free(buffer);
3253 micro_chunk_size += 2 + num_bytes;
3255 else // constant size configuration data (1, 2 or 4 bytes)
3257 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3258 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3259 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3261 for (i = 0; conf[i].data_type != -1; i++)
3263 if (conf[i].element == element &&
3264 conf[i].conf_type == conf_type)
3266 int data_type = conf[i].data_type;
3268 if (data_type == TYPE_ELEMENT)
3269 value = getMappedElement(value);
3271 if (data_type == TYPE_BOOLEAN)
3272 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3274 *(int *) (conf[i].value) = value;
3276 element_found = TRUE;
3282 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3287 char *error_conf_chunk_bytes =
3288 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3289 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3290 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3291 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3292 int error_element = real_element;
3294 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3295 error_conf_chunk_bytes, error_conf_chunk_token,
3296 error_element, EL_NAME(error_element));
3299 return micro_chunk_size;
3302 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3304 int real_chunk_size = 0;
3306 li = *level; // copy level data into temporary buffer
3308 while (!checkEndOfFile(file))
3310 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3312 if (real_chunk_size >= chunk_size)
3316 *level = li; // copy temporary buffer back to level data
3318 return real_chunk_size;
3321 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3323 int real_chunk_size = 0;
3325 li = *level; // copy level data into temporary buffer
3327 while (!checkEndOfFile(file))
3329 int element = getMappedElement(getFile16BitBE(file));
3331 real_chunk_size += 2;
3332 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3334 if (real_chunk_size >= chunk_size)
3338 *level = li; // copy temporary buffer back to level data
3340 return real_chunk_size;
3343 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3345 int real_chunk_size = 0;
3347 li = *level; // copy level data into temporary buffer
3349 while (!checkEndOfFile(file))
3351 int element = getMappedElement(getFile16BitBE(file));
3353 real_chunk_size += 2;
3354 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3356 if (real_chunk_size >= chunk_size)
3360 *level = li; // copy temporary buffer back to level data
3362 return real_chunk_size;
3365 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3367 int element = getMappedElement(getFile16BitBE(file));
3368 int envelope_nr = element - EL_ENVELOPE_1;
3369 int real_chunk_size = 2;
3371 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3373 while (!checkEndOfFile(file))
3375 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3378 if (real_chunk_size >= chunk_size)
3382 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3384 return real_chunk_size;
3387 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3389 int element = getMappedElement(getFile16BitBE(file));
3390 int real_chunk_size = 2;
3391 struct ElementInfo *ei = &element_info[element];
3394 xx_ei = *ei; // copy element data into temporary buffer
3396 xx_ei.num_change_pages = -1;
3398 while (!checkEndOfFile(file))
3400 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3402 if (xx_ei.num_change_pages != -1)
3405 if (real_chunk_size >= chunk_size)
3411 if (ei->num_change_pages == -1)
3413 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3416 ei->num_change_pages = 1;
3418 setElementChangePages(ei, 1);
3419 setElementChangeInfoToDefaults(ei->change);
3421 return real_chunk_size;
3424 // initialize number of change pages stored for this custom element
3425 setElementChangePages(ei, ei->num_change_pages);
3426 for (i = 0; i < ei->num_change_pages; i++)
3427 setElementChangeInfoToDefaults(&ei->change_page[i]);
3429 // start with reading properties for the first change page
3430 xx_current_change_page = 0;
3432 while (!checkEndOfFile(file))
3434 // level file might contain invalid change page number
3435 if (xx_current_change_page >= ei->num_change_pages)
3438 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3440 xx_change = *change; // copy change data into temporary buffer
3442 resetEventBits(); // reset bits; change page might have changed
3444 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3447 *change = xx_change;
3449 setEventFlagsFromEventBits(change);
3451 if (real_chunk_size >= chunk_size)
3455 level->file_has_custom_elements = TRUE;
3457 return real_chunk_size;
3460 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3462 int element = getMappedElement(getFile16BitBE(file));
3463 int real_chunk_size = 2;
3464 struct ElementInfo *ei = &element_info[element];
3465 struct ElementGroupInfo *group = ei->group;
3470 xx_ei = *ei; // copy element data into temporary buffer
3471 xx_group = *group; // copy group data into temporary buffer
3473 while (!checkEndOfFile(file))
3475 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3478 if (real_chunk_size >= chunk_size)
3485 level->file_has_custom_elements = TRUE;
3487 return real_chunk_size;
3490 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3492 int element = getMappedElement(getFile16BitBE(file));
3493 int real_chunk_size = 2;
3494 struct ElementInfo *ei = &element_info[element];
3496 xx_ei = *ei; // copy element data into temporary buffer
3498 while (!checkEndOfFile(file))
3500 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3503 if (real_chunk_size >= chunk_size)
3509 level->file_has_custom_elements = TRUE;
3511 return real_chunk_size;
3514 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3515 struct LevelFileInfo *level_file_info,
3516 boolean level_info_only)
3518 char *filename = level_file_info->filename;
3519 char cookie[MAX_LINE_LEN];
3520 char chunk_name[CHUNK_ID_LEN + 1];
3524 if (!(file = openFile(filename, MODE_READ)))
3526 level->no_valid_file = TRUE;
3527 level->no_level_file = TRUE;
3529 if (level_info_only)
3532 Warn("cannot read level '%s' -- using empty level", filename);
3534 if (!setup.editor.use_template_for_new_levels)
3537 // if level file not found, try to initialize level data from template
3538 filename = getGlobalLevelTemplateFilename();
3540 if (!(file = openFile(filename, MODE_READ)))
3543 // default: for empty levels, use level template for custom elements
3544 level->use_custom_template = TRUE;
3546 level->no_valid_file = FALSE;
3549 getFileChunkBE(file, chunk_name, NULL);
3550 if (strEqual(chunk_name, "RND1"))
3552 getFile32BitBE(file); // not used
3554 getFileChunkBE(file, chunk_name, NULL);
3555 if (!strEqual(chunk_name, "CAVE"))
3557 level->no_valid_file = TRUE;
3559 Warn("unknown format of level file '%s'", filename);
3566 else // check for pre-2.0 file format with cookie string
3568 strcpy(cookie, chunk_name);
3569 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3571 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3572 cookie[strlen(cookie) - 1] = '\0';
3574 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3576 level->no_valid_file = TRUE;
3578 Warn("unknown format of level file '%s'", filename);
3585 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3587 level->no_valid_file = TRUE;
3589 Warn("unsupported version of level file '%s'", filename);
3596 // pre-2.0 level files have no game version, so use file version here
3597 level->game_version = level->file_version;
3600 if (level->file_version < FILE_VERSION_1_2)
3602 // level files from versions before 1.2.0 without chunk structure
3603 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3604 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3612 int (*loader)(File *, int, struct LevelInfo *);
3616 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3617 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3618 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3619 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3620 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3621 { "INFO", -1, LoadLevel_INFO },
3622 { "BODY", -1, LoadLevel_BODY },
3623 { "CONT", -1, LoadLevel_CONT },
3624 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3625 { "CNT3", -1, LoadLevel_CNT3 },
3626 { "CUS1", -1, LoadLevel_CUS1 },
3627 { "CUS2", -1, LoadLevel_CUS2 },
3628 { "CUS3", -1, LoadLevel_CUS3 },
3629 { "CUS4", -1, LoadLevel_CUS4 },
3630 { "GRP1", -1, LoadLevel_GRP1 },
3631 { "CONF", -1, LoadLevel_CONF },
3632 { "ELEM", -1, LoadLevel_ELEM },
3633 { "NOTE", -1, LoadLevel_NOTE },
3634 { "CUSX", -1, LoadLevel_CUSX },
3635 { "GRPX", -1, LoadLevel_GRPX },
3636 { "EMPX", -1, LoadLevel_EMPX },
3641 while (getFileChunkBE(file, chunk_name, &chunk_size))
3645 while (chunk_info[i].name != NULL &&
3646 !strEqual(chunk_name, chunk_info[i].name))
3649 if (chunk_info[i].name == NULL)
3651 Warn("unknown chunk '%s' in level file '%s'",
3652 chunk_name, filename);
3654 ReadUnusedBytesFromFile(file, chunk_size);
3656 else if (chunk_info[i].size != -1 &&
3657 chunk_info[i].size != chunk_size)
3659 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3660 chunk_size, chunk_name, filename);
3662 ReadUnusedBytesFromFile(file, chunk_size);
3666 // call function to load this level chunk
3667 int chunk_size_expected =
3668 (chunk_info[i].loader)(file, chunk_size, level);
3670 if (chunk_size_expected < 0)
3672 Warn("error reading chunk '%s' in level file '%s'",
3673 chunk_name, filename);
3678 // the size of some chunks cannot be checked before reading other
3679 // chunks first (like "HEAD" and "BODY") that contain some header
3680 // information, so check them here
3681 if (chunk_size_expected != chunk_size)
3683 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3684 chunk_size, chunk_name, filename);
3696 // ----------------------------------------------------------------------------
3697 // functions for loading BD level
3698 // ----------------------------------------------------------------------------
3700 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3702 struct LevelInfo_BD *level_bd = level->native_bd_level;
3703 GdCave *cave = NULL; // will be changed below
3704 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3705 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3708 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3710 // cave and map newly allocated when set to defaults above
3711 cave = level_bd->cave;
3713 for (i = 0; i < 5; i++)
3715 cave->level_time[i] = level->time;
3716 cave->level_diamonds[i] = level->gems_needed;
3717 cave->level_magic_wall_time[i] = level->time_magic_wall;
3718 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3721 cave->diamond_value = level->score[SC_EMERALD];
3722 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3724 cave->level_speed[0] = 160; // set cave speed
3726 cave->intermission = level->bd_intermission;
3727 cave->diagonal_movements = level->bd_diagonal_movements;
3729 strncpy(cave->name, level->name, sizeof(GdString));
3730 cave->name[sizeof(GdString) - 1] = '\0';
3732 for (x = 0; x < cave->w; x++)
3733 for (y = 0; y < cave->h; y++)
3734 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3737 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3739 struct LevelInfo_BD *level_bd = level->native_bd_level;
3740 GdCave *cave = level_bd->cave;
3741 int bd_level_nr = level_bd->level_nr;
3744 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3745 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3747 level->time = cave->level_time[bd_level_nr];
3748 level->gems_needed = cave->level_diamonds[bd_level_nr];
3749 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3751 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3752 level->score[SC_EMERALD] = cave->diamond_value;
3753 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3755 level->bd_intermission = cave->intermission;
3756 level->bd_diagonal_movements = cave->diagonal_movements;
3758 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3759 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3761 for (x = 0; x < level->fieldx; x++)
3762 for (y = 0; y < level->fieldy; y++)
3763 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3766 static void setTapeInfoToDefaults(void);
3768 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3770 struct LevelInfo_BD *level_bd = level->native_bd_level;
3771 GdCave *cave = level_bd->cave;
3772 GdReplay *replay = level_bd->replay;
3778 // always start with reliable default values
3779 setTapeInfoToDefaults();
3781 tape.level_nr = level_nr; // (currently not used)
3782 tape.random_seed = replay->seed;
3784 TapeSetDateFromIsoDateString(replay->date);
3787 tape.pos[tape.counter].delay = 0;
3789 tape.bd_replay = TRUE;
3791 // all time calculations only used to display approximate tape time
3792 int cave_speed = cave->speed;
3793 int milliseconds_game = 0;
3794 int milliseconds_elapsed = 20;
3796 for (i = 0; i < replay->movements->len; i++)
3798 int replay_action = replay->movements->data[i];
3799 int tape_action = map_action_BD_to_RND(replay_action);
3800 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3801 boolean success = 0;
3805 success = TapeAddAction(action);
3807 milliseconds_game += milliseconds_elapsed;
3809 if (milliseconds_game >= cave_speed)
3811 milliseconds_game -= cave_speed;
3818 tape.pos[tape.counter].delay = 0;
3819 tape.pos[tape.counter].action[0] = 0;
3823 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3829 TapeHaltRecording();
3833 // ----------------------------------------------------------------------------
3834 // functions for loading EM level
3835 // ----------------------------------------------------------------------------
3837 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3839 static int ball_xy[8][2] =
3850 struct LevelInfo_EM *level_em = level->native_em_level;
3851 struct CAVE *cav = level_em->cav;
3854 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3855 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3857 cav->time_seconds = level->time;
3858 cav->gems_needed = level->gems_needed;
3860 cav->emerald_score = level->score[SC_EMERALD];
3861 cav->diamond_score = level->score[SC_DIAMOND];
3862 cav->alien_score = level->score[SC_ROBOT];
3863 cav->tank_score = level->score[SC_SPACESHIP];
3864 cav->bug_score = level->score[SC_BUG];
3865 cav->eater_score = level->score[SC_YAMYAM];
3866 cav->nut_score = level->score[SC_NUT];
3867 cav->dynamite_score = level->score[SC_DYNAMITE];
3868 cav->key_score = level->score[SC_KEY];
3869 cav->exit_score = level->score[SC_TIME_BONUS];
3871 cav->num_eater_arrays = level->num_yamyam_contents;
3873 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3874 for (y = 0; y < 3; y++)
3875 for (x = 0; x < 3; x++)
3876 cav->eater_array[i][y * 3 + x] =
3877 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3879 cav->amoeba_time = level->amoeba_speed;
3880 cav->wonderwall_time = level->time_magic_wall;
3881 cav->wheel_time = level->time_wheel;
3883 cav->android_move_time = level->android_move_time;
3884 cav->android_clone_time = level->android_clone_time;
3885 cav->ball_random = level->ball_random;
3886 cav->ball_active = level->ball_active_initial;
3887 cav->ball_time = level->ball_time;
3888 cav->num_ball_arrays = level->num_ball_contents;
3890 cav->lenses_score = level->lenses_score;
3891 cav->magnify_score = level->magnify_score;
3892 cav->slurp_score = level->slurp_score;
3894 cav->lenses_time = level->lenses_time;
3895 cav->magnify_time = level->magnify_time;
3897 cav->wind_time = 9999;
3898 cav->wind_direction =
3899 map_direction_RND_to_EM(level->wind_direction_initial);
3901 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3902 for (j = 0; j < 8; j++)
3903 cav->ball_array[i][j] =
3904 map_element_RND_to_EM_cave(level->ball_content[i].
3905 e[ball_xy[j][0]][ball_xy[j][1]]);
3907 map_android_clone_elements_RND_to_EM(level);
3909 // first fill the complete playfield with the empty space element
3910 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3911 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3912 cav->cave[x][y] = Cblank;
3914 // then copy the real level contents from level file into the playfield
3915 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3917 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3919 if (level->field[x][y] == EL_AMOEBA_DEAD)
3920 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3922 cav->cave[x][y] = new_element;
3925 for (i = 0; i < MAX_PLAYERS; i++)
3927 cav->player_x[i] = -1;
3928 cav->player_y[i] = -1;
3931 // initialize player positions and delete players from the playfield
3932 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3934 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3936 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3938 cav->player_x[player_nr] = x;
3939 cav->player_y[player_nr] = y;
3941 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3946 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3948 static int ball_xy[8][2] =
3959 struct LevelInfo_EM *level_em = level->native_em_level;
3960 struct CAVE *cav = level_em->cav;
3963 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3964 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3966 level->time = cav->time_seconds;
3967 level->gems_needed = cav->gems_needed;
3969 sprintf(level->name, "Level %d", level->file_info.nr);
3971 level->score[SC_EMERALD] = cav->emerald_score;
3972 level->score[SC_DIAMOND] = cav->diamond_score;
3973 level->score[SC_ROBOT] = cav->alien_score;
3974 level->score[SC_SPACESHIP] = cav->tank_score;
3975 level->score[SC_BUG] = cav->bug_score;
3976 level->score[SC_YAMYAM] = cav->eater_score;
3977 level->score[SC_NUT] = cav->nut_score;
3978 level->score[SC_DYNAMITE] = cav->dynamite_score;
3979 level->score[SC_KEY] = cav->key_score;
3980 level->score[SC_TIME_BONUS] = cav->exit_score;
3982 level->num_yamyam_contents = cav->num_eater_arrays;
3984 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3985 for (y = 0; y < 3; y++)
3986 for (x = 0; x < 3; x++)
3987 level->yamyam_content[i].e[x][y] =
3988 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3990 level->amoeba_speed = cav->amoeba_time;
3991 level->time_magic_wall = cav->wonderwall_time;
3992 level->time_wheel = cav->wheel_time;
3994 level->android_move_time = cav->android_move_time;
3995 level->android_clone_time = cav->android_clone_time;
3996 level->ball_random = cav->ball_random;
3997 level->ball_active_initial = cav->ball_active;
3998 level->ball_time = cav->ball_time;
3999 level->num_ball_contents = cav->num_ball_arrays;
4001 level->lenses_score = cav->lenses_score;
4002 level->magnify_score = cav->magnify_score;
4003 level->slurp_score = cav->slurp_score;
4005 level->lenses_time = cav->lenses_time;
4006 level->magnify_time = cav->magnify_time;
4008 level->wind_direction_initial =
4009 map_direction_EM_to_RND(cav->wind_direction);
4011 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4012 for (j = 0; j < 8; j++)
4013 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4014 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4016 map_android_clone_elements_EM_to_RND(level);
4018 // convert the playfield (some elements need special treatment)
4019 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4021 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4023 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4024 new_element = EL_AMOEBA_DEAD;
4026 level->field[x][y] = new_element;
4029 for (i = 0; i < MAX_PLAYERS; i++)
4031 // in case of all players set to the same field, use the first player
4032 int nr = MAX_PLAYERS - i - 1;
4033 int jx = cav->player_x[nr];
4034 int jy = cav->player_y[nr];
4036 if (jx != -1 && jy != -1)
4037 level->field[jx][jy] = EL_PLAYER_1 + nr;
4040 // time score is counted for each 10 seconds left in Emerald Mine levels
4041 level->time_score_base = 10;
4045 // ----------------------------------------------------------------------------
4046 // functions for loading SP level
4047 // ----------------------------------------------------------------------------
4049 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4051 struct LevelInfo_SP *level_sp = level->native_sp_level;
4052 LevelInfoType *header = &level_sp->header;
4055 level_sp->width = level->fieldx;
4056 level_sp->height = level->fieldy;
4058 for (x = 0; x < level->fieldx; x++)
4059 for (y = 0; y < level->fieldy; y++)
4060 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4062 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4064 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4065 header->LevelTitle[i] = level->name[i];
4066 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4068 header->InfotronsNeeded = level->gems_needed;
4070 header->SpecialPortCount = 0;
4072 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4074 boolean gravity_port_found = FALSE;
4075 boolean gravity_port_valid = FALSE;
4076 int gravity_port_flag;
4077 int gravity_port_base_element;
4078 int element = level->field[x][y];
4080 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4081 element <= EL_SP_GRAVITY_ON_PORT_UP)
4083 gravity_port_found = TRUE;
4084 gravity_port_valid = TRUE;
4085 gravity_port_flag = 1;
4086 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4088 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4089 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4091 gravity_port_found = TRUE;
4092 gravity_port_valid = TRUE;
4093 gravity_port_flag = 0;
4094 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4096 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4097 element <= EL_SP_GRAVITY_PORT_UP)
4099 // change R'n'D style gravity inverting special port to normal port
4100 // (there are no gravity inverting ports in native Supaplex engine)
4102 gravity_port_found = TRUE;
4103 gravity_port_valid = FALSE;
4104 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4107 if (gravity_port_found)
4109 if (gravity_port_valid &&
4110 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4112 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4114 port->PortLocation = (y * level->fieldx + x) * 2;
4115 port->Gravity = gravity_port_flag;
4117 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4119 header->SpecialPortCount++;
4123 // change special gravity port to normal port
4125 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4128 level_sp->playfield[x][y] = element - EL_SP_START;
4133 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4135 struct LevelInfo_SP *level_sp = level->native_sp_level;
4136 LevelInfoType *header = &level_sp->header;
4137 boolean num_invalid_elements = 0;
4140 level->fieldx = level_sp->width;
4141 level->fieldy = level_sp->height;
4143 for (x = 0; x < level->fieldx; x++)
4145 for (y = 0; y < level->fieldy; y++)
4147 int element_old = level_sp->playfield[x][y];
4148 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4150 if (element_new == EL_UNKNOWN)
4152 num_invalid_elements++;
4154 Debug("level:native:SP", "invalid element %d at position %d, %d",
4158 level->field[x][y] = element_new;
4162 if (num_invalid_elements > 0)
4163 Warn("found %d invalid elements%s", num_invalid_elements,
4164 (!options.debug ? " (use '--debug' for more details)" : ""));
4166 for (i = 0; i < MAX_PLAYERS; i++)
4167 level->initial_player_gravity[i] =
4168 (header->InitialGravity == 1 ? TRUE : FALSE);
4170 // skip leading spaces
4171 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4172 if (header->LevelTitle[i] != ' ')
4176 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4177 level->name[j] = header->LevelTitle[i];
4178 level->name[j] = '\0';
4180 // cut trailing spaces
4182 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4183 level->name[j - 1] = '\0';
4185 level->gems_needed = header->InfotronsNeeded;
4187 for (i = 0; i < header->SpecialPortCount; i++)
4189 SpecialPortType *port = &header->SpecialPort[i];
4190 int port_location = port->PortLocation;
4191 int gravity = port->Gravity;
4192 int port_x, port_y, port_element;
4194 port_x = (port_location / 2) % level->fieldx;
4195 port_y = (port_location / 2) / level->fieldx;
4197 if (port_x < 0 || port_x >= level->fieldx ||
4198 port_y < 0 || port_y >= level->fieldy)
4200 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4205 port_element = level->field[port_x][port_y];
4207 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4208 port_element > EL_SP_GRAVITY_PORT_UP)
4210 Warn("no special port at position (%d, %d)", port_x, port_y);
4215 // change previous (wrong) gravity inverting special port to either
4216 // gravity enabling special port or gravity disabling special port
4217 level->field[port_x][port_y] +=
4218 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4219 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4222 // change special gravity ports without database entries to normal ports
4223 for (x = 0; x < level->fieldx; x++)
4224 for (y = 0; y < level->fieldy; y++)
4225 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4226 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4227 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4229 level->time = 0; // no time limit
4230 level->amoeba_speed = 0;
4231 level->time_magic_wall = 0;
4232 level->time_wheel = 0;
4233 level->amoeba_content = EL_EMPTY;
4235 // original Supaplex does not use score values -- rate by playing time
4236 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4237 level->score[i] = 0;
4239 level->rate_time_over_score = TRUE;
4241 // there are no yamyams in supaplex levels
4242 for (i = 0; i < level->num_yamyam_contents; i++)
4243 for (x = 0; x < 3; x++)
4244 for (y = 0; y < 3; y++)
4245 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4248 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4250 struct LevelInfo_SP *level_sp = level->native_sp_level;
4251 struct DemoInfo_SP *demo = &level_sp->demo;
4254 // always start with reliable default values
4255 demo->is_available = FALSE;
4258 if (TAPE_IS_EMPTY(tape))
4261 demo->level_nr = tape.level_nr; // (currently not used)
4263 level_sp->header.DemoRandomSeed = tape.random_seed;
4267 for (i = 0; i < tape.length; i++)
4269 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4270 int demo_repeat = tape.pos[i].delay;
4271 int demo_entries = (demo_repeat + 15) / 16;
4273 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4275 Warn("tape truncated: size exceeds maximum SP demo size %d",
4281 for (j = 0; j < demo_repeat / 16; j++)
4282 demo->data[demo->length++] = 0xf0 | demo_action;
4284 if (demo_repeat % 16)
4285 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4288 demo->is_available = TRUE;
4291 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4293 struct LevelInfo_SP *level_sp = level->native_sp_level;
4294 struct DemoInfo_SP *demo = &level_sp->demo;
4295 char *filename = level->file_info.filename;
4298 // always start with reliable default values
4299 setTapeInfoToDefaults();
4301 if (!demo->is_available)
4304 tape.level_nr = demo->level_nr; // (currently not used)
4305 tape.random_seed = level_sp->header.DemoRandomSeed;
4307 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4310 tape.pos[tape.counter].delay = 0;
4312 for (i = 0; i < demo->length; i++)
4314 int demo_action = demo->data[i] & 0x0f;
4315 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4316 int tape_action = map_key_SP_to_RND(demo_action);
4317 int tape_repeat = demo_repeat + 1;
4318 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4319 boolean success = 0;
4322 for (j = 0; j < tape_repeat; j++)
4323 success = TapeAddAction(action);
4327 Warn("SP demo truncated: size exceeds maximum tape size %d",
4334 TapeHaltRecording();
4338 // ----------------------------------------------------------------------------
4339 // functions for loading MM level
4340 // ----------------------------------------------------------------------------
4342 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4344 struct LevelInfo_MM *level_mm = level->native_mm_level;
4347 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4348 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4350 level_mm->time = level->time;
4351 level_mm->kettles_needed = level->gems_needed;
4352 level_mm->auto_count_kettles = level->auto_count_gems;
4354 level_mm->mm_laser_red = level->mm_laser_red;
4355 level_mm->mm_laser_green = level->mm_laser_green;
4356 level_mm->mm_laser_blue = level->mm_laser_blue;
4358 level_mm->df_laser_red = level->df_laser_red;
4359 level_mm->df_laser_green = level->df_laser_green;
4360 level_mm->df_laser_blue = level->df_laser_blue;
4362 strcpy(level_mm->name, level->name);
4363 strcpy(level_mm->author, level->author);
4365 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4366 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4367 level_mm->score[SC_KEY] = level->score[SC_KEY];
4368 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4369 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4371 level_mm->amoeba_speed = level->amoeba_speed;
4372 level_mm->time_fuse = level->mm_time_fuse;
4373 level_mm->time_bomb = level->mm_time_bomb;
4374 level_mm->time_ball = level->mm_time_ball;
4375 level_mm->time_block = level->mm_time_block;
4377 level_mm->num_ball_contents = level->num_mm_ball_contents;
4378 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4379 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4380 level_mm->explode_ball = level->explode_mm_ball;
4382 for (i = 0; i < level->num_mm_ball_contents; i++)
4383 level_mm->ball_content[i] =
4384 map_element_RND_to_MM(level->mm_ball_content[i]);
4386 for (x = 0; x < level->fieldx; x++)
4387 for (y = 0; y < level->fieldy; y++)
4389 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4392 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4394 struct LevelInfo_MM *level_mm = level->native_mm_level;
4397 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4398 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4400 level->time = level_mm->time;
4401 level->gems_needed = level_mm->kettles_needed;
4402 level->auto_count_gems = level_mm->auto_count_kettles;
4404 level->mm_laser_red = level_mm->mm_laser_red;
4405 level->mm_laser_green = level_mm->mm_laser_green;
4406 level->mm_laser_blue = level_mm->mm_laser_blue;
4408 level->df_laser_red = level_mm->df_laser_red;
4409 level->df_laser_green = level_mm->df_laser_green;
4410 level->df_laser_blue = level_mm->df_laser_blue;
4412 strcpy(level->name, level_mm->name);
4414 // only overwrite author from 'levelinfo.conf' if author defined in level
4415 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4416 strcpy(level->author, level_mm->author);
4418 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4419 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4420 level->score[SC_KEY] = level_mm->score[SC_KEY];
4421 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4422 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4424 level->amoeba_speed = level_mm->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->num_mm_ball_contents = level_mm->num_ball_contents;
4431 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4432 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4433 level->explode_mm_ball = level_mm->explode_ball;
4435 for (i = 0; i < level->num_mm_ball_contents; i++)
4436 level->mm_ball_content[i] =
4437 map_element_MM_to_RND(level_mm->ball_content[i]);
4439 for (x = 0; x < level->fieldx; x++)
4440 for (y = 0; y < level->fieldy; y++)
4441 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4445 // ----------------------------------------------------------------------------
4446 // functions for loading DC level
4447 // ----------------------------------------------------------------------------
4449 #define DC_LEVEL_HEADER_SIZE 344
4451 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4454 static int last_data_encoded;
4458 int diff_hi, diff_lo;
4459 int data_hi, data_lo;
4460 unsigned short data_decoded;
4464 last_data_encoded = 0;
4471 diff = data_encoded - last_data_encoded;
4472 diff_hi = diff & ~0xff;
4473 diff_lo = diff & 0xff;
4477 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4478 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4479 data_hi = data_hi & 0xff00;
4481 data_decoded = data_hi | data_lo;
4483 last_data_encoded = data_encoded;
4485 offset1 = (offset1 + 1) % 31;
4486 offset2 = offset2 & 0xff;
4488 return data_decoded;
4491 static int getMappedElement_DC(int element)
4499 // 0x0117 - 0x036e: (?)
4502 // 0x042d - 0x0684: (?)
4518 element = EL_CRYSTAL;
4521 case 0x0e77: // quicksand (boulder)
4522 element = EL_QUICKSAND_FAST_FULL;
4525 case 0x0e99: // slow quicksand (boulder)
4526 element = EL_QUICKSAND_FULL;
4530 element = EL_EM_EXIT_OPEN;
4534 element = EL_EM_EXIT_CLOSED;
4538 element = EL_EM_STEEL_EXIT_OPEN;
4542 element = EL_EM_STEEL_EXIT_CLOSED;
4545 case 0x0f4f: // dynamite (lit 1)
4546 element = EL_EM_DYNAMITE_ACTIVE;
4549 case 0x0f57: // dynamite (lit 2)
4550 element = EL_EM_DYNAMITE_ACTIVE;
4553 case 0x0f5f: // dynamite (lit 3)
4554 element = EL_EM_DYNAMITE_ACTIVE;
4557 case 0x0f67: // dynamite (lit 4)
4558 element = EL_EM_DYNAMITE_ACTIVE;
4565 element = EL_AMOEBA_WET;
4569 element = EL_AMOEBA_DROP;
4573 element = EL_DC_MAGIC_WALL;
4577 element = EL_SPACESHIP_UP;
4581 element = EL_SPACESHIP_DOWN;
4585 element = EL_SPACESHIP_LEFT;
4589 element = EL_SPACESHIP_RIGHT;
4593 element = EL_BUG_UP;
4597 element = EL_BUG_DOWN;
4601 element = EL_BUG_LEFT;
4605 element = EL_BUG_RIGHT;
4609 element = EL_MOLE_UP;
4613 element = EL_MOLE_DOWN;
4617 element = EL_MOLE_LEFT;
4621 element = EL_MOLE_RIGHT;
4629 element = EL_YAMYAM_UP;
4633 element = EL_SWITCHGATE_OPEN;
4637 element = EL_SWITCHGATE_CLOSED;
4641 element = EL_DC_SWITCHGATE_SWITCH_UP;
4645 element = EL_TIMEGATE_CLOSED;
4648 case 0x144c: // conveyor belt switch (green)
4649 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4652 case 0x144f: // conveyor belt switch (red)
4653 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4656 case 0x1452: // conveyor belt switch (blue)
4657 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4661 element = EL_CONVEYOR_BELT_3_MIDDLE;
4665 element = EL_CONVEYOR_BELT_3_LEFT;
4669 element = EL_CONVEYOR_BELT_3_RIGHT;
4673 element = EL_CONVEYOR_BELT_1_MIDDLE;
4677 element = EL_CONVEYOR_BELT_1_LEFT;
4681 element = EL_CONVEYOR_BELT_1_RIGHT;
4685 element = EL_CONVEYOR_BELT_4_MIDDLE;
4689 element = EL_CONVEYOR_BELT_4_LEFT;
4693 element = EL_CONVEYOR_BELT_4_RIGHT;
4697 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4701 element = EL_EXPANDABLE_WALL_VERTICAL;
4705 element = EL_EXPANDABLE_WALL_ANY;
4708 case 0x14ce: // growing steel wall (left/right)
4709 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4712 case 0x14df: // growing steel wall (up/down)
4713 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4716 case 0x14e8: // growing steel wall (up/down/left/right)
4717 element = EL_EXPANDABLE_STEELWALL_ANY;
4721 element = EL_SHIELD_DEADLY;
4725 element = EL_EXTRA_TIME;
4733 element = EL_EMPTY_SPACE;
4736 case 0x1578: // quicksand (empty)
4737 element = EL_QUICKSAND_FAST_EMPTY;
4740 case 0x1579: // slow quicksand (empty)
4741 element = EL_QUICKSAND_EMPTY;
4751 element = EL_EM_DYNAMITE;
4754 case 0x15a1: // key (red)
4755 element = EL_EM_KEY_1;
4758 case 0x15a2: // key (yellow)
4759 element = EL_EM_KEY_2;
4762 case 0x15a3: // key (blue)
4763 element = EL_EM_KEY_4;
4766 case 0x15a4: // key (green)
4767 element = EL_EM_KEY_3;
4770 case 0x15a5: // key (white)
4771 element = EL_DC_KEY_WHITE;
4775 element = EL_WALL_SLIPPERY;
4782 case 0x15a8: // wall (not round)
4786 case 0x15a9: // (blue)
4787 element = EL_CHAR_A;
4790 case 0x15aa: // (blue)
4791 element = EL_CHAR_B;
4794 case 0x15ab: // (blue)
4795 element = EL_CHAR_C;
4798 case 0x15ac: // (blue)
4799 element = EL_CHAR_D;
4802 case 0x15ad: // (blue)
4803 element = EL_CHAR_E;
4806 case 0x15ae: // (blue)
4807 element = EL_CHAR_F;
4810 case 0x15af: // (blue)
4811 element = EL_CHAR_G;
4814 case 0x15b0: // (blue)
4815 element = EL_CHAR_H;
4818 case 0x15b1: // (blue)
4819 element = EL_CHAR_I;
4822 case 0x15b2: // (blue)
4823 element = EL_CHAR_J;
4826 case 0x15b3: // (blue)
4827 element = EL_CHAR_K;
4830 case 0x15b4: // (blue)
4831 element = EL_CHAR_L;
4834 case 0x15b5: // (blue)
4835 element = EL_CHAR_M;
4838 case 0x15b6: // (blue)
4839 element = EL_CHAR_N;
4842 case 0x15b7: // (blue)
4843 element = EL_CHAR_O;
4846 case 0x15b8: // (blue)
4847 element = EL_CHAR_P;
4850 case 0x15b9: // (blue)
4851 element = EL_CHAR_Q;
4854 case 0x15ba: // (blue)
4855 element = EL_CHAR_R;
4858 case 0x15bb: // (blue)
4859 element = EL_CHAR_S;
4862 case 0x15bc: // (blue)
4863 element = EL_CHAR_T;
4866 case 0x15bd: // (blue)
4867 element = EL_CHAR_U;
4870 case 0x15be: // (blue)
4871 element = EL_CHAR_V;
4874 case 0x15bf: // (blue)
4875 element = EL_CHAR_W;
4878 case 0x15c0: // (blue)
4879 element = EL_CHAR_X;
4882 case 0x15c1: // (blue)
4883 element = EL_CHAR_Y;
4886 case 0x15c2: // (blue)
4887 element = EL_CHAR_Z;
4890 case 0x15c3: // (blue)
4891 element = EL_CHAR_AUMLAUT;
4894 case 0x15c4: // (blue)
4895 element = EL_CHAR_OUMLAUT;
4898 case 0x15c5: // (blue)
4899 element = EL_CHAR_UUMLAUT;
4902 case 0x15c6: // (blue)
4903 element = EL_CHAR_0;
4906 case 0x15c7: // (blue)
4907 element = EL_CHAR_1;
4910 case 0x15c8: // (blue)
4911 element = EL_CHAR_2;
4914 case 0x15c9: // (blue)
4915 element = EL_CHAR_3;
4918 case 0x15ca: // (blue)
4919 element = EL_CHAR_4;
4922 case 0x15cb: // (blue)
4923 element = EL_CHAR_5;
4926 case 0x15cc: // (blue)
4927 element = EL_CHAR_6;
4930 case 0x15cd: // (blue)
4931 element = EL_CHAR_7;
4934 case 0x15ce: // (blue)
4935 element = EL_CHAR_8;
4938 case 0x15cf: // (blue)
4939 element = EL_CHAR_9;
4942 case 0x15d0: // (blue)
4943 element = EL_CHAR_PERIOD;
4946 case 0x15d1: // (blue)
4947 element = EL_CHAR_EXCLAM;
4950 case 0x15d2: // (blue)
4951 element = EL_CHAR_COLON;
4954 case 0x15d3: // (blue)
4955 element = EL_CHAR_LESS;
4958 case 0x15d4: // (blue)
4959 element = EL_CHAR_GREATER;
4962 case 0x15d5: // (blue)
4963 element = EL_CHAR_QUESTION;
4966 case 0x15d6: // (blue)
4967 element = EL_CHAR_COPYRIGHT;
4970 case 0x15d7: // (blue)
4971 element = EL_CHAR_UP;
4974 case 0x15d8: // (blue)
4975 element = EL_CHAR_DOWN;
4978 case 0x15d9: // (blue)
4979 element = EL_CHAR_BUTTON;
4982 case 0x15da: // (blue)
4983 element = EL_CHAR_PLUS;
4986 case 0x15db: // (blue)
4987 element = EL_CHAR_MINUS;
4990 case 0x15dc: // (blue)
4991 element = EL_CHAR_APOSTROPHE;
4994 case 0x15dd: // (blue)
4995 element = EL_CHAR_PARENLEFT;
4998 case 0x15de: // (blue)
4999 element = EL_CHAR_PARENRIGHT;
5002 case 0x15df: // (green)
5003 element = EL_CHAR_A;
5006 case 0x15e0: // (green)
5007 element = EL_CHAR_B;
5010 case 0x15e1: // (green)
5011 element = EL_CHAR_C;
5014 case 0x15e2: // (green)
5015 element = EL_CHAR_D;
5018 case 0x15e3: // (green)
5019 element = EL_CHAR_E;
5022 case 0x15e4: // (green)
5023 element = EL_CHAR_F;
5026 case 0x15e5: // (green)
5027 element = EL_CHAR_G;
5030 case 0x15e6: // (green)
5031 element = EL_CHAR_H;
5034 case 0x15e7: // (green)
5035 element = EL_CHAR_I;
5038 case 0x15e8: // (green)
5039 element = EL_CHAR_J;
5042 case 0x15e9: // (green)
5043 element = EL_CHAR_K;
5046 case 0x15ea: // (green)
5047 element = EL_CHAR_L;
5050 case 0x15eb: // (green)
5051 element = EL_CHAR_M;
5054 case 0x15ec: // (green)
5055 element = EL_CHAR_N;
5058 case 0x15ed: // (green)
5059 element = EL_CHAR_O;
5062 case 0x15ee: // (green)
5063 element = EL_CHAR_P;
5066 case 0x15ef: // (green)
5067 element = EL_CHAR_Q;
5070 case 0x15f0: // (green)
5071 element = EL_CHAR_R;
5074 case 0x15f1: // (green)
5075 element = EL_CHAR_S;
5078 case 0x15f2: // (green)
5079 element = EL_CHAR_T;
5082 case 0x15f3: // (green)
5083 element = EL_CHAR_U;
5086 case 0x15f4: // (green)
5087 element = EL_CHAR_V;
5090 case 0x15f5: // (green)
5091 element = EL_CHAR_W;
5094 case 0x15f6: // (green)
5095 element = EL_CHAR_X;
5098 case 0x15f7: // (green)
5099 element = EL_CHAR_Y;
5102 case 0x15f8: // (green)
5103 element = EL_CHAR_Z;
5106 case 0x15f9: // (green)
5107 element = EL_CHAR_AUMLAUT;
5110 case 0x15fa: // (green)
5111 element = EL_CHAR_OUMLAUT;
5114 case 0x15fb: // (green)
5115 element = EL_CHAR_UUMLAUT;
5118 case 0x15fc: // (green)
5119 element = EL_CHAR_0;
5122 case 0x15fd: // (green)
5123 element = EL_CHAR_1;
5126 case 0x15fe: // (green)
5127 element = EL_CHAR_2;
5130 case 0x15ff: // (green)
5131 element = EL_CHAR_3;
5134 case 0x1600: // (green)
5135 element = EL_CHAR_4;
5138 case 0x1601: // (green)
5139 element = EL_CHAR_5;
5142 case 0x1602: // (green)
5143 element = EL_CHAR_6;
5146 case 0x1603: // (green)
5147 element = EL_CHAR_7;
5150 case 0x1604: // (green)
5151 element = EL_CHAR_8;
5154 case 0x1605: // (green)
5155 element = EL_CHAR_9;
5158 case 0x1606: // (green)
5159 element = EL_CHAR_PERIOD;
5162 case 0x1607: // (green)
5163 element = EL_CHAR_EXCLAM;
5166 case 0x1608: // (green)
5167 element = EL_CHAR_COLON;
5170 case 0x1609: // (green)
5171 element = EL_CHAR_LESS;
5174 case 0x160a: // (green)
5175 element = EL_CHAR_GREATER;
5178 case 0x160b: // (green)
5179 element = EL_CHAR_QUESTION;
5182 case 0x160c: // (green)
5183 element = EL_CHAR_COPYRIGHT;
5186 case 0x160d: // (green)
5187 element = EL_CHAR_UP;
5190 case 0x160e: // (green)
5191 element = EL_CHAR_DOWN;
5194 case 0x160f: // (green)
5195 element = EL_CHAR_BUTTON;
5198 case 0x1610: // (green)
5199 element = EL_CHAR_PLUS;
5202 case 0x1611: // (green)
5203 element = EL_CHAR_MINUS;
5206 case 0x1612: // (green)
5207 element = EL_CHAR_APOSTROPHE;
5210 case 0x1613: // (green)
5211 element = EL_CHAR_PARENLEFT;
5214 case 0x1614: // (green)
5215 element = EL_CHAR_PARENRIGHT;
5218 case 0x1615: // (blue steel)
5219 element = EL_STEEL_CHAR_A;
5222 case 0x1616: // (blue steel)
5223 element = EL_STEEL_CHAR_B;
5226 case 0x1617: // (blue steel)
5227 element = EL_STEEL_CHAR_C;
5230 case 0x1618: // (blue steel)
5231 element = EL_STEEL_CHAR_D;
5234 case 0x1619: // (blue steel)
5235 element = EL_STEEL_CHAR_E;
5238 case 0x161a: // (blue steel)
5239 element = EL_STEEL_CHAR_F;
5242 case 0x161b: // (blue steel)
5243 element = EL_STEEL_CHAR_G;
5246 case 0x161c: // (blue steel)
5247 element = EL_STEEL_CHAR_H;
5250 case 0x161d: // (blue steel)
5251 element = EL_STEEL_CHAR_I;
5254 case 0x161e: // (blue steel)
5255 element = EL_STEEL_CHAR_J;
5258 case 0x161f: // (blue steel)
5259 element = EL_STEEL_CHAR_K;
5262 case 0x1620: // (blue steel)
5263 element = EL_STEEL_CHAR_L;
5266 case 0x1621: // (blue steel)
5267 element = EL_STEEL_CHAR_M;
5270 case 0x1622: // (blue steel)
5271 element = EL_STEEL_CHAR_N;
5274 case 0x1623: // (blue steel)
5275 element = EL_STEEL_CHAR_O;
5278 case 0x1624: // (blue steel)
5279 element = EL_STEEL_CHAR_P;
5282 case 0x1625: // (blue steel)
5283 element = EL_STEEL_CHAR_Q;
5286 case 0x1626: // (blue steel)
5287 element = EL_STEEL_CHAR_R;
5290 case 0x1627: // (blue steel)
5291 element = EL_STEEL_CHAR_S;
5294 case 0x1628: // (blue steel)
5295 element = EL_STEEL_CHAR_T;
5298 case 0x1629: // (blue steel)
5299 element = EL_STEEL_CHAR_U;
5302 case 0x162a: // (blue steel)
5303 element = EL_STEEL_CHAR_V;
5306 case 0x162b: // (blue steel)
5307 element = EL_STEEL_CHAR_W;
5310 case 0x162c: // (blue steel)
5311 element = EL_STEEL_CHAR_X;
5314 case 0x162d: // (blue steel)
5315 element = EL_STEEL_CHAR_Y;
5318 case 0x162e: // (blue steel)
5319 element = EL_STEEL_CHAR_Z;
5322 case 0x162f: // (blue steel)
5323 element = EL_STEEL_CHAR_AUMLAUT;
5326 case 0x1630: // (blue steel)
5327 element = EL_STEEL_CHAR_OUMLAUT;
5330 case 0x1631: // (blue steel)
5331 element = EL_STEEL_CHAR_UUMLAUT;
5334 case 0x1632: // (blue steel)
5335 element = EL_STEEL_CHAR_0;
5338 case 0x1633: // (blue steel)
5339 element = EL_STEEL_CHAR_1;
5342 case 0x1634: // (blue steel)
5343 element = EL_STEEL_CHAR_2;
5346 case 0x1635: // (blue steel)
5347 element = EL_STEEL_CHAR_3;
5350 case 0x1636: // (blue steel)
5351 element = EL_STEEL_CHAR_4;
5354 case 0x1637: // (blue steel)
5355 element = EL_STEEL_CHAR_5;
5358 case 0x1638: // (blue steel)
5359 element = EL_STEEL_CHAR_6;
5362 case 0x1639: // (blue steel)
5363 element = EL_STEEL_CHAR_7;
5366 case 0x163a: // (blue steel)
5367 element = EL_STEEL_CHAR_8;
5370 case 0x163b: // (blue steel)
5371 element = EL_STEEL_CHAR_9;
5374 case 0x163c: // (blue steel)
5375 element = EL_STEEL_CHAR_PERIOD;
5378 case 0x163d: // (blue steel)
5379 element = EL_STEEL_CHAR_EXCLAM;
5382 case 0x163e: // (blue steel)
5383 element = EL_STEEL_CHAR_COLON;
5386 case 0x163f: // (blue steel)
5387 element = EL_STEEL_CHAR_LESS;
5390 case 0x1640: // (blue steel)
5391 element = EL_STEEL_CHAR_GREATER;
5394 case 0x1641: // (blue steel)
5395 element = EL_STEEL_CHAR_QUESTION;
5398 case 0x1642: // (blue steel)
5399 element = EL_STEEL_CHAR_COPYRIGHT;
5402 case 0x1643: // (blue steel)
5403 element = EL_STEEL_CHAR_UP;
5406 case 0x1644: // (blue steel)
5407 element = EL_STEEL_CHAR_DOWN;
5410 case 0x1645: // (blue steel)
5411 element = EL_STEEL_CHAR_BUTTON;
5414 case 0x1646: // (blue steel)
5415 element = EL_STEEL_CHAR_PLUS;
5418 case 0x1647: // (blue steel)
5419 element = EL_STEEL_CHAR_MINUS;
5422 case 0x1648: // (blue steel)
5423 element = EL_STEEL_CHAR_APOSTROPHE;
5426 case 0x1649: // (blue steel)
5427 element = EL_STEEL_CHAR_PARENLEFT;
5430 case 0x164a: // (blue steel)
5431 element = EL_STEEL_CHAR_PARENRIGHT;
5434 case 0x164b: // (green steel)
5435 element = EL_STEEL_CHAR_A;
5438 case 0x164c: // (green steel)
5439 element = EL_STEEL_CHAR_B;
5442 case 0x164d: // (green steel)
5443 element = EL_STEEL_CHAR_C;
5446 case 0x164e: // (green steel)
5447 element = EL_STEEL_CHAR_D;
5450 case 0x164f: // (green steel)
5451 element = EL_STEEL_CHAR_E;
5454 case 0x1650: // (green steel)
5455 element = EL_STEEL_CHAR_F;
5458 case 0x1651: // (green steel)
5459 element = EL_STEEL_CHAR_G;
5462 case 0x1652: // (green steel)
5463 element = EL_STEEL_CHAR_H;
5466 case 0x1653: // (green steel)
5467 element = EL_STEEL_CHAR_I;
5470 case 0x1654: // (green steel)
5471 element = EL_STEEL_CHAR_J;
5474 case 0x1655: // (green steel)
5475 element = EL_STEEL_CHAR_K;
5478 case 0x1656: // (green steel)
5479 element = EL_STEEL_CHAR_L;
5482 case 0x1657: // (green steel)
5483 element = EL_STEEL_CHAR_M;
5486 case 0x1658: // (green steel)
5487 element = EL_STEEL_CHAR_N;
5490 case 0x1659: // (green steel)
5491 element = EL_STEEL_CHAR_O;
5494 case 0x165a: // (green steel)
5495 element = EL_STEEL_CHAR_P;
5498 case 0x165b: // (green steel)
5499 element = EL_STEEL_CHAR_Q;
5502 case 0x165c: // (green steel)
5503 element = EL_STEEL_CHAR_R;
5506 case 0x165d: // (green steel)
5507 element = EL_STEEL_CHAR_S;
5510 case 0x165e: // (green steel)
5511 element = EL_STEEL_CHAR_T;
5514 case 0x165f: // (green steel)
5515 element = EL_STEEL_CHAR_U;
5518 case 0x1660: // (green steel)
5519 element = EL_STEEL_CHAR_V;
5522 case 0x1661: // (green steel)
5523 element = EL_STEEL_CHAR_W;
5526 case 0x1662: // (green steel)
5527 element = EL_STEEL_CHAR_X;
5530 case 0x1663: // (green steel)
5531 element = EL_STEEL_CHAR_Y;
5534 case 0x1664: // (green steel)
5535 element = EL_STEEL_CHAR_Z;
5538 case 0x1665: // (green steel)
5539 element = EL_STEEL_CHAR_AUMLAUT;
5542 case 0x1666: // (green steel)
5543 element = EL_STEEL_CHAR_OUMLAUT;
5546 case 0x1667: // (green steel)
5547 element = EL_STEEL_CHAR_UUMLAUT;
5550 case 0x1668: // (green steel)
5551 element = EL_STEEL_CHAR_0;
5554 case 0x1669: // (green steel)
5555 element = EL_STEEL_CHAR_1;
5558 case 0x166a: // (green steel)
5559 element = EL_STEEL_CHAR_2;
5562 case 0x166b: // (green steel)
5563 element = EL_STEEL_CHAR_3;
5566 case 0x166c: // (green steel)
5567 element = EL_STEEL_CHAR_4;
5570 case 0x166d: // (green steel)
5571 element = EL_STEEL_CHAR_5;
5574 case 0x166e: // (green steel)
5575 element = EL_STEEL_CHAR_6;
5578 case 0x166f: // (green steel)
5579 element = EL_STEEL_CHAR_7;
5582 case 0x1670: // (green steel)
5583 element = EL_STEEL_CHAR_8;
5586 case 0x1671: // (green steel)
5587 element = EL_STEEL_CHAR_9;
5590 case 0x1672: // (green steel)
5591 element = EL_STEEL_CHAR_PERIOD;
5594 case 0x1673: // (green steel)
5595 element = EL_STEEL_CHAR_EXCLAM;
5598 case 0x1674: // (green steel)
5599 element = EL_STEEL_CHAR_COLON;
5602 case 0x1675: // (green steel)
5603 element = EL_STEEL_CHAR_LESS;
5606 case 0x1676: // (green steel)
5607 element = EL_STEEL_CHAR_GREATER;
5610 case 0x1677: // (green steel)
5611 element = EL_STEEL_CHAR_QUESTION;
5614 case 0x1678: // (green steel)
5615 element = EL_STEEL_CHAR_COPYRIGHT;
5618 case 0x1679: // (green steel)
5619 element = EL_STEEL_CHAR_UP;
5622 case 0x167a: // (green steel)
5623 element = EL_STEEL_CHAR_DOWN;
5626 case 0x167b: // (green steel)
5627 element = EL_STEEL_CHAR_BUTTON;
5630 case 0x167c: // (green steel)
5631 element = EL_STEEL_CHAR_PLUS;
5634 case 0x167d: // (green steel)
5635 element = EL_STEEL_CHAR_MINUS;
5638 case 0x167e: // (green steel)
5639 element = EL_STEEL_CHAR_APOSTROPHE;
5642 case 0x167f: // (green steel)
5643 element = EL_STEEL_CHAR_PARENLEFT;
5646 case 0x1680: // (green steel)
5647 element = EL_STEEL_CHAR_PARENRIGHT;
5650 case 0x1681: // gate (red)
5651 element = EL_EM_GATE_1;
5654 case 0x1682: // secret gate (red)
5655 element = EL_EM_GATE_1_GRAY;
5658 case 0x1683: // gate (yellow)
5659 element = EL_EM_GATE_2;
5662 case 0x1684: // secret gate (yellow)
5663 element = EL_EM_GATE_2_GRAY;
5666 case 0x1685: // gate (blue)
5667 element = EL_EM_GATE_4;
5670 case 0x1686: // secret gate (blue)
5671 element = EL_EM_GATE_4_GRAY;
5674 case 0x1687: // gate (green)
5675 element = EL_EM_GATE_3;
5678 case 0x1688: // secret gate (green)
5679 element = EL_EM_GATE_3_GRAY;
5682 case 0x1689: // gate (white)
5683 element = EL_DC_GATE_WHITE;
5686 case 0x168a: // secret gate (white)
5687 element = EL_DC_GATE_WHITE_GRAY;
5690 case 0x168b: // secret gate (no key)
5691 element = EL_DC_GATE_FAKE_GRAY;
5695 element = EL_ROBOT_WHEEL;
5699 element = EL_DC_TIMEGATE_SWITCH;
5703 element = EL_ACID_POOL_BOTTOM;
5707 element = EL_ACID_POOL_TOPLEFT;
5711 element = EL_ACID_POOL_TOPRIGHT;
5715 element = EL_ACID_POOL_BOTTOMLEFT;
5719 element = EL_ACID_POOL_BOTTOMRIGHT;
5723 element = EL_STEELWALL;
5727 element = EL_STEELWALL_SLIPPERY;
5730 case 0x1695: // steel wall (not round)
5731 element = EL_STEELWALL;
5734 case 0x1696: // steel wall (left)
5735 element = EL_DC_STEELWALL_1_LEFT;
5738 case 0x1697: // steel wall (bottom)
5739 element = EL_DC_STEELWALL_1_BOTTOM;
5742 case 0x1698: // steel wall (right)
5743 element = EL_DC_STEELWALL_1_RIGHT;
5746 case 0x1699: // steel wall (top)
5747 element = EL_DC_STEELWALL_1_TOP;
5750 case 0x169a: // steel wall (left/bottom)
5751 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5754 case 0x169b: // steel wall (right/bottom)
5755 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5758 case 0x169c: // steel wall (right/top)
5759 element = EL_DC_STEELWALL_1_TOPRIGHT;
5762 case 0x169d: // steel wall (left/top)
5763 element = EL_DC_STEELWALL_1_TOPLEFT;
5766 case 0x169e: // steel wall (right/bottom small)
5767 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5770 case 0x169f: // steel wall (left/bottom small)
5771 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5774 case 0x16a0: // steel wall (right/top small)
5775 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5778 case 0x16a1: // steel wall (left/top small)
5779 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5782 case 0x16a2: // steel wall (left/right)
5783 element = EL_DC_STEELWALL_1_VERTICAL;
5786 case 0x16a3: // steel wall (top/bottom)
5787 element = EL_DC_STEELWALL_1_HORIZONTAL;
5790 case 0x16a4: // steel wall 2 (left end)
5791 element = EL_DC_STEELWALL_2_LEFT;
5794 case 0x16a5: // steel wall 2 (right end)
5795 element = EL_DC_STEELWALL_2_RIGHT;
5798 case 0x16a6: // steel wall 2 (top end)
5799 element = EL_DC_STEELWALL_2_TOP;
5802 case 0x16a7: // steel wall 2 (bottom end)
5803 element = EL_DC_STEELWALL_2_BOTTOM;
5806 case 0x16a8: // steel wall 2 (left/right)
5807 element = EL_DC_STEELWALL_2_HORIZONTAL;
5810 case 0x16a9: // steel wall 2 (up/down)
5811 element = EL_DC_STEELWALL_2_VERTICAL;
5814 case 0x16aa: // steel wall 2 (mid)
5815 element = EL_DC_STEELWALL_2_MIDDLE;
5819 element = EL_SIGN_EXCLAMATION;
5823 element = EL_SIGN_RADIOACTIVITY;
5827 element = EL_SIGN_STOP;
5831 element = EL_SIGN_WHEELCHAIR;
5835 element = EL_SIGN_PARKING;
5839 element = EL_SIGN_NO_ENTRY;
5843 element = EL_SIGN_HEART;
5847 element = EL_SIGN_GIVE_WAY;
5851 element = EL_SIGN_ENTRY_FORBIDDEN;
5855 element = EL_SIGN_EMERGENCY_EXIT;
5859 element = EL_SIGN_YIN_YANG;
5863 element = EL_WALL_EMERALD;
5867 element = EL_WALL_DIAMOND;
5871 element = EL_WALL_PEARL;
5875 element = EL_WALL_CRYSTAL;
5879 element = EL_INVISIBLE_WALL;
5883 element = EL_INVISIBLE_STEELWALL;
5887 // EL_INVISIBLE_SAND
5890 element = EL_LIGHT_SWITCH;
5894 element = EL_ENVELOPE_1;
5898 if (element >= 0x0117 && element <= 0x036e) // (?)
5899 element = EL_DIAMOND;
5900 else if (element >= 0x042d && element <= 0x0684) // (?)
5901 element = EL_EMERALD;
5902 else if (element >= 0x157c && element <= 0x158b)
5904 else if (element >= 0x1590 && element <= 0x159f)
5905 element = EL_DC_LANDMINE;
5906 else if (element >= 0x16bc && element <= 0x16cb)
5907 element = EL_INVISIBLE_SAND;
5910 Warn("unknown Diamond Caves element 0x%04x", element);
5912 element = EL_UNKNOWN;
5917 return getMappedElement(element);
5920 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5922 byte header[DC_LEVEL_HEADER_SIZE];
5924 int envelope_header_pos = 62;
5925 int envelope_content_pos = 94;
5926 int level_name_pos = 251;
5927 int level_author_pos = 292;
5928 int envelope_header_len;
5929 int envelope_content_len;
5931 int level_author_len;
5933 int num_yamyam_contents;
5936 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5938 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5940 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5942 header[i * 2 + 0] = header_word >> 8;
5943 header[i * 2 + 1] = header_word & 0xff;
5946 // read some values from level header to check level decoding integrity
5947 fieldx = header[6] | (header[7] << 8);
5948 fieldy = header[8] | (header[9] << 8);
5949 num_yamyam_contents = header[60] | (header[61] << 8);
5951 // do some simple sanity checks to ensure that level was correctly decoded
5952 if (fieldx < 1 || fieldx > 256 ||
5953 fieldy < 1 || fieldy > 256 ||
5954 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5956 level->no_valid_file = TRUE;
5958 Warn("cannot decode level from stream -- using empty level");
5963 // maximum envelope header size is 31 bytes
5964 envelope_header_len = header[envelope_header_pos];
5965 // maximum envelope content size is 110 (156?) bytes
5966 envelope_content_len = header[envelope_content_pos];
5968 // maximum level title size is 40 bytes
5969 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5970 // maximum level author size is 30 (51?) bytes
5971 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5975 for (i = 0; i < envelope_header_len; i++)
5976 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5977 level->envelope[0].text[envelope_size++] =
5978 header[envelope_header_pos + 1 + i];
5980 if (envelope_header_len > 0 && envelope_content_len > 0)
5982 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5983 level->envelope[0].text[envelope_size++] = '\n';
5984 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5985 level->envelope[0].text[envelope_size++] = '\n';
5988 for (i = 0; i < envelope_content_len; i++)
5989 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5990 level->envelope[0].text[envelope_size++] =
5991 header[envelope_content_pos + 1 + i];
5993 level->envelope[0].text[envelope_size] = '\0';
5995 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5996 level->envelope[0].ysize = 10;
5997 level->envelope[0].autowrap = TRUE;
5998 level->envelope[0].centered = TRUE;
6000 for (i = 0; i < level_name_len; i++)
6001 level->name[i] = header[level_name_pos + 1 + i];
6002 level->name[level_name_len] = '\0';
6004 for (i = 0; i < level_author_len; i++)
6005 level->author[i] = header[level_author_pos + 1 + i];
6006 level->author[level_author_len] = '\0';
6008 num_yamyam_contents = header[60] | (header[61] << 8);
6009 level->num_yamyam_contents =
6010 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6012 for (i = 0; i < num_yamyam_contents; i++)
6014 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6016 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6017 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6019 if (i < MAX_ELEMENT_CONTENTS)
6020 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6024 fieldx = header[6] | (header[7] << 8);
6025 fieldy = header[8] | (header[9] << 8);
6026 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6027 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6029 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6031 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6032 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6034 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6035 level->field[x][y] = getMappedElement_DC(element_dc);
6038 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6039 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6040 level->field[x][y] = EL_PLAYER_1;
6042 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6043 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6044 level->field[x][y] = EL_PLAYER_2;
6046 level->gems_needed = header[18] | (header[19] << 8);
6048 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6049 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6050 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6051 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6052 level->score[SC_NUT] = header[28] | (header[29] << 8);
6053 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6054 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6055 level->score[SC_BUG] = header[34] | (header[35] << 8);
6056 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6057 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6058 level->score[SC_KEY] = header[40] | (header[41] << 8);
6059 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6061 level->time = header[44] | (header[45] << 8);
6063 level->amoeba_speed = header[46] | (header[47] << 8);
6064 level->time_light = header[48] | (header[49] << 8);
6065 level->time_timegate = header[50] | (header[51] << 8);
6066 level->time_wheel = header[52] | (header[53] << 8);
6067 level->time_magic_wall = header[54] | (header[55] << 8);
6068 level->extra_time = header[56] | (header[57] << 8);
6069 level->shield_normal_time = header[58] | (header[59] << 8);
6071 // shield and extra time elements do not have a score
6072 level->score[SC_SHIELD] = 0;
6073 level->extra_time_score = 0;
6075 // set time for normal and deadly shields to the same value
6076 level->shield_deadly_time = level->shield_normal_time;
6078 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6079 // can slip down from flat walls, like normal walls and steel walls
6080 level->em_slippery_gems = TRUE;
6082 // time score is counted for each 10 seconds left in Diamond Caves levels
6083 level->time_score_base = 10;
6086 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6087 struct LevelFileInfo *level_file_info,
6088 boolean level_info_only)
6090 char *filename = level_file_info->filename;
6092 int num_magic_bytes = 8;
6093 char magic_bytes[num_magic_bytes + 1];
6094 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6096 if (!(file = openFile(filename, MODE_READ)))
6098 level->no_valid_file = TRUE;
6100 if (!level_info_only)
6101 Warn("cannot read level '%s' -- using empty level", filename);
6106 // fseek(file, 0x0000, SEEK_SET);
6108 if (level_file_info->packed)
6110 // read "magic bytes" from start of file
6111 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6112 magic_bytes[0] = '\0';
6114 // check "magic bytes" for correct file format
6115 if (!strPrefix(magic_bytes, "DC2"))
6117 level->no_valid_file = TRUE;
6119 Warn("unknown DC level file '%s' -- using empty level", filename);
6124 if (strPrefix(magic_bytes, "DC2Win95") ||
6125 strPrefix(magic_bytes, "DC2Win98"))
6127 int position_first_level = 0x00fa;
6128 int extra_bytes = 4;
6131 // advance file stream to first level inside the level package
6132 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6134 // each block of level data is followed by block of non-level data
6135 num_levels_to_skip *= 2;
6137 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6138 while (num_levels_to_skip >= 0)
6140 // advance file stream to next level inside the level package
6141 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6143 level->no_valid_file = TRUE;
6145 Warn("cannot fseek in file '%s' -- using empty level", filename);
6150 // skip apparently unused extra bytes following each level
6151 ReadUnusedBytesFromFile(file, extra_bytes);
6153 // read size of next level in level package
6154 skip_bytes = getFile32BitLE(file);
6156 num_levels_to_skip--;
6161 level->no_valid_file = TRUE;
6163 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6169 LoadLevelFromFileStream_DC(file, level);
6175 // ----------------------------------------------------------------------------
6176 // functions for loading SB level
6177 // ----------------------------------------------------------------------------
6179 int getMappedElement_SB(int element_ascii, boolean use_ces)
6187 sb_element_mapping[] =
6189 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6190 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6191 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6192 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6193 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6194 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6195 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6196 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6203 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6204 if (element_ascii == sb_element_mapping[i].ascii)
6205 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6207 return EL_UNDEFINED;
6210 static void SetLevelSettings_SB(struct LevelInfo *level)
6214 level->use_step_counter = TRUE;
6217 level->score[SC_TIME_BONUS] = 0;
6218 level->time_score_base = 1;
6219 level->rate_time_over_score = TRUE;
6222 level->auto_exit_sokoban = TRUE;
6225 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6226 struct LevelFileInfo *level_file_info,
6227 boolean level_info_only)
6229 char *filename = level_file_info->filename;
6230 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6231 char last_comment[MAX_LINE_LEN];
6232 char level_name[MAX_LINE_LEN];
6235 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6236 boolean read_continued_line = FALSE;
6237 boolean reading_playfield = FALSE;
6238 boolean got_valid_playfield_line = FALSE;
6239 boolean invalid_playfield_char = FALSE;
6240 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6241 int file_level_nr = 0;
6242 int x = 0, y = 0; // initialized to make compilers happy
6244 last_comment[0] = '\0';
6245 level_name[0] = '\0';
6247 if (!(file = openFile(filename, MODE_READ)))
6249 level->no_valid_file = TRUE;
6251 if (!level_info_only)
6252 Warn("cannot read level '%s' -- using empty level", filename);
6257 while (!checkEndOfFile(file))
6259 // level successfully read, but next level may follow here
6260 if (!got_valid_playfield_line && reading_playfield)
6262 // read playfield from single level file -- skip remaining file
6263 if (!level_file_info->packed)
6266 if (file_level_nr >= num_levels_to_skip)
6271 last_comment[0] = '\0';
6272 level_name[0] = '\0';
6274 reading_playfield = FALSE;
6277 got_valid_playfield_line = FALSE;
6279 // read next line of input file
6280 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6283 // cut trailing line break (this can be newline and/or carriage return)
6284 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6285 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6288 // copy raw input line for later use (mainly debugging output)
6289 strcpy(line_raw, line);
6291 if (read_continued_line)
6293 // append new line to existing line, if there is enough space
6294 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6295 strcat(previous_line, line_ptr);
6297 strcpy(line, previous_line); // copy storage buffer to line
6299 read_continued_line = FALSE;
6302 // if the last character is '\', continue at next line
6303 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6305 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6306 strcpy(previous_line, line); // copy line to storage buffer
6308 read_continued_line = TRUE;
6314 if (line[0] == '\0')
6317 // extract comment text from comment line
6320 for (line_ptr = line; *line_ptr; line_ptr++)
6321 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6324 strcpy(last_comment, line_ptr);
6329 // extract level title text from line containing level title
6330 if (line[0] == '\'')
6332 strcpy(level_name, &line[1]);
6334 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6335 level_name[strlen(level_name) - 1] = '\0';
6340 // skip lines containing only spaces (or empty lines)
6341 for (line_ptr = line; *line_ptr; line_ptr++)
6342 if (*line_ptr != ' ')
6344 if (*line_ptr == '\0')
6347 // at this point, we have found a line containing part of a playfield
6349 got_valid_playfield_line = TRUE;
6351 if (!reading_playfield)
6353 reading_playfield = TRUE;
6354 invalid_playfield_char = FALSE;
6356 for (x = 0; x < MAX_LEV_FIELDX; x++)
6357 for (y = 0; y < MAX_LEV_FIELDY; y++)
6358 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6363 // start with topmost tile row
6367 // skip playfield line if larger row than allowed
6368 if (y >= MAX_LEV_FIELDY)
6371 // start with leftmost tile column
6374 // read playfield elements from line
6375 for (line_ptr = line; *line_ptr; line_ptr++)
6377 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6379 // stop parsing playfield line if larger column than allowed
6380 if (x >= MAX_LEV_FIELDX)
6383 if (mapped_sb_element == EL_UNDEFINED)
6385 invalid_playfield_char = TRUE;
6390 level->field[x][y] = mapped_sb_element;
6392 // continue with next tile column
6395 level->fieldx = MAX(x, level->fieldx);
6398 if (invalid_playfield_char)
6400 // if first playfield line, treat invalid lines as comment lines
6402 reading_playfield = FALSE;
6407 // continue with next tile row
6415 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6416 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6418 if (!reading_playfield)
6420 level->no_valid_file = TRUE;
6422 Warn("cannot read level '%s' -- using empty level", filename);
6427 if (*level_name != '\0')
6429 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6430 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6432 else if (*last_comment != '\0')
6434 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6435 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6439 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6442 // set all empty fields beyond the border walls to invisible steel wall
6443 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6445 if ((x == 0 || x == level->fieldx - 1 ||
6446 y == 0 || y == level->fieldy - 1) &&
6447 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6448 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6449 level->field, level->fieldx, level->fieldy);
6452 // set special level settings for Sokoban levels
6453 SetLevelSettings_SB(level);
6455 if (load_xsb_to_ces)
6457 // special global settings can now be set in level template
6458 level->use_custom_template = TRUE;
6463 // -------------------------------------------------------------------------
6464 // functions for handling native levels
6465 // -------------------------------------------------------------------------
6467 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6468 struct LevelFileInfo *level_file_info,
6469 boolean level_info_only)
6473 // determine position of requested level inside level package
6474 if (level_file_info->packed)
6475 pos = level_file_info->nr - leveldir_current->first_level;
6477 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6478 level->no_valid_file = TRUE;
6481 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6482 struct LevelFileInfo *level_file_info,
6483 boolean level_info_only)
6485 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6486 level->no_valid_file = TRUE;
6489 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6490 struct LevelFileInfo *level_file_info,
6491 boolean level_info_only)
6495 // determine position of requested level inside level package
6496 if (level_file_info->packed)
6497 pos = level_file_info->nr - leveldir_current->first_level;
6499 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6500 level->no_valid_file = TRUE;
6503 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6504 struct LevelFileInfo *level_file_info,
6505 boolean level_info_only)
6507 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6508 level->no_valid_file = TRUE;
6511 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6513 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6514 CopyNativeLevel_RND_to_BD(level);
6515 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6516 CopyNativeLevel_RND_to_EM(level);
6517 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6518 CopyNativeLevel_RND_to_SP(level);
6519 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6520 CopyNativeLevel_RND_to_MM(level);
6523 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6525 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6526 CopyNativeLevel_BD_to_RND(level);
6527 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6528 CopyNativeLevel_EM_to_RND(level);
6529 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6530 CopyNativeLevel_SP_to_RND(level);
6531 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6532 CopyNativeLevel_MM_to_RND(level);
6535 void SaveNativeLevel(struct LevelInfo *level)
6537 // saving native level files only supported for some game engines
6538 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6539 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6542 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6543 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6544 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6545 char *filename = getLevelFilenameFromBasename(basename);
6547 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6550 boolean success = FALSE;
6552 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6554 CopyNativeLevel_RND_to_BD(level);
6555 // CopyNativeTape_RND_to_BD(level);
6557 success = SaveNativeLevel_BD(filename);
6559 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6561 CopyNativeLevel_RND_to_SP(level);
6562 CopyNativeTape_RND_to_SP(level);
6564 success = SaveNativeLevel_SP(filename);
6568 Request("Native level file saved!", REQ_CONFIRM);
6570 Request("Failed to save native level file!", REQ_CONFIRM);
6574 // ----------------------------------------------------------------------------
6575 // functions for loading generic level
6576 // ----------------------------------------------------------------------------
6578 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6579 struct LevelFileInfo *level_file_info,
6580 boolean level_info_only)
6582 // always start with reliable default values
6583 setLevelInfoToDefaults(level, level_info_only, TRUE);
6585 switch (level_file_info->type)
6587 case LEVEL_FILE_TYPE_RND:
6588 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6591 case LEVEL_FILE_TYPE_BD:
6592 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6593 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6596 case LEVEL_FILE_TYPE_EM:
6597 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6598 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6601 case LEVEL_FILE_TYPE_SP:
6602 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6603 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6606 case LEVEL_FILE_TYPE_MM:
6607 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6608 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6611 case LEVEL_FILE_TYPE_DC:
6612 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6615 case LEVEL_FILE_TYPE_SB:
6616 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6620 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6624 // if level file is invalid, restore level structure to default values
6625 if (level->no_valid_file)
6626 setLevelInfoToDefaults(level, level_info_only, FALSE);
6628 if (check_special_flags("use_native_bd_game_engine"))
6629 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6631 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6632 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6634 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6635 CopyNativeLevel_Native_to_RND(level);
6638 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6640 static struct LevelFileInfo level_file_info;
6642 // always start with reliable default values
6643 setFileInfoToDefaults(&level_file_info);
6645 level_file_info.nr = 0; // unknown level number
6646 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6648 setString(&level_file_info.filename, filename);
6650 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6653 static void LoadLevel_InitVersion(struct LevelInfo *level)
6657 if (leveldir_current == NULL) // only when dumping level
6660 // all engine modifications also valid for levels which use latest engine
6661 if (level->game_version < VERSION_IDENT(3,2,0,5))
6663 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6664 level->time_score_base = 10;
6667 if (leveldir_current->latest_engine)
6669 // ---------- use latest game engine --------------------------------------
6671 /* For all levels which are forced to use the latest game engine version
6672 (normally all but user contributed, private and undefined levels), set
6673 the game engine version to the actual version; this allows for actual
6674 corrections in the game engine to take effect for existing, converted
6675 levels (from "classic" or other existing games) to make the emulation
6676 of the corresponding game more accurate, while (hopefully) not breaking
6677 existing levels created from other players. */
6679 level->game_version = GAME_VERSION_ACTUAL;
6681 /* Set special EM style gems behaviour: EM style gems slip down from
6682 normal, steel and growing wall. As this is a more fundamental change,
6683 it seems better to set the default behaviour to "off" (as it is more
6684 natural) and make it configurable in the level editor (as a property
6685 of gem style elements). Already existing converted levels (neither
6686 private nor contributed levels) are changed to the new behaviour. */
6688 if (level->file_version < FILE_VERSION_2_0)
6689 level->em_slippery_gems = TRUE;
6694 // ---------- use game engine the level was created with --------------------
6696 /* For all levels which are not forced to use the latest game engine
6697 version (normally user contributed, private and undefined levels),
6698 use the version of the game engine the levels were created for.
6700 Since 2.0.1, the game engine version is now directly stored
6701 in the level file (chunk "VERS"), so there is no need anymore
6702 to set the game version from the file version (except for old,
6703 pre-2.0 levels, where the game version is still taken from the
6704 file format version used to store the level -- see above). */
6706 // player was faster than enemies in 1.0.0 and before
6707 if (level->file_version == FILE_VERSION_1_0)
6708 for (i = 0; i < MAX_PLAYERS; i++)
6709 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6711 // default behaviour for EM style gems was "slippery" only in 2.0.1
6712 if (level->game_version == VERSION_IDENT(2,0,1,0))
6713 level->em_slippery_gems = TRUE;
6715 // springs could be pushed over pits before (pre-release version) 2.2.0
6716 if (level->game_version < VERSION_IDENT(2,2,0,0))
6717 level->use_spring_bug = TRUE;
6719 if (level->game_version < VERSION_IDENT(3,2,0,5))
6721 // time orb caused limited time in endless time levels before 3.2.0-5
6722 level->use_time_orb_bug = TRUE;
6724 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6725 level->block_snap_field = FALSE;
6727 // extra time score was same value as time left score before 3.2.0-5
6728 level->extra_time_score = level->score[SC_TIME_BONUS];
6731 if (level->game_version < VERSION_IDENT(3,2,0,7))
6733 // default behaviour for snapping was "not continuous" before 3.2.0-7
6734 level->continuous_snapping = FALSE;
6737 // only few elements were able to actively move into acid before 3.1.0
6738 // trigger settings did not exist before 3.1.0; set to default "any"
6739 if (level->game_version < VERSION_IDENT(3,1,0,0))
6741 // correct "can move into acid" settings (all zero in old levels)
6743 level->can_move_into_acid_bits = 0; // nothing can move into acid
6744 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6746 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6747 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6748 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6749 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6751 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6752 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6754 // correct trigger settings (stored as zero == "none" in old levels)
6756 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6758 int element = EL_CUSTOM_START + i;
6759 struct ElementInfo *ei = &element_info[element];
6761 for (j = 0; j < ei->num_change_pages; j++)
6763 struct ElementChangeInfo *change = &ei->change_page[j];
6765 change->trigger_player = CH_PLAYER_ANY;
6766 change->trigger_page = CH_PAGE_ANY;
6771 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6773 int element = EL_CUSTOM_256;
6774 struct ElementInfo *ei = &element_info[element];
6775 struct ElementChangeInfo *change = &ei->change_page[0];
6777 /* This is needed to fix a problem that was caused by a bugfix in function
6778 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6779 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6780 not replace walkable elements, but instead just placed the player on it,
6781 without placing the Sokoban field under the player). Unfortunately, this
6782 breaks "Snake Bite" style levels when the snake is halfway through a door
6783 that just closes (the snake head is still alive and can be moved in this
6784 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6785 player (without Sokoban element) which then gets killed as designed). */
6787 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6788 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6789 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6790 change->target_element = EL_PLAYER_1;
6793 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6794 if (level->game_version < VERSION_IDENT(3,2,5,0))
6796 /* This is needed to fix a problem that was caused by a bugfix in function
6797 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6798 corrects the behaviour when a custom element changes to another custom
6799 element with a higher element number that has change actions defined.
6800 Normally, only one change per frame is allowed for custom elements.
6801 Therefore, it is checked if a custom element already changed in the
6802 current frame; if it did, subsequent changes are suppressed.
6803 Unfortunately, this is only checked for element changes, but not for
6804 change actions, which are still executed. As the function above loops
6805 through all custom elements from lower to higher, an element change
6806 resulting in a lower CE number won't be checked again, while a target
6807 element with a higher number will also be checked, and potential change
6808 actions will get executed for this CE, too (which is wrong), while
6809 further changes are ignored (which is correct). As this bugfix breaks
6810 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6811 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6812 behaviour for existing levels and tapes that make use of this bug */
6814 level->use_action_after_change_bug = TRUE;
6817 // not centering level after relocating player was default only in 3.2.3
6818 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6819 level->shifted_relocation = TRUE;
6821 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6822 if (level->game_version < VERSION_IDENT(3,2,6,0))
6823 level->em_explodes_by_fire = TRUE;
6825 // levels were solved by the first player entering an exit up to 4.1.0.0
6826 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6827 level->solved_by_one_player = TRUE;
6829 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6830 if (level->game_version < VERSION_IDENT(4,1,1,1))
6831 level->use_life_bugs = TRUE;
6833 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6834 if (level->game_version < VERSION_IDENT(4,1,1,1))
6835 level->sb_objects_needed = FALSE;
6837 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6838 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6839 level->finish_dig_collect = FALSE;
6841 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6842 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6843 level->keep_walkable_ce = TRUE;
6846 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6848 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6851 // check if this level is (not) a Sokoban level
6852 for (y = 0; y < level->fieldy; y++)
6853 for (x = 0; x < level->fieldx; x++)
6854 if (!IS_SB_ELEMENT(Tile[x][y]))
6855 is_sokoban_level = FALSE;
6857 if (is_sokoban_level)
6859 // set special level settings for Sokoban levels
6860 SetLevelSettings_SB(level);
6864 static void LoadLevel_InitSettings(struct LevelInfo *level)
6866 // adjust level settings for (non-native) Sokoban-style levels
6867 LoadLevel_InitSettings_SB(level);
6869 // rename levels with title "nameless level" or if renaming is forced
6870 if (leveldir_current->empty_level_name != NULL &&
6871 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6872 leveldir_current->force_level_name))
6873 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6874 leveldir_current->empty_level_name, level_nr);
6877 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6881 // map elements that have changed in newer versions
6882 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6883 level->game_version);
6884 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6885 for (x = 0; x < 3; x++)
6886 for (y = 0; y < 3; y++)
6887 level->yamyam_content[i].e[x][y] =
6888 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6889 level->game_version);
6893 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6897 // map custom element change events that have changed in newer versions
6898 // (these following values were accidentally changed in version 3.0.1)
6899 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6900 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6902 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6904 int element = EL_CUSTOM_START + i;
6906 // order of checking and copying events to be mapped is important
6907 // (do not change the start and end value -- they are constant)
6908 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6910 if (HAS_CHANGE_EVENT(element, j - 2))
6912 SET_CHANGE_EVENT(element, j - 2, FALSE);
6913 SET_CHANGE_EVENT(element, j, TRUE);
6917 // order of checking and copying events to be mapped is important
6918 // (do not change the start and end value -- they are constant)
6919 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6921 if (HAS_CHANGE_EVENT(element, j - 1))
6923 SET_CHANGE_EVENT(element, j - 1, FALSE);
6924 SET_CHANGE_EVENT(element, j, TRUE);
6930 // initialize "can_change" field for old levels with only one change page
6931 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6933 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6935 int element = EL_CUSTOM_START + i;
6937 if (CAN_CHANGE(element))
6938 element_info[element].change->can_change = TRUE;
6942 // correct custom element values (for old levels without these options)
6943 if (level->game_version < VERSION_IDENT(3,1,1,0))
6945 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6947 int element = EL_CUSTOM_START + i;
6948 struct ElementInfo *ei = &element_info[element];
6950 if (ei->access_direction == MV_NO_DIRECTION)
6951 ei->access_direction = MV_ALL_DIRECTIONS;
6955 // correct custom element values (fix invalid values for all versions)
6958 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6960 int element = EL_CUSTOM_START + i;
6961 struct ElementInfo *ei = &element_info[element];
6963 for (j = 0; j < ei->num_change_pages; j++)
6965 struct ElementChangeInfo *change = &ei->change_page[j];
6967 if (change->trigger_player == CH_PLAYER_NONE)
6968 change->trigger_player = CH_PLAYER_ANY;
6970 if (change->trigger_side == CH_SIDE_NONE)
6971 change->trigger_side = CH_SIDE_ANY;
6976 // initialize "can_explode" field for old levels which did not store this
6977 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6978 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6980 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6982 int element = EL_CUSTOM_START + i;
6984 if (EXPLODES_1X1_OLD(element))
6985 element_info[element].explosion_type = EXPLODES_1X1;
6987 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6988 EXPLODES_SMASHED(element) ||
6989 EXPLODES_IMPACT(element)));
6993 // correct previously hard-coded move delay values for maze runner style
6994 if (level->game_version < VERSION_IDENT(3,1,1,0))
6996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6998 int element = EL_CUSTOM_START + i;
7000 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7002 // previously hard-coded and therefore ignored
7003 element_info[element].move_delay_fixed = 9;
7004 element_info[element].move_delay_random = 0;
7009 // set some other uninitialized values of custom elements in older levels
7010 if (level->game_version < VERSION_IDENT(3,1,0,0))
7012 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7014 int element = EL_CUSTOM_START + i;
7016 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7018 element_info[element].explosion_delay = 17;
7019 element_info[element].ignition_delay = 8;
7023 // set mouse click change events to work for left/middle/right mouse button
7024 if (level->game_version < VERSION_IDENT(4,2,3,0))
7026 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7028 int element = EL_CUSTOM_START + i;
7029 struct ElementInfo *ei = &element_info[element];
7031 for (j = 0; j < ei->num_change_pages; j++)
7033 struct ElementChangeInfo *change = &ei->change_page[j];
7035 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7036 change->has_event[CE_PRESSED_BY_MOUSE] ||
7037 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7038 change->has_event[CE_MOUSE_PRESSED_ON_X])
7039 change->trigger_side = CH_SIDE_ANY;
7045 static void LoadLevel_InitElements(struct LevelInfo *level)
7047 LoadLevel_InitStandardElements(level);
7049 if (level->file_has_custom_elements)
7050 LoadLevel_InitCustomElements(level);
7052 // initialize element properties for level editor etc.
7053 InitElementPropertiesEngine(level->game_version);
7054 InitElementPropertiesGfxElement();
7057 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7061 // map elements that have changed in newer versions
7062 for (y = 0; y < level->fieldy; y++)
7063 for (x = 0; x < level->fieldx; x++)
7064 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7065 level->game_version);
7067 // clear unused playfield data (nicer if level gets resized in editor)
7068 for (x = 0; x < MAX_LEV_FIELDX; x++)
7069 for (y = 0; y < MAX_LEV_FIELDY; y++)
7070 if (x >= level->fieldx || y >= level->fieldy)
7071 level->field[x][y] = EL_EMPTY;
7073 // copy elements to runtime playfield array
7074 for (x = 0; x < MAX_LEV_FIELDX; x++)
7075 for (y = 0; y < MAX_LEV_FIELDY; y++)
7076 Tile[x][y] = level->field[x][y];
7078 // initialize level size variables for faster access
7079 lev_fieldx = level->fieldx;
7080 lev_fieldy = level->fieldy;
7082 // determine border element for this level
7083 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7084 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7089 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7091 struct LevelFileInfo *level_file_info = &level->file_info;
7093 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7094 CopyNativeLevel_RND_to_Native(level);
7097 static void LoadLevelTemplate_LoadAndInit(void)
7099 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7101 LoadLevel_InitVersion(&level_template);
7102 LoadLevel_InitElements(&level_template);
7103 LoadLevel_InitSettings(&level_template);
7105 ActivateLevelTemplate();
7108 void LoadLevelTemplate(int nr)
7110 if (!fileExists(getGlobalLevelTemplateFilename()))
7112 Warn("no level template found for this level");
7117 setLevelFileInfo(&level_template.file_info, nr);
7119 LoadLevelTemplate_LoadAndInit();
7122 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7124 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7126 LoadLevelTemplate_LoadAndInit();
7129 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7131 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7133 if (level.use_custom_template)
7135 if (network_level != NULL)
7136 LoadNetworkLevelTemplate(network_level);
7138 LoadLevelTemplate(-1);
7141 LoadLevel_InitVersion(&level);
7142 LoadLevel_InitElements(&level);
7143 LoadLevel_InitPlayfield(&level);
7144 LoadLevel_InitSettings(&level);
7146 LoadLevel_InitNativeEngines(&level);
7149 void LoadLevel(int nr)
7151 SetLevelSetInfo(leveldir_current->identifier, nr);
7153 setLevelFileInfo(&level.file_info, nr);
7155 LoadLevel_LoadAndInit(NULL);
7158 void LoadLevelInfoOnly(int nr)
7160 setLevelFileInfo(&level.file_info, nr);
7162 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7165 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7167 SetLevelSetInfo(network_level->leveldir_identifier,
7168 network_level->file_info.nr);
7170 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7172 LoadLevel_LoadAndInit(network_level);
7175 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7179 chunk_size += putFileVersion(file, level->file_version);
7180 chunk_size += putFileVersion(file, level->game_version);
7185 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7189 chunk_size += putFile16BitBE(file, level->creation_date.year);
7190 chunk_size += putFile8Bit(file, level->creation_date.month);
7191 chunk_size += putFile8Bit(file, level->creation_date.day);
7196 #if ENABLE_HISTORIC_CHUNKS
7197 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7201 putFile8Bit(file, level->fieldx);
7202 putFile8Bit(file, level->fieldy);
7204 putFile16BitBE(file, level->time);
7205 putFile16BitBE(file, level->gems_needed);
7207 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7208 putFile8Bit(file, level->name[i]);
7210 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7211 putFile8Bit(file, level->score[i]);
7213 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7214 for (y = 0; y < 3; y++)
7215 for (x = 0; x < 3; x++)
7216 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7217 level->yamyam_content[i].e[x][y]));
7218 putFile8Bit(file, level->amoeba_speed);
7219 putFile8Bit(file, level->time_magic_wall);
7220 putFile8Bit(file, level->time_wheel);
7221 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7222 level->amoeba_content));
7223 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7224 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7225 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7226 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7228 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7230 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7231 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7232 putFile32BitBE(file, level->can_move_into_acid_bits);
7233 putFile8Bit(file, level->dont_collide_with_bits);
7235 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7236 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7238 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7239 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7240 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7242 putFile8Bit(file, level->game_engine_type);
7244 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7248 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7253 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7254 chunk_size += putFile8Bit(file, level->name[i]);
7259 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7264 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7265 chunk_size += putFile8Bit(file, level->author[i]);
7270 #if ENABLE_HISTORIC_CHUNKS
7271 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7276 for (y = 0; y < level->fieldy; y++)
7277 for (x = 0; x < level->fieldx; x++)
7278 if (level->encoding_16bit_field)
7279 chunk_size += putFile16BitBE(file, level->field[x][y]);
7281 chunk_size += putFile8Bit(file, level->field[x][y]);
7287 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7292 for (y = 0; y < level->fieldy; y++)
7293 for (x = 0; x < level->fieldx; x++)
7294 chunk_size += putFile16BitBE(file, level->field[x][y]);
7299 #if ENABLE_HISTORIC_CHUNKS
7300 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7304 putFile8Bit(file, EL_YAMYAM);
7305 putFile8Bit(file, level->num_yamyam_contents);
7306 putFile8Bit(file, 0);
7307 putFile8Bit(file, 0);
7309 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7310 for (y = 0; y < 3; y++)
7311 for (x = 0; x < 3; x++)
7312 if (level->encoding_16bit_field)
7313 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7315 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7319 #if ENABLE_HISTORIC_CHUNKS
7320 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7323 int num_contents, content_xsize, content_ysize;
7324 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7326 if (element == EL_YAMYAM)
7328 num_contents = level->num_yamyam_contents;
7332 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7333 for (y = 0; y < 3; y++)
7334 for (x = 0; x < 3; x++)
7335 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7337 else if (element == EL_BD_AMOEBA)
7343 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7344 for (y = 0; y < 3; y++)
7345 for (x = 0; x < 3; x++)
7346 content_array[i][x][y] = EL_EMPTY;
7347 content_array[0][0][0] = level->amoeba_content;
7351 // chunk header already written -- write empty chunk data
7352 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7354 Warn("cannot save content for element '%d'", element);
7359 putFile16BitBE(file, element);
7360 putFile8Bit(file, num_contents);
7361 putFile8Bit(file, content_xsize);
7362 putFile8Bit(file, content_ysize);
7364 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7366 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7367 for (y = 0; y < 3; y++)
7368 for (x = 0; x < 3; x++)
7369 putFile16BitBE(file, content_array[i][x][y]);
7373 #if ENABLE_HISTORIC_CHUNKS
7374 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7376 int envelope_nr = element - EL_ENVELOPE_1;
7377 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7381 chunk_size += putFile16BitBE(file, element);
7382 chunk_size += putFile16BitBE(file, envelope_len);
7383 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7384 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7386 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7387 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7389 for (i = 0; i < envelope_len; i++)
7390 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7396 #if ENABLE_HISTORIC_CHUNKS
7397 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7398 int num_changed_custom_elements)
7402 putFile16BitBE(file, num_changed_custom_elements);
7404 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7406 int element = EL_CUSTOM_START + i;
7408 struct ElementInfo *ei = &element_info[element];
7410 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7412 if (check < num_changed_custom_elements)
7414 putFile16BitBE(file, element);
7415 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7422 if (check != num_changed_custom_elements) // should not happen
7423 Warn("inconsistent number of custom element properties");
7427 #if ENABLE_HISTORIC_CHUNKS
7428 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7429 int num_changed_custom_elements)
7433 putFile16BitBE(file, num_changed_custom_elements);
7435 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7437 int element = EL_CUSTOM_START + i;
7439 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7441 if (check < num_changed_custom_elements)
7443 putFile16BitBE(file, element);
7444 putFile16BitBE(file, element_info[element].change->target_element);
7451 if (check != num_changed_custom_elements) // should not happen
7452 Warn("inconsistent number of custom target elements");
7456 #if ENABLE_HISTORIC_CHUNKS
7457 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7458 int num_changed_custom_elements)
7460 int i, j, x, y, check = 0;
7462 putFile16BitBE(file, num_changed_custom_elements);
7464 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7466 int element = EL_CUSTOM_START + i;
7467 struct ElementInfo *ei = &element_info[element];
7469 if (ei->modified_settings)
7471 if (check < num_changed_custom_elements)
7473 putFile16BitBE(file, element);
7475 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7476 putFile8Bit(file, ei->description[j]);
7478 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7480 // some free bytes for future properties and padding
7481 WriteUnusedBytesToFile(file, 7);
7483 putFile8Bit(file, ei->use_gfx_element);
7484 putFile16BitBE(file, ei->gfx_element_initial);
7486 putFile8Bit(file, ei->collect_score_initial);
7487 putFile8Bit(file, ei->collect_count_initial);
7489 putFile16BitBE(file, ei->push_delay_fixed);
7490 putFile16BitBE(file, ei->push_delay_random);
7491 putFile16BitBE(file, ei->move_delay_fixed);
7492 putFile16BitBE(file, ei->move_delay_random);
7494 putFile16BitBE(file, ei->move_pattern);
7495 putFile8Bit(file, ei->move_direction_initial);
7496 putFile8Bit(file, ei->move_stepsize);
7498 for (y = 0; y < 3; y++)
7499 for (x = 0; x < 3; x++)
7500 putFile16BitBE(file, ei->content.e[x][y]);
7502 putFile32BitBE(file, ei->change->events);
7504 putFile16BitBE(file, ei->change->target_element);
7506 putFile16BitBE(file, ei->change->delay_fixed);
7507 putFile16BitBE(file, ei->change->delay_random);
7508 putFile16BitBE(file, ei->change->delay_frames);
7510 putFile16BitBE(file, ei->change->initial_trigger_element);
7512 putFile8Bit(file, ei->change->explode);
7513 putFile8Bit(file, ei->change->use_target_content);
7514 putFile8Bit(file, ei->change->only_if_complete);
7515 putFile8Bit(file, ei->change->use_random_replace);
7517 putFile8Bit(file, ei->change->random_percentage);
7518 putFile8Bit(file, ei->change->replace_when);
7520 for (y = 0; y < 3; y++)
7521 for (x = 0; x < 3; x++)
7522 putFile16BitBE(file, ei->change->content.e[x][y]);
7524 putFile8Bit(file, ei->slippery_type);
7526 // some free bytes for future properties and padding
7527 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7534 if (check != num_changed_custom_elements) // should not happen
7535 Warn("inconsistent number of custom element properties");
7539 #if ENABLE_HISTORIC_CHUNKS
7540 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7542 struct ElementInfo *ei = &element_info[element];
7545 // ---------- custom element base property values (96 bytes) ----------------
7547 putFile16BitBE(file, element);
7549 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7550 putFile8Bit(file, ei->description[i]);
7552 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7554 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7556 putFile8Bit(file, ei->num_change_pages);
7558 putFile16BitBE(file, ei->ce_value_fixed_initial);
7559 putFile16BitBE(file, ei->ce_value_random_initial);
7560 putFile8Bit(file, ei->use_last_ce_value);
7562 putFile8Bit(file, ei->use_gfx_element);
7563 putFile16BitBE(file, ei->gfx_element_initial);
7565 putFile8Bit(file, ei->collect_score_initial);
7566 putFile8Bit(file, ei->collect_count_initial);
7568 putFile8Bit(file, ei->drop_delay_fixed);
7569 putFile8Bit(file, ei->push_delay_fixed);
7570 putFile8Bit(file, ei->drop_delay_random);
7571 putFile8Bit(file, ei->push_delay_random);
7572 putFile16BitBE(file, ei->move_delay_fixed);
7573 putFile16BitBE(file, ei->move_delay_random);
7575 // bits 0 - 15 of "move_pattern" ...
7576 putFile16BitBE(file, ei->move_pattern & 0xffff);
7577 putFile8Bit(file, ei->move_direction_initial);
7578 putFile8Bit(file, ei->move_stepsize);
7580 putFile8Bit(file, ei->slippery_type);
7582 for (y = 0; y < 3; y++)
7583 for (x = 0; x < 3; x++)
7584 putFile16BitBE(file, ei->content.e[x][y]);
7586 putFile16BitBE(file, ei->move_enter_element);
7587 putFile16BitBE(file, ei->move_leave_element);
7588 putFile8Bit(file, ei->move_leave_type);
7590 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7591 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7593 putFile8Bit(file, ei->access_direction);
7595 putFile8Bit(file, ei->explosion_delay);
7596 putFile8Bit(file, ei->ignition_delay);
7597 putFile8Bit(file, ei->explosion_type);
7599 // some free bytes for future custom property values and padding
7600 WriteUnusedBytesToFile(file, 1);
7602 // ---------- change page property values (48 bytes) ------------------------
7604 for (i = 0; i < ei->num_change_pages; i++)
7606 struct ElementChangeInfo *change = &ei->change_page[i];
7607 unsigned int event_bits;
7609 // bits 0 - 31 of "has_event[]" ...
7611 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7612 if (change->has_event[j])
7613 event_bits |= (1u << j);
7614 putFile32BitBE(file, event_bits);
7616 putFile16BitBE(file, change->target_element);
7618 putFile16BitBE(file, change->delay_fixed);
7619 putFile16BitBE(file, change->delay_random);
7620 putFile16BitBE(file, change->delay_frames);
7622 putFile16BitBE(file, change->initial_trigger_element);
7624 putFile8Bit(file, change->explode);
7625 putFile8Bit(file, change->use_target_content);
7626 putFile8Bit(file, change->only_if_complete);
7627 putFile8Bit(file, change->use_random_replace);
7629 putFile8Bit(file, change->random_percentage);
7630 putFile8Bit(file, change->replace_when);
7632 for (y = 0; y < 3; y++)
7633 for (x = 0; x < 3; x++)
7634 putFile16BitBE(file, change->target_content.e[x][y]);
7636 putFile8Bit(file, change->can_change);
7638 putFile8Bit(file, change->trigger_side);
7640 putFile8Bit(file, change->trigger_player);
7641 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7642 log_2(change->trigger_page)));
7644 putFile8Bit(file, change->has_action);
7645 putFile8Bit(file, change->action_type);
7646 putFile8Bit(file, change->action_mode);
7647 putFile16BitBE(file, change->action_arg);
7649 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7651 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7652 if (change->has_event[j])
7653 event_bits |= (1u << (j - 32));
7654 putFile8Bit(file, event_bits);
7659 #if ENABLE_HISTORIC_CHUNKS
7660 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7662 struct ElementInfo *ei = &element_info[element];
7663 struct ElementGroupInfo *group = ei->group;
7666 putFile16BitBE(file, element);
7668 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7669 putFile8Bit(file, ei->description[i]);
7671 putFile8Bit(file, group->num_elements);
7673 putFile8Bit(file, ei->use_gfx_element);
7674 putFile16BitBE(file, ei->gfx_element_initial);
7676 putFile8Bit(file, group->choice_mode);
7678 // some free bytes for future values and padding
7679 WriteUnusedBytesToFile(file, 3);
7681 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7682 putFile16BitBE(file, group->element[i]);
7686 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7687 boolean write_element)
7689 int save_type = entry->save_type;
7690 int data_type = entry->data_type;
7691 int conf_type = entry->conf_type;
7692 int byte_mask = conf_type & CONF_MASK_BYTES;
7693 int element = entry->element;
7694 int default_value = entry->default_value;
7696 boolean modified = FALSE;
7698 if (byte_mask != CONF_MASK_MULTI_BYTES)
7700 void *value_ptr = entry->value;
7701 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7704 // check if any settings have been modified before saving them
7705 if (value != default_value)
7708 // do not save if explicitly told or if unmodified default settings
7709 if ((save_type == SAVE_CONF_NEVER) ||
7710 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7714 num_bytes += putFile16BitBE(file, element);
7716 num_bytes += putFile8Bit(file, conf_type);
7717 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7718 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7719 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7722 else if (data_type == TYPE_STRING)
7724 char *default_string = entry->default_string;
7725 char *string = (char *)(entry->value);
7726 int string_length = strlen(string);
7729 // check if any settings have been modified before saving them
7730 if (!strEqual(string, default_string))
7733 // do not save if explicitly told or if unmodified default settings
7734 if ((save_type == SAVE_CONF_NEVER) ||
7735 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7739 num_bytes += putFile16BitBE(file, element);
7741 num_bytes += putFile8Bit(file, conf_type);
7742 num_bytes += putFile16BitBE(file, string_length);
7744 for (i = 0; i < string_length; i++)
7745 num_bytes += putFile8Bit(file, string[i]);
7747 else if (data_type == TYPE_ELEMENT_LIST)
7749 int *element_array = (int *)(entry->value);
7750 int num_elements = *(int *)(entry->num_entities);
7753 // check if any settings have been modified before saving them
7754 for (i = 0; i < num_elements; i++)
7755 if (element_array[i] != default_value)
7758 // do not save if explicitly told or if unmodified default settings
7759 if ((save_type == SAVE_CONF_NEVER) ||
7760 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7764 num_bytes += putFile16BitBE(file, element);
7766 num_bytes += putFile8Bit(file, conf_type);
7767 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7769 for (i = 0; i < num_elements; i++)
7770 num_bytes += putFile16BitBE(file, element_array[i]);
7772 else if (data_type == TYPE_CONTENT_LIST)
7774 struct Content *content = (struct Content *)(entry->value);
7775 int num_contents = *(int *)(entry->num_entities);
7778 // check if any settings have been modified before saving them
7779 for (i = 0; i < num_contents; i++)
7780 for (y = 0; y < 3; y++)
7781 for (x = 0; x < 3; x++)
7782 if (content[i].e[x][y] != default_value)
7785 // do not save if explicitly told or if unmodified default settings
7786 if ((save_type == SAVE_CONF_NEVER) ||
7787 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7791 num_bytes += putFile16BitBE(file, element);
7793 num_bytes += putFile8Bit(file, conf_type);
7794 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7796 for (i = 0; i < num_contents; i++)
7797 for (y = 0; y < 3; y++)
7798 for (x = 0; x < 3; x++)
7799 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7805 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7810 li = *level; // copy level data into temporary buffer
7812 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7813 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7818 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7823 li = *level; // copy level data into temporary buffer
7825 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7826 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7831 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7833 int envelope_nr = element - EL_ENVELOPE_1;
7837 chunk_size += putFile16BitBE(file, element);
7839 // copy envelope data into temporary buffer
7840 xx_envelope = level->envelope[envelope_nr];
7842 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7843 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7848 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7850 struct ElementInfo *ei = &element_info[element];
7854 chunk_size += putFile16BitBE(file, element);
7856 xx_ei = *ei; // copy element data into temporary buffer
7858 // set default description string for this specific element
7859 strcpy(xx_default_description, getDefaultElementDescription(ei));
7861 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7862 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7864 for (i = 0; i < ei->num_change_pages; i++)
7866 struct ElementChangeInfo *change = &ei->change_page[i];
7868 xx_current_change_page = i;
7870 xx_change = *change; // copy change data into temporary buffer
7873 setEventBitsFromEventFlags(change);
7875 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7876 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7883 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7885 struct ElementInfo *ei = &element_info[element];
7886 struct ElementGroupInfo *group = ei->group;
7890 chunk_size += putFile16BitBE(file, element);
7892 xx_ei = *ei; // copy element data into temporary buffer
7893 xx_group = *group; // copy group data into temporary buffer
7895 // set default description string for this specific element
7896 strcpy(xx_default_description, getDefaultElementDescription(ei));
7898 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7899 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7904 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7906 struct ElementInfo *ei = &element_info[element];
7910 chunk_size += putFile16BitBE(file, element);
7912 xx_ei = *ei; // copy element data into temporary buffer
7914 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7915 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7920 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7921 boolean save_as_template)
7927 if (!(file = fopen(filename, MODE_WRITE)))
7929 Warn("cannot save level file '%s'", filename);
7934 level->file_version = FILE_VERSION_ACTUAL;
7935 level->game_version = GAME_VERSION_ACTUAL;
7937 level->creation_date = getCurrentDate();
7939 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7940 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7942 chunk_size = SaveLevel_VERS(NULL, level);
7943 putFileChunkBE(file, "VERS", chunk_size);
7944 SaveLevel_VERS(file, level);
7946 chunk_size = SaveLevel_DATE(NULL, level);
7947 putFileChunkBE(file, "DATE", chunk_size);
7948 SaveLevel_DATE(file, level);
7950 chunk_size = SaveLevel_NAME(NULL, level);
7951 putFileChunkBE(file, "NAME", chunk_size);
7952 SaveLevel_NAME(file, level);
7954 chunk_size = SaveLevel_AUTH(NULL, level);
7955 putFileChunkBE(file, "AUTH", chunk_size);
7956 SaveLevel_AUTH(file, level);
7958 chunk_size = SaveLevel_INFO(NULL, level);
7959 putFileChunkBE(file, "INFO", chunk_size);
7960 SaveLevel_INFO(file, level);
7962 chunk_size = SaveLevel_BODY(NULL, level);
7963 putFileChunkBE(file, "BODY", chunk_size);
7964 SaveLevel_BODY(file, level);
7966 chunk_size = SaveLevel_ELEM(NULL, level);
7967 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7969 putFileChunkBE(file, "ELEM", chunk_size);
7970 SaveLevel_ELEM(file, level);
7973 for (i = 0; i < NUM_ENVELOPES; i++)
7975 int element = EL_ENVELOPE_1 + i;
7977 chunk_size = SaveLevel_NOTE(NULL, level, element);
7978 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7980 putFileChunkBE(file, "NOTE", chunk_size);
7981 SaveLevel_NOTE(file, level, element);
7985 // if not using template level, check for non-default custom/group elements
7986 if (!level->use_custom_template || save_as_template)
7988 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7990 int element = EL_CUSTOM_START + i;
7992 chunk_size = SaveLevel_CUSX(NULL, level, element);
7993 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7995 putFileChunkBE(file, "CUSX", chunk_size);
7996 SaveLevel_CUSX(file, level, element);
8000 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8002 int element = EL_GROUP_START + i;
8004 chunk_size = SaveLevel_GRPX(NULL, level, element);
8005 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8007 putFileChunkBE(file, "GRPX", chunk_size);
8008 SaveLevel_GRPX(file, level, element);
8012 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8014 int element = GET_EMPTY_ELEMENT(i);
8016 chunk_size = SaveLevel_EMPX(NULL, level, element);
8017 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8019 putFileChunkBE(file, "EMPX", chunk_size);
8020 SaveLevel_EMPX(file, level, element);
8027 SetFilePermissions(filename, PERMS_PRIVATE);
8030 void SaveLevel(int nr)
8032 char *filename = getDefaultLevelFilename(nr);
8034 SaveLevelFromFilename(&level, filename, FALSE);
8037 void SaveLevelTemplate(void)
8039 char *filename = getLocalLevelTemplateFilename();
8041 SaveLevelFromFilename(&level, filename, TRUE);
8044 boolean SaveLevelChecked(int nr)
8046 char *filename = getDefaultLevelFilename(nr);
8047 boolean new_level = !fileExists(filename);
8048 boolean level_saved = FALSE;
8050 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8055 Request("Level saved!", REQ_CONFIRM);
8063 void DumpLevel(struct LevelInfo *level)
8065 if (level->no_level_file || level->no_valid_file)
8067 Warn("cannot dump -- no valid level file found");
8073 Print("Level xxx (file version %08d, game version %08d)\n",
8074 level->file_version, level->game_version);
8077 Print("Level author: '%s'\n", level->author);
8078 Print("Level title: '%s'\n", level->name);
8080 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8082 Print("Level time: %d seconds\n", level->time);
8083 Print("Gems needed: %d\n", level->gems_needed);
8085 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8086 Print("Time for wheel: %d seconds\n", level->time_wheel);
8087 Print("Time for light: %d seconds\n", level->time_light);
8088 Print("Time for timegate: %d seconds\n", level->time_timegate);
8090 Print("Amoeba speed: %d\n", level->amoeba_speed);
8093 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8094 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8095 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8096 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8097 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8098 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8104 for (i = 0; i < NUM_ENVELOPES; i++)
8106 char *text = level->envelope[i].text;
8107 int text_len = strlen(text);
8108 boolean has_text = FALSE;
8110 for (j = 0; j < text_len; j++)
8111 if (text[j] != ' ' && text[j] != '\n')
8117 Print("Envelope %d:\n'%s'\n", i + 1, text);
8125 void DumpLevels(void)
8127 static LevelDirTree *dumplevel_leveldir = NULL;
8129 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8130 global.dumplevel_leveldir);
8132 if (dumplevel_leveldir == NULL)
8133 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8135 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8136 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8137 Fail("no such level number: %d", global.dumplevel_level_nr);
8139 leveldir_current = dumplevel_leveldir;
8141 LoadLevel(global.dumplevel_level_nr);
8148 // ============================================================================
8149 // tape file functions
8150 // ============================================================================
8152 static void setTapeInfoToDefaults(void)
8156 // always start with reliable default values (empty tape)
8159 // default values (also for pre-1.2 tapes) with only the first player
8160 tape.player_participates[0] = TRUE;
8161 for (i = 1; i < MAX_PLAYERS; i++)
8162 tape.player_participates[i] = FALSE;
8164 // at least one (default: the first) player participates in every tape
8165 tape.num_participating_players = 1;
8167 tape.property_bits = TAPE_PROPERTY_NONE;
8169 tape.level_nr = level_nr;
8171 tape.changed = FALSE;
8172 tape.solved = FALSE;
8174 tape.recording = FALSE;
8175 tape.playing = FALSE;
8176 tape.pausing = FALSE;
8178 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8179 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8181 tape.no_info_chunk = TRUE;
8182 tape.no_valid_file = FALSE;
8185 static int getTapePosSize(struct TapeInfo *tape)
8187 int tape_pos_size = 0;
8189 if (tape->use_key_actions)
8190 tape_pos_size += tape->num_participating_players;
8192 if (tape->use_mouse_actions)
8193 tape_pos_size += 3; // x and y position and mouse button mask
8195 tape_pos_size += 1; // tape action delay value
8197 return tape_pos_size;
8200 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8202 tape->use_key_actions = FALSE;
8203 tape->use_mouse_actions = FALSE;
8205 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8206 tape->use_key_actions = TRUE;
8208 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8209 tape->use_mouse_actions = TRUE;
8212 static int getTapeActionValue(struct TapeInfo *tape)
8214 return (tape->use_key_actions &&
8215 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8216 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8217 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8218 TAPE_ACTIONS_DEFAULT);
8221 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8223 tape->file_version = getFileVersion(file);
8224 tape->game_version = getFileVersion(file);
8229 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8233 tape->random_seed = getFile32BitBE(file);
8234 tape->date = getFile32BitBE(file);
8235 tape->length = getFile32BitBE(file);
8237 // read header fields that are new since version 1.2
8238 if (tape->file_version >= FILE_VERSION_1_2)
8240 byte store_participating_players = getFile8Bit(file);
8243 // since version 1.2, tapes store which players participate in the tape
8244 tape->num_participating_players = 0;
8245 for (i = 0; i < MAX_PLAYERS; i++)
8247 tape->player_participates[i] = FALSE;
8249 if (store_participating_players & (1 << i))
8251 tape->player_participates[i] = TRUE;
8252 tape->num_participating_players++;
8256 setTapeActionFlags(tape, getFile8Bit(file));
8258 tape->property_bits = getFile8Bit(file);
8259 tape->solved = getFile8Bit(file);
8261 engine_version = getFileVersion(file);
8262 if (engine_version > 0)
8263 tape->engine_version = engine_version;
8265 tape->engine_version = tape->game_version;
8271 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8273 tape->scr_fieldx = getFile8Bit(file);
8274 tape->scr_fieldy = getFile8Bit(file);
8279 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8281 char *level_identifier = NULL;
8282 int level_identifier_size;
8285 tape->no_info_chunk = FALSE;
8287 level_identifier_size = getFile16BitBE(file);
8289 level_identifier = checked_malloc(level_identifier_size);
8291 for (i = 0; i < level_identifier_size; i++)
8292 level_identifier[i] = getFile8Bit(file);
8294 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8295 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8297 checked_free(level_identifier);
8299 tape->level_nr = getFile16BitBE(file);
8301 chunk_size = 2 + level_identifier_size + 2;
8306 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8309 int tape_pos_size = getTapePosSize(tape);
8310 int chunk_size_expected = tape_pos_size * tape->length;
8312 if (chunk_size_expected != chunk_size)
8314 ReadUnusedBytesFromFile(file, chunk_size);
8315 return chunk_size_expected;
8318 for (i = 0; i < tape->length; i++)
8320 if (i >= MAX_TAPE_LEN)
8322 Warn("tape truncated -- size exceeds maximum tape size %d",
8325 // tape too large; read and ignore remaining tape data from this chunk
8326 for (;i < tape->length; i++)
8327 ReadUnusedBytesFromFile(file, tape_pos_size);
8332 if (tape->use_key_actions)
8334 for (j = 0; j < MAX_PLAYERS; j++)
8336 tape->pos[i].action[j] = MV_NONE;
8338 if (tape->player_participates[j])
8339 tape->pos[i].action[j] = getFile8Bit(file);
8343 if (tape->use_mouse_actions)
8345 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8346 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8347 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8350 tape->pos[i].delay = getFile8Bit(file);
8352 if (tape->file_version == FILE_VERSION_1_0)
8354 // eliminate possible diagonal moves in old tapes
8355 // this is only for backward compatibility
8357 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8358 byte action = tape->pos[i].action[0];
8359 int k, num_moves = 0;
8361 for (k = 0; k < 4; k++)
8363 if (action & joy_dir[k])
8365 tape->pos[i + num_moves].action[0] = joy_dir[k];
8367 tape->pos[i + num_moves].delay = 0;
8376 tape->length += num_moves;
8379 else if (tape->file_version < FILE_VERSION_2_0)
8381 // convert pre-2.0 tapes to new tape format
8383 if (tape->pos[i].delay > 1)
8386 tape->pos[i + 1] = tape->pos[i];
8387 tape->pos[i + 1].delay = 1;
8390 for (j = 0; j < MAX_PLAYERS; j++)
8391 tape->pos[i].action[j] = MV_NONE;
8392 tape->pos[i].delay--;
8399 if (checkEndOfFile(file))
8403 if (i != tape->length)
8404 chunk_size = tape_pos_size * i;
8409 static void LoadTape_SokobanSolution(char *filename)
8412 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8414 if (!(file = openFile(filename, MODE_READ)))
8416 tape.no_valid_file = TRUE;
8421 while (!checkEndOfFile(file))
8423 unsigned char c = getByteFromFile(file);
8425 if (checkEndOfFile(file))
8432 tape.pos[tape.length].action[0] = MV_UP;
8433 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8439 tape.pos[tape.length].action[0] = MV_DOWN;
8440 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8446 tape.pos[tape.length].action[0] = MV_LEFT;
8447 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8453 tape.pos[tape.length].action[0] = MV_RIGHT;
8454 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8462 // ignore white-space characters
8466 tape.no_valid_file = TRUE;
8468 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8476 if (tape.no_valid_file)
8479 tape.length_frames = GetTapeLengthFrames();
8480 tape.length_seconds = GetTapeLengthSeconds();
8483 void LoadTapeFromFilename(char *filename)
8485 char cookie[MAX_LINE_LEN];
8486 char chunk_name[CHUNK_ID_LEN + 1];
8490 // always start with reliable default values
8491 setTapeInfoToDefaults();
8493 if (strSuffix(filename, ".sln"))
8495 LoadTape_SokobanSolution(filename);
8500 if (!(file = openFile(filename, MODE_READ)))
8502 tape.no_valid_file = TRUE;
8507 getFileChunkBE(file, chunk_name, NULL);
8508 if (strEqual(chunk_name, "RND1"))
8510 getFile32BitBE(file); // not used
8512 getFileChunkBE(file, chunk_name, NULL);
8513 if (!strEqual(chunk_name, "TAPE"))
8515 tape.no_valid_file = TRUE;
8517 Warn("unknown format of tape file '%s'", filename);
8524 else // check for pre-2.0 file format with cookie string
8526 strcpy(cookie, chunk_name);
8527 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8529 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8530 cookie[strlen(cookie) - 1] = '\0';
8532 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8534 tape.no_valid_file = TRUE;
8536 Warn("unknown format of tape file '%s'", filename);
8543 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8545 tape.no_valid_file = TRUE;
8547 Warn("unsupported version of tape file '%s'", filename);
8554 // pre-2.0 tape files have no game version, so use file version here
8555 tape.game_version = tape.file_version;
8558 if (tape.file_version < FILE_VERSION_1_2)
8560 // tape files from versions before 1.2.0 without chunk structure
8561 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8562 LoadTape_BODY(file, 2 * tape.length, &tape);
8570 int (*loader)(File *, int, struct TapeInfo *);
8574 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8575 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8576 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8577 { "INFO", -1, LoadTape_INFO },
8578 { "BODY", -1, LoadTape_BODY },
8582 while (getFileChunkBE(file, chunk_name, &chunk_size))
8586 while (chunk_info[i].name != NULL &&
8587 !strEqual(chunk_name, chunk_info[i].name))
8590 if (chunk_info[i].name == NULL)
8592 Warn("unknown chunk '%s' in tape file '%s'",
8593 chunk_name, filename);
8595 ReadUnusedBytesFromFile(file, chunk_size);
8597 else if (chunk_info[i].size != -1 &&
8598 chunk_info[i].size != chunk_size)
8600 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8601 chunk_size, chunk_name, filename);
8603 ReadUnusedBytesFromFile(file, chunk_size);
8607 // call function to load this tape chunk
8608 int chunk_size_expected =
8609 (chunk_info[i].loader)(file, chunk_size, &tape);
8611 // the size of some chunks cannot be checked before reading other
8612 // chunks first (like "HEAD" and "BODY") that contain some header
8613 // information, so check them here
8614 if (chunk_size_expected != chunk_size)
8616 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8617 chunk_size, chunk_name, filename);
8625 tape.length_frames = GetTapeLengthFrames();
8626 tape.length_seconds = GetTapeLengthSeconds();
8629 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8631 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8633 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8634 tape.engine_version);
8638 void LoadTape(int nr)
8640 char *filename = getTapeFilename(nr);
8642 LoadTapeFromFilename(filename);
8645 void LoadSolutionTape(int nr)
8647 char *filename = getSolutionTapeFilename(nr);
8649 LoadTapeFromFilename(filename);
8651 if (TAPE_IS_EMPTY(tape))
8653 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8654 level.native_bd_level->replay != NULL)
8655 CopyNativeTape_BD_to_RND(&level);
8656 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8657 level.native_sp_level->demo.is_available)
8658 CopyNativeTape_SP_to_RND(&level);
8662 void LoadScoreTape(char *score_tape_basename, int nr)
8664 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8666 LoadTapeFromFilename(filename);
8669 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8671 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8673 LoadTapeFromFilename(filename);
8676 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8678 // chunk required for team mode tapes with non-default screen size
8679 return (tape->num_participating_players > 1 &&
8680 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8681 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8684 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8686 putFileVersion(file, tape->file_version);
8687 putFileVersion(file, tape->game_version);
8690 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8693 byte store_participating_players = 0;
8695 // set bits for participating players for compact storage
8696 for (i = 0; i < MAX_PLAYERS; i++)
8697 if (tape->player_participates[i])
8698 store_participating_players |= (1 << i);
8700 putFile32BitBE(file, tape->random_seed);
8701 putFile32BitBE(file, tape->date);
8702 putFile32BitBE(file, tape->length);
8704 putFile8Bit(file, store_participating_players);
8706 putFile8Bit(file, getTapeActionValue(tape));
8708 putFile8Bit(file, tape->property_bits);
8709 putFile8Bit(file, tape->solved);
8711 putFileVersion(file, tape->engine_version);
8714 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8716 putFile8Bit(file, tape->scr_fieldx);
8717 putFile8Bit(file, tape->scr_fieldy);
8720 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8722 int level_identifier_size = strlen(tape->level_identifier) + 1;
8725 putFile16BitBE(file, level_identifier_size);
8727 for (i = 0; i < level_identifier_size; i++)
8728 putFile8Bit(file, tape->level_identifier[i]);
8730 putFile16BitBE(file, tape->level_nr);
8733 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8737 for (i = 0; i < tape->length; i++)
8739 if (tape->use_key_actions)
8741 for (j = 0; j < MAX_PLAYERS; j++)
8742 if (tape->player_participates[j])
8743 putFile8Bit(file, tape->pos[i].action[j]);
8746 if (tape->use_mouse_actions)
8748 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8749 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8750 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8753 putFile8Bit(file, tape->pos[i].delay);
8757 void SaveTapeToFilename(char *filename)
8761 int info_chunk_size;
8762 int body_chunk_size;
8764 if (!(file = fopen(filename, MODE_WRITE)))
8766 Warn("cannot save level recording file '%s'", filename);
8771 tape_pos_size = getTapePosSize(&tape);
8773 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8774 body_chunk_size = tape_pos_size * tape.length;
8776 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8777 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8779 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8780 SaveTape_VERS(file, &tape);
8782 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8783 SaveTape_HEAD(file, &tape);
8785 if (checkSaveTape_SCRN(&tape))
8787 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8788 SaveTape_SCRN(file, &tape);
8791 putFileChunkBE(file, "INFO", info_chunk_size);
8792 SaveTape_INFO(file, &tape);
8794 putFileChunkBE(file, "BODY", body_chunk_size);
8795 SaveTape_BODY(file, &tape);
8799 SetFilePermissions(filename, PERMS_PRIVATE);
8802 static void SaveTapeExt(char *filename)
8806 tape.file_version = FILE_VERSION_ACTUAL;
8807 tape.game_version = GAME_VERSION_ACTUAL;
8809 tape.num_participating_players = 0;
8811 // count number of participating players
8812 for (i = 0; i < MAX_PLAYERS; i++)
8813 if (tape.player_participates[i])
8814 tape.num_participating_players++;
8816 SaveTapeToFilename(filename);
8818 tape.changed = FALSE;
8821 void SaveTape(int nr)
8823 char *filename = getTapeFilename(nr);
8825 InitTapeDirectory(leveldir_current->subdir);
8827 SaveTapeExt(filename);
8830 void SaveScoreTape(int nr)
8832 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8834 // used instead of "leveldir_current->subdir" (for network games)
8835 InitScoreTapeDirectory(levelset.identifier, nr);
8837 SaveTapeExt(filename);
8840 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8841 unsigned int req_state_added)
8843 char *filename = getTapeFilename(nr);
8844 boolean new_tape = !fileExists(filename);
8845 boolean tape_saved = FALSE;
8847 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8852 Request(msg_saved, REQ_CONFIRM | req_state_added);
8860 boolean SaveTapeChecked(int nr)
8862 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8865 boolean SaveTapeChecked_LevelSolved(int nr)
8867 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8868 "Level solved! Tape saved!", REQ_STAY_OPEN);
8871 void DumpTape(struct TapeInfo *tape)
8873 int tape_frame_counter;
8876 if (tape->no_valid_file)
8878 Warn("cannot dump -- no valid tape file found");
8885 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8886 tape->level_nr, tape->file_version, tape->game_version);
8887 Print(" (effective engine version %08d)\n",
8888 tape->engine_version);
8889 Print("Level series identifier: '%s'\n", tape->level_identifier);
8891 Print("Solution tape: %s\n",
8892 tape->solved ? "yes" :
8893 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8895 Print("Special tape properties: ");
8896 if (tape->property_bits == TAPE_PROPERTY_NONE)
8898 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8899 Print("[em_random_bug]");
8900 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8901 Print("[game_speed]");
8902 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8904 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8905 Print("[single_step]");
8906 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8907 Print("[snapshot]");
8908 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8909 Print("[replayed]");
8910 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8911 Print("[tas_keys]");
8912 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8913 Print("[small_graphics]");
8916 int year2 = tape->date / 10000;
8917 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8918 int month_index_raw = (tape->date / 100) % 100;
8919 int month_index = month_index_raw % 12; // prevent invalid index
8920 int month = month_index + 1;
8921 int day = tape->date % 100;
8923 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8927 tape_frame_counter = 0;
8929 for (i = 0; i < tape->length; i++)
8931 if (i >= MAX_TAPE_LEN)
8936 for (j = 0; j < MAX_PLAYERS; j++)
8938 if (tape->player_participates[j])
8940 int action = tape->pos[i].action[j];
8942 Print("%d:%02x ", j, action);
8943 Print("[%c%c%c%c|%c%c] - ",
8944 (action & JOY_LEFT ? '<' : ' '),
8945 (action & JOY_RIGHT ? '>' : ' '),
8946 (action & JOY_UP ? '^' : ' '),
8947 (action & JOY_DOWN ? 'v' : ' '),
8948 (action & JOY_BUTTON_1 ? '1' : ' '),
8949 (action & JOY_BUTTON_2 ? '2' : ' '));
8953 Print("(%03d) ", tape->pos[i].delay);
8954 Print("[%05d]\n", tape_frame_counter);
8956 tape_frame_counter += tape->pos[i].delay;
8962 void DumpTapes(void)
8964 static LevelDirTree *dumptape_leveldir = NULL;
8966 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8967 global.dumptape_leveldir);
8969 if (dumptape_leveldir == NULL)
8970 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8972 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8973 global.dumptape_level_nr > dumptape_leveldir->last_level)
8974 Fail("no such level number: %d", global.dumptape_level_nr);
8976 leveldir_current = dumptape_leveldir;
8978 if (options.mytapes)
8979 LoadTape(global.dumptape_level_nr);
8981 LoadSolutionTape(global.dumptape_level_nr);
8989 // ============================================================================
8990 // score file functions
8991 // ============================================================================
8993 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8997 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8999 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9000 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9001 scores->entry[i].score = 0;
9002 scores->entry[i].time = 0;
9004 scores->entry[i].id = -1;
9005 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9006 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9007 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9008 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9009 strcpy(scores->entry[i].country_code, "??");
9012 scores->num_entries = 0;
9013 scores->last_added = -1;
9014 scores->last_added_local = -1;
9016 scores->updated = FALSE;
9017 scores->uploaded = FALSE;
9018 scores->tape_downloaded = FALSE;
9019 scores->force_last_added = FALSE;
9021 // The following values are intentionally not reset here:
9025 // - continue_playing
9026 // - continue_on_return
9029 static void setScoreInfoToDefaults(void)
9031 setScoreInfoToDefaultsExt(&scores);
9034 static void setServerScoreInfoToDefaults(void)
9036 setScoreInfoToDefaultsExt(&server_scores);
9039 static void LoadScore_OLD(int nr)
9042 char *filename = getScoreFilename(nr);
9043 char cookie[MAX_LINE_LEN];
9044 char line[MAX_LINE_LEN];
9048 if (!(file = fopen(filename, MODE_READ)))
9051 // check file identifier
9052 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9054 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9055 cookie[strlen(cookie) - 1] = '\0';
9057 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9059 Warn("unknown format of score file '%s'", filename);
9066 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9068 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9069 Warn("fscanf() failed; %s", strerror(errno));
9071 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9074 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9075 line[strlen(line) - 1] = '\0';
9077 for (line_ptr = line; *line_ptr; line_ptr++)
9079 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9081 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9082 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9091 static void ConvertScore_OLD(void)
9093 // only convert score to time for levels that rate playing time over score
9094 if (!level.rate_time_over_score)
9097 // convert old score to playing time for score-less levels (like Supaplex)
9098 int time_final_max = 999;
9101 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9103 int score = scores.entry[i].score;
9105 if (score > 0 && score < time_final_max)
9106 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9110 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9112 scores->file_version = getFileVersion(file);
9113 scores->game_version = getFileVersion(file);
9118 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9120 char *level_identifier = NULL;
9121 int level_identifier_size;
9124 level_identifier_size = getFile16BitBE(file);
9126 level_identifier = checked_malloc(level_identifier_size);
9128 for (i = 0; i < level_identifier_size; i++)
9129 level_identifier[i] = getFile8Bit(file);
9131 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9132 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9134 checked_free(level_identifier);
9136 scores->level_nr = getFile16BitBE(file);
9137 scores->num_entries = getFile16BitBE(file);
9139 chunk_size = 2 + level_identifier_size + 2 + 2;
9144 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9148 for (i = 0; i < scores->num_entries; i++)
9150 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9151 scores->entry[i].name[j] = getFile8Bit(file);
9153 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9156 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9161 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9165 for (i = 0; i < scores->num_entries; i++)
9166 scores->entry[i].score = getFile16BitBE(file);
9168 chunk_size = scores->num_entries * 2;
9173 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9177 for (i = 0; i < scores->num_entries; i++)
9178 scores->entry[i].score = getFile32BitBE(file);
9180 chunk_size = scores->num_entries * 4;
9185 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9189 for (i = 0; i < scores->num_entries; i++)
9190 scores->entry[i].time = getFile32BitBE(file);
9192 chunk_size = scores->num_entries * 4;
9197 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9201 for (i = 0; i < scores->num_entries; i++)
9203 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9204 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9206 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9209 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9214 void LoadScore(int nr)
9216 char *filename = getScoreFilename(nr);
9217 char cookie[MAX_LINE_LEN];
9218 char chunk_name[CHUNK_ID_LEN + 1];
9220 boolean old_score_file_format = FALSE;
9223 // always start with reliable default values
9224 setScoreInfoToDefaults();
9226 if (!(file = openFile(filename, MODE_READ)))
9229 getFileChunkBE(file, chunk_name, NULL);
9230 if (strEqual(chunk_name, "RND1"))
9232 getFile32BitBE(file); // not used
9234 getFileChunkBE(file, chunk_name, NULL);
9235 if (!strEqual(chunk_name, "SCOR"))
9237 Warn("unknown format of score file '%s'", filename);
9244 else // check for old file format with cookie string
9246 strcpy(cookie, chunk_name);
9247 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9249 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9250 cookie[strlen(cookie) - 1] = '\0';
9252 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9254 Warn("unknown format of score file '%s'", filename);
9261 old_score_file_format = TRUE;
9264 if (old_score_file_format)
9266 // score files from versions before 4.2.4.0 without chunk structure
9269 // convert score to time, if possible (mainly for Supaplex levels)
9278 int (*loader)(File *, int, struct ScoreInfo *);
9282 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9283 { "INFO", -1, LoadScore_INFO },
9284 { "NAME", -1, LoadScore_NAME },
9285 { "SCOR", -1, LoadScore_SCOR },
9286 { "SC4R", -1, LoadScore_SC4R },
9287 { "TIME", -1, LoadScore_TIME },
9288 { "TAPE", -1, LoadScore_TAPE },
9293 while (getFileChunkBE(file, chunk_name, &chunk_size))
9297 while (chunk_info[i].name != NULL &&
9298 !strEqual(chunk_name, chunk_info[i].name))
9301 if (chunk_info[i].name == NULL)
9303 Warn("unknown chunk '%s' in score file '%s'",
9304 chunk_name, filename);
9306 ReadUnusedBytesFromFile(file, chunk_size);
9308 else if (chunk_info[i].size != -1 &&
9309 chunk_info[i].size != chunk_size)
9311 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9312 chunk_size, chunk_name, filename);
9314 ReadUnusedBytesFromFile(file, chunk_size);
9318 // call function to load this score chunk
9319 int chunk_size_expected =
9320 (chunk_info[i].loader)(file, chunk_size, &scores);
9322 // the size of some chunks cannot be checked before reading other
9323 // chunks first (like "HEAD" and "BODY") that contain some header
9324 // information, so check them here
9325 if (chunk_size_expected != chunk_size)
9327 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9328 chunk_size, chunk_name, filename);
9337 #if ENABLE_HISTORIC_CHUNKS
9338 void SaveScore_OLD(int nr)
9341 char *filename = getScoreFilename(nr);
9344 // used instead of "leveldir_current->subdir" (for network games)
9345 InitScoreDirectory(levelset.identifier);
9347 if (!(file = fopen(filename, MODE_WRITE)))
9349 Warn("cannot save score for level %d", nr);
9354 fprintf(file, "%s\n\n", SCORE_COOKIE);
9356 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9357 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9361 SetFilePermissions(filename, PERMS_PRIVATE);
9365 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9367 putFileVersion(file, scores->file_version);
9368 putFileVersion(file, scores->game_version);
9371 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9373 int level_identifier_size = strlen(scores->level_identifier) + 1;
9376 putFile16BitBE(file, level_identifier_size);
9378 for (i = 0; i < level_identifier_size; i++)
9379 putFile8Bit(file, scores->level_identifier[i]);
9381 putFile16BitBE(file, scores->level_nr);
9382 putFile16BitBE(file, scores->num_entries);
9385 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9389 for (i = 0; i < scores->num_entries; i++)
9391 int name_size = strlen(scores->entry[i].name);
9393 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9394 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9398 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9402 for (i = 0; i < scores->num_entries; i++)
9403 putFile16BitBE(file, scores->entry[i].score);
9406 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9410 for (i = 0; i < scores->num_entries; i++)
9411 putFile32BitBE(file, scores->entry[i].score);
9414 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9418 for (i = 0; i < scores->num_entries; i++)
9419 putFile32BitBE(file, scores->entry[i].time);
9422 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9426 for (i = 0; i < scores->num_entries; i++)
9428 int size = strlen(scores->entry[i].tape_basename);
9430 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9431 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9435 static void SaveScoreToFilename(char *filename)
9438 int info_chunk_size;
9439 int name_chunk_size;
9440 int scor_chunk_size;
9441 int sc4r_chunk_size;
9442 int time_chunk_size;
9443 int tape_chunk_size;
9444 boolean has_large_score_values;
9447 if (!(file = fopen(filename, MODE_WRITE)))
9449 Warn("cannot save score file '%s'", filename);
9454 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9455 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9456 scor_chunk_size = scores.num_entries * 2;
9457 sc4r_chunk_size = scores.num_entries * 4;
9458 time_chunk_size = scores.num_entries * 4;
9459 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9461 has_large_score_values = FALSE;
9462 for (i = 0; i < scores.num_entries; i++)
9463 if (scores.entry[i].score > 0xffff)
9464 has_large_score_values = TRUE;
9466 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9467 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9469 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9470 SaveScore_VERS(file, &scores);
9472 putFileChunkBE(file, "INFO", info_chunk_size);
9473 SaveScore_INFO(file, &scores);
9475 putFileChunkBE(file, "NAME", name_chunk_size);
9476 SaveScore_NAME(file, &scores);
9478 if (has_large_score_values)
9480 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9481 SaveScore_SC4R(file, &scores);
9485 putFileChunkBE(file, "SCOR", scor_chunk_size);
9486 SaveScore_SCOR(file, &scores);
9489 putFileChunkBE(file, "TIME", time_chunk_size);
9490 SaveScore_TIME(file, &scores);
9492 putFileChunkBE(file, "TAPE", tape_chunk_size);
9493 SaveScore_TAPE(file, &scores);
9497 SetFilePermissions(filename, PERMS_PRIVATE);
9500 void SaveScore(int nr)
9502 char *filename = getScoreFilename(nr);
9505 // used instead of "leveldir_current->subdir" (for network games)
9506 InitScoreDirectory(levelset.identifier);
9508 scores.file_version = FILE_VERSION_ACTUAL;
9509 scores.game_version = GAME_VERSION_ACTUAL;
9511 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9512 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9513 scores.level_nr = level_nr;
9515 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9516 if (scores.entry[i].score == 0 &&
9517 scores.entry[i].time == 0 &&
9518 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9521 scores.num_entries = i;
9523 if (scores.num_entries == 0)
9526 SaveScoreToFilename(filename);
9529 static void LoadServerScoreFromCache(int nr)
9531 struct ScoreEntry score_entry;
9540 { &score_entry.score, FALSE, 0 },
9541 { &score_entry.time, FALSE, 0 },
9542 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9543 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9544 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9545 { &score_entry.id, FALSE, 0 },
9546 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9547 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9548 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9549 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9553 char *filename = getScoreCacheFilename(nr);
9554 SetupFileHash *score_hash = loadSetupFileHash(filename);
9557 server_scores.num_entries = 0;
9559 if (score_hash == NULL)
9562 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9564 score_entry = server_scores.entry[i];
9566 for (j = 0; score_mapping[j].value != NULL; j++)
9570 sprintf(token, "%02d.%d", i, j);
9572 char *value = getHashEntry(score_hash, token);
9577 if (score_mapping[j].is_string)
9579 char *score_value = (char *)score_mapping[j].value;
9580 int value_size = score_mapping[j].string_size;
9582 strncpy(score_value, value, value_size);
9583 score_value[value_size] = '\0';
9587 int *score_value = (int *)score_mapping[j].value;
9589 *score_value = atoi(value);
9592 server_scores.num_entries = i + 1;
9595 server_scores.entry[i] = score_entry;
9598 freeSetupFileHash(score_hash);
9601 void LoadServerScore(int nr, boolean download_score)
9603 if (!setup.use_api_server)
9606 // always start with reliable default values
9607 setServerScoreInfoToDefaults();
9609 // 1st step: load server scores from cache file (which may not exist)
9610 // (this should prevent reading it while the thread is writing to it)
9611 LoadServerScoreFromCache(nr);
9613 if (download_score && runtime.use_api_server)
9615 // 2nd step: download server scores from score server to cache file
9616 // (as thread, as it might time out if the server is not reachable)
9617 ApiGetScoreAsThread(nr);
9621 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9623 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9625 // if score tape not uploaded, ask for uploading missing tapes later
9626 if (!setup.has_remaining_tapes)
9627 setup.ask_for_remaining_tapes = TRUE;
9629 setup.provide_uploading_tapes = TRUE;
9630 setup.has_remaining_tapes = TRUE;
9632 SaveSetup_ServerSetup();
9635 void SaveServerScore(int nr, boolean tape_saved)
9637 if (!runtime.use_api_server)
9639 PrepareScoreTapesForUpload(leveldir_current->subdir);
9644 ApiAddScoreAsThread(nr, tape_saved, NULL);
9647 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9648 char *score_tape_filename)
9650 if (!runtime.use_api_server)
9653 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9656 void LoadLocalAndServerScore(int nr, boolean download_score)
9658 int last_added_local = scores.last_added_local;
9659 boolean force_last_added = scores.force_last_added;
9661 // needed if only showing server scores
9662 setScoreInfoToDefaults();
9664 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9667 // restore last added local score entry (before merging server scores)
9668 scores.last_added = scores.last_added_local = last_added_local;
9670 if (setup.use_api_server &&
9671 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9673 // load server scores from cache file and trigger update from server
9674 LoadServerScore(nr, download_score);
9676 // merge local scores with scores from server
9680 if (force_last_added)
9681 scores.force_last_added = force_last_added;
9685 // ============================================================================
9686 // setup file functions
9687 // ============================================================================
9689 #define TOKEN_STR_PLAYER_PREFIX "player_"
9692 static struct TokenInfo global_setup_tokens[] =
9696 &setup.player_name, "player_name"
9700 &setup.multiple_users, "multiple_users"
9704 &setup.sound, "sound"
9708 &setup.sound_loops, "repeating_sound_loops"
9712 &setup.sound_music, "background_music"
9716 &setup.sound_simple, "simple_sound_effects"
9720 &setup.toons, "toons"
9724 &setup.global_animations, "global_animations"
9728 &setup.scroll_delay, "scroll_delay"
9732 &setup.forced_scroll_delay, "forced_scroll_delay"
9736 &setup.scroll_delay_value, "scroll_delay_value"
9740 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9744 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9748 &setup.fade_screens, "fade_screens"
9752 &setup.autorecord, "automatic_tape_recording"
9756 &setup.autorecord_after_replay, "autorecord_after_replay"
9760 &setup.auto_pause_on_start, "auto_pause_on_start"
9764 &setup.show_titlescreen, "show_titlescreen"
9768 &setup.quick_doors, "quick_doors"
9772 &setup.team_mode, "team_mode"
9776 &setup.handicap, "handicap"
9780 &setup.skip_levels, "skip_levels"
9784 &setup.increment_levels, "increment_levels"
9788 &setup.auto_play_next_level, "auto_play_next_level"
9792 &setup.count_score_after_game, "count_score_after_game"
9796 &setup.show_scores_after_game, "show_scores_after_game"
9800 &setup.time_limit, "time_limit"
9804 &setup.fullscreen, "fullscreen"
9808 &setup.window_scaling_percent, "window_scaling_percent"
9812 &setup.window_scaling_quality, "window_scaling_quality"
9816 &setup.screen_rendering_mode, "screen_rendering_mode"
9820 &setup.vsync_mode, "vsync_mode"
9824 &setup.ask_on_escape, "ask_on_escape"
9828 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9832 &setup.ask_on_game_over, "ask_on_game_over"
9836 &setup.ask_on_quit_game, "ask_on_quit_game"
9840 &setup.ask_on_quit_program, "ask_on_quit_program"
9844 &setup.quick_switch, "quick_player_switch"
9848 &setup.input_on_focus, "input_on_focus"
9852 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9856 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9860 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9864 &setup.game_speed_extended, "game_speed_extended"
9868 &setup.game_frame_delay, "game_frame_delay"
9872 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9876 &setup.bd_skip_hatching, "bd_skip_hatching"
9880 &setup.bd_scroll_delay, "bd_scroll_delay"
9884 &setup.bd_smooth_movements, "bd_smooth_movements"
9888 &setup.sp_show_border_elements, "sp_show_border_elements"
9892 &setup.small_game_graphics, "small_game_graphics"
9896 &setup.show_load_save_buttons, "show_load_save_buttons"
9900 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9904 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9908 &setup.graphics_set, "graphics_set"
9912 &setup.sounds_set, "sounds_set"
9916 &setup.music_set, "music_set"
9920 &setup.override_level_graphics, "override_level_graphics"
9924 &setup.override_level_sounds, "override_level_sounds"
9928 &setup.override_level_music, "override_level_music"
9932 &setup.volume_simple, "volume_simple"
9936 &setup.volume_loops, "volume_loops"
9940 &setup.volume_music, "volume_music"
9944 &setup.network_mode, "network_mode"
9948 &setup.network_player_nr, "network_player"
9952 &setup.network_server_hostname, "network_server_hostname"
9956 &setup.touch.control_type, "touch.control_type"
9960 &setup.touch.move_distance, "touch.move_distance"
9964 &setup.touch.drop_distance, "touch.drop_distance"
9968 &setup.touch.transparency, "touch.transparency"
9972 &setup.touch.draw_outlined, "touch.draw_outlined"
9976 &setup.touch.draw_pressed, "touch.draw_pressed"
9980 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9984 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9988 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9992 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9996 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10000 static struct TokenInfo auto_setup_tokens[] =
10004 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10008 static struct TokenInfo server_setup_tokens[] =
10012 &setup.player_uuid, "player_uuid"
10016 &setup.player_version, "player_version"
10020 &setup.use_api_server, TEST_PREFIX "use_api_server"
10024 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10028 &setup.api_server_password, TEST_PREFIX "api_server_password"
10032 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10036 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10040 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10044 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10048 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10052 static struct TokenInfo editor_setup_tokens[] =
10056 &setup.editor.el_classic, "editor.el_classic"
10060 &setup.editor.el_custom, "editor.el_custom"
10064 &setup.editor.el_user_defined, "editor.el_user_defined"
10068 &setup.editor.el_dynamic, "editor.el_dynamic"
10072 &setup.editor.el_headlines, "editor.el_headlines"
10076 &setup.editor.show_element_token, "editor.show_element_token"
10080 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10084 static struct TokenInfo editor_cascade_setup_tokens[] =
10088 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10092 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10096 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10100 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10104 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10108 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10112 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10116 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10120 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10124 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10128 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10132 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10136 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10140 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10144 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10148 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10152 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10156 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10160 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10164 static struct TokenInfo shortcut_setup_tokens[] =
10168 &setup.shortcut.save_game, "shortcut.save_game"
10172 &setup.shortcut.load_game, "shortcut.load_game"
10176 &setup.shortcut.restart_game, "shortcut.restart_game"
10180 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10184 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10188 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10192 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10196 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10200 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10204 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10208 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10212 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10216 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10220 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10224 &setup.shortcut.tape_record, "shortcut.tape_record"
10228 &setup.shortcut.tape_play, "shortcut.tape_play"
10232 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10236 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10240 &setup.shortcut.sound_music, "shortcut.sound_music"
10244 &setup.shortcut.snap_left, "shortcut.snap_left"
10248 &setup.shortcut.snap_right, "shortcut.snap_right"
10252 &setup.shortcut.snap_up, "shortcut.snap_up"
10256 &setup.shortcut.snap_down, "shortcut.snap_down"
10260 static struct SetupInputInfo setup_input;
10261 static struct TokenInfo player_setup_tokens[] =
10265 &setup_input.use_joystick, ".use_joystick"
10269 &setup_input.joy.device_name, ".joy.device_name"
10273 &setup_input.joy.xleft, ".joy.xleft"
10277 &setup_input.joy.xmiddle, ".joy.xmiddle"
10281 &setup_input.joy.xright, ".joy.xright"
10285 &setup_input.joy.yupper, ".joy.yupper"
10289 &setup_input.joy.ymiddle, ".joy.ymiddle"
10293 &setup_input.joy.ylower, ".joy.ylower"
10297 &setup_input.joy.snap, ".joy.snap_field"
10301 &setup_input.joy.drop, ".joy.place_bomb"
10305 &setup_input.key.left, ".key.move_left"
10309 &setup_input.key.right, ".key.move_right"
10313 &setup_input.key.up, ".key.move_up"
10317 &setup_input.key.down, ".key.move_down"
10321 &setup_input.key.snap, ".key.snap_field"
10325 &setup_input.key.drop, ".key.place_bomb"
10329 static struct TokenInfo system_setup_tokens[] =
10333 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10337 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10341 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10345 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10349 static struct TokenInfo internal_setup_tokens[] =
10353 &setup.internal.program_title, "program_title"
10357 &setup.internal.program_version, "program_version"
10361 &setup.internal.program_author, "program_author"
10365 &setup.internal.program_email, "program_email"
10369 &setup.internal.program_website, "program_website"
10373 &setup.internal.program_copyright, "program_copyright"
10377 &setup.internal.program_company, "program_company"
10381 &setup.internal.program_icon_file, "program_icon_file"
10385 &setup.internal.default_graphics_set, "default_graphics_set"
10389 &setup.internal.default_sounds_set, "default_sounds_set"
10393 &setup.internal.default_music_set, "default_music_set"
10397 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10401 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10405 &setup.internal.fallback_music_file, "fallback_music_file"
10409 &setup.internal.default_level_series, "default_level_series"
10413 &setup.internal.default_window_width, "default_window_width"
10417 &setup.internal.default_window_height, "default_window_height"
10421 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10425 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10429 &setup.internal.create_user_levelset, "create_user_levelset"
10433 &setup.internal.info_screens_from_main, "info_screens_from_main"
10437 &setup.internal.menu_game, "menu_game"
10441 &setup.internal.menu_engines, "menu_engines"
10445 &setup.internal.menu_editor, "menu_editor"
10449 &setup.internal.menu_graphics, "menu_graphics"
10453 &setup.internal.menu_sound, "menu_sound"
10457 &setup.internal.menu_artwork, "menu_artwork"
10461 &setup.internal.menu_input, "menu_input"
10465 &setup.internal.menu_touch, "menu_touch"
10469 &setup.internal.menu_shortcuts, "menu_shortcuts"
10473 &setup.internal.menu_exit, "menu_exit"
10477 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10481 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10485 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10489 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10493 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10497 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10501 &setup.internal.info_title, "info_title"
10505 &setup.internal.info_elements, "info_elements"
10509 &setup.internal.info_music, "info_music"
10513 &setup.internal.info_credits, "info_credits"
10517 &setup.internal.info_program, "info_program"
10521 &setup.internal.info_version, "info_version"
10525 &setup.internal.info_levelset, "info_levelset"
10529 &setup.internal.info_exit, "info_exit"
10533 static struct TokenInfo debug_setup_tokens[] =
10537 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10541 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10545 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10549 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10553 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10557 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10561 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10565 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10569 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10573 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10577 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10581 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10585 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10589 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10593 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10597 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10601 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10605 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10609 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10613 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10617 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10620 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10624 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10628 &setup.debug.xsn_mode, "debug.xsn_mode"
10632 &setup.debug.xsn_percent, "debug.xsn_percent"
10636 static struct TokenInfo options_setup_tokens[] =
10640 &setup.options.verbose, "options.verbose"
10644 &setup.options.debug, "options.debug"
10648 &setup.options.debug_mode, "options.debug_mode"
10652 static void setSetupInfoToDefaults(struct SetupInfo *si)
10656 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10658 si->multiple_users = TRUE;
10661 si->sound_loops = TRUE;
10662 si->sound_music = TRUE;
10663 si->sound_simple = TRUE;
10665 si->global_animations = TRUE;
10666 si->scroll_delay = TRUE;
10667 si->forced_scroll_delay = FALSE;
10668 si->scroll_delay_value = STD_SCROLL_DELAY;
10669 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10670 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10671 si->fade_screens = TRUE;
10672 si->autorecord = TRUE;
10673 si->autorecord_after_replay = TRUE;
10674 si->auto_pause_on_start = FALSE;
10675 si->show_titlescreen = TRUE;
10676 si->quick_doors = FALSE;
10677 si->team_mode = FALSE;
10678 si->handicap = TRUE;
10679 si->skip_levels = TRUE;
10680 si->increment_levels = TRUE;
10681 si->auto_play_next_level = TRUE;
10682 si->count_score_after_game = TRUE;
10683 si->show_scores_after_game = TRUE;
10684 si->time_limit = TRUE;
10685 si->fullscreen = FALSE;
10686 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10687 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10688 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10689 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10690 si->ask_on_escape = TRUE;
10691 si->ask_on_escape_editor = TRUE;
10692 si->ask_on_game_over = TRUE;
10693 si->ask_on_quit_game = TRUE;
10694 si->ask_on_quit_program = TRUE;
10695 si->quick_switch = FALSE;
10696 si->input_on_focus = FALSE;
10697 si->prefer_aga_graphics = TRUE;
10698 si->prefer_lowpass_sounds = FALSE;
10699 si->prefer_extra_panel_items = TRUE;
10700 si->game_speed_extended = FALSE;
10701 si->game_frame_delay = GAME_FRAME_DELAY;
10702 si->bd_skip_uncovering = FALSE;
10703 si->bd_skip_hatching = FALSE;
10704 si->bd_scroll_delay = TRUE;
10705 si->bd_smooth_movements = AUTO;
10706 si->sp_show_border_elements = FALSE;
10707 si->small_game_graphics = FALSE;
10708 si->show_load_save_buttons = FALSE;
10709 si->show_undo_redo_buttons = FALSE;
10710 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10712 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10713 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10714 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10716 si->override_level_graphics = FALSE;
10717 si->override_level_sounds = FALSE;
10718 si->override_level_music = FALSE;
10720 si->volume_simple = 100; // percent
10721 si->volume_loops = 100; // percent
10722 si->volume_music = 100; // percent
10724 si->network_mode = FALSE;
10725 si->network_player_nr = 0; // first player
10726 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10728 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10729 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10730 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10731 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10732 si->touch.draw_outlined = TRUE;
10733 si->touch.draw_pressed = TRUE;
10735 for (i = 0; i < 2; i++)
10737 char *default_grid_button[6][2] =
10743 { "111222", " vv " },
10744 { "111222", " vv " }
10746 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10747 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10748 int min_xsize = MIN(6, grid_xsize);
10749 int min_ysize = MIN(6, grid_ysize);
10750 int startx = grid_xsize - min_xsize;
10751 int starty = grid_ysize - min_ysize;
10754 // virtual buttons grid can only be set to defaults if video is initialized
10755 // (this will be repeated if virtual buttons are not loaded from setup file)
10756 if (video.initialized)
10758 si->touch.grid_xsize[i] = grid_xsize;
10759 si->touch.grid_ysize[i] = grid_ysize;
10763 si->touch.grid_xsize[i] = -1;
10764 si->touch.grid_ysize[i] = -1;
10767 for (x = 0; x < MAX_GRID_XSIZE; x++)
10768 for (y = 0; y < MAX_GRID_YSIZE; y++)
10769 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10771 for (x = 0; x < min_xsize; x++)
10772 for (y = 0; y < min_ysize; y++)
10773 si->touch.grid_button[i][x][starty + y] =
10774 default_grid_button[y][0][x];
10776 for (x = 0; x < min_xsize; x++)
10777 for (y = 0; y < min_ysize; y++)
10778 si->touch.grid_button[i][startx + x][starty + y] =
10779 default_grid_button[y][1][x];
10782 si->touch.grid_initialized = video.initialized;
10784 si->touch.overlay_buttons = FALSE;
10786 si->editor.el_boulderdash = TRUE;
10787 si->editor.el_boulderdash_native = TRUE;
10788 si->editor.el_emerald_mine = TRUE;
10789 si->editor.el_emerald_mine_club = TRUE;
10790 si->editor.el_more = TRUE;
10791 si->editor.el_sokoban = TRUE;
10792 si->editor.el_supaplex = TRUE;
10793 si->editor.el_diamond_caves = TRUE;
10794 si->editor.el_dx_boulderdash = TRUE;
10796 si->editor.el_mirror_magic = TRUE;
10797 si->editor.el_deflektor = TRUE;
10799 si->editor.el_chars = TRUE;
10800 si->editor.el_steel_chars = TRUE;
10802 si->editor.el_classic = TRUE;
10803 si->editor.el_custom = TRUE;
10805 si->editor.el_user_defined = FALSE;
10806 si->editor.el_dynamic = TRUE;
10808 si->editor.el_headlines = TRUE;
10810 si->editor.show_element_token = FALSE;
10812 si->editor.show_read_only_warning = TRUE;
10814 si->editor.use_template_for_new_levels = TRUE;
10816 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10817 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10818 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10819 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10820 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10822 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10823 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10824 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10825 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10826 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10828 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10829 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10830 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10831 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10832 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10833 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10835 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10836 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10837 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10839 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10840 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10841 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10842 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10844 for (i = 0; i < MAX_PLAYERS; i++)
10846 si->input[i].use_joystick = FALSE;
10847 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10848 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10849 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10850 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10851 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10852 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10853 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10854 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10855 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10856 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10857 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10858 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10859 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10860 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10861 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10864 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10865 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10866 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10867 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10869 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10870 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10871 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10872 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10873 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10874 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10875 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10877 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10879 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10880 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10881 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10883 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10884 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10885 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10887 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10888 si->internal.choose_from_top_leveldir = FALSE;
10889 si->internal.show_scaling_in_title = TRUE;
10890 si->internal.create_user_levelset = TRUE;
10891 si->internal.info_screens_from_main = FALSE;
10893 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10894 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10896 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10897 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10898 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10899 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10900 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10901 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10902 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10903 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10904 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10905 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10907 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10908 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10909 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10910 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10911 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10912 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10913 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10914 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10915 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10916 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10918 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10919 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10921 si->debug.show_frames_per_second = FALSE;
10923 si->debug.xsn_mode = AUTO;
10924 si->debug.xsn_percent = 0;
10926 si->options.verbose = FALSE;
10927 si->options.debug = FALSE;
10928 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10930 #if defined(PLATFORM_ANDROID)
10931 si->fullscreen = TRUE;
10932 si->touch.overlay_buttons = TRUE;
10935 setHideSetupEntry(&setup.debug.xsn_mode);
10938 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10940 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10943 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10945 si->player_uuid = NULL; // (will be set later)
10946 si->player_version = 1; // (will be set later)
10948 si->use_api_server = TRUE;
10949 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10950 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10951 si->ask_for_uploading_tapes = TRUE;
10952 si->ask_for_remaining_tapes = FALSE;
10953 si->provide_uploading_tapes = TRUE;
10954 si->ask_for_using_api_server = TRUE;
10955 si->has_remaining_tapes = FALSE;
10958 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10960 si->editor_cascade.el_bd = TRUE;
10961 si->editor_cascade.el_bd_native = TRUE;
10962 si->editor_cascade.el_em = TRUE;
10963 si->editor_cascade.el_emc = TRUE;
10964 si->editor_cascade.el_rnd = TRUE;
10965 si->editor_cascade.el_sb = TRUE;
10966 si->editor_cascade.el_sp = TRUE;
10967 si->editor_cascade.el_dc = TRUE;
10968 si->editor_cascade.el_dx = TRUE;
10970 si->editor_cascade.el_mm = TRUE;
10971 si->editor_cascade.el_df = TRUE;
10973 si->editor_cascade.el_chars = FALSE;
10974 si->editor_cascade.el_steel_chars = FALSE;
10975 si->editor_cascade.el_ce = FALSE;
10976 si->editor_cascade.el_ge = FALSE;
10977 si->editor_cascade.el_es = FALSE;
10978 si->editor_cascade.el_ref = FALSE;
10979 si->editor_cascade.el_user = FALSE;
10980 si->editor_cascade.el_dynamic = FALSE;
10983 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10985 static char *getHideSetupToken(void *setup_value)
10987 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10989 if (setup_value != NULL)
10990 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10992 return hide_setup_token;
10995 void setHideSetupEntry(void *setup_value)
10997 char *hide_setup_token = getHideSetupToken(setup_value);
10999 if (hide_setup_hash == NULL)
11000 hide_setup_hash = newSetupFileHash();
11002 if (setup_value != NULL)
11003 setHashEntry(hide_setup_hash, hide_setup_token, "");
11006 void removeHideSetupEntry(void *setup_value)
11008 char *hide_setup_token = getHideSetupToken(setup_value);
11010 if (setup_value != NULL)
11011 removeHashEntry(hide_setup_hash, hide_setup_token);
11014 boolean hideSetupEntry(void *setup_value)
11016 char *hide_setup_token = getHideSetupToken(setup_value);
11018 return (setup_value != NULL &&
11019 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11022 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11023 struct TokenInfo *token_info,
11024 int token_nr, char *token_text)
11026 char *token_hide_text = getStringCat2(token_text, ".hide");
11027 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11029 // set the value of this setup option in the setup option structure
11030 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11032 // check if this setup option should be hidden in the setup menu
11033 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11034 setHideSetupEntry(token_info[token_nr].value);
11036 free(token_hide_text);
11039 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11040 struct TokenInfo *token_info,
11043 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11044 token_info[token_nr].text);
11047 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11051 if (!setup_file_hash)
11054 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11055 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11057 setup.touch.grid_initialized = TRUE;
11058 for (i = 0; i < 2; i++)
11060 int grid_xsize = setup.touch.grid_xsize[i];
11061 int grid_ysize = setup.touch.grid_ysize[i];
11064 // if virtual buttons are not loaded from setup file, repeat initializing
11065 // virtual buttons grid with default values later when video is initialized
11066 if (grid_xsize == -1 ||
11069 setup.touch.grid_initialized = FALSE;
11074 for (y = 0; y < grid_ysize; y++)
11076 char token_string[MAX_LINE_LEN];
11078 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11080 char *value_string = getHashEntry(setup_file_hash, token_string);
11082 if (value_string == NULL)
11085 for (x = 0; x < grid_xsize; x++)
11087 char c = value_string[x];
11089 setup.touch.grid_button[i][x][y] =
11090 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11095 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11096 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11098 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11099 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11101 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11105 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11107 setup_input = setup.input[pnr];
11108 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11110 char full_token[100];
11112 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11113 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11116 setup.input[pnr] = setup_input;
11119 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11120 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11122 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11123 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11125 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11126 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11128 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11129 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11131 setHideRelatedSetupEntries();
11134 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11138 if (!setup_file_hash)
11141 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11142 setSetupInfo(auto_setup_tokens, i,
11143 getHashEntry(setup_file_hash,
11144 auto_setup_tokens[i].text));
11147 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11151 if (!setup_file_hash)
11154 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11155 setSetupInfo(server_setup_tokens, i,
11156 getHashEntry(setup_file_hash,
11157 server_setup_tokens[i].text));
11160 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11164 if (!setup_file_hash)
11167 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11168 setSetupInfo(editor_cascade_setup_tokens, i,
11169 getHashEntry(setup_file_hash,
11170 editor_cascade_setup_tokens[i].text));
11173 void LoadUserNames(void)
11175 int last_user_nr = user.nr;
11178 if (global.user_names != NULL)
11180 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11181 checked_free(global.user_names[i]);
11183 checked_free(global.user_names);
11186 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11188 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11192 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11194 if (setup_file_hash)
11196 char *player_name = getHashEntry(setup_file_hash, "player_name");
11198 global.user_names[i] = getFixedUserName(player_name);
11200 freeSetupFileHash(setup_file_hash);
11203 if (global.user_names[i] == NULL)
11204 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11207 user.nr = last_user_nr;
11210 void LoadSetupFromFilename(char *filename)
11212 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11214 if (setup_file_hash)
11216 decodeSetupFileHash_Default(setup_file_hash);
11218 freeSetupFileHash(setup_file_hash);
11222 Debug("setup", "using default setup values");
11226 static void LoadSetup_SpecialPostProcessing(void)
11228 char *player_name_new;
11230 // needed to work around problems with fixed length strings
11231 player_name_new = getFixedUserName(setup.player_name);
11232 free(setup.player_name);
11233 setup.player_name = player_name_new;
11235 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11236 if (setup.scroll_delay == FALSE)
11238 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11239 setup.scroll_delay = TRUE; // now always "on"
11242 // make sure that scroll delay value stays inside valid range
11243 setup.scroll_delay_value =
11244 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11247 void LoadSetup_Default(void)
11251 // always start with reliable default values
11252 setSetupInfoToDefaults(&setup);
11254 // try to load setup values from default setup file
11255 filename = getDefaultSetupFilename();
11257 if (fileExists(filename))
11258 LoadSetupFromFilename(filename);
11260 // try to load setup values from platform setup file
11261 filename = getPlatformSetupFilename();
11263 if (fileExists(filename))
11264 LoadSetupFromFilename(filename);
11266 // try to load setup values from user setup file
11267 filename = getSetupFilename();
11269 LoadSetupFromFilename(filename);
11271 LoadSetup_SpecialPostProcessing();
11274 void LoadSetup_AutoSetup(void)
11276 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11277 SetupFileHash *setup_file_hash = NULL;
11279 // always start with reliable default values
11280 setSetupInfoToDefaults_AutoSetup(&setup);
11282 setup_file_hash = loadSetupFileHash(filename);
11284 if (setup_file_hash)
11286 decodeSetupFileHash_AutoSetup(setup_file_hash);
11288 freeSetupFileHash(setup_file_hash);
11294 void LoadSetup_ServerSetup(void)
11296 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11297 SetupFileHash *setup_file_hash = NULL;
11299 // always start with reliable default values
11300 setSetupInfoToDefaults_ServerSetup(&setup);
11302 setup_file_hash = loadSetupFileHash(filename);
11304 if (setup_file_hash)
11306 decodeSetupFileHash_ServerSetup(setup_file_hash);
11308 freeSetupFileHash(setup_file_hash);
11313 if (setup.player_uuid == NULL)
11315 // player UUID does not yet exist in setup file
11316 setup.player_uuid = getStringCopy(getUUID());
11317 setup.player_version = 2;
11319 SaveSetup_ServerSetup();
11323 void LoadSetup_EditorCascade(void)
11325 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11326 SetupFileHash *setup_file_hash = NULL;
11328 // always start with reliable default values
11329 setSetupInfoToDefaults_EditorCascade(&setup);
11331 setup_file_hash = loadSetupFileHash(filename);
11333 if (setup_file_hash)
11335 decodeSetupFileHash_EditorCascade(setup_file_hash);
11337 freeSetupFileHash(setup_file_hash);
11343 void LoadSetup(void)
11345 LoadSetup_Default();
11346 LoadSetup_AutoSetup();
11347 LoadSetup_ServerSetup();
11348 LoadSetup_EditorCascade();
11351 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11352 char *mapping_line)
11354 char mapping_guid[MAX_LINE_LEN];
11355 char *mapping_start, *mapping_end;
11357 // get GUID from game controller mapping line: copy complete line
11358 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11359 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11361 // get GUID from game controller mapping line: cut after GUID part
11362 mapping_start = strchr(mapping_guid, ',');
11363 if (mapping_start != NULL)
11364 *mapping_start = '\0';
11366 // cut newline from game controller mapping line
11367 mapping_end = strchr(mapping_line, '\n');
11368 if (mapping_end != NULL)
11369 *mapping_end = '\0';
11371 // add mapping entry to game controller mappings hash
11372 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11375 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11380 if (!(file = fopen(filename, MODE_READ)))
11382 Warn("cannot read game controller mappings file '%s'", filename);
11387 while (!feof(file))
11389 char line[MAX_LINE_LEN];
11391 if (!fgets(line, MAX_LINE_LEN, file))
11394 addGameControllerMappingToHash(mappings_hash, line);
11400 void SaveSetup_Default(void)
11402 char *filename = getSetupFilename();
11406 InitUserDataDirectory();
11408 if (!(file = fopen(filename, MODE_WRITE)))
11410 Warn("cannot write setup file '%s'", filename);
11415 fprintFileHeader(file, SETUP_FILENAME);
11417 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11419 // just to make things nicer :)
11420 if (global_setup_tokens[i].value == &setup.multiple_users ||
11421 global_setup_tokens[i].value == &setup.sound ||
11422 global_setup_tokens[i].value == &setup.graphics_set ||
11423 global_setup_tokens[i].value == &setup.volume_simple ||
11424 global_setup_tokens[i].value == &setup.network_mode ||
11425 global_setup_tokens[i].value == &setup.touch.control_type ||
11426 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11427 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11428 fprintf(file, "\n");
11430 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11433 for (i = 0; i < 2; i++)
11435 int grid_xsize = setup.touch.grid_xsize[i];
11436 int grid_ysize = setup.touch.grid_ysize[i];
11439 fprintf(file, "\n");
11441 for (y = 0; y < grid_ysize; y++)
11443 char token_string[MAX_LINE_LEN];
11444 char value_string[MAX_LINE_LEN];
11446 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11448 for (x = 0; x < grid_xsize; x++)
11450 char c = setup.touch.grid_button[i][x][y];
11452 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11455 value_string[grid_xsize] = '\0';
11457 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11461 fprintf(file, "\n");
11462 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11463 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11465 fprintf(file, "\n");
11466 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11467 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11469 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11473 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11474 fprintf(file, "\n");
11476 setup_input = setup.input[pnr];
11477 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11478 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11481 fprintf(file, "\n");
11482 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11483 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11485 // (internal setup values not saved to user setup file)
11487 fprintf(file, "\n");
11488 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11489 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11490 setup.debug.xsn_mode != AUTO)
11491 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11493 fprintf(file, "\n");
11494 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11495 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11499 SetFilePermissions(filename, PERMS_PRIVATE);
11502 void SaveSetup_AutoSetup(void)
11504 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11508 InitUserDataDirectory();
11510 if (!(file = fopen(filename, MODE_WRITE)))
11512 Warn("cannot write auto setup file '%s'", filename);
11519 fprintFileHeader(file, AUTOSETUP_FILENAME);
11521 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11522 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11526 SetFilePermissions(filename, PERMS_PRIVATE);
11531 void SaveSetup_ServerSetup(void)
11533 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11537 InitUserDataDirectory();
11539 if (!(file = fopen(filename, MODE_WRITE)))
11541 Warn("cannot write server setup file '%s'", filename);
11548 fprintFileHeader(file, SERVERSETUP_FILENAME);
11550 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11552 // just to make things nicer :)
11553 if (server_setup_tokens[i].value == &setup.use_api_server)
11554 fprintf(file, "\n");
11556 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11561 SetFilePermissions(filename, PERMS_PRIVATE);
11566 void SaveSetup_EditorCascade(void)
11568 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11572 InitUserDataDirectory();
11574 if (!(file = fopen(filename, MODE_WRITE)))
11576 Warn("cannot write editor cascade state file '%s'", filename);
11583 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11585 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11586 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11590 SetFilePermissions(filename, PERMS_PRIVATE);
11595 void SaveSetup(void)
11597 SaveSetup_Default();
11598 SaveSetup_AutoSetup();
11599 SaveSetup_ServerSetup();
11600 SaveSetup_EditorCascade();
11603 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11608 if (!(file = fopen(filename, MODE_WRITE)))
11610 Warn("cannot write game controller mappings file '%s'", filename);
11615 BEGIN_HASH_ITERATION(mappings_hash, itr)
11617 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11619 END_HASH_ITERATION(mappings_hash, itr)
11624 void SaveSetup_AddGameControllerMapping(char *mapping)
11626 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11627 SetupFileHash *mappings_hash = newSetupFileHash();
11629 InitUserDataDirectory();
11631 // load existing personal game controller mappings
11632 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11634 // add new mapping to personal game controller mappings
11635 addGameControllerMappingToHash(mappings_hash, mapping);
11637 // save updated personal game controller mappings
11638 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11640 freeSetupFileHash(mappings_hash);
11644 void LoadCustomElementDescriptions(void)
11646 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11647 SetupFileHash *setup_file_hash;
11650 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11652 if (element_info[i].custom_description != NULL)
11654 free(element_info[i].custom_description);
11655 element_info[i].custom_description = NULL;
11659 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11662 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11664 char *token = getStringCat2(element_info[i].token_name, ".name");
11665 char *value = getHashEntry(setup_file_hash, token);
11668 element_info[i].custom_description = getStringCopy(value);
11673 freeSetupFileHash(setup_file_hash);
11676 static int getElementFromToken(char *token)
11678 char *value = getHashEntry(element_token_hash, token);
11681 return atoi(value);
11683 Warn("unknown element token '%s'", token);
11685 return EL_UNDEFINED;
11688 void FreeGlobalAnimEventInfo(void)
11690 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11692 if (gaei->event_list == NULL)
11697 for (i = 0; i < gaei->num_event_lists; i++)
11699 checked_free(gaei->event_list[i]->event_value);
11700 checked_free(gaei->event_list[i]);
11703 checked_free(gaei->event_list);
11705 gaei->event_list = NULL;
11706 gaei->num_event_lists = 0;
11709 static int AddGlobalAnimEventList(void)
11711 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11712 int list_pos = gaei->num_event_lists++;
11714 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11715 sizeof(struct GlobalAnimEventListInfo *));
11717 gaei->event_list[list_pos] =
11718 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11720 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11722 gaeli->event_value = NULL;
11723 gaeli->num_event_values = 0;
11728 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11730 // do not add empty global animation events
11731 if (event_value == ANIM_EVENT_NONE)
11734 // if list position is undefined, create new list
11735 if (list_pos == ANIM_EVENT_UNDEFINED)
11736 list_pos = AddGlobalAnimEventList();
11738 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11739 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11740 int value_pos = gaeli->num_event_values++;
11742 gaeli->event_value = checked_realloc(gaeli->event_value,
11743 gaeli->num_event_values * sizeof(int *));
11745 gaeli->event_value[value_pos] = event_value;
11750 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11752 if (list_pos == ANIM_EVENT_UNDEFINED)
11753 return ANIM_EVENT_NONE;
11755 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11756 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11758 return gaeli->event_value[value_pos];
11761 int GetGlobalAnimEventValueCount(int list_pos)
11763 if (list_pos == ANIM_EVENT_UNDEFINED)
11766 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11767 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11769 return gaeli->num_event_values;
11772 // This function checks if a string <s> of the format "string1, string2, ..."
11773 // exactly contains a string <s_contained>.
11775 static boolean string_has_parameter(char *s, char *s_contained)
11779 if (s == NULL || s_contained == NULL)
11782 if (strlen(s_contained) > strlen(s))
11785 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11787 char next_char = s[strlen(s_contained)];
11789 // check if next character is delimiter or whitespace
11790 if (next_char == ',' || next_char == '\0' ||
11791 next_char == ' ' || next_char == '\t')
11795 // check if string contains another parameter string after a comma
11796 substring = strchr(s, ',');
11797 if (substring == NULL) // string does not contain a comma
11800 // advance string pointer to next character after the comma
11803 // skip potential whitespaces after the comma
11804 while (*substring == ' ' || *substring == '\t')
11807 return string_has_parameter(substring, s_contained);
11810 static int get_anim_parameter_value_ce(char *s)
11813 char *pattern_1 = "ce_change:custom_";
11814 char *pattern_2 = ".page_";
11815 int pattern_1_len = strlen(pattern_1);
11816 char *matching_char = strstr(s_ptr, pattern_1);
11817 int result = ANIM_EVENT_NONE;
11819 if (matching_char == NULL)
11820 return ANIM_EVENT_NONE;
11822 result = ANIM_EVENT_CE_CHANGE;
11824 s_ptr = matching_char + pattern_1_len;
11826 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11827 if (*s_ptr >= '0' && *s_ptr <= '9')
11829 int gic_ce_nr = (*s_ptr++ - '0');
11831 if (*s_ptr >= '0' && *s_ptr <= '9')
11833 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11835 if (*s_ptr >= '0' && *s_ptr <= '9')
11836 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11839 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11840 return ANIM_EVENT_NONE;
11842 // custom element stored as 0 to 255
11845 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11849 // invalid custom element number specified
11851 return ANIM_EVENT_NONE;
11854 // check for change page number ("page_X" or "page_XX") (optional)
11855 if (strPrefix(s_ptr, pattern_2))
11857 s_ptr += strlen(pattern_2);
11859 if (*s_ptr >= '0' && *s_ptr <= '9')
11861 int gic_page_nr = (*s_ptr++ - '0');
11863 if (*s_ptr >= '0' && *s_ptr <= '9')
11864 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11866 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11867 return ANIM_EVENT_NONE;
11869 // change page stored as 1 to 32 (0 means "all change pages")
11871 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11875 // invalid animation part number specified
11877 return ANIM_EVENT_NONE;
11881 // discard result if next character is neither delimiter nor whitespace
11882 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11883 *s_ptr == ' ' || *s_ptr == '\t'))
11884 return ANIM_EVENT_NONE;
11889 static int get_anim_parameter_value(char *s)
11891 int event_value[] =
11899 char *pattern_1[] =
11907 char *pattern_2 = ".part_";
11908 char *matching_char = NULL;
11910 int pattern_1_len = 0;
11911 int result = ANIM_EVENT_NONE;
11914 result = get_anim_parameter_value_ce(s);
11916 if (result != ANIM_EVENT_NONE)
11919 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11921 matching_char = strstr(s_ptr, pattern_1[i]);
11922 pattern_1_len = strlen(pattern_1[i]);
11923 result = event_value[i];
11925 if (matching_char != NULL)
11929 if (matching_char == NULL)
11930 return ANIM_EVENT_NONE;
11932 s_ptr = matching_char + pattern_1_len;
11934 // check for main animation number ("anim_X" or "anim_XX")
11935 if (*s_ptr >= '0' && *s_ptr <= '9')
11937 int gic_anim_nr = (*s_ptr++ - '0');
11939 if (*s_ptr >= '0' && *s_ptr <= '9')
11940 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11942 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11943 return ANIM_EVENT_NONE;
11945 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11949 // invalid main animation number specified
11951 return ANIM_EVENT_NONE;
11954 // check for animation part number ("part_X" or "part_XX") (optional)
11955 if (strPrefix(s_ptr, pattern_2))
11957 s_ptr += strlen(pattern_2);
11959 if (*s_ptr >= '0' && *s_ptr <= '9')
11961 int gic_part_nr = (*s_ptr++ - '0');
11963 if (*s_ptr >= '0' && *s_ptr <= '9')
11964 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11966 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11967 return ANIM_EVENT_NONE;
11969 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11973 // invalid animation part number specified
11975 return ANIM_EVENT_NONE;
11979 // discard result if next character is neither delimiter nor whitespace
11980 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11981 *s_ptr == ' ' || *s_ptr == '\t'))
11982 return ANIM_EVENT_NONE;
11987 static int get_anim_parameter_values(char *s)
11989 int list_pos = ANIM_EVENT_UNDEFINED;
11990 int event_value = ANIM_EVENT_DEFAULT;
11992 if (string_has_parameter(s, "any"))
11993 event_value |= ANIM_EVENT_ANY;
11995 if (string_has_parameter(s, "click:self") ||
11996 string_has_parameter(s, "click") ||
11997 string_has_parameter(s, "self"))
11998 event_value |= ANIM_EVENT_SELF;
12000 if (string_has_parameter(s, "unclick:any"))
12001 event_value |= ANIM_EVENT_UNCLICK_ANY;
12003 // if animation event found, add it to global animation event list
12004 if (event_value != ANIM_EVENT_NONE)
12005 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12009 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12010 event_value = get_anim_parameter_value(s);
12012 // if animation event found, add it to global animation event list
12013 if (event_value != ANIM_EVENT_NONE)
12014 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12016 // continue with next part of the string, starting with next comma
12017 s = strchr(s + 1, ',');
12023 static int get_anim_action_parameter_value(char *token)
12025 // check most common default case first to massively speed things up
12026 if (strEqual(token, ARG_UNDEFINED))
12027 return ANIM_EVENT_ACTION_NONE;
12029 int result = getImageIDFromToken(token);
12033 char *gfx_token = getStringCat2("gfx.", token);
12035 result = getImageIDFromToken(gfx_token);
12037 checked_free(gfx_token);
12042 Key key = getKeyFromX11KeyName(token);
12044 if (key != KSYM_UNDEFINED)
12045 result = -(int)key;
12052 result = get_hash_from_string(token); // unsigned int => int
12053 result = ABS(result); // may be negative now
12054 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12056 setHashEntry(anim_url_hash, int2str(result, 0), token);
12061 result = ANIM_EVENT_ACTION_NONE;
12066 int get_parameter_value(char *value_raw, char *suffix, int type)
12068 char *value = getStringToLower(value_raw);
12069 int result = 0; // probably a save default value
12071 if (strEqual(suffix, ".direction"))
12073 result = (strEqual(value, "left") ? MV_LEFT :
12074 strEqual(value, "right") ? MV_RIGHT :
12075 strEqual(value, "up") ? MV_UP :
12076 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12078 else if (strEqual(suffix, ".position"))
12080 result = (strEqual(value, "left") ? POS_LEFT :
12081 strEqual(value, "right") ? POS_RIGHT :
12082 strEqual(value, "top") ? POS_TOP :
12083 strEqual(value, "upper") ? POS_UPPER :
12084 strEqual(value, "middle") ? POS_MIDDLE :
12085 strEqual(value, "lower") ? POS_LOWER :
12086 strEqual(value, "bottom") ? POS_BOTTOM :
12087 strEqual(value, "any") ? POS_ANY :
12088 strEqual(value, "ce") ? POS_CE :
12089 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12090 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12092 else if (strEqual(suffix, ".align"))
12094 result = (strEqual(value, "left") ? ALIGN_LEFT :
12095 strEqual(value, "right") ? ALIGN_RIGHT :
12096 strEqual(value, "center") ? ALIGN_CENTER :
12097 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12099 else if (strEqual(suffix, ".valign"))
12101 result = (strEqual(value, "top") ? VALIGN_TOP :
12102 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12103 strEqual(value, "middle") ? VALIGN_MIDDLE :
12104 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12106 else if (strEqual(suffix, ".anim_mode"))
12108 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12109 string_has_parameter(value, "loop") ? ANIM_LOOP :
12110 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12111 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12112 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12113 string_has_parameter(value, "random") ? ANIM_RANDOM :
12114 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12115 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12116 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12117 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12118 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12119 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12120 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12121 string_has_parameter(value, "all") ? ANIM_ALL :
12122 string_has_parameter(value, "tiled") ? ANIM_TILED :
12123 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12126 if (string_has_parameter(value, "once"))
12127 result |= ANIM_ONCE;
12129 if (string_has_parameter(value, "reverse"))
12130 result |= ANIM_REVERSE;
12132 if (string_has_parameter(value, "opaque_player"))
12133 result |= ANIM_OPAQUE_PLAYER;
12135 if (string_has_parameter(value, "static_panel"))
12136 result |= ANIM_STATIC_PANEL;
12138 else if (strEqual(suffix, ".init_event") ||
12139 strEqual(suffix, ".anim_event"))
12141 result = get_anim_parameter_values(value);
12143 else if (strEqual(suffix, ".init_delay_action") ||
12144 strEqual(suffix, ".anim_delay_action") ||
12145 strEqual(suffix, ".post_delay_action") ||
12146 strEqual(suffix, ".init_event_action") ||
12147 strEqual(suffix, ".anim_event_action"))
12149 result = get_anim_action_parameter_value(value_raw);
12151 else if (strEqual(suffix, ".class"))
12153 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12154 get_hash_from_string(value));
12156 else if (strEqual(suffix, ".style"))
12158 result = STYLE_DEFAULT;
12160 if (string_has_parameter(value, "accurate_borders"))
12161 result |= STYLE_ACCURATE_BORDERS;
12163 if (string_has_parameter(value, "inner_corners"))
12164 result |= STYLE_INNER_CORNERS;
12166 if (string_has_parameter(value, "reverse"))
12167 result |= STYLE_REVERSE;
12169 if (string_has_parameter(value, "leftmost_position"))
12170 result |= STYLE_LEFTMOST_POSITION;
12172 if (string_has_parameter(value, "block_clicks"))
12173 result |= STYLE_BLOCK;
12175 if (string_has_parameter(value, "passthrough_clicks"))
12176 result |= STYLE_PASSTHROUGH;
12178 if (string_has_parameter(value, "multiple_actions"))
12179 result |= STYLE_MULTIPLE_ACTIONS;
12181 if (string_has_parameter(value, "consume_ce_event"))
12182 result |= STYLE_CONSUME_CE_EVENT;
12184 else if (strEqual(suffix, ".fade_mode"))
12186 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12187 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12188 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12189 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12190 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12191 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12192 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12193 FADE_MODE_DEFAULT);
12195 else if (strEqual(suffix, ".auto_delay_unit"))
12197 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12198 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12199 AUTO_DELAY_UNIT_DEFAULT);
12201 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12203 result = gfx.get_font_from_token_function(value);
12205 else // generic parameter of type integer or boolean
12207 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12208 type == TYPE_INTEGER ? get_integer_from_string(value) :
12209 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12210 ARG_UNDEFINED_VALUE);
12218 static int get_token_parameter_value(char *token, char *value_raw)
12222 if (token == NULL || value_raw == NULL)
12223 return ARG_UNDEFINED_VALUE;
12225 suffix = strrchr(token, '.');
12226 if (suffix == NULL)
12229 if (strEqual(suffix, ".element"))
12230 return getElementFromToken(value_raw);
12232 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12233 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12236 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12237 boolean ignore_defaults)
12241 for (i = 0; image_config_vars[i].token != NULL; i++)
12243 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12245 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12246 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12250 *image_config_vars[i].value =
12251 get_token_parameter_value(image_config_vars[i].token, value);
12255 void InitMenuDesignSettings_Static(void)
12257 // always start with reliable default values from static default config
12258 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12261 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12265 // the following initializes hierarchical values from static configuration
12267 // special case: initialize "ARG_DEFAULT" values in static default config
12268 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12269 titlescreen_initial_first_default.fade_mode =
12270 title_initial_first_default.fade_mode;
12271 titlescreen_initial_first_default.fade_delay =
12272 title_initial_first_default.fade_delay;
12273 titlescreen_initial_first_default.post_delay =
12274 title_initial_first_default.post_delay;
12275 titlescreen_initial_first_default.auto_delay =
12276 title_initial_first_default.auto_delay;
12277 titlescreen_initial_first_default.auto_delay_unit =
12278 title_initial_first_default.auto_delay_unit;
12279 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12280 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12281 titlescreen_first_default.post_delay = title_first_default.post_delay;
12282 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12283 titlescreen_first_default.auto_delay_unit =
12284 title_first_default.auto_delay_unit;
12285 titlemessage_initial_first_default.fade_mode =
12286 title_initial_first_default.fade_mode;
12287 titlemessage_initial_first_default.fade_delay =
12288 title_initial_first_default.fade_delay;
12289 titlemessage_initial_first_default.post_delay =
12290 title_initial_first_default.post_delay;
12291 titlemessage_initial_first_default.auto_delay =
12292 title_initial_first_default.auto_delay;
12293 titlemessage_initial_first_default.auto_delay_unit =
12294 title_initial_first_default.auto_delay_unit;
12295 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12296 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12297 titlemessage_first_default.post_delay = title_first_default.post_delay;
12298 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12299 titlemessage_first_default.auto_delay_unit =
12300 title_first_default.auto_delay_unit;
12302 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12303 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12304 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12305 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12306 titlescreen_initial_default.auto_delay_unit =
12307 title_initial_default.auto_delay_unit;
12308 titlescreen_default.fade_mode = title_default.fade_mode;
12309 titlescreen_default.fade_delay = title_default.fade_delay;
12310 titlescreen_default.post_delay = title_default.post_delay;
12311 titlescreen_default.auto_delay = title_default.auto_delay;
12312 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12313 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12314 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12315 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12316 titlemessage_initial_default.auto_delay_unit =
12317 title_initial_default.auto_delay_unit;
12318 titlemessage_default.fade_mode = title_default.fade_mode;
12319 titlemessage_default.fade_delay = title_default.fade_delay;
12320 titlemessage_default.post_delay = title_default.post_delay;
12321 titlemessage_default.auto_delay = title_default.auto_delay;
12322 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12324 // special case: initialize "ARG_DEFAULT" values in static default config
12325 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12326 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12328 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12329 titlescreen_first[i] = titlescreen_first_default;
12330 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12331 titlemessage_first[i] = titlemessage_first_default;
12333 titlescreen_initial[i] = titlescreen_initial_default;
12334 titlescreen[i] = titlescreen_default;
12335 titlemessage_initial[i] = titlemessage_initial_default;
12336 titlemessage[i] = titlemessage_default;
12339 // special case: initialize "ARG_DEFAULT" values in static default config
12340 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12341 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12343 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12346 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12347 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12348 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12351 // special case: initialize "ARG_DEFAULT" values in static default config
12352 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12353 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12355 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12356 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12357 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12359 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12362 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12366 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12370 struct XY *dst, *src;
12372 game_buttons_xy[] =
12374 { &game.button.save, &game.button.stop },
12375 { &game.button.pause2, &game.button.pause },
12376 { &game.button.load, &game.button.play },
12377 { &game.button.undo, &game.button.stop },
12378 { &game.button.redo, &game.button.play },
12384 // special case: initialize later added SETUP list size from LEVELS value
12385 if (menu.list_size[GAME_MODE_SETUP] == -1)
12386 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12388 // set default position for snapshot buttons to stop/pause/play buttons
12389 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12390 if ((*game_buttons_xy[i].dst).x == -1 &&
12391 (*game_buttons_xy[i].dst).y == -1)
12392 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12394 // --------------------------------------------------------------------------
12395 // dynamic viewports (including playfield margins, borders and alignments)
12396 // --------------------------------------------------------------------------
12398 // dynamic viewports currently only supported for landscape mode
12399 int display_width = MAX(video.display_width, video.display_height);
12400 int display_height = MIN(video.display_width, video.display_height);
12402 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12404 struct RectWithBorder *vp_window = &viewport.window[i];
12405 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12406 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12407 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12408 boolean dynamic_window_width = (vp_window->min_width != -1);
12409 boolean dynamic_window_height = (vp_window->min_height != -1);
12410 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12411 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12413 // adjust window size if min/max width/height is specified
12415 if (vp_window->min_width != -1)
12417 int window_width = display_width;
12419 // when using static window height, use aspect ratio of display
12420 if (vp_window->min_height == -1)
12421 window_width = vp_window->height * display_width / display_height;
12423 vp_window->width = MAX(vp_window->min_width, window_width);
12426 if (vp_window->min_height != -1)
12428 int window_height = display_height;
12430 // when using static window width, use aspect ratio of display
12431 if (vp_window->min_width == -1)
12432 window_height = vp_window->width * display_height / display_width;
12434 vp_window->height = MAX(vp_window->min_height, window_height);
12437 if (vp_window->max_width != -1)
12438 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12440 if (vp_window->max_height != -1)
12441 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12443 int playfield_width = vp_window->width;
12444 int playfield_height = vp_window->height;
12446 // adjust playfield size and position according to specified margins
12448 playfield_width -= vp_playfield->margin_left;
12449 playfield_width -= vp_playfield->margin_right;
12451 playfield_height -= vp_playfield->margin_top;
12452 playfield_height -= vp_playfield->margin_bottom;
12454 // adjust playfield size if min/max width/height is specified
12456 if (vp_playfield->min_width != -1)
12457 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12459 if (vp_playfield->min_height != -1)
12460 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12462 if (vp_playfield->max_width != -1)
12463 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12465 if (vp_playfield->max_height != -1)
12466 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12468 // adjust playfield position according to specified alignment
12470 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12471 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12472 else if (vp_playfield->align == ALIGN_CENTER)
12473 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12474 else if (vp_playfield->align == ALIGN_RIGHT)
12475 vp_playfield->x += playfield_width - vp_playfield->width;
12477 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12478 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12479 else if (vp_playfield->valign == VALIGN_MIDDLE)
12480 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12481 else if (vp_playfield->valign == VALIGN_BOTTOM)
12482 vp_playfield->y += playfield_height - vp_playfield->height;
12484 vp_playfield->x += vp_playfield->margin_left;
12485 vp_playfield->y += vp_playfield->margin_top;
12487 // adjust individual playfield borders if only default border is specified
12489 if (vp_playfield->border_left == -1)
12490 vp_playfield->border_left = vp_playfield->border_size;
12491 if (vp_playfield->border_right == -1)
12492 vp_playfield->border_right = vp_playfield->border_size;
12493 if (vp_playfield->border_top == -1)
12494 vp_playfield->border_top = vp_playfield->border_size;
12495 if (vp_playfield->border_bottom == -1)
12496 vp_playfield->border_bottom = vp_playfield->border_size;
12498 // set dynamic playfield borders if borders are specified as undefined
12499 // (but only if window size was dynamic and playfield size was static)
12501 if (dynamic_window_width && !dynamic_playfield_width)
12503 if (vp_playfield->border_left == -1)
12505 vp_playfield->border_left = (vp_playfield->x -
12506 vp_playfield->margin_left);
12507 vp_playfield->x -= vp_playfield->border_left;
12508 vp_playfield->width += vp_playfield->border_left;
12511 if (vp_playfield->border_right == -1)
12513 vp_playfield->border_right = (vp_window->width -
12515 vp_playfield->width -
12516 vp_playfield->margin_right);
12517 vp_playfield->width += vp_playfield->border_right;
12521 if (dynamic_window_height && !dynamic_playfield_height)
12523 if (vp_playfield->border_top == -1)
12525 vp_playfield->border_top = (vp_playfield->y -
12526 vp_playfield->margin_top);
12527 vp_playfield->y -= vp_playfield->border_top;
12528 vp_playfield->height += vp_playfield->border_top;
12531 if (vp_playfield->border_bottom == -1)
12533 vp_playfield->border_bottom = (vp_window->height -
12535 vp_playfield->height -
12536 vp_playfield->margin_bottom);
12537 vp_playfield->height += vp_playfield->border_bottom;
12541 // adjust playfield size to be a multiple of a defined alignment tile size
12543 int align_size = vp_playfield->align_size;
12544 int playfield_xtiles = vp_playfield->width / align_size;
12545 int playfield_ytiles = vp_playfield->height / align_size;
12546 int playfield_width_corrected = playfield_xtiles * align_size;
12547 int playfield_height_corrected = playfield_ytiles * align_size;
12548 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12549 i == GFX_SPECIAL_ARG_EDITOR);
12551 if (is_playfield_mode &&
12552 dynamic_playfield_width &&
12553 vp_playfield->width != playfield_width_corrected)
12555 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12557 vp_playfield->width = playfield_width_corrected;
12559 if (vp_playfield->align == ALIGN_LEFT)
12561 vp_playfield->border_left += playfield_xdiff;
12563 else if (vp_playfield->align == ALIGN_RIGHT)
12565 vp_playfield->border_right += playfield_xdiff;
12567 else if (vp_playfield->align == ALIGN_CENTER)
12569 int border_left_diff = playfield_xdiff / 2;
12570 int border_right_diff = playfield_xdiff - border_left_diff;
12572 vp_playfield->border_left += border_left_diff;
12573 vp_playfield->border_right += border_right_diff;
12577 if (is_playfield_mode &&
12578 dynamic_playfield_height &&
12579 vp_playfield->height != playfield_height_corrected)
12581 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12583 vp_playfield->height = playfield_height_corrected;
12585 if (vp_playfield->valign == VALIGN_TOP)
12587 vp_playfield->border_top += playfield_ydiff;
12589 else if (vp_playfield->align == VALIGN_BOTTOM)
12591 vp_playfield->border_right += playfield_ydiff;
12593 else if (vp_playfield->align == VALIGN_MIDDLE)
12595 int border_top_diff = playfield_ydiff / 2;
12596 int border_bottom_diff = playfield_ydiff - border_top_diff;
12598 vp_playfield->border_top += border_top_diff;
12599 vp_playfield->border_bottom += border_bottom_diff;
12603 // adjust door positions according to specified alignment
12605 for (j = 0; j < 2; j++)
12607 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12609 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12610 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12611 else if (vp_door->align == ALIGN_CENTER)
12612 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12613 else if (vp_door->align == ALIGN_RIGHT)
12614 vp_door->x += vp_window->width - vp_door->width;
12616 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12617 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12618 else if (vp_door->valign == VALIGN_MIDDLE)
12619 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12620 else if (vp_door->valign == VALIGN_BOTTOM)
12621 vp_door->y += vp_window->height - vp_door->height;
12626 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12630 struct XYTileSize *dst, *src;
12633 editor_buttons_xy[] =
12636 &editor.button.element_left, &editor.palette.element_left,
12637 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12640 &editor.button.element_middle, &editor.palette.element_middle,
12641 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12644 &editor.button.element_right, &editor.palette.element_right,
12645 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12652 // set default position for element buttons to element graphics
12653 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12655 if ((*editor_buttons_xy[i].dst).x == -1 &&
12656 (*editor_buttons_xy[i].dst).y == -1)
12658 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12660 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12662 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12666 // adjust editor palette rows and columns if specified to be dynamic
12668 if (editor.palette.cols == -1)
12670 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12671 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12672 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12674 editor.palette.cols = (vp_width - sc_width) / bt_width;
12676 if (editor.palette.x == -1)
12678 int palette_width = editor.palette.cols * bt_width + sc_width;
12680 editor.palette.x = (vp_width - palette_width) / 2;
12684 if (editor.palette.rows == -1)
12686 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12687 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12688 int tx_height = getFontHeight(FONT_TEXT_2);
12690 editor.palette.rows = (vp_height - tx_height) / bt_height;
12692 if (editor.palette.y == -1)
12694 int palette_height = editor.palette.rows * bt_height + tx_height;
12696 editor.palette.y = (vp_height - palette_height) / 2;
12701 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12702 boolean initialize)
12704 // special case: check if network and preview player positions are redefined,
12705 // to compare this later against the main menu level preview being redefined
12706 struct TokenIntPtrInfo menu_config_players[] =
12708 { "main.network_players.x", &menu.main.network_players.redefined },
12709 { "main.network_players.y", &menu.main.network_players.redefined },
12710 { "main.preview_players.x", &menu.main.preview_players.redefined },
12711 { "main.preview_players.y", &menu.main.preview_players.redefined },
12712 { "preview.x", &preview.redefined },
12713 { "preview.y", &preview.redefined }
12719 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12720 *menu_config_players[i].value = FALSE;
12724 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12725 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12726 *menu_config_players[i].value = TRUE;
12730 static void InitMenuDesignSettings_PreviewPlayers(void)
12732 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12735 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12737 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12740 static void LoadMenuDesignSettingsFromFilename(char *filename)
12742 static struct TitleFadingInfo tfi;
12743 static struct TitleMessageInfo tmi;
12744 static struct TokenInfo title_tokens[] =
12746 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12747 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12748 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12749 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12750 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12754 static struct TokenInfo titlemessage_tokens[] =
12756 { TYPE_INTEGER, &tmi.x, ".x" },
12757 { TYPE_INTEGER, &tmi.y, ".y" },
12758 { TYPE_INTEGER, &tmi.width, ".width" },
12759 { TYPE_INTEGER, &tmi.height, ".height" },
12760 { TYPE_INTEGER, &tmi.chars, ".chars" },
12761 { TYPE_INTEGER, &tmi.lines, ".lines" },
12762 { TYPE_INTEGER, &tmi.align, ".align" },
12763 { TYPE_INTEGER, &tmi.valign, ".valign" },
12764 { TYPE_INTEGER, &tmi.font, ".font" },
12765 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12766 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12767 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12768 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12769 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12770 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12771 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12772 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12773 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12779 struct TitleFadingInfo *info;
12784 // initialize first titles from "enter screen" definitions, if defined
12785 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12786 { &title_first_default, "menu.enter_screen.TITLE" },
12788 // initialize title screens from "next screen" definitions, if defined
12789 { &title_initial_default, "menu.next_screen.TITLE" },
12790 { &title_default, "menu.next_screen.TITLE" },
12796 struct TitleMessageInfo *array;
12799 titlemessage_arrays[] =
12801 // initialize first titles from "enter screen" definitions, if defined
12802 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12803 { titlescreen_first, "menu.enter_screen.TITLE" },
12804 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12805 { titlemessage_first, "menu.enter_screen.TITLE" },
12807 // initialize titles from "next screen" definitions, if defined
12808 { titlescreen_initial, "menu.next_screen.TITLE" },
12809 { titlescreen, "menu.next_screen.TITLE" },
12810 { titlemessage_initial, "menu.next_screen.TITLE" },
12811 { titlemessage, "menu.next_screen.TITLE" },
12813 // overwrite titles with title definitions, if defined
12814 { titlescreen_initial_first, "[title_initial]" },
12815 { titlescreen_first, "[title]" },
12816 { titlemessage_initial_first, "[title_initial]" },
12817 { titlemessage_first, "[title]" },
12819 { titlescreen_initial, "[title_initial]" },
12820 { titlescreen, "[title]" },
12821 { titlemessage_initial, "[title_initial]" },
12822 { titlemessage, "[title]" },
12824 // overwrite titles with title screen/message definitions, if defined
12825 { titlescreen_initial_first, "[titlescreen_initial]" },
12826 { titlescreen_first, "[titlescreen]" },
12827 { titlemessage_initial_first, "[titlemessage_initial]" },
12828 { titlemessage_first, "[titlemessage]" },
12830 { titlescreen_initial, "[titlescreen_initial]" },
12831 { titlescreen, "[titlescreen]" },
12832 { titlemessage_initial, "[titlemessage_initial]" },
12833 { titlemessage, "[titlemessage]" },
12837 SetupFileHash *setup_file_hash;
12840 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12843 // the following initializes hierarchical values from dynamic configuration
12845 // special case: initialize with default values that may be overwritten
12846 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12847 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12849 struct TokenIntPtrInfo menu_config[] =
12851 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12852 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12853 { "menu.list_size", &menu.list_size[i] }
12856 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12858 char *token = menu_config[j].token;
12859 char *value = getHashEntry(setup_file_hash, token);
12862 *menu_config[j].value = get_integer_from_string(value);
12866 // special case: initialize with default values that may be overwritten
12867 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12868 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12870 struct TokenIntPtrInfo menu_config[] =
12872 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12873 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12874 { "menu.list_size.INFO", &menu.list_size_info[i] },
12875 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12876 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12879 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12881 char *token = menu_config[j].token;
12882 char *value = getHashEntry(setup_file_hash, token);
12885 *menu_config[j].value = get_integer_from_string(value);
12889 // special case: initialize with default values that may be overwritten
12890 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12891 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12893 struct TokenIntPtrInfo menu_config[] =
12895 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12896 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12899 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12901 char *token = menu_config[j].token;
12902 char *value = getHashEntry(setup_file_hash, token);
12905 *menu_config[j].value = get_integer_from_string(value);
12909 // special case: initialize with default values that may be overwritten
12910 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12911 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12913 struct TokenIntPtrInfo menu_config[] =
12915 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12916 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12917 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12918 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12919 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12920 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12921 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12922 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12923 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12924 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12927 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12929 char *token = menu_config[j].token;
12930 char *value = getHashEntry(setup_file_hash, token);
12933 *menu_config[j].value = get_integer_from_string(value);
12937 // special case: initialize with default values that may be overwritten
12938 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12939 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12941 struct TokenIntPtrInfo menu_config[] =
12943 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12944 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12945 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12946 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12947 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12948 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12949 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12950 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12951 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12954 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12956 char *token = menu_config[j].token;
12957 char *value = getHashEntry(setup_file_hash, token);
12960 *menu_config[j].value = get_token_parameter_value(token, value);
12964 // special case: initialize with default values that may be overwritten
12965 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12966 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12970 char *token_prefix;
12971 struct RectWithBorder *struct_ptr;
12975 { "viewport.window", &viewport.window[i] },
12976 { "viewport.playfield", &viewport.playfield[i] },
12977 { "viewport.door_1", &viewport.door_1[i] },
12978 { "viewport.door_2", &viewport.door_2[i] }
12981 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12983 struct TokenIntPtrInfo vp_config[] =
12985 { ".x", &vp_struct[j].struct_ptr->x },
12986 { ".y", &vp_struct[j].struct_ptr->y },
12987 { ".width", &vp_struct[j].struct_ptr->width },
12988 { ".height", &vp_struct[j].struct_ptr->height },
12989 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12990 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12991 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12992 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12993 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12994 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12995 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12996 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12997 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12998 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12999 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13000 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13001 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13002 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13003 { ".align", &vp_struct[j].struct_ptr->align },
13004 { ".valign", &vp_struct[j].struct_ptr->valign }
13007 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13009 char *token = getStringCat2(vp_struct[j].token_prefix,
13010 vp_config[k].token);
13011 char *value = getHashEntry(setup_file_hash, token);
13014 *vp_config[k].value = get_token_parameter_value(token, value);
13021 // special case: initialize with default values that may be overwritten
13022 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13023 for (i = 0; title_info[i].info != NULL; i++)
13025 struct TitleFadingInfo *info = title_info[i].info;
13026 char *base_token = title_info[i].text;
13028 for (j = 0; title_tokens[j].type != -1; j++)
13030 char *token = getStringCat2(base_token, title_tokens[j].text);
13031 char *value = getHashEntry(setup_file_hash, token);
13035 int parameter_value = get_token_parameter_value(token, value);
13039 *(int *)title_tokens[j].value = (int)parameter_value;
13048 // special case: initialize with default values that may be overwritten
13049 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13050 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13052 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13053 char *base_token = titlemessage_arrays[i].text;
13055 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13057 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13058 char *value = getHashEntry(setup_file_hash, token);
13062 int parameter_value = get_token_parameter_value(token, value);
13064 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13068 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13069 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13071 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13081 // read (and overwrite with) values that may be specified in config file
13082 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13084 // special case: check if network and preview player positions are redefined
13085 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13087 freeSetupFileHash(setup_file_hash);
13090 void LoadMenuDesignSettings(void)
13092 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13094 InitMenuDesignSettings_Static();
13095 InitMenuDesignSettings_SpecialPreProcessing();
13096 InitMenuDesignSettings_PreviewPlayers();
13098 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13100 // first look for special settings configured in level series config
13101 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13103 if (fileExists(filename_base))
13104 LoadMenuDesignSettingsFromFilename(filename_base);
13107 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13109 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13110 LoadMenuDesignSettingsFromFilename(filename_local);
13112 InitMenuDesignSettings_SpecialPostProcessing();
13115 void LoadMenuDesignSettings_AfterGraphics(void)
13117 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13120 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13121 boolean ignore_defaults)
13125 for (i = 0; sound_config_vars[i].token != NULL; i++)
13127 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13129 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13130 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13134 *sound_config_vars[i].value =
13135 get_token_parameter_value(sound_config_vars[i].token, value);
13139 void InitSoundSettings_Static(void)
13141 // always start with reliable default values from static default config
13142 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13145 static void LoadSoundSettingsFromFilename(char *filename)
13147 SetupFileHash *setup_file_hash;
13149 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13152 // read (and overwrite with) values that may be specified in config file
13153 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13155 freeSetupFileHash(setup_file_hash);
13158 void LoadSoundSettings(void)
13160 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13162 InitSoundSettings_Static();
13164 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13166 // first look for special settings configured in level series config
13167 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13169 if (fileExists(filename_base))
13170 LoadSoundSettingsFromFilename(filename_base);
13173 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13175 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13176 LoadSoundSettingsFromFilename(filename_local);
13179 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13181 char *filename = getEditorSetupFilename();
13182 SetupFileList *setup_file_list, *list;
13183 SetupFileHash *element_hash;
13184 int num_unknown_tokens = 0;
13187 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13190 element_hash = newSetupFileHash();
13192 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13193 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13195 // determined size may be larger than needed (due to unknown elements)
13197 for (list = setup_file_list; list != NULL; list = list->next)
13200 // add space for up to 3 more elements for padding that may be needed
13201 *num_elements += 3;
13203 // free memory for old list of elements, if needed
13204 checked_free(*elements);
13206 // allocate memory for new list of elements
13207 *elements = checked_malloc(*num_elements * sizeof(int));
13210 for (list = setup_file_list; list != NULL; list = list->next)
13212 char *value = getHashEntry(element_hash, list->token);
13214 if (value == NULL) // try to find obsolete token mapping
13216 char *mapped_token = get_mapped_token(list->token);
13218 if (mapped_token != NULL)
13220 value = getHashEntry(element_hash, mapped_token);
13222 free(mapped_token);
13228 (*elements)[(*num_elements)++] = atoi(value);
13232 if (num_unknown_tokens == 0)
13235 Warn("unknown token(s) found in config file:");
13236 Warn("- config file: '%s'", filename);
13238 num_unknown_tokens++;
13241 Warn("- token: '%s'", list->token);
13245 if (num_unknown_tokens > 0)
13248 while (*num_elements % 4) // pad with empty elements, if needed
13249 (*elements)[(*num_elements)++] = EL_EMPTY;
13251 freeSetupFileList(setup_file_list);
13252 freeSetupFileHash(element_hash);
13255 for (i = 0; i < *num_elements; i++)
13256 Debug("editor", "element '%s' [%d]\n",
13257 element_info[(*elements)[i]].token_name, (*elements)[i]);
13261 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13264 SetupFileHash *setup_file_hash = NULL;
13265 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13266 char *filename_music, *filename_prefix, *filename_info;
13272 token_to_value_ptr[] =
13274 { "title_header", &tmp_music_file_info.title_header },
13275 { "artist_header", &tmp_music_file_info.artist_header },
13276 { "album_header", &tmp_music_file_info.album_header },
13277 { "year_header", &tmp_music_file_info.year_header },
13278 { "played_header", &tmp_music_file_info.played_header },
13280 { "title", &tmp_music_file_info.title },
13281 { "artist", &tmp_music_file_info.artist },
13282 { "album", &tmp_music_file_info.album },
13283 { "year", &tmp_music_file_info.year },
13284 { "played", &tmp_music_file_info.played },
13290 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13291 getCustomMusicFilename(basename));
13293 if (filename_music == NULL)
13296 // ---------- try to replace file extension ----------
13298 filename_prefix = getStringCopy(filename_music);
13299 if (strrchr(filename_prefix, '.') != NULL)
13300 *strrchr(filename_prefix, '.') = '\0';
13301 filename_info = getStringCat2(filename_prefix, ".txt");
13303 if (fileExists(filename_info))
13304 setup_file_hash = loadSetupFileHash(filename_info);
13306 free(filename_prefix);
13307 free(filename_info);
13309 if (setup_file_hash == NULL)
13311 // ---------- try to add file extension ----------
13313 filename_prefix = getStringCopy(filename_music);
13314 filename_info = getStringCat2(filename_prefix, ".txt");
13316 if (fileExists(filename_info))
13317 setup_file_hash = loadSetupFileHash(filename_info);
13319 free(filename_prefix);
13320 free(filename_info);
13323 if (setup_file_hash == NULL)
13326 // ---------- music file info found ----------
13328 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13330 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13332 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13334 *token_to_value_ptr[i].value_ptr =
13335 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13338 tmp_music_file_info.basename = getStringCopy(basename);
13339 tmp_music_file_info.music = music;
13340 tmp_music_file_info.is_sound = is_sound;
13342 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13343 *new_music_file_info = tmp_music_file_info;
13345 return new_music_file_info;
13348 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13350 return get_music_file_info_ext(basename, music, FALSE);
13353 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13355 return get_music_file_info_ext(basename, sound, TRUE);
13358 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13359 char *basename, boolean is_sound)
13361 for (; list != NULL; list = list->next)
13362 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13368 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13370 return music_info_listed_ext(list, basename, FALSE);
13373 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13375 return music_info_listed_ext(list, basename, TRUE);
13378 void LoadMusicInfo(void)
13380 int num_music_noconf = getMusicListSize_NoConf();
13381 int num_music = getMusicListSize();
13382 int num_sounds = getSoundListSize();
13383 struct FileInfo *music, *sound;
13384 struct MusicFileInfo *next, **new;
13388 while (music_file_info != NULL)
13390 next = music_file_info->next;
13392 checked_free(music_file_info->basename);
13394 checked_free(music_file_info->title_header);
13395 checked_free(music_file_info->artist_header);
13396 checked_free(music_file_info->album_header);
13397 checked_free(music_file_info->year_header);
13398 checked_free(music_file_info->played_header);
13400 checked_free(music_file_info->title);
13401 checked_free(music_file_info->artist);
13402 checked_free(music_file_info->album);
13403 checked_free(music_file_info->year);
13404 checked_free(music_file_info->played);
13406 free(music_file_info);
13408 music_file_info = next;
13411 new = &music_file_info;
13413 // get (configured or unconfigured) music file info for all levels
13414 for (i = leveldir_current->first_level;
13415 i <= leveldir_current->last_level; i++)
13419 if (levelset.music[i] != MUS_UNDEFINED)
13421 // get music file info for configured level music
13422 music_nr = levelset.music[i];
13424 else if (num_music_noconf > 0)
13426 // get music file info for unconfigured level music
13427 int level_pos = i - leveldir_current->first_level;
13429 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13436 char *basename = getMusicInfoEntryFilename(music_nr);
13438 if (basename == NULL)
13441 if (!music_info_listed(music_file_info, basename))
13443 *new = get_music_file_info(basename, music_nr);
13446 new = &(*new)->next;
13450 // get music file info for all remaining configured music files
13451 for (i = 0; i < num_music; i++)
13453 music = getMusicListEntry(i);
13455 if (music->filename == NULL)
13458 if (strEqual(music->filename, UNDEFINED_FILENAME))
13461 // a configured file may be not recognized as music
13462 if (!FileIsMusic(music->filename))
13465 if (!music_info_listed(music_file_info, music->filename))
13467 *new = get_music_file_info(music->filename, i);
13470 new = &(*new)->next;
13474 // get sound file info for all configured sound files
13475 for (i = 0; i < num_sounds; i++)
13477 sound = getSoundListEntry(i);
13479 if (sound->filename == NULL)
13482 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13485 // a configured file may be not recognized as sound
13486 if (!FileIsSound(sound->filename))
13489 if (!sound_info_listed(music_file_info, sound->filename))
13491 *new = get_sound_file_info(sound->filename, i);
13493 new = &(*new)->next;
13497 // add pointers to previous list nodes
13499 struct MusicFileInfo *node = music_file_info;
13501 while (node != NULL)
13504 node->next->prev = node;
13510 static void add_helpanim_entry(int element, int action, int direction,
13511 int delay, int *num_list_entries)
13513 struct HelpAnimInfo *new_list_entry;
13514 (*num_list_entries)++;
13517 checked_realloc(helpanim_info,
13518 *num_list_entries * sizeof(struct HelpAnimInfo));
13519 new_list_entry = &helpanim_info[*num_list_entries - 1];
13521 new_list_entry->element = element;
13522 new_list_entry->action = action;
13523 new_list_entry->direction = direction;
13524 new_list_entry->delay = delay;
13527 static void print_unknown_token(char *filename, char *token, int token_nr)
13532 Warn("unknown token(s) found in config file:");
13533 Warn("- config file: '%s'", filename);
13536 Warn("- token: '%s'", token);
13539 static void print_unknown_token_end(int token_nr)
13545 void LoadHelpAnimInfo(void)
13547 char *filename = getHelpAnimFilename();
13548 SetupFileList *setup_file_list = NULL, *list;
13549 SetupFileHash *element_hash, *action_hash, *direction_hash;
13550 int num_list_entries = 0;
13551 int num_unknown_tokens = 0;
13554 if (fileExists(filename))
13555 setup_file_list = loadSetupFileList(filename);
13557 if (setup_file_list == NULL)
13559 // use reliable default values from static configuration
13560 SetupFileList *insert_ptr;
13562 insert_ptr = setup_file_list =
13563 newSetupFileList(helpanim_config[0].token,
13564 helpanim_config[0].value);
13566 for (i = 1; helpanim_config[i].token; i++)
13567 insert_ptr = addListEntry(insert_ptr,
13568 helpanim_config[i].token,
13569 helpanim_config[i].value);
13572 element_hash = newSetupFileHash();
13573 action_hash = newSetupFileHash();
13574 direction_hash = newSetupFileHash();
13576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13577 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13579 for (i = 0; i < NUM_ACTIONS; i++)
13580 setHashEntry(action_hash, element_action_info[i].suffix,
13581 i_to_a(element_action_info[i].value));
13583 // do not store direction index (bit) here, but direction value!
13584 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13585 setHashEntry(direction_hash, element_direction_info[i].suffix,
13586 i_to_a(1 << element_direction_info[i].value));
13588 for (list = setup_file_list; list != NULL; list = list->next)
13590 char *element_token, *action_token, *direction_token;
13591 char *element_value, *action_value, *direction_value;
13592 int delay = atoi(list->value);
13594 if (strEqual(list->token, "end"))
13596 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13601 /* first try to break element into element/action/direction parts;
13602 if this does not work, also accept combined "element[.act][.dir]"
13603 elements (like "dynamite.active"), which are unique elements */
13605 if (strchr(list->token, '.') == NULL) // token contains no '.'
13607 element_value = getHashEntry(element_hash, list->token);
13608 if (element_value != NULL) // element found
13609 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13610 &num_list_entries);
13613 // no further suffixes found -- this is not an element
13614 print_unknown_token(filename, list->token, num_unknown_tokens++);
13620 // token has format "<prefix>.<something>"
13622 action_token = strchr(list->token, '.'); // suffix may be action ...
13623 direction_token = action_token; // ... or direction
13625 element_token = getStringCopy(list->token);
13626 *strchr(element_token, '.') = '\0';
13628 element_value = getHashEntry(element_hash, element_token);
13630 if (element_value == NULL) // this is no element
13632 element_value = getHashEntry(element_hash, list->token);
13633 if (element_value != NULL) // combined element found
13634 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13635 &num_list_entries);
13637 print_unknown_token(filename, list->token, num_unknown_tokens++);
13639 free(element_token);
13644 action_value = getHashEntry(action_hash, action_token);
13646 if (action_value != NULL) // action found
13648 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13649 &num_list_entries);
13651 free(element_token);
13656 direction_value = getHashEntry(direction_hash, direction_token);
13658 if (direction_value != NULL) // direction found
13660 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13661 &num_list_entries);
13663 free(element_token);
13668 if (strchr(action_token + 1, '.') == NULL)
13670 // no further suffixes found -- this is not an action nor direction
13672 element_value = getHashEntry(element_hash, list->token);
13673 if (element_value != NULL) // combined element found
13674 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13675 &num_list_entries);
13677 print_unknown_token(filename, list->token, num_unknown_tokens++);
13679 free(element_token);
13684 // token has format "<prefix>.<suffix>.<something>"
13686 direction_token = strchr(action_token + 1, '.');
13688 action_token = getStringCopy(action_token);
13689 *strchr(action_token + 1, '.') = '\0';
13691 action_value = getHashEntry(action_hash, action_token);
13693 if (action_value == NULL) // this is no action
13695 element_value = getHashEntry(element_hash, list->token);
13696 if (element_value != NULL) // combined element found
13697 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13698 &num_list_entries);
13700 print_unknown_token(filename, list->token, num_unknown_tokens++);
13702 free(element_token);
13703 free(action_token);
13708 direction_value = getHashEntry(direction_hash, direction_token);
13710 if (direction_value != NULL) // direction found
13712 add_helpanim_entry(atoi(element_value), atoi(action_value),
13713 atoi(direction_value), delay, &num_list_entries);
13715 free(element_token);
13716 free(action_token);
13721 // this is no direction
13723 element_value = getHashEntry(element_hash, list->token);
13724 if (element_value != NULL) // combined element found
13725 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13726 &num_list_entries);
13728 print_unknown_token(filename, list->token, num_unknown_tokens++);
13730 free(element_token);
13731 free(action_token);
13734 print_unknown_token_end(num_unknown_tokens);
13736 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13737 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13739 freeSetupFileList(setup_file_list);
13740 freeSetupFileHash(element_hash);
13741 freeSetupFileHash(action_hash);
13742 freeSetupFileHash(direction_hash);
13745 for (i = 0; i < num_list_entries; i++)
13746 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13747 EL_NAME(helpanim_info[i].element),
13748 helpanim_info[i].element,
13749 helpanim_info[i].action,
13750 helpanim_info[i].direction,
13751 helpanim_info[i].delay);
13755 void LoadHelpTextInfo(void)
13757 char *filename = getHelpTextFilename();
13760 if (helptext_info != NULL)
13762 freeSetupFileHash(helptext_info);
13763 helptext_info = NULL;
13766 if (fileExists(filename))
13767 helptext_info = loadSetupFileHash(filename);
13769 if (helptext_info == NULL)
13771 // use reliable default values from static configuration
13772 helptext_info = newSetupFileHash();
13774 for (i = 0; helptext_config[i].token; i++)
13775 setHashEntry(helptext_info,
13776 helptext_config[i].token,
13777 helptext_config[i].value);
13781 BEGIN_HASH_ITERATION(helptext_info, itr)
13783 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13784 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13786 END_HASH_ITERATION(hash, itr)
13791 // ----------------------------------------------------------------------------
13793 // ----------------------------------------------------------------------------
13795 #define MAX_NUM_CONVERT_LEVELS 1000
13797 void ConvertLevels(void)
13799 static LevelDirTree *convert_leveldir = NULL;
13800 static int convert_level_nr = -1;
13801 static int num_levels_handled = 0;
13802 static int num_levels_converted = 0;
13803 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13806 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13807 global.convert_leveldir);
13809 if (convert_leveldir == NULL)
13810 Fail("no such level identifier: '%s'", global.convert_leveldir);
13812 leveldir_current = convert_leveldir;
13814 if (global.convert_level_nr != -1)
13816 convert_leveldir->first_level = global.convert_level_nr;
13817 convert_leveldir->last_level = global.convert_level_nr;
13820 convert_level_nr = convert_leveldir->first_level;
13822 PrintLine("=", 79);
13823 Print("Converting levels\n");
13824 PrintLine("-", 79);
13825 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13826 Print("Level series name: '%s'\n", convert_leveldir->name);
13827 Print("Level series author: '%s'\n", convert_leveldir->author);
13828 Print("Number of levels: %d\n", convert_leveldir->levels);
13829 PrintLine("=", 79);
13832 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13833 levels_failed[i] = FALSE;
13835 while (convert_level_nr <= convert_leveldir->last_level)
13837 char *level_filename;
13840 level_nr = convert_level_nr++;
13842 Print("Level %03d: ", level_nr);
13844 LoadLevel(level_nr);
13845 if (level.no_level_file || level.no_valid_file)
13847 Print("(no level)\n");
13851 Print("converting level ... ");
13854 // special case: conversion of some EMC levels as requested by ACME
13855 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13858 level_filename = getDefaultLevelFilename(level_nr);
13859 new_level = !fileExists(level_filename);
13863 SaveLevel(level_nr);
13865 num_levels_converted++;
13867 Print("converted.\n");
13871 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13872 levels_failed[level_nr] = TRUE;
13874 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13877 num_levels_handled++;
13881 PrintLine("=", 79);
13882 Print("Number of levels handled: %d\n", num_levels_handled);
13883 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13884 (num_levels_handled ?
13885 num_levels_converted * 100 / num_levels_handled : 0));
13886 PrintLine("-", 79);
13887 Print("Summary (for automatic parsing by scripts):\n");
13888 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13889 convert_leveldir->identifier, num_levels_converted,
13890 num_levels_handled,
13891 (num_levels_handled ?
13892 num_levels_converted * 100 / num_levels_handled : 0));
13894 if (num_levels_handled != num_levels_converted)
13896 Print(", FAILED:");
13897 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13898 if (levels_failed[i])
13903 PrintLine("=", 79);
13905 CloseAllAndExit(0);
13909 // ----------------------------------------------------------------------------
13910 // create and save images for use in level sketches (raw BMP format)
13911 // ----------------------------------------------------------------------------
13913 void CreateLevelSketchImages(void)
13919 InitElementPropertiesGfxElement();
13921 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13922 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13924 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13926 int element = getMappedElement(i);
13927 char basename1[16];
13928 char basename2[16];
13932 sprintf(basename1, "%04d.bmp", i);
13933 sprintf(basename2, "%04ds.bmp", i);
13935 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13936 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13938 DrawSizedElement(0, 0, element, TILESIZE);
13939 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13941 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13942 Fail("cannot save level sketch image file '%s'", filename1);
13944 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13945 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13947 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13948 Fail("cannot save level sketch image file '%s'", filename2);
13953 // create corresponding SQL statements (for normal and small images)
13956 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13957 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13960 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13961 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13963 // optional: create content for forum level sketch demonstration post
13965 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13968 FreeBitmap(bitmap1);
13969 FreeBitmap(bitmap2);
13972 fprintf(stderr, "\n");
13974 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13976 CloseAllAndExit(0);
13980 // ----------------------------------------------------------------------------
13981 // create and save images for element collecting animations (raw BMP format)
13982 // ----------------------------------------------------------------------------
13984 static boolean createCollectImage(int element)
13986 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13989 void CreateCollectElementImages(void)
13993 int anim_frames = num_steps - 1;
13994 int tile_size = TILESIZE;
13995 int anim_width = tile_size * anim_frames;
13996 int anim_height = tile_size;
13997 int num_collect_images = 0;
13998 int pos_collect_images = 0;
14000 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14001 if (createCollectImage(i))
14002 num_collect_images++;
14004 Info("Creating %d element collecting animation images ...",
14005 num_collect_images);
14007 int dst_width = anim_width * 2;
14008 int dst_height = anim_height * num_collect_images / 2;
14009 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14010 char *basename_bmp = "RocksCollect.bmp";
14011 char *basename_png = "RocksCollect.png";
14012 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14013 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14014 int len_filename_bmp = strlen(filename_bmp);
14015 int len_filename_png = strlen(filename_png);
14016 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14017 char cmd_convert[max_command_len];
14019 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14023 // force using RGBA surface for destination bitmap
14024 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14025 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14027 dst_bitmap->surface =
14028 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14030 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14032 if (!createCollectImage(i))
14035 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14036 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14037 int graphic = el2img(i);
14038 char *token_name = element_info[i].token_name;
14039 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14040 Bitmap *src_bitmap;
14043 Info("- creating collecting image for '%s' ...", token_name);
14045 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14047 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14048 tile_size, tile_size, 0, 0);
14050 // force using RGBA surface for temporary bitmap (using transparent black)
14051 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14052 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14054 tmp_bitmap->surface =
14055 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14057 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14059 for (j = 0; j < anim_frames; j++)
14061 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14062 int frame_size = frame_size_final * num_steps;
14063 int offset = (tile_size - frame_size_final) / 2;
14064 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14066 while (frame_size > frame_size_final)
14070 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14072 FreeBitmap(frame_bitmap);
14074 frame_bitmap = half_bitmap;
14077 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14078 frame_size_final, frame_size_final,
14079 dst_x + j * tile_size + offset, dst_y + offset);
14081 FreeBitmap(frame_bitmap);
14084 tmp_bitmap->surface_masked = NULL;
14086 FreeBitmap(tmp_bitmap);
14088 pos_collect_images++;
14091 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14092 Fail("cannot save element collecting image file '%s'", filename_bmp);
14094 FreeBitmap(dst_bitmap);
14096 Info("Converting image file from BMP to PNG ...");
14098 if (system(cmd_convert) != 0)
14099 Fail("converting image file failed");
14101 unlink(filename_bmp);
14105 CloseAllAndExit(0);
14109 // ----------------------------------------------------------------------------
14110 // create and save images for custom and group elements (raw BMP format)
14111 // ----------------------------------------------------------------------------
14113 void CreateCustomElementImages(char *directory)
14115 char *src_basename = "RocksCE-template.ilbm";
14116 char *dst_basename = "RocksCE.bmp";
14117 char *src_filename = getPath2(directory, src_basename);
14118 char *dst_filename = getPath2(directory, dst_basename);
14119 Bitmap *src_bitmap;
14121 int yoffset_ce = 0;
14122 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14125 InitVideoDefaults();
14127 ReCreateBitmap(&backbuffer, video.width, video.height);
14129 src_bitmap = LoadImage(src_filename);
14131 bitmap = CreateBitmap(TILEX * 16 * 2,
14132 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14135 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14142 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14143 TILEX * x, TILEY * y + yoffset_ce);
14145 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14147 TILEX * x + TILEX * 16,
14148 TILEY * y + yoffset_ce);
14150 for (j = 2; j >= 0; j--)
14154 BlitBitmap(src_bitmap, bitmap,
14155 TILEX + c * 7, 0, 6, 10,
14156 TILEX * x + 6 + j * 7,
14157 TILEY * y + 11 + yoffset_ce);
14159 BlitBitmap(src_bitmap, bitmap,
14160 TILEX + c * 8, TILEY, 6, 10,
14161 TILEX * 16 + TILEX * x + 6 + j * 8,
14162 TILEY * y + 10 + yoffset_ce);
14168 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14175 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14176 TILEX * x, TILEY * y + yoffset_ge);
14178 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14180 TILEX * x + TILEX * 16,
14181 TILEY * y + yoffset_ge);
14183 for (j = 1; j >= 0; j--)
14187 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14188 TILEX * x + 6 + j * 10,
14189 TILEY * y + 11 + yoffset_ge);
14191 BlitBitmap(src_bitmap, bitmap,
14192 TILEX + c * 8, TILEY + 12, 6, 10,
14193 TILEX * 16 + TILEX * x + 10 + j * 8,
14194 TILEY * y + 10 + yoffset_ge);
14200 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14201 Fail("cannot save CE graphics file '%s'", dst_filename);
14203 FreeBitmap(bitmap);
14205 CloseAllAndExit(0);