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
579 // (the following values are related to various game elements)
583 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
584 &li.score[SC_EMERALD], 10
589 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
590 &li.score[SC_DIAMOND], 10
595 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
596 &li.score[SC_BUG], 10
601 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
602 &li.score[SC_SPACESHIP], 10
607 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
608 &li.score[SC_PACMAN], 10
613 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
614 &li.score[SC_NUT], 10
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
620 &li.score[SC_DYNAMITE], 10
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_KEY], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_PEARL], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_CRYSTAL], 10
643 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
644 &li.amoeba_content, EL_DIAMOND
648 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
653 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
654 &li.grow_into_diggable, TRUE
659 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
660 &li.yamyam_content, EL_ROCK, NULL,
661 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
665 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
666 &li.score[SC_YAMYAM], 10
671 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 &li.score[SC_ROBOT], 10
676 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.time_magic_wall, 10
694 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
695 &li.game_of_life[0], 2
699 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
700 &li.game_of_life[1], 3
704 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
705 &li.game_of_life[2], 3
709 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
710 &li.game_of_life[3], 3
714 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
715 &li.use_life_bugs, FALSE
720 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
725 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
730 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
735 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
740 EL_TIMEGATE_SWITCH, -1,
741 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
742 &li.time_timegate, 10
746 EL_LIGHT_SWITCH_ACTIVE, -1,
747 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
752 EL_SHIELD_NORMAL, -1,
753 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
754 &li.shield_normal_time, 10
757 EL_SHIELD_NORMAL, -1,
758 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
759 &li.score[SC_SHIELD], 10
763 EL_SHIELD_DEADLY, -1,
764 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 &li.shield_deadly_time, 10
768 EL_SHIELD_DEADLY, -1,
769 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
770 &li.score[SC_SHIELD], 10
775 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
781 &li.extra_time_score, 10
785 EL_TIME_ORB_FULL, -1,
786 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
787 &li.time_orb_time, 10
790 EL_TIME_ORB_FULL, -1,
791 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
792 &li.use_time_orb_bug, FALSE
797 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
798 &li.use_spring_bug, FALSE
803 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
804 &li.android_move_time, 10
808 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
809 &li.android_clone_time, 10
812 EL_EMC_ANDROID, SAVE_CONF_NEVER,
813 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
814 &li.android_clone_element[0], EL_EMPTY, NULL,
815 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
819 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
820 &li.android_clone_element[0], EL_EMPTY, NULL,
821 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
826 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
831 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
836 EL_EMC_MAGNIFIER, -1,
837 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
838 &li.magnify_score, 10
841 EL_EMC_MAGNIFIER, -1,
842 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 EL_EMC_MAGIC_BALL, -1,
853 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
854 &li.ball_random, FALSE
857 EL_EMC_MAGIC_BALL, -1,
858 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
859 &li.ball_active_initial, FALSE
862 EL_EMC_MAGIC_BALL, -1,
863 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
864 &li.ball_content, EL_EMPTY, NULL,
865 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
869 EL_SOKOBAN_FIELD_EMPTY, -1,
870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
871 &li.sb_fields_needed, TRUE
875 EL_SOKOBAN_OBJECT, -1,
876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
877 &li.sb_objects_needed, TRUE
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
883 &li.mm_laser_red, FALSE
887 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
888 &li.mm_laser_green, FALSE
892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
893 &li.mm_laser_blue, TRUE
898 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
899 &li.df_laser_red, TRUE
903 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
904 &li.df_laser_green, TRUE
908 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
909 &li.df_laser_blue, FALSE
913 EL_MM_FUSE_ACTIVE, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
930 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
931 &li.mm_ball_choice_mode, ANIM_RANDOM
935 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
936 &li.mm_ball_content, EL_EMPTY, NULL,
937 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
941 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
942 &li.rotate_mm_ball_content, TRUE
946 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
947 &li.explode_mm_ball, FALSE
951 EL_MM_STEEL_BLOCK, -1,
952 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
953 &li.mm_time_block, 75
957 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
958 &li.score[SC_ELEM_BONUS], 10
961 // ---------- unused values -------------------------------------------------
964 EL_UNKNOWN, SAVE_CONF_NEVER,
965 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
966 &li.score[SC_UNKNOWN_15], 10
976 static struct LevelFileConfigInfo chunk_config_NOTE[] =
980 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
981 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
985 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
986 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
991 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
992 &xx_envelope.autowrap, FALSE
996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
997 &xx_envelope.centered, FALSE
1002 TYPE_STRING, CONF_VALUE_BYTES(1),
1003 &xx_envelope.text, -1, NULL,
1004 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1005 &xx_default_string_empty[0]
1015 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1019 TYPE_STRING, CONF_VALUE_BYTES(1),
1020 &xx_ei.description[0], -1,
1021 &yy_ei.description[0],
1022 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1023 &xx_default_description[0]
1028 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1029 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1030 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1032 #if ENABLE_RESERVED_CODE
1033 // (reserved for later use)
1036 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1037 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1038 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1044 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1045 &xx_ei.use_gfx_element, FALSE,
1046 &yy_ei.use_gfx_element
1050 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1051 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1052 &yy_ei.gfx_element_initial
1057 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1058 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1059 &yy_ei.access_direction
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1065 &xx_ei.collect_score_initial, 10,
1066 &yy_ei.collect_score_initial
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1071 &xx_ei.collect_count_initial, 1,
1072 &yy_ei.collect_count_initial
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1078 &xx_ei.ce_value_fixed_initial, 0,
1079 &yy_ei.ce_value_fixed_initial
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1084 &xx_ei.ce_value_random_initial, 0,
1085 &yy_ei.ce_value_random_initial
1089 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1090 &xx_ei.use_last_ce_value, FALSE,
1091 &yy_ei.use_last_ce_value
1096 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1097 &xx_ei.push_delay_fixed, 8,
1098 &yy_ei.push_delay_fixed
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1103 &xx_ei.push_delay_random, 8,
1104 &yy_ei.push_delay_random
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1109 &xx_ei.drop_delay_fixed, 0,
1110 &yy_ei.drop_delay_fixed
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1115 &xx_ei.drop_delay_random, 0,
1116 &yy_ei.drop_delay_random
1120 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1121 &xx_ei.move_delay_fixed, 0,
1122 &yy_ei.move_delay_fixed
1126 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1127 &xx_ei.move_delay_random, 0,
1128 &yy_ei.move_delay_random
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1133 &xx_ei.step_delay_fixed, 0,
1134 &yy_ei.step_delay_fixed
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1139 &xx_ei.step_delay_random, 0,
1140 &yy_ei.step_delay_random
1145 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1146 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1151 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1152 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1153 &yy_ei.move_direction_initial
1157 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1158 &xx_ei.move_stepsize, TILEX / 8,
1159 &yy_ei.move_stepsize
1164 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1165 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1166 &yy_ei.move_enter_element
1170 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1171 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1172 &yy_ei.move_leave_element
1176 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1177 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1178 &yy_ei.move_leave_type
1183 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1184 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1185 &yy_ei.slippery_type
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1191 &xx_ei.explosion_type, EXPLODES_3X3,
1192 &yy_ei.explosion_type
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1197 &xx_ei.explosion_delay, 16,
1198 &yy_ei.explosion_delay
1202 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1203 &xx_ei.ignition_delay, 8,
1204 &yy_ei.ignition_delay
1209 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1210 &xx_ei.content, EL_EMPTY_SPACE,
1212 &xx_num_contents, 1, 1
1215 // ---------- "num_change_pages" must be the last entry ---------------------
1218 -1, SAVE_CONF_ALWAYS,
1219 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1220 &xx_ei.num_change_pages, 1,
1221 &yy_ei.num_change_pages
1232 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1234 // ---------- "current_change_page" must be the first entry -----------------
1237 -1, SAVE_CONF_ALWAYS,
1238 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1239 &xx_current_change_page, -1
1242 // ---------- (the remaining entries can be in any order) -------------------
1246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1247 &xx_change.can_change, FALSE
1252 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1253 &xx_event_bits[0], 0
1257 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1258 &xx_event_bits[1], 0
1263 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1264 &xx_change.trigger_player, CH_PLAYER_ANY
1268 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1269 &xx_change.trigger_side, CH_SIDE_ANY
1273 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1274 &xx_change.trigger_page, CH_PAGE_ANY
1279 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1280 &xx_change.target_element, EL_EMPTY_SPACE
1285 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1286 &xx_change.delay_fixed, 0
1290 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1291 &xx_change.delay_random, 0
1295 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1296 &xx_change.delay_frames, FRAMES_PER_SECOND
1301 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1302 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1308 &xx_change.explode, FALSE
1312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1313 &xx_change.use_target_content, FALSE
1317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1318 &xx_change.only_if_complete, FALSE
1322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1323 &xx_change.use_random_replace, FALSE
1327 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1328 &xx_change.random_percentage, 100
1332 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1333 &xx_change.replace_when, CP_WHEN_EMPTY
1338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1339 &xx_change.has_action, FALSE
1343 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1344 &xx_change.action_type, CA_NO_ACTION
1348 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1349 &xx_change.action_mode, CA_MODE_UNDEFINED
1353 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1354 &xx_change.action_arg, CA_ARG_UNDEFINED
1359 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1360 &xx_change.action_element, EL_EMPTY_SPACE
1365 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1366 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1367 &xx_num_contents, 1, 1
1377 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1381 TYPE_STRING, CONF_VALUE_BYTES(1),
1382 &xx_ei.description[0], -1, NULL,
1383 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1384 &xx_default_description[0]
1389 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1390 &xx_ei.use_gfx_element, FALSE
1394 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1395 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1400 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1401 &xx_group.choice_mode, ANIM_RANDOM
1406 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1407 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1408 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1418 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1423 &xx_ei.use_gfx_element, FALSE
1427 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1428 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1438 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1443 &li.block_snap_field, TRUE
1447 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1448 &li.continuous_snapping, TRUE
1452 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1453 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1458 &li.use_start_element[0], FALSE
1462 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1463 &li.start_element[0], EL_PLAYER_1
1467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1468 &li.use_artwork_element[0], FALSE
1472 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1473 &li.artwork_element[0], EL_PLAYER_1
1477 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1478 &li.use_explosion_element[0], FALSE
1482 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1483 &li.explosion_element[0], EL_PLAYER_1
1498 filetype_id_list[] =
1500 { LEVEL_FILE_TYPE_RND, "RND" },
1501 { LEVEL_FILE_TYPE_BD, "BD" },
1502 { LEVEL_FILE_TYPE_EM, "EM" },
1503 { LEVEL_FILE_TYPE_SP, "SP" },
1504 { LEVEL_FILE_TYPE_DX, "DX" },
1505 { LEVEL_FILE_TYPE_SB, "SB" },
1506 { LEVEL_FILE_TYPE_DC, "DC" },
1507 { LEVEL_FILE_TYPE_MM, "MM" },
1508 { LEVEL_FILE_TYPE_MM, "DF" },
1513 // ============================================================================
1514 // level file functions
1515 // ============================================================================
1517 static boolean check_special_flags(char *flag)
1519 if (strEqual(options.special_flags, flag) ||
1520 strEqual(leveldir_current->special_flags, flag))
1526 static struct DateInfo getCurrentDate(void)
1528 time_t epoch_seconds = time(NULL);
1529 struct tm *now = localtime(&epoch_seconds);
1530 struct DateInfo date;
1532 date.year = now->tm_year + 1900;
1533 date.month = now->tm_mon + 1;
1534 date.day = now->tm_mday;
1536 date.src = DATE_SRC_CLOCK;
1541 static void resetEventFlags(struct ElementChangeInfo *change)
1545 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1546 change->has_event[i] = FALSE;
1549 static void resetEventBits(void)
1553 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1554 xx_event_bits[i] = 0;
1557 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1561 /* important: only change event flag if corresponding event bit is set
1562 (this is because all xx_event_bits[] values are loaded separately,
1563 and all xx_event_bits[] values are set back to zero before loading
1564 another value xx_event_bits[x] (each value representing 32 flags)) */
1566 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1567 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1568 change->has_event[i] = TRUE;
1571 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1575 /* in contrast to the above function setEventFlagsFromEventBits(), it
1576 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1577 depending on the corresponding change->has_event[i] values here, as
1578 all xx_event_bits[] values are reset in resetEventBits() before */
1580 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1581 if (change->has_event[i])
1582 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1585 static char *getDefaultElementDescription(struct ElementInfo *ei)
1587 static char description[MAX_ELEMENT_NAME_LEN + 1];
1588 char *default_description = (ei->custom_description != NULL ?
1589 ei->custom_description :
1590 ei->editor_description);
1593 // always start with reliable default values
1594 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1595 description[i] = '\0';
1597 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1598 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1600 return &description[0];
1603 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1605 char *default_description = getDefaultElementDescription(ei);
1608 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1609 ei->description[i] = default_description[i];
1612 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1616 for (i = 0; conf[i].data_type != -1; i++)
1618 int default_value = conf[i].default_value;
1619 int data_type = conf[i].data_type;
1620 int conf_type = conf[i].conf_type;
1621 int byte_mask = conf_type & CONF_MASK_BYTES;
1623 if (byte_mask == CONF_MASK_MULTI_BYTES)
1625 int default_num_entities = conf[i].default_num_entities;
1626 int max_num_entities = conf[i].max_num_entities;
1628 *(int *)(conf[i].num_entities) = default_num_entities;
1630 if (data_type == TYPE_STRING)
1632 char *default_string = conf[i].default_string;
1633 char *string = (char *)(conf[i].value);
1635 strncpy(string, default_string, max_num_entities);
1637 else if (data_type == TYPE_ELEMENT_LIST)
1639 int *element_array = (int *)(conf[i].value);
1642 for (j = 0; j < max_num_entities; j++)
1643 element_array[j] = default_value;
1645 else if (data_type == TYPE_CONTENT_LIST)
1647 struct Content *content = (struct Content *)(conf[i].value);
1650 for (c = 0; c < max_num_entities; c++)
1651 for (y = 0; y < 3; y++)
1652 for (x = 0; x < 3; x++)
1653 content[c].e[x][y] = default_value;
1656 else // constant size configuration data (1, 2 or 4 bytes)
1658 if (data_type == TYPE_BOOLEAN)
1659 *(boolean *)(conf[i].value) = default_value;
1661 *(int *) (conf[i].value) = default_value;
1666 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1670 for (i = 0; conf[i].data_type != -1; i++)
1672 int data_type = conf[i].data_type;
1673 int conf_type = conf[i].conf_type;
1674 int byte_mask = conf_type & CONF_MASK_BYTES;
1676 if (byte_mask == CONF_MASK_MULTI_BYTES)
1678 int max_num_entities = conf[i].max_num_entities;
1680 if (data_type == TYPE_STRING)
1682 char *string = (char *)(conf[i].value);
1683 char *string_copy = (char *)(conf[i].value_copy);
1685 strncpy(string_copy, string, max_num_entities);
1687 else if (data_type == TYPE_ELEMENT_LIST)
1689 int *element_array = (int *)(conf[i].value);
1690 int *element_array_copy = (int *)(conf[i].value_copy);
1693 for (j = 0; j < max_num_entities; j++)
1694 element_array_copy[j] = element_array[j];
1696 else if (data_type == TYPE_CONTENT_LIST)
1698 struct Content *content = (struct Content *)(conf[i].value);
1699 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1702 for (c = 0; c < max_num_entities; c++)
1703 for (y = 0; y < 3; y++)
1704 for (x = 0; x < 3; x++)
1705 content_copy[c].e[x][y] = content[c].e[x][y];
1708 else // constant size configuration data (1, 2 or 4 bytes)
1710 if (data_type == TYPE_BOOLEAN)
1711 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1713 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1718 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1722 xx_ei = *ei_from; // copy element data into temporary buffer
1723 yy_ei = *ei_to; // copy element data into temporary buffer
1725 copyConfigFromConfigList(chunk_config_CUSX_base);
1730 // ---------- reinitialize and copy change pages ----------
1732 ei_to->num_change_pages = ei_from->num_change_pages;
1733 ei_to->current_change_page = ei_from->current_change_page;
1735 setElementChangePages(ei_to, ei_to->num_change_pages);
1737 for (i = 0; i < ei_to->num_change_pages; i++)
1738 ei_to->change_page[i] = ei_from->change_page[i];
1740 // ---------- copy group element info ----------
1741 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1742 *ei_to->group = *ei_from->group;
1744 // mark this custom element as modified
1745 ei_to->modified_settings = TRUE;
1748 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1750 int change_page_size = sizeof(struct ElementChangeInfo);
1752 ei->num_change_pages = MAX(1, change_pages);
1755 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1757 if (ei->current_change_page >= ei->num_change_pages)
1758 ei->current_change_page = ei->num_change_pages - 1;
1760 ei->change = &ei->change_page[ei->current_change_page];
1763 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1765 xx_change = *change; // copy change data into temporary buffer
1767 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1769 *change = xx_change;
1771 resetEventFlags(change);
1773 change->direct_action = 0;
1774 change->other_action = 0;
1776 change->pre_change_function = NULL;
1777 change->change_function = NULL;
1778 change->post_change_function = NULL;
1781 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1785 li = *level; // copy level data into temporary buffer
1786 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1787 *level = li; // copy temporary buffer back to level data
1789 setLevelInfoToDefaults_BD();
1790 setLevelInfoToDefaults_EM();
1791 setLevelInfoToDefaults_SP();
1792 setLevelInfoToDefaults_MM();
1794 level->native_bd_level = &native_bd_level;
1795 level->native_em_level = &native_em_level;
1796 level->native_sp_level = &native_sp_level;
1797 level->native_mm_level = &native_mm_level;
1799 level->file_version = FILE_VERSION_ACTUAL;
1800 level->game_version = GAME_VERSION_ACTUAL;
1802 level->creation_date = getCurrentDate();
1804 level->encoding_16bit_field = TRUE;
1805 level->encoding_16bit_yamyam = TRUE;
1806 level->encoding_16bit_amoeba = TRUE;
1808 // clear level name and level author string buffers
1809 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1810 level->name[i] = '\0';
1811 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1812 level->author[i] = '\0';
1814 // set level name and level author to default values
1815 strcpy(level->name, NAMELESS_LEVEL_NAME);
1816 strcpy(level->author, ANONYMOUS_NAME);
1818 // set level playfield to playable default level with player and exit
1819 for (x = 0; x < MAX_LEV_FIELDX; x++)
1820 for (y = 0; y < MAX_LEV_FIELDY; y++)
1821 level->field[x][y] = EL_SAND;
1823 level->field[0][0] = EL_PLAYER_1;
1824 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1826 BorderElement = EL_STEELWALL;
1828 // detect custom elements when loading them
1829 level->file_has_custom_elements = FALSE;
1831 // set all bug compatibility flags to "false" => do not emulate this bug
1832 level->use_action_after_change_bug = FALSE;
1834 if (leveldir_current)
1836 // try to determine better author name than 'anonymous'
1837 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1839 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1840 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1844 switch (LEVELCLASS(leveldir_current))
1846 case LEVELCLASS_TUTORIAL:
1847 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1850 case LEVELCLASS_CONTRIB:
1851 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1852 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1855 case LEVELCLASS_PRIVATE:
1856 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1857 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1861 // keep default value
1868 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1870 static boolean clipboard_elements_initialized = FALSE;
1873 InitElementPropertiesStatic();
1875 li = *level; // copy level data into temporary buffer
1876 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1877 *level = li; // copy temporary buffer back to level data
1879 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1882 struct ElementInfo *ei = &element_info[element];
1884 if (element == EL_MM_GRAY_BALL)
1886 struct LevelInfo_MM *level_mm = level->native_mm_level;
1889 for (j = 0; j < level->num_mm_ball_contents; j++)
1890 level->mm_ball_content[j] =
1891 map_element_MM_to_RND(level_mm->ball_content[j]);
1894 // never initialize clipboard elements after the very first time
1895 // (to be able to use clipboard elements between several levels)
1896 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1899 if (IS_ENVELOPE(element))
1901 int envelope_nr = element - EL_ENVELOPE_1;
1903 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1905 level->envelope[envelope_nr] = xx_envelope;
1908 if (IS_CUSTOM_ELEMENT(element) ||
1909 IS_GROUP_ELEMENT(element) ||
1910 IS_INTERNAL_ELEMENT(element))
1912 xx_ei = *ei; // copy element data into temporary buffer
1914 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1919 setElementChangePages(ei, 1);
1920 setElementChangeInfoToDefaults(ei->change);
1922 if (IS_CUSTOM_ELEMENT(element) ||
1923 IS_GROUP_ELEMENT(element))
1925 setElementDescriptionToDefault(ei);
1927 ei->modified_settings = FALSE;
1930 if (IS_CUSTOM_ELEMENT(element) ||
1931 IS_INTERNAL_ELEMENT(element))
1933 // internal values used in level editor
1935 ei->access_type = 0;
1936 ei->access_layer = 0;
1937 ei->access_protected = 0;
1938 ei->walk_to_action = 0;
1939 ei->smash_targets = 0;
1942 ei->can_explode_by_fire = FALSE;
1943 ei->can_explode_smashed = FALSE;
1944 ei->can_explode_impact = FALSE;
1946 ei->current_change_page = 0;
1949 if (IS_GROUP_ELEMENT(element) ||
1950 IS_INTERNAL_ELEMENT(element))
1952 struct ElementGroupInfo *group;
1954 // initialize memory for list of elements in group
1955 if (ei->group == NULL)
1956 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1960 xx_group = *group; // copy group data into temporary buffer
1962 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1967 if (IS_EMPTY_ELEMENT(element) ||
1968 IS_INTERNAL_ELEMENT(element))
1970 xx_ei = *ei; // copy element data into temporary buffer
1972 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1978 clipboard_elements_initialized = TRUE;
1981 static void setLevelInfoToDefaults(struct LevelInfo *level,
1982 boolean level_info_only,
1983 boolean reset_file_status)
1985 setLevelInfoToDefaults_Level(level);
1987 if (!level_info_only)
1988 setLevelInfoToDefaults_Elements(level);
1990 if (reset_file_status)
1992 level->no_valid_file = FALSE;
1993 level->no_level_file = FALSE;
1996 level->changed = FALSE;
1999 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2001 level_file_info->nr = 0;
2002 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2003 level_file_info->packed = FALSE;
2005 setString(&level_file_info->basename, NULL);
2006 setString(&level_file_info->filename, NULL);
2009 int getMappedElement_SB(int, boolean);
2011 static void ActivateLevelTemplate(void)
2015 if (check_special_flags("load_xsb_to_ces"))
2017 // fill smaller playfields with padding "beyond border wall" elements
2018 if (level.fieldx < level_template.fieldx ||
2019 level.fieldy < level_template.fieldy)
2021 short field[level.fieldx][level.fieldy];
2022 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2023 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2024 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2025 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2027 // copy old playfield (which is smaller than the visible area)
2028 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2029 field[x][y] = level.field[x][y];
2031 // fill new, larger playfield with "beyond border wall" elements
2032 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2033 level.field[x][y] = getMappedElement_SB('_', TRUE);
2035 // copy the old playfield to the middle of the new playfield
2036 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2037 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2039 level.fieldx = new_fieldx;
2040 level.fieldy = new_fieldy;
2044 // Currently there is no special action needed to activate the template
2045 // data, because 'element_info' property settings overwrite the original
2046 // level data, while all other variables do not change.
2048 // Exception: 'from_level_template' elements in the original level playfield
2049 // are overwritten with the corresponding elements at the same position in
2050 // playfield from the level template.
2052 for (x = 0; x < level.fieldx; x++)
2053 for (y = 0; y < level.fieldy; y++)
2054 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2055 level.field[x][y] = level_template.field[x][y];
2057 if (check_special_flags("load_xsb_to_ces"))
2059 struct LevelInfo level_backup = level;
2061 // overwrite all individual level settings from template level settings
2062 level = level_template;
2064 // restore level file info
2065 level.file_info = level_backup.file_info;
2067 // restore playfield size
2068 level.fieldx = level_backup.fieldx;
2069 level.fieldy = level_backup.fieldy;
2071 // restore playfield content
2072 for (x = 0; x < level.fieldx; x++)
2073 for (y = 0; y < level.fieldy; y++)
2074 level.field[x][y] = level_backup.field[x][y];
2076 // restore name and author from individual level
2077 strcpy(level.name, level_backup.name);
2078 strcpy(level.author, level_backup.author);
2080 // restore flag "use_custom_template"
2081 level.use_custom_template = level_backup.use_custom_template;
2085 static boolean checkForPackageFromBasename_BD(char *basename)
2087 // check for native BD level file extensions
2088 if (!strSuffixLower(basename, ".bd") &&
2089 !strSuffixLower(basename, ".bdr") &&
2090 !strSuffixLower(basename, ".brc") &&
2091 !strSuffixLower(basename, ".gds"))
2094 // check for standard single-level BD files (like "001.bd")
2095 if (strSuffixLower(basename, ".bd") &&
2096 strlen(basename) == 6 &&
2097 basename[0] >= '0' && basename[0] <= '9' &&
2098 basename[1] >= '0' && basename[1] <= '9' &&
2099 basename[2] >= '0' && basename[2] <= '9')
2102 // this is a level package in native BD file format
2106 static char *getLevelFilenameFromBasename(char *basename)
2108 static char *filename = NULL;
2110 checked_free(filename);
2112 filename = getPath2(getCurrentLevelDir(), basename);
2117 static int getFileTypeFromBasename(char *basename)
2119 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2121 static char *filename = NULL;
2122 struct stat file_status;
2124 // ---------- try to determine file type from filename ----------
2126 // check for typical filename of a Supaplex level package file
2127 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2128 return LEVEL_FILE_TYPE_SP;
2130 // check for typical filename of a Diamond Caves II level package file
2131 if (strSuffixLower(basename, ".dc") ||
2132 strSuffixLower(basename, ".dc2"))
2133 return LEVEL_FILE_TYPE_DC;
2135 // check for typical filename of a Sokoban level package file
2136 if (strSuffixLower(basename, ".xsb") &&
2137 strchr(basename, '%') == NULL)
2138 return LEVEL_FILE_TYPE_SB;
2140 // check for typical filename of a Boulder Dash (GDash) level package file
2141 if (checkForPackageFromBasename_BD(basename))
2142 return LEVEL_FILE_TYPE_BD;
2144 // ---------- try to determine file type from filesize ----------
2146 checked_free(filename);
2147 filename = getPath2(getCurrentLevelDir(), basename);
2149 if (stat(filename, &file_status) == 0)
2151 // check for typical filesize of a Supaplex level package file
2152 if (file_status.st_size == 170496)
2153 return LEVEL_FILE_TYPE_SP;
2156 return LEVEL_FILE_TYPE_UNKNOWN;
2159 static int getFileTypeFromMagicBytes(char *filename, int type)
2163 if ((file = openFile(filename, MODE_READ)))
2165 char chunk_name[CHUNK_ID_LEN + 1];
2167 getFileChunkBE(file, chunk_name, NULL);
2169 if (strEqual(chunk_name, "MMII") ||
2170 strEqual(chunk_name, "MIRR"))
2171 type = LEVEL_FILE_TYPE_MM;
2179 static boolean checkForPackageFromBasename(char *basename)
2181 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2182 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2184 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2187 static char *getSingleLevelBasenameExt(int nr, char *extension)
2189 static char basename[MAX_FILENAME_LEN];
2192 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2194 sprintf(basename, "%03d.%s", nr, extension);
2199 static char *getSingleLevelBasename(int nr)
2201 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2204 static char *getPackedLevelBasename(int type)
2206 static char basename[MAX_FILENAME_LEN];
2207 char *directory = getCurrentLevelDir();
2209 DirectoryEntry *dir_entry;
2211 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2213 if ((dir = openDirectory(directory)) == NULL)
2215 Warn("cannot read current level directory '%s'", directory);
2220 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2222 char *entry_basename = dir_entry->basename;
2223 int entry_type = getFileTypeFromBasename(entry_basename);
2225 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2227 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2230 strcpy(basename, entry_basename);
2237 closeDirectory(dir);
2242 static char *getSingleLevelFilename(int nr)
2244 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2247 #if ENABLE_UNUSED_CODE
2248 static char *getPackedLevelFilename(int type)
2250 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2254 char *getDefaultLevelFilename(int nr)
2256 return getSingleLevelFilename(nr);
2259 #if ENABLE_UNUSED_CODE
2260 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2264 lfi->packed = FALSE;
2266 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2267 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2271 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2272 int type, char *format, ...)
2274 static char basename[MAX_FILENAME_LEN];
2277 va_start(ap, format);
2278 vsprintf(basename, format, ap);
2282 lfi->packed = FALSE;
2284 setString(&lfi->basename, basename);
2285 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2288 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2294 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2295 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2298 static int getFiletypeFromID(char *filetype_id)
2300 char *filetype_id_lower;
2301 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2304 if (filetype_id == NULL)
2305 return LEVEL_FILE_TYPE_UNKNOWN;
2307 filetype_id_lower = getStringToLower(filetype_id);
2309 for (i = 0; filetype_id_list[i].id != NULL; i++)
2311 char *id_lower = getStringToLower(filetype_id_list[i].id);
2313 if (strEqual(filetype_id_lower, id_lower))
2314 filetype = filetype_id_list[i].filetype;
2318 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2322 free(filetype_id_lower);
2327 char *getLocalLevelTemplateFilename(void)
2329 return getDefaultLevelFilename(-1);
2332 char *getGlobalLevelTemplateFilename(void)
2334 // global variable "leveldir_current" must be modified in the loop below
2335 LevelDirTree *leveldir_current_last = leveldir_current;
2336 char *filename = NULL;
2338 // check for template level in path from current to topmost tree node
2340 while (leveldir_current != NULL)
2342 filename = getDefaultLevelFilename(-1);
2344 if (fileExists(filename))
2347 leveldir_current = leveldir_current->node_parent;
2350 // restore global variable "leveldir_current" modified in above loop
2351 leveldir_current = leveldir_current_last;
2356 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2360 // special case: level number is negative => check for level template file
2363 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2364 getSingleLevelBasename(-1));
2366 // replace local level template filename with global template filename
2367 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2369 // no fallback if template file not existing
2373 // special case: check for file name/pattern specified in "levelinfo.conf"
2374 if (leveldir_current->level_filename != NULL)
2376 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2378 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2379 leveldir_current->level_filename, nr);
2381 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2383 if (fileExists(lfi->filename))
2386 else if (leveldir_current->level_filetype != NULL)
2388 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2390 // check for specified native level file with standard file name
2391 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2392 "%03d.%s", nr, LEVELFILE_EXTENSION);
2393 if (fileExists(lfi->filename))
2397 // check for native Rocks'n'Diamonds level file
2398 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2399 "%03d.%s", nr, LEVELFILE_EXTENSION);
2400 if (fileExists(lfi->filename))
2403 // check for native Boulder Dash level file
2404 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2405 if (fileExists(lfi->filename))
2408 // check for Emerald Mine level file (V1)
2409 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2410 'a' + (nr / 10) % 26, '0' + nr % 10);
2411 if (fileExists(lfi->filename))
2413 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2414 'A' + (nr / 10) % 26, '0' + nr % 10);
2415 if (fileExists(lfi->filename))
2418 // check for Emerald Mine level file (V2 to V5)
2419 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2420 if (fileExists(lfi->filename))
2423 // check for Emerald Mine level file (V6 / single mode)
2424 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2425 if (fileExists(lfi->filename))
2427 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2428 if (fileExists(lfi->filename))
2431 // check for Emerald Mine level file (V6 / teamwork mode)
2432 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2433 if (fileExists(lfi->filename))
2435 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2436 if (fileExists(lfi->filename))
2439 // check for various packed level file formats
2440 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2441 if (fileExists(lfi->filename))
2444 // no known level file found -- use default values (and fail later)
2445 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2446 "%03d.%s", nr, LEVELFILE_EXTENSION);
2449 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2451 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2452 lfi->type = getFileTypeFromBasename(lfi->basename);
2454 if (lfi->type == LEVEL_FILE_TYPE_RND)
2455 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2458 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2460 // always start with reliable default values
2461 setFileInfoToDefaults(level_file_info);
2463 level_file_info->nr = nr; // set requested level number
2465 determineLevelFileInfo_Filename(level_file_info);
2466 determineLevelFileInfo_Filetype(level_file_info);
2469 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2470 struct LevelFileInfo *lfi_to)
2472 lfi_to->nr = lfi_from->nr;
2473 lfi_to->type = lfi_from->type;
2474 lfi_to->packed = lfi_from->packed;
2476 setString(&lfi_to->basename, lfi_from->basename);
2477 setString(&lfi_to->filename, lfi_from->filename);
2480 // ----------------------------------------------------------------------------
2481 // functions for loading R'n'D level
2482 // ----------------------------------------------------------------------------
2484 int getMappedElement(int element)
2486 // remap some (historic, now obsolete) elements
2490 case EL_PLAYER_OBSOLETE:
2491 element = EL_PLAYER_1;
2494 case EL_KEY_OBSOLETE:
2498 case EL_EM_KEY_1_FILE_OBSOLETE:
2499 element = EL_EM_KEY_1;
2502 case EL_EM_KEY_2_FILE_OBSOLETE:
2503 element = EL_EM_KEY_2;
2506 case EL_EM_KEY_3_FILE_OBSOLETE:
2507 element = EL_EM_KEY_3;
2510 case EL_EM_KEY_4_FILE_OBSOLETE:
2511 element = EL_EM_KEY_4;
2514 case EL_ENVELOPE_OBSOLETE:
2515 element = EL_ENVELOPE_1;
2523 if (element >= NUM_FILE_ELEMENTS)
2525 Warn("invalid level element %d", element);
2527 element = EL_UNKNOWN;
2535 static int getMappedElementByVersion(int element, int game_version)
2537 // remap some elements due to certain game version
2539 if (game_version <= VERSION_IDENT(2,2,0,0))
2541 // map game font elements
2542 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2543 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2544 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2545 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2548 if (game_version < VERSION_IDENT(3,0,0,0))
2550 // map Supaplex gravity tube elements
2551 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2552 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2553 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2554 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2561 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2563 level->file_version = getFileVersion(file);
2564 level->game_version = getFileVersion(file);
2569 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2571 level->creation_date.year = getFile16BitBE(file);
2572 level->creation_date.month = getFile8Bit(file);
2573 level->creation_date.day = getFile8Bit(file);
2575 level->creation_date.src = DATE_SRC_LEVELFILE;
2580 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2582 int initial_player_stepsize;
2583 int initial_player_gravity;
2586 level->fieldx = getFile8Bit(file);
2587 level->fieldy = getFile8Bit(file);
2589 level->time = getFile16BitBE(file);
2590 level->gems_needed = getFile16BitBE(file);
2592 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2593 level->name[i] = getFile8Bit(file);
2594 level->name[MAX_LEVEL_NAME_LEN] = 0;
2596 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2597 level->score[i] = getFile8Bit(file);
2599 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2600 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2601 for (y = 0; y < 3; y++)
2602 for (x = 0; x < 3; x++)
2603 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2605 level->amoeba_speed = getFile8Bit(file);
2606 level->time_magic_wall = getFile8Bit(file);
2607 level->time_wheel = getFile8Bit(file);
2608 level->amoeba_content = getMappedElement(getFile8Bit(file));
2610 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2613 for (i = 0; i < MAX_PLAYERS; i++)
2614 level->initial_player_stepsize[i] = initial_player_stepsize;
2616 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2618 for (i = 0; i < MAX_PLAYERS; i++)
2619 level->initial_player_gravity[i] = initial_player_gravity;
2621 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2622 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2624 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2626 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2627 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2628 level->can_move_into_acid_bits = getFile32BitBE(file);
2629 level->dont_collide_with_bits = getFile8Bit(file);
2631 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2632 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2634 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2635 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2636 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2638 level->game_engine_type = getFile8Bit(file);
2640 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2645 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2649 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2650 level->name[i] = getFile8Bit(file);
2651 level->name[MAX_LEVEL_NAME_LEN] = 0;
2656 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2660 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2661 level->author[i] = getFile8Bit(file);
2662 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2667 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2670 int chunk_size_expected = level->fieldx * level->fieldy;
2672 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2673 stored with 16-bit encoding (and should be twice as big then).
2674 Even worse, playfield data was stored 16-bit when only yamyam content
2675 contained 16-bit elements and vice versa. */
2677 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2678 chunk_size_expected *= 2;
2680 if (chunk_size_expected != chunk_size)
2682 ReadUnusedBytesFromFile(file, chunk_size);
2683 return chunk_size_expected;
2686 for (y = 0; y < level->fieldy; y++)
2687 for (x = 0; x < level->fieldx; x++)
2688 level->field[x][y] =
2689 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2694 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2697 int header_size = 4;
2698 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2699 int chunk_size_expected = header_size + content_size;
2701 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2702 stored with 16-bit encoding (and should be twice as big then).
2703 Even worse, playfield data was stored 16-bit when only yamyam content
2704 contained 16-bit elements and vice versa. */
2706 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2707 chunk_size_expected += content_size;
2709 if (chunk_size_expected != chunk_size)
2711 ReadUnusedBytesFromFile(file, chunk_size);
2712 return chunk_size_expected;
2716 level->num_yamyam_contents = getFile8Bit(file);
2720 // correct invalid number of content fields -- should never happen
2721 if (level->num_yamyam_contents < 1 ||
2722 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2723 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2725 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2726 for (y = 0; y < 3; y++)
2727 for (x = 0; x < 3; x++)
2728 level->yamyam_content[i].e[x][y] =
2729 getMappedElement(level->encoding_16bit_field ?
2730 getFile16BitBE(file) : getFile8Bit(file));
2734 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2739 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2741 element = getMappedElement(getFile16BitBE(file));
2742 num_contents = getFile8Bit(file);
2744 getFile8Bit(file); // content x size (unused)
2745 getFile8Bit(file); // content y size (unused)
2747 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2749 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2750 for (y = 0; y < 3; y++)
2751 for (x = 0; x < 3; x++)
2752 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2754 // correct invalid number of content fields -- should never happen
2755 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2756 num_contents = STD_ELEMENT_CONTENTS;
2758 if (element == EL_YAMYAM)
2760 level->num_yamyam_contents = num_contents;
2762 for (i = 0; i < num_contents; i++)
2763 for (y = 0; y < 3; y++)
2764 for (x = 0; x < 3; x++)
2765 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2767 else if (element == EL_BD_AMOEBA)
2769 level->amoeba_content = content_array[0][0][0];
2773 Warn("cannot load content for element '%d'", element);
2779 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2785 int chunk_size_expected;
2787 element = getMappedElement(getFile16BitBE(file));
2788 if (!IS_ENVELOPE(element))
2789 element = EL_ENVELOPE_1;
2791 envelope_nr = element - EL_ENVELOPE_1;
2793 envelope_len = getFile16BitBE(file);
2795 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2796 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2798 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2800 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2801 if (chunk_size_expected != chunk_size)
2803 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2804 return chunk_size_expected;
2807 for (i = 0; i < envelope_len; i++)
2808 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2813 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2815 int num_changed_custom_elements = getFile16BitBE(file);
2816 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2819 if (chunk_size_expected != chunk_size)
2821 ReadUnusedBytesFromFile(file, chunk_size - 2);
2822 return chunk_size_expected;
2825 for (i = 0; i < num_changed_custom_elements; i++)
2827 int element = getMappedElement(getFile16BitBE(file));
2828 int properties = getFile32BitBE(file);
2830 if (IS_CUSTOM_ELEMENT(element))
2831 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2833 Warn("invalid custom element number %d", element);
2835 // older game versions that wrote level files with CUS1 chunks used
2836 // different default push delay values (not yet stored in level file)
2837 element_info[element].push_delay_fixed = 2;
2838 element_info[element].push_delay_random = 8;
2841 level->file_has_custom_elements = TRUE;
2846 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2848 int num_changed_custom_elements = getFile16BitBE(file);
2849 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2852 if (chunk_size_expected != chunk_size)
2854 ReadUnusedBytesFromFile(file, chunk_size - 2);
2855 return chunk_size_expected;
2858 for (i = 0; i < num_changed_custom_elements; i++)
2860 int element = getMappedElement(getFile16BitBE(file));
2861 int custom_target_element = getMappedElement(getFile16BitBE(file));
2863 if (IS_CUSTOM_ELEMENT(element))
2864 element_info[element].change->target_element = custom_target_element;
2866 Warn("invalid custom element number %d", element);
2869 level->file_has_custom_elements = TRUE;
2874 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2876 int num_changed_custom_elements = getFile16BitBE(file);
2877 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2880 if (chunk_size_expected != chunk_size)
2882 ReadUnusedBytesFromFile(file, chunk_size - 2);
2883 return chunk_size_expected;
2886 for (i = 0; i < num_changed_custom_elements; i++)
2888 int element = getMappedElement(getFile16BitBE(file));
2889 struct ElementInfo *ei = &element_info[element];
2890 unsigned int event_bits;
2892 if (!IS_CUSTOM_ELEMENT(element))
2894 Warn("invalid custom element number %d", element);
2896 element = EL_INTERNAL_DUMMY;
2899 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2900 ei->description[j] = getFile8Bit(file);
2901 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2903 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2905 // some free bytes for future properties and padding
2906 ReadUnusedBytesFromFile(file, 7);
2908 ei->use_gfx_element = getFile8Bit(file);
2909 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2911 ei->collect_score_initial = getFile8Bit(file);
2912 ei->collect_count_initial = getFile8Bit(file);
2914 ei->push_delay_fixed = getFile16BitBE(file);
2915 ei->push_delay_random = getFile16BitBE(file);
2916 ei->move_delay_fixed = getFile16BitBE(file);
2917 ei->move_delay_random = getFile16BitBE(file);
2919 ei->move_pattern = getFile16BitBE(file);
2920 ei->move_direction_initial = getFile8Bit(file);
2921 ei->move_stepsize = getFile8Bit(file);
2923 for (y = 0; y < 3; y++)
2924 for (x = 0; x < 3; x++)
2925 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2927 // bits 0 - 31 of "has_event[]"
2928 event_bits = getFile32BitBE(file);
2929 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2930 if (event_bits & (1u << j))
2931 ei->change->has_event[j] = TRUE;
2933 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2935 ei->change->delay_fixed = getFile16BitBE(file);
2936 ei->change->delay_random = getFile16BitBE(file);
2937 ei->change->delay_frames = getFile16BitBE(file);
2939 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2941 ei->change->explode = getFile8Bit(file);
2942 ei->change->use_target_content = getFile8Bit(file);
2943 ei->change->only_if_complete = getFile8Bit(file);
2944 ei->change->use_random_replace = getFile8Bit(file);
2946 ei->change->random_percentage = getFile8Bit(file);
2947 ei->change->replace_when = getFile8Bit(file);
2949 for (y = 0; y < 3; y++)
2950 for (x = 0; x < 3; x++)
2951 ei->change->target_content.e[x][y] =
2952 getMappedElement(getFile16BitBE(file));
2954 ei->slippery_type = getFile8Bit(file);
2956 // some free bytes for future properties and padding
2957 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2959 // mark that this custom element has been modified
2960 ei->modified_settings = TRUE;
2963 level->file_has_custom_elements = TRUE;
2968 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2970 struct ElementInfo *ei;
2971 int chunk_size_expected;
2975 // ---------- custom element base property values (96 bytes) ----------------
2977 element = getMappedElement(getFile16BitBE(file));
2979 if (!IS_CUSTOM_ELEMENT(element))
2981 Warn("invalid custom element number %d", element);
2983 ReadUnusedBytesFromFile(file, chunk_size - 2);
2988 ei = &element_info[element];
2990 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2991 ei->description[i] = getFile8Bit(file);
2992 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2994 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2996 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2998 ei->num_change_pages = getFile8Bit(file);
3000 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3001 if (chunk_size_expected != chunk_size)
3003 ReadUnusedBytesFromFile(file, chunk_size - 43);
3004 return chunk_size_expected;
3007 ei->ce_value_fixed_initial = getFile16BitBE(file);
3008 ei->ce_value_random_initial = getFile16BitBE(file);
3009 ei->use_last_ce_value = getFile8Bit(file);
3011 ei->use_gfx_element = getFile8Bit(file);
3012 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3014 ei->collect_score_initial = getFile8Bit(file);
3015 ei->collect_count_initial = getFile8Bit(file);
3017 ei->drop_delay_fixed = getFile8Bit(file);
3018 ei->push_delay_fixed = getFile8Bit(file);
3019 ei->drop_delay_random = getFile8Bit(file);
3020 ei->push_delay_random = getFile8Bit(file);
3021 ei->move_delay_fixed = getFile16BitBE(file);
3022 ei->move_delay_random = getFile16BitBE(file);
3024 // bits 0 - 15 of "move_pattern" ...
3025 ei->move_pattern = getFile16BitBE(file);
3026 ei->move_direction_initial = getFile8Bit(file);
3027 ei->move_stepsize = getFile8Bit(file);
3029 ei->slippery_type = getFile8Bit(file);
3031 for (y = 0; y < 3; y++)
3032 for (x = 0; x < 3; x++)
3033 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3035 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3036 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3037 ei->move_leave_type = getFile8Bit(file);
3039 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3040 ei->move_pattern |= (getFile16BitBE(file) << 16);
3042 ei->access_direction = getFile8Bit(file);
3044 ei->explosion_delay = getFile8Bit(file);
3045 ei->ignition_delay = getFile8Bit(file);
3046 ei->explosion_type = getFile8Bit(file);
3048 // some free bytes for future custom property values and padding
3049 ReadUnusedBytesFromFile(file, 1);
3051 // ---------- change page property values (48 bytes) ------------------------
3053 setElementChangePages(ei, ei->num_change_pages);
3055 for (i = 0; i < ei->num_change_pages; i++)
3057 struct ElementChangeInfo *change = &ei->change_page[i];
3058 unsigned int event_bits;
3060 // always start with reliable default values
3061 setElementChangeInfoToDefaults(change);
3063 // bits 0 - 31 of "has_event[]" ...
3064 event_bits = getFile32BitBE(file);
3065 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3066 if (event_bits & (1u << j))
3067 change->has_event[j] = TRUE;
3069 change->target_element = getMappedElement(getFile16BitBE(file));
3071 change->delay_fixed = getFile16BitBE(file);
3072 change->delay_random = getFile16BitBE(file);
3073 change->delay_frames = getFile16BitBE(file);
3075 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3077 change->explode = getFile8Bit(file);
3078 change->use_target_content = getFile8Bit(file);
3079 change->only_if_complete = getFile8Bit(file);
3080 change->use_random_replace = getFile8Bit(file);
3082 change->random_percentage = getFile8Bit(file);
3083 change->replace_when = getFile8Bit(file);
3085 for (y = 0; y < 3; y++)
3086 for (x = 0; x < 3; x++)
3087 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3089 change->can_change = getFile8Bit(file);
3091 change->trigger_side = getFile8Bit(file);
3093 change->trigger_player = getFile8Bit(file);
3094 change->trigger_page = getFile8Bit(file);
3096 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3097 CH_PAGE_ANY : (1 << change->trigger_page));
3099 change->has_action = getFile8Bit(file);
3100 change->action_type = getFile8Bit(file);
3101 change->action_mode = getFile8Bit(file);
3102 change->action_arg = getFile16BitBE(file);
3104 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3105 event_bits = getFile8Bit(file);
3106 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3107 if (event_bits & (1u << (j - 32)))
3108 change->has_event[j] = TRUE;
3111 // mark this custom element as modified
3112 ei->modified_settings = TRUE;
3114 level->file_has_custom_elements = TRUE;
3119 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3121 struct ElementInfo *ei;
3122 struct ElementGroupInfo *group;
3126 element = getMappedElement(getFile16BitBE(file));
3128 if (!IS_GROUP_ELEMENT(element))
3130 Warn("invalid group element number %d", element);
3132 ReadUnusedBytesFromFile(file, chunk_size - 2);
3137 ei = &element_info[element];
3139 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3140 ei->description[i] = getFile8Bit(file);
3141 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3143 group = element_info[element].group;
3145 group->num_elements = getFile8Bit(file);
3147 ei->use_gfx_element = getFile8Bit(file);
3148 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3150 group->choice_mode = getFile8Bit(file);
3152 // some free bytes for future values and padding
3153 ReadUnusedBytesFromFile(file, 3);
3155 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3156 group->element[i] = getMappedElement(getFile16BitBE(file));
3158 // mark this group element as modified
3159 element_info[element].modified_settings = TRUE;
3161 level->file_has_custom_elements = TRUE;
3166 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3167 int element, int real_element)
3169 int micro_chunk_size = 0;
3170 int conf_type = getFile8Bit(file);
3171 int byte_mask = conf_type & CONF_MASK_BYTES;
3172 boolean element_found = FALSE;
3175 micro_chunk_size += 1;
3177 if (byte_mask == CONF_MASK_MULTI_BYTES)
3179 int num_bytes = getFile16BitBE(file);
3180 byte *buffer = checked_malloc(num_bytes);
3182 ReadBytesFromFile(file, buffer, num_bytes);
3184 for (i = 0; conf[i].data_type != -1; i++)
3186 if (conf[i].element == element &&
3187 conf[i].conf_type == conf_type)
3189 int data_type = conf[i].data_type;
3190 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3191 int max_num_entities = conf[i].max_num_entities;
3193 if (num_entities > max_num_entities)
3195 Warn("truncating number of entities for element %d from %d to %d",
3196 element, num_entities, max_num_entities);
3198 num_entities = max_num_entities;
3201 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3202 data_type == TYPE_CONTENT_LIST))
3204 // for element and content lists, zero entities are not allowed
3205 Warn("found empty list of entities for element %d", element);
3207 // do not set "num_entities" here to prevent reading behind buffer
3209 *(int *)(conf[i].num_entities) = 1; // at least one is required
3213 *(int *)(conf[i].num_entities) = num_entities;
3216 element_found = TRUE;
3218 if (data_type == TYPE_STRING)
3220 char *string = (char *)(conf[i].value);
3223 for (j = 0; j < max_num_entities; j++)
3224 string[j] = (j < num_entities ? buffer[j] : '\0');
3226 else if (data_type == TYPE_ELEMENT_LIST)
3228 int *element_array = (int *)(conf[i].value);
3231 for (j = 0; j < num_entities; j++)
3233 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3235 else if (data_type == TYPE_CONTENT_LIST)
3237 struct Content *content= (struct Content *)(conf[i].value);
3240 for (c = 0; c < num_entities; c++)
3241 for (y = 0; y < 3; y++)
3242 for (x = 0; x < 3; x++)
3243 content[c].e[x][y] =
3244 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3247 element_found = FALSE;
3253 checked_free(buffer);
3255 micro_chunk_size += 2 + num_bytes;
3257 else // constant size configuration data (1, 2 or 4 bytes)
3259 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3260 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3261 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3263 for (i = 0; conf[i].data_type != -1; i++)
3265 if (conf[i].element == element &&
3266 conf[i].conf_type == conf_type)
3268 int data_type = conf[i].data_type;
3270 if (data_type == TYPE_ELEMENT)
3271 value = getMappedElement(value);
3273 if (data_type == TYPE_BOOLEAN)
3274 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3276 *(int *) (conf[i].value) = value;
3278 element_found = TRUE;
3284 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3289 char *error_conf_chunk_bytes =
3290 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3291 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3292 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3293 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3294 int error_element = real_element;
3296 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3297 error_conf_chunk_bytes, error_conf_chunk_token,
3298 error_element, EL_NAME(error_element));
3301 return micro_chunk_size;
3304 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3306 int real_chunk_size = 0;
3308 li = *level; // copy level data into temporary buffer
3310 while (!checkEndOfFile(file))
3312 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3314 if (real_chunk_size >= chunk_size)
3318 *level = li; // copy temporary buffer back to level data
3320 return real_chunk_size;
3323 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3325 int real_chunk_size = 0;
3327 li = *level; // copy level data into temporary buffer
3329 while (!checkEndOfFile(file))
3331 int element = getMappedElement(getFile16BitBE(file));
3333 real_chunk_size += 2;
3334 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3336 if (real_chunk_size >= chunk_size)
3340 *level = li; // copy temporary buffer back to level data
3342 return real_chunk_size;
3345 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3347 int real_chunk_size = 0;
3349 li = *level; // copy level data into temporary buffer
3351 while (!checkEndOfFile(file))
3353 int element = getMappedElement(getFile16BitBE(file));
3355 real_chunk_size += 2;
3356 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3358 if (real_chunk_size >= chunk_size)
3362 *level = li; // copy temporary buffer back to level data
3364 return real_chunk_size;
3367 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3369 int element = getMappedElement(getFile16BitBE(file));
3370 int envelope_nr = element - EL_ENVELOPE_1;
3371 int real_chunk_size = 2;
3373 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3375 while (!checkEndOfFile(file))
3377 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3380 if (real_chunk_size >= chunk_size)
3384 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3386 return real_chunk_size;
3389 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3391 int element = getMappedElement(getFile16BitBE(file));
3392 int real_chunk_size = 2;
3393 struct ElementInfo *ei = &element_info[element];
3396 xx_ei = *ei; // copy element data into temporary buffer
3398 xx_ei.num_change_pages = -1;
3400 while (!checkEndOfFile(file))
3402 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3404 if (xx_ei.num_change_pages != -1)
3407 if (real_chunk_size >= chunk_size)
3413 if (ei->num_change_pages == -1)
3415 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3418 ei->num_change_pages = 1;
3420 setElementChangePages(ei, 1);
3421 setElementChangeInfoToDefaults(ei->change);
3423 return real_chunk_size;
3426 // initialize number of change pages stored for this custom element
3427 setElementChangePages(ei, ei->num_change_pages);
3428 for (i = 0; i < ei->num_change_pages; i++)
3429 setElementChangeInfoToDefaults(&ei->change_page[i]);
3431 // start with reading properties for the first change page
3432 xx_current_change_page = 0;
3434 while (!checkEndOfFile(file))
3436 // level file might contain invalid change page number
3437 if (xx_current_change_page >= ei->num_change_pages)
3440 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3442 xx_change = *change; // copy change data into temporary buffer
3444 resetEventBits(); // reset bits; change page might have changed
3446 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3449 *change = xx_change;
3451 setEventFlagsFromEventBits(change);
3453 if (real_chunk_size >= chunk_size)
3457 level->file_has_custom_elements = TRUE;
3459 return real_chunk_size;
3462 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3464 int element = getMappedElement(getFile16BitBE(file));
3465 int real_chunk_size = 2;
3466 struct ElementInfo *ei = &element_info[element];
3467 struct ElementGroupInfo *group = ei->group;
3472 xx_ei = *ei; // copy element data into temporary buffer
3473 xx_group = *group; // copy group data into temporary buffer
3475 while (!checkEndOfFile(file))
3477 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3480 if (real_chunk_size >= chunk_size)
3487 level->file_has_custom_elements = TRUE;
3489 return real_chunk_size;
3492 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3494 int element = getMappedElement(getFile16BitBE(file));
3495 int real_chunk_size = 2;
3496 struct ElementInfo *ei = &element_info[element];
3498 xx_ei = *ei; // copy element data into temporary buffer
3500 while (!checkEndOfFile(file))
3502 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3505 if (real_chunk_size >= chunk_size)
3511 level->file_has_custom_elements = TRUE;
3513 return real_chunk_size;
3516 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3517 struct LevelFileInfo *level_file_info,
3518 boolean level_info_only)
3520 char *filename = level_file_info->filename;
3521 char cookie[MAX_LINE_LEN];
3522 char chunk_name[CHUNK_ID_LEN + 1];
3526 if (!(file = openFile(filename, MODE_READ)))
3528 level->no_valid_file = TRUE;
3529 level->no_level_file = TRUE;
3531 if (level_info_only)
3534 Warn("cannot read level '%s' -- using empty level", filename);
3536 if (!setup.editor.use_template_for_new_levels)
3539 // if level file not found, try to initialize level data from template
3540 filename = getGlobalLevelTemplateFilename();
3542 if (!(file = openFile(filename, MODE_READ)))
3545 // default: for empty levels, use level template for custom elements
3546 level->use_custom_template = TRUE;
3548 level->no_valid_file = FALSE;
3551 getFileChunkBE(file, chunk_name, NULL);
3552 if (strEqual(chunk_name, "RND1"))
3554 getFile32BitBE(file); // not used
3556 getFileChunkBE(file, chunk_name, NULL);
3557 if (!strEqual(chunk_name, "CAVE"))
3559 level->no_valid_file = TRUE;
3561 Warn("unknown format of level file '%s'", filename);
3568 else // check for pre-2.0 file format with cookie string
3570 strcpy(cookie, chunk_name);
3571 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3573 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3574 cookie[strlen(cookie) - 1] = '\0';
3576 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3578 level->no_valid_file = TRUE;
3580 Warn("unknown format of level file '%s'", filename);
3587 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3589 level->no_valid_file = TRUE;
3591 Warn("unsupported version of level file '%s'", filename);
3598 // pre-2.0 level files have no game version, so use file version here
3599 level->game_version = level->file_version;
3602 if (level->file_version < FILE_VERSION_1_2)
3604 // level files from versions before 1.2.0 without chunk structure
3605 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3606 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3614 int (*loader)(File *, int, struct LevelInfo *);
3618 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3619 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3620 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3621 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3622 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3623 { "INFO", -1, LoadLevel_INFO },
3624 { "BODY", -1, LoadLevel_BODY },
3625 { "CONT", -1, LoadLevel_CONT },
3626 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3627 { "CNT3", -1, LoadLevel_CNT3 },
3628 { "CUS1", -1, LoadLevel_CUS1 },
3629 { "CUS2", -1, LoadLevel_CUS2 },
3630 { "CUS3", -1, LoadLevel_CUS3 },
3631 { "CUS4", -1, LoadLevel_CUS4 },
3632 { "GRP1", -1, LoadLevel_GRP1 },
3633 { "CONF", -1, LoadLevel_CONF },
3634 { "ELEM", -1, LoadLevel_ELEM },
3635 { "NOTE", -1, LoadLevel_NOTE },
3636 { "CUSX", -1, LoadLevel_CUSX },
3637 { "GRPX", -1, LoadLevel_GRPX },
3638 { "EMPX", -1, LoadLevel_EMPX },
3643 while (getFileChunkBE(file, chunk_name, &chunk_size))
3647 while (chunk_info[i].name != NULL &&
3648 !strEqual(chunk_name, chunk_info[i].name))
3651 if (chunk_info[i].name == NULL)
3653 Warn("unknown chunk '%s' in level file '%s'",
3654 chunk_name, filename);
3656 ReadUnusedBytesFromFile(file, chunk_size);
3658 else if (chunk_info[i].size != -1 &&
3659 chunk_info[i].size != chunk_size)
3661 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3662 chunk_size, chunk_name, filename);
3664 ReadUnusedBytesFromFile(file, chunk_size);
3668 // call function to load this level chunk
3669 int chunk_size_expected =
3670 (chunk_info[i].loader)(file, chunk_size, level);
3672 if (chunk_size_expected < 0)
3674 Warn("error reading chunk '%s' in level file '%s'",
3675 chunk_name, filename);
3680 // the size of some chunks cannot be checked before reading other
3681 // chunks first (like "HEAD" and "BODY") that contain some header
3682 // information, so check them here
3683 if (chunk_size_expected != chunk_size)
3685 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3686 chunk_size, chunk_name, filename);
3698 // ----------------------------------------------------------------------------
3699 // functions for loading BD level
3700 // ----------------------------------------------------------------------------
3702 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3704 struct LevelInfo_BD *level_bd = level->native_bd_level;
3705 GdCave *cave = NULL; // will be changed below
3706 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3707 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3710 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3712 // cave and map newly allocated when set to defaults above
3713 cave = level_bd->cave;
3715 for (i = 0; i < 5; i++)
3717 cave->level_time[i] = level->time;
3718 cave->level_diamonds[i] = level->gems_needed;
3719 cave->level_magic_wall_time[i] = level->time_magic_wall;
3720 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3723 cave->diamond_value = level->score[SC_DIAMOND];
3724 cave->extra_diamond_value = level->score[SC_DIAMOND];
3726 cave->level_speed[0] = 160; // set cave speed
3728 cave->intermission = level->bd_intermission;
3729 cave->diagonal_movements = level->bd_diagonal_movements;
3731 strncpy(cave->name, level->name, sizeof(GdString));
3732 cave->name[sizeof(GdString) - 1] = '\0';
3734 for (x = 0; x < cave->w; x++)
3735 for (y = 0; y < cave->h; y++)
3736 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3739 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3741 struct LevelInfo_BD *level_bd = level->native_bd_level;
3742 GdCave *cave = level_bd->cave;
3743 int bd_level_nr = level_bd->level_nr;
3746 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3747 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3749 level->time = cave->level_time[bd_level_nr];
3750 level->gems_needed = cave->level_diamonds[bd_level_nr];
3751 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3753 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3754 level->score[SC_DIAMOND] = cave->diamond_value;
3756 level->bd_intermission = cave->intermission;
3757 level->bd_diagonal_movements = cave->diagonal_movements;
3759 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3760 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3762 for (x = 0; x < level->fieldx; x++)
3763 for (y = 0; y < level->fieldy; y++)
3764 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3767 static void setTapeInfoToDefaults(void);
3769 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3771 struct LevelInfo_BD *level_bd = level->native_bd_level;
3772 GdCave *cave = level_bd->cave;
3773 GdReplay *replay = level_bd->replay;
3779 // always start with reliable default values
3780 setTapeInfoToDefaults();
3782 tape.level_nr = level_nr; // (currently not used)
3783 tape.random_seed = replay->seed;
3785 TapeSetDateFromIsoDateString(replay->date);
3788 tape.pos[tape.counter].delay = 0;
3790 tape.bd_replay = TRUE;
3792 // all time calculations only used to display approximate tape time
3793 int cave_speed = cave->speed;
3794 int milliseconds_game = 0;
3795 int milliseconds_elapsed = 20;
3797 for (i = 0; i < replay->movements->len; i++)
3799 int replay_action = replay->movements->data[i];
3800 int tape_action = map_action_BD_to_RND(replay_action);
3801 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3802 boolean success = 0;
3806 success = TapeAddAction(action);
3808 milliseconds_game += milliseconds_elapsed;
3810 if (milliseconds_game >= cave_speed)
3812 milliseconds_game -= cave_speed;
3819 tape.pos[tape.counter].delay = 0;
3820 tape.pos[tape.counter].action[0] = 0;
3824 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3830 TapeHaltRecording();
3834 // ----------------------------------------------------------------------------
3835 // functions for loading EM level
3836 // ----------------------------------------------------------------------------
3838 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3840 static int ball_xy[8][2] =
3851 struct LevelInfo_EM *level_em = level->native_em_level;
3852 struct CAVE *cav = level_em->cav;
3855 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3856 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3858 cav->time_seconds = level->time;
3859 cav->gems_needed = level->gems_needed;
3861 cav->emerald_score = level->score[SC_EMERALD];
3862 cav->diamond_score = level->score[SC_DIAMOND];
3863 cav->alien_score = level->score[SC_ROBOT];
3864 cav->tank_score = level->score[SC_SPACESHIP];
3865 cav->bug_score = level->score[SC_BUG];
3866 cav->eater_score = level->score[SC_YAMYAM];
3867 cav->nut_score = level->score[SC_NUT];
3868 cav->dynamite_score = level->score[SC_DYNAMITE];
3869 cav->key_score = level->score[SC_KEY];
3870 cav->exit_score = level->score[SC_TIME_BONUS];
3872 cav->num_eater_arrays = level->num_yamyam_contents;
3874 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3875 for (y = 0; y < 3; y++)
3876 for (x = 0; x < 3; x++)
3877 cav->eater_array[i][y * 3 + x] =
3878 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3880 cav->amoeba_time = level->amoeba_speed;
3881 cav->wonderwall_time = level->time_magic_wall;
3882 cav->wheel_time = level->time_wheel;
3884 cav->android_move_time = level->android_move_time;
3885 cav->android_clone_time = level->android_clone_time;
3886 cav->ball_random = level->ball_random;
3887 cav->ball_active = level->ball_active_initial;
3888 cav->ball_time = level->ball_time;
3889 cav->num_ball_arrays = level->num_ball_contents;
3891 cav->lenses_score = level->lenses_score;
3892 cav->magnify_score = level->magnify_score;
3893 cav->slurp_score = level->slurp_score;
3895 cav->lenses_time = level->lenses_time;
3896 cav->magnify_time = level->magnify_time;
3898 cav->wind_time = 9999;
3899 cav->wind_direction =
3900 map_direction_RND_to_EM(level->wind_direction_initial);
3902 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3903 for (j = 0; j < 8; j++)
3904 cav->ball_array[i][j] =
3905 map_element_RND_to_EM_cave(level->ball_content[i].
3906 e[ball_xy[j][0]][ball_xy[j][1]]);
3908 map_android_clone_elements_RND_to_EM(level);
3910 // first fill the complete playfield with the empty space element
3911 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3912 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3913 cav->cave[x][y] = Cblank;
3915 // then copy the real level contents from level file into the playfield
3916 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3918 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3920 if (level->field[x][y] == EL_AMOEBA_DEAD)
3921 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3923 cav->cave[x][y] = new_element;
3926 for (i = 0; i < MAX_PLAYERS; i++)
3928 cav->player_x[i] = -1;
3929 cav->player_y[i] = -1;
3932 // initialize player positions and delete players from the playfield
3933 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3935 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3937 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3939 cav->player_x[player_nr] = x;
3940 cav->player_y[player_nr] = y;
3942 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3947 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3949 static int ball_xy[8][2] =
3960 struct LevelInfo_EM *level_em = level->native_em_level;
3961 struct CAVE *cav = level_em->cav;
3964 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3965 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3967 level->time = cav->time_seconds;
3968 level->gems_needed = cav->gems_needed;
3970 sprintf(level->name, "Level %d", level->file_info.nr);
3972 level->score[SC_EMERALD] = cav->emerald_score;
3973 level->score[SC_DIAMOND] = cav->diamond_score;
3974 level->score[SC_ROBOT] = cav->alien_score;
3975 level->score[SC_SPACESHIP] = cav->tank_score;
3976 level->score[SC_BUG] = cav->bug_score;
3977 level->score[SC_YAMYAM] = cav->eater_score;
3978 level->score[SC_NUT] = cav->nut_score;
3979 level->score[SC_DYNAMITE] = cav->dynamite_score;
3980 level->score[SC_KEY] = cav->key_score;
3981 level->score[SC_TIME_BONUS] = cav->exit_score;
3983 level->num_yamyam_contents = cav->num_eater_arrays;
3985 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3986 for (y = 0; y < 3; y++)
3987 for (x = 0; x < 3; x++)
3988 level->yamyam_content[i].e[x][y] =
3989 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3991 level->amoeba_speed = cav->amoeba_time;
3992 level->time_magic_wall = cav->wonderwall_time;
3993 level->time_wheel = cav->wheel_time;
3995 level->android_move_time = cav->android_move_time;
3996 level->android_clone_time = cav->android_clone_time;
3997 level->ball_random = cav->ball_random;
3998 level->ball_active_initial = cav->ball_active;
3999 level->ball_time = cav->ball_time;
4000 level->num_ball_contents = cav->num_ball_arrays;
4002 level->lenses_score = cav->lenses_score;
4003 level->magnify_score = cav->magnify_score;
4004 level->slurp_score = cav->slurp_score;
4006 level->lenses_time = cav->lenses_time;
4007 level->magnify_time = cav->magnify_time;
4009 level->wind_direction_initial =
4010 map_direction_EM_to_RND(cav->wind_direction);
4012 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4013 for (j = 0; j < 8; j++)
4014 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4015 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4017 map_android_clone_elements_EM_to_RND(level);
4019 // convert the playfield (some elements need special treatment)
4020 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4022 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4024 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4025 new_element = EL_AMOEBA_DEAD;
4027 level->field[x][y] = new_element;
4030 for (i = 0; i < MAX_PLAYERS; i++)
4032 // in case of all players set to the same field, use the first player
4033 int nr = MAX_PLAYERS - i - 1;
4034 int jx = cav->player_x[nr];
4035 int jy = cav->player_y[nr];
4037 if (jx != -1 && jy != -1)
4038 level->field[jx][jy] = EL_PLAYER_1 + nr;
4041 // time score is counted for each 10 seconds left in Emerald Mine levels
4042 level->time_score_base = 10;
4046 // ----------------------------------------------------------------------------
4047 // functions for loading SP level
4048 // ----------------------------------------------------------------------------
4050 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4052 struct LevelInfo_SP *level_sp = level->native_sp_level;
4053 LevelInfoType *header = &level_sp->header;
4056 level_sp->width = level->fieldx;
4057 level_sp->height = level->fieldy;
4059 for (x = 0; x < level->fieldx; x++)
4060 for (y = 0; y < level->fieldy; y++)
4061 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4063 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4065 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4066 header->LevelTitle[i] = level->name[i];
4067 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4069 header->InfotronsNeeded = level->gems_needed;
4071 header->SpecialPortCount = 0;
4073 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4075 boolean gravity_port_found = FALSE;
4076 boolean gravity_port_valid = FALSE;
4077 int gravity_port_flag;
4078 int gravity_port_base_element;
4079 int element = level->field[x][y];
4081 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4082 element <= EL_SP_GRAVITY_ON_PORT_UP)
4084 gravity_port_found = TRUE;
4085 gravity_port_valid = TRUE;
4086 gravity_port_flag = 1;
4087 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4089 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4090 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4092 gravity_port_found = TRUE;
4093 gravity_port_valid = TRUE;
4094 gravity_port_flag = 0;
4095 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4097 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4098 element <= EL_SP_GRAVITY_PORT_UP)
4100 // change R'n'D style gravity inverting special port to normal port
4101 // (there are no gravity inverting ports in native Supaplex engine)
4103 gravity_port_found = TRUE;
4104 gravity_port_valid = FALSE;
4105 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4108 if (gravity_port_found)
4110 if (gravity_port_valid &&
4111 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4113 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4115 port->PortLocation = (y * level->fieldx + x) * 2;
4116 port->Gravity = gravity_port_flag;
4118 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4120 header->SpecialPortCount++;
4124 // change special gravity port to normal port
4126 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4129 level_sp->playfield[x][y] = element - EL_SP_START;
4134 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4136 struct LevelInfo_SP *level_sp = level->native_sp_level;
4137 LevelInfoType *header = &level_sp->header;
4138 boolean num_invalid_elements = 0;
4141 level->fieldx = level_sp->width;
4142 level->fieldy = level_sp->height;
4144 for (x = 0; x < level->fieldx; x++)
4146 for (y = 0; y < level->fieldy; y++)
4148 int element_old = level_sp->playfield[x][y];
4149 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4151 if (element_new == EL_UNKNOWN)
4153 num_invalid_elements++;
4155 Debug("level:native:SP", "invalid element %d at position %d, %d",
4159 level->field[x][y] = element_new;
4163 if (num_invalid_elements > 0)
4164 Warn("found %d invalid elements%s", num_invalid_elements,
4165 (!options.debug ? " (use '--debug' for more details)" : ""));
4167 for (i = 0; i < MAX_PLAYERS; i++)
4168 level->initial_player_gravity[i] =
4169 (header->InitialGravity == 1 ? TRUE : FALSE);
4171 // skip leading spaces
4172 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4173 if (header->LevelTitle[i] != ' ')
4177 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4178 level->name[j] = header->LevelTitle[i];
4179 level->name[j] = '\0';
4181 // cut trailing spaces
4183 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4184 level->name[j - 1] = '\0';
4186 level->gems_needed = header->InfotronsNeeded;
4188 for (i = 0; i < header->SpecialPortCount; i++)
4190 SpecialPortType *port = &header->SpecialPort[i];
4191 int port_location = port->PortLocation;
4192 int gravity = port->Gravity;
4193 int port_x, port_y, port_element;
4195 port_x = (port_location / 2) % level->fieldx;
4196 port_y = (port_location / 2) / level->fieldx;
4198 if (port_x < 0 || port_x >= level->fieldx ||
4199 port_y < 0 || port_y >= level->fieldy)
4201 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4206 port_element = level->field[port_x][port_y];
4208 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4209 port_element > EL_SP_GRAVITY_PORT_UP)
4211 Warn("no special port at position (%d, %d)", port_x, port_y);
4216 // change previous (wrong) gravity inverting special port to either
4217 // gravity enabling special port or gravity disabling special port
4218 level->field[port_x][port_y] +=
4219 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4220 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4223 // change special gravity ports without database entries to normal ports
4224 for (x = 0; x < level->fieldx; x++)
4225 for (y = 0; y < level->fieldy; y++)
4226 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4227 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4228 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4230 level->time = 0; // no time limit
4231 level->amoeba_speed = 0;
4232 level->time_magic_wall = 0;
4233 level->time_wheel = 0;
4234 level->amoeba_content = EL_EMPTY;
4236 // original Supaplex does not use score values -- rate by playing time
4237 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4238 level->score[i] = 0;
4240 level->rate_time_over_score = TRUE;
4242 // there are no yamyams in supaplex levels
4243 for (i = 0; i < level->num_yamyam_contents; i++)
4244 for (x = 0; x < 3; x++)
4245 for (y = 0; y < 3; y++)
4246 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4249 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4251 struct LevelInfo_SP *level_sp = level->native_sp_level;
4252 struct DemoInfo_SP *demo = &level_sp->demo;
4255 // always start with reliable default values
4256 demo->is_available = FALSE;
4259 if (TAPE_IS_EMPTY(tape))
4262 demo->level_nr = tape.level_nr; // (currently not used)
4264 level_sp->header.DemoRandomSeed = tape.random_seed;
4268 for (i = 0; i < tape.length; i++)
4270 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4271 int demo_repeat = tape.pos[i].delay;
4272 int demo_entries = (demo_repeat + 15) / 16;
4274 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4276 Warn("tape truncated: size exceeds maximum SP demo size %d",
4282 for (j = 0; j < demo_repeat / 16; j++)
4283 demo->data[demo->length++] = 0xf0 | demo_action;
4285 if (demo_repeat % 16)
4286 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4289 demo->is_available = TRUE;
4292 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4294 struct LevelInfo_SP *level_sp = level->native_sp_level;
4295 struct DemoInfo_SP *demo = &level_sp->demo;
4296 char *filename = level->file_info.filename;
4299 // always start with reliable default values
4300 setTapeInfoToDefaults();
4302 if (!demo->is_available)
4305 tape.level_nr = demo->level_nr; // (currently not used)
4306 tape.random_seed = level_sp->header.DemoRandomSeed;
4308 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4311 tape.pos[tape.counter].delay = 0;
4313 for (i = 0; i < demo->length; i++)
4315 int demo_action = demo->data[i] & 0x0f;
4316 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4317 int tape_action = map_key_SP_to_RND(demo_action);
4318 int tape_repeat = demo_repeat + 1;
4319 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4320 boolean success = 0;
4323 for (j = 0; j < tape_repeat; j++)
4324 success = TapeAddAction(action);
4328 Warn("SP demo truncated: size exceeds maximum tape size %d",
4335 TapeHaltRecording();
4339 // ----------------------------------------------------------------------------
4340 // functions for loading MM level
4341 // ----------------------------------------------------------------------------
4343 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4345 struct LevelInfo_MM *level_mm = level->native_mm_level;
4348 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4349 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4351 level_mm->time = level->time;
4352 level_mm->kettles_needed = level->gems_needed;
4353 level_mm->auto_count_kettles = level->auto_count_gems;
4355 level_mm->mm_laser_red = level->mm_laser_red;
4356 level_mm->mm_laser_green = level->mm_laser_green;
4357 level_mm->mm_laser_blue = level->mm_laser_blue;
4359 level_mm->df_laser_red = level->df_laser_red;
4360 level_mm->df_laser_green = level->df_laser_green;
4361 level_mm->df_laser_blue = level->df_laser_blue;
4363 strcpy(level_mm->name, level->name);
4364 strcpy(level_mm->author, level->author);
4366 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4367 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4368 level_mm->score[SC_KEY] = level->score[SC_KEY];
4369 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4370 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4372 level_mm->amoeba_speed = level->amoeba_speed;
4373 level_mm->time_fuse = level->mm_time_fuse;
4374 level_mm->time_bomb = level->mm_time_bomb;
4375 level_mm->time_ball = level->mm_time_ball;
4376 level_mm->time_block = level->mm_time_block;
4378 level_mm->num_ball_contents = level->num_mm_ball_contents;
4379 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4380 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4381 level_mm->explode_ball = level->explode_mm_ball;
4383 for (i = 0; i < level->num_mm_ball_contents; i++)
4384 level_mm->ball_content[i] =
4385 map_element_RND_to_MM(level->mm_ball_content[i]);
4387 for (x = 0; x < level->fieldx; x++)
4388 for (y = 0; y < level->fieldy; y++)
4390 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4393 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4395 struct LevelInfo_MM *level_mm = level->native_mm_level;
4398 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4399 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4401 level->time = level_mm->time;
4402 level->gems_needed = level_mm->kettles_needed;
4403 level->auto_count_gems = level_mm->auto_count_kettles;
4405 level->mm_laser_red = level_mm->mm_laser_red;
4406 level->mm_laser_green = level_mm->mm_laser_green;
4407 level->mm_laser_blue = level_mm->mm_laser_blue;
4409 level->df_laser_red = level_mm->df_laser_red;
4410 level->df_laser_green = level_mm->df_laser_green;
4411 level->df_laser_blue = level_mm->df_laser_blue;
4413 strcpy(level->name, level_mm->name);
4415 // only overwrite author from 'levelinfo.conf' if author defined in level
4416 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4417 strcpy(level->author, level_mm->author);
4419 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4420 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4421 level->score[SC_KEY] = level_mm->score[SC_KEY];
4422 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4423 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4425 level->amoeba_speed = level_mm->amoeba_speed;
4426 level->mm_time_fuse = level_mm->time_fuse;
4427 level->mm_time_bomb = level_mm->time_bomb;
4428 level->mm_time_ball = level_mm->time_ball;
4429 level->mm_time_block = level_mm->time_block;
4431 level->num_mm_ball_contents = level_mm->num_ball_contents;
4432 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4433 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4434 level->explode_mm_ball = level_mm->explode_ball;
4436 for (i = 0; i < level->num_mm_ball_contents; i++)
4437 level->mm_ball_content[i] =
4438 map_element_MM_to_RND(level_mm->ball_content[i]);
4440 for (x = 0; x < level->fieldx; x++)
4441 for (y = 0; y < level->fieldy; y++)
4442 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4446 // ----------------------------------------------------------------------------
4447 // functions for loading DC level
4448 // ----------------------------------------------------------------------------
4450 #define DC_LEVEL_HEADER_SIZE 344
4452 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4455 static int last_data_encoded;
4459 int diff_hi, diff_lo;
4460 int data_hi, data_lo;
4461 unsigned short data_decoded;
4465 last_data_encoded = 0;
4472 diff = data_encoded - last_data_encoded;
4473 diff_hi = diff & ~0xff;
4474 diff_lo = diff & 0xff;
4478 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4479 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4480 data_hi = data_hi & 0xff00;
4482 data_decoded = data_hi | data_lo;
4484 last_data_encoded = data_encoded;
4486 offset1 = (offset1 + 1) % 31;
4487 offset2 = offset2 & 0xff;
4489 return data_decoded;
4492 static int getMappedElement_DC(int element)
4500 // 0x0117 - 0x036e: (?)
4503 // 0x042d - 0x0684: (?)
4519 element = EL_CRYSTAL;
4522 case 0x0e77: // quicksand (boulder)
4523 element = EL_QUICKSAND_FAST_FULL;
4526 case 0x0e99: // slow quicksand (boulder)
4527 element = EL_QUICKSAND_FULL;
4531 element = EL_EM_EXIT_OPEN;
4535 element = EL_EM_EXIT_CLOSED;
4539 element = EL_EM_STEEL_EXIT_OPEN;
4543 element = EL_EM_STEEL_EXIT_CLOSED;
4546 case 0x0f4f: // dynamite (lit 1)
4547 element = EL_EM_DYNAMITE_ACTIVE;
4550 case 0x0f57: // dynamite (lit 2)
4551 element = EL_EM_DYNAMITE_ACTIVE;
4554 case 0x0f5f: // dynamite (lit 3)
4555 element = EL_EM_DYNAMITE_ACTIVE;
4558 case 0x0f67: // dynamite (lit 4)
4559 element = EL_EM_DYNAMITE_ACTIVE;
4566 element = EL_AMOEBA_WET;
4570 element = EL_AMOEBA_DROP;
4574 element = EL_DC_MAGIC_WALL;
4578 element = EL_SPACESHIP_UP;
4582 element = EL_SPACESHIP_DOWN;
4586 element = EL_SPACESHIP_LEFT;
4590 element = EL_SPACESHIP_RIGHT;
4594 element = EL_BUG_UP;
4598 element = EL_BUG_DOWN;
4602 element = EL_BUG_LEFT;
4606 element = EL_BUG_RIGHT;
4610 element = EL_MOLE_UP;
4614 element = EL_MOLE_DOWN;
4618 element = EL_MOLE_LEFT;
4622 element = EL_MOLE_RIGHT;
4630 element = EL_YAMYAM_UP;
4634 element = EL_SWITCHGATE_OPEN;
4638 element = EL_SWITCHGATE_CLOSED;
4642 element = EL_DC_SWITCHGATE_SWITCH_UP;
4646 element = EL_TIMEGATE_CLOSED;
4649 case 0x144c: // conveyor belt switch (green)
4650 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4653 case 0x144f: // conveyor belt switch (red)
4654 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4657 case 0x1452: // conveyor belt switch (blue)
4658 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4662 element = EL_CONVEYOR_BELT_3_MIDDLE;
4666 element = EL_CONVEYOR_BELT_3_LEFT;
4670 element = EL_CONVEYOR_BELT_3_RIGHT;
4674 element = EL_CONVEYOR_BELT_1_MIDDLE;
4678 element = EL_CONVEYOR_BELT_1_LEFT;
4682 element = EL_CONVEYOR_BELT_1_RIGHT;
4686 element = EL_CONVEYOR_BELT_4_MIDDLE;
4690 element = EL_CONVEYOR_BELT_4_LEFT;
4694 element = EL_CONVEYOR_BELT_4_RIGHT;
4698 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4702 element = EL_EXPANDABLE_WALL_VERTICAL;
4706 element = EL_EXPANDABLE_WALL_ANY;
4709 case 0x14ce: // growing steel wall (left/right)
4710 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4713 case 0x14df: // growing steel wall (up/down)
4714 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4717 case 0x14e8: // growing steel wall (up/down/left/right)
4718 element = EL_EXPANDABLE_STEELWALL_ANY;
4722 element = EL_SHIELD_DEADLY;
4726 element = EL_EXTRA_TIME;
4734 element = EL_EMPTY_SPACE;
4737 case 0x1578: // quicksand (empty)
4738 element = EL_QUICKSAND_FAST_EMPTY;
4741 case 0x1579: // slow quicksand (empty)
4742 element = EL_QUICKSAND_EMPTY;
4752 element = EL_EM_DYNAMITE;
4755 case 0x15a1: // key (red)
4756 element = EL_EM_KEY_1;
4759 case 0x15a2: // key (yellow)
4760 element = EL_EM_KEY_2;
4763 case 0x15a3: // key (blue)
4764 element = EL_EM_KEY_4;
4767 case 0x15a4: // key (green)
4768 element = EL_EM_KEY_3;
4771 case 0x15a5: // key (white)
4772 element = EL_DC_KEY_WHITE;
4776 element = EL_WALL_SLIPPERY;
4783 case 0x15a8: // wall (not round)
4787 case 0x15a9: // (blue)
4788 element = EL_CHAR_A;
4791 case 0x15aa: // (blue)
4792 element = EL_CHAR_B;
4795 case 0x15ab: // (blue)
4796 element = EL_CHAR_C;
4799 case 0x15ac: // (blue)
4800 element = EL_CHAR_D;
4803 case 0x15ad: // (blue)
4804 element = EL_CHAR_E;
4807 case 0x15ae: // (blue)
4808 element = EL_CHAR_F;
4811 case 0x15af: // (blue)
4812 element = EL_CHAR_G;
4815 case 0x15b0: // (blue)
4816 element = EL_CHAR_H;
4819 case 0x15b1: // (blue)
4820 element = EL_CHAR_I;
4823 case 0x15b2: // (blue)
4824 element = EL_CHAR_J;
4827 case 0x15b3: // (blue)
4828 element = EL_CHAR_K;
4831 case 0x15b4: // (blue)
4832 element = EL_CHAR_L;
4835 case 0x15b5: // (blue)
4836 element = EL_CHAR_M;
4839 case 0x15b6: // (blue)
4840 element = EL_CHAR_N;
4843 case 0x15b7: // (blue)
4844 element = EL_CHAR_O;
4847 case 0x15b8: // (blue)
4848 element = EL_CHAR_P;
4851 case 0x15b9: // (blue)
4852 element = EL_CHAR_Q;
4855 case 0x15ba: // (blue)
4856 element = EL_CHAR_R;
4859 case 0x15bb: // (blue)
4860 element = EL_CHAR_S;
4863 case 0x15bc: // (blue)
4864 element = EL_CHAR_T;
4867 case 0x15bd: // (blue)
4868 element = EL_CHAR_U;
4871 case 0x15be: // (blue)
4872 element = EL_CHAR_V;
4875 case 0x15bf: // (blue)
4876 element = EL_CHAR_W;
4879 case 0x15c0: // (blue)
4880 element = EL_CHAR_X;
4883 case 0x15c1: // (blue)
4884 element = EL_CHAR_Y;
4887 case 0x15c2: // (blue)
4888 element = EL_CHAR_Z;
4891 case 0x15c3: // (blue)
4892 element = EL_CHAR_AUMLAUT;
4895 case 0x15c4: // (blue)
4896 element = EL_CHAR_OUMLAUT;
4899 case 0x15c5: // (blue)
4900 element = EL_CHAR_UUMLAUT;
4903 case 0x15c6: // (blue)
4904 element = EL_CHAR_0;
4907 case 0x15c7: // (blue)
4908 element = EL_CHAR_1;
4911 case 0x15c8: // (blue)
4912 element = EL_CHAR_2;
4915 case 0x15c9: // (blue)
4916 element = EL_CHAR_3;
4919 case 0x15ca: // (blue)
4920 element = EL_CHAR_4;
4923 case 0x15cb: // (blue)
4924 element = EL_CHAR_5;
4927 case 0x15cc: // (blue)
4928 element = EL_CHAR_6;
4931 case 0x15cd: // (blue)
4932 element = EL_CHAR_7;
4935 case 0x15ce: // (blue)
4936 element = EL_CHAR_8;
4939 case 0x15cf: // (blue)
4940 element = EL_CHAR_9;
4943 case 0x15d0: // (blue)
4944 element = EL_CHAR_PERIOD;
4947 case 0x15d1: // (blue)
4948 element = EL_CHAR_EXCLAM;
4951 case 0x15d2: // (blue)
4952 element = EL_CHAR_COLON;
4955 case 0x15d3: // (blue)
4956 element = EL_CHAR_LESS;
4959 case 0x15d4: // (blue)
4960 element = EL_CHAR_GREATER;
4963 case 0x15d5: // (blue)
4964 element = EL_CHAR_QUESTION;
4967 case 0x15d6: // (blue)
4968 element = EL_CHAR_COPYRIGHT;
4971 case 0x15d7: // (blue)
4972 element = EL_CHAR_UP;
4975 case 0x15d8: // (blue)
4976 element = EL_CHAR_DOWN;
4979 case 0x15d9: // (blue)
4980 element = EL_CHAR_BUTTON;
4983 case 0x15da: // (blue)
4984 element = EL_CHAR_PLUS;
4987 case 0x15db: // (blue)
4988 element = EL_CHAR_MINUS;
4991 case 0x15dc: // (blue)
4992 element = EL_CHAR_APOSTROPHE;
4995 case 0x15dd: // (blue)
4996 element = EL_CHAR_PARENLEFT;
4999 case 0x15de: // (blue)
5000 element = EL_CHAR_PARENRIGHT;
5003 case 0x15df: // (green)
5004 element = EL_CHAR_A;
5007 case 0x15e0: // (green)
5008 element = EL_CHAR_B;
5011 case 0x15e1: // (green)
5012 element = EL_CHAR_C;
5015 case 0x15e2: // (green)
5016 element = EL_CHAR_D;
5019 case 0x15e3: // (green)
5020 element = EL_CHAR_E;
5023 case 0x15e4: // (green)
5024 element = EL_CHAR_F;
5027 case 0x15e5: // (green)
5028 element = EL_CHAR_G;
5031 case 0x15e6: // (green)
5032 element = EL_CHAR_H;
5035 case 0x15e7: // (green)
5036 element = EL_CHAR_I;
5039 case 0x15e8: // (green)
5040 element = EL_CHAR_J;
5043 case 0x15e9: // (green)
5044 element = EL_CHAR_K;
5047 case 0x15ea: // (green)
5048 element = EL_CHAR_L;
5051 case 0x15eb: // (green)
5052 element = EL_CHAR_M;
5055 case 0x15ec: // (green)
5056 element = EL_CHAR_N;
5059 case 0x15ed: // (green)
5060 element = EL_CHAR_O;
5063 case 0x15ee: // (green)
5064 element = EL_CHAR_P;
5067 case 0x15ef: // (green)
5068 element = EL_CHAR_Q;
5071 case 0x15f0: // (green)
5072 element = EL_CHAR_R;
5075 case 0x15f1: // (green)
5076 element = EL_CHAR_S;
5079 case 0x15f2: // (green)
5080 element = EL_CHAR_T;
5083 case 0x15f3: // (green)
5084 element = EL_CHAR_U;
5087 case 0x15f4: // (green)
5088 element = EL_CHAR_V;
5091 case 0x15f5: // (green)
5092 element = EL_CHAR_W;
5095 case 0x15f6: // (green)
5096 element = EL_CHAR_X;
5099 case 0x15f7: // (green)
5100 element = EL_CHAR_Y;
5103 case 0x15f8: // (green)
5104 element = EL_CHAR_Z;
5107 case 0x15f9: // (green)
5108 element = EL_CHAR_AUMLAUT;
5111 case 0x15fa: // (green)
5112 element = EL_CHAR_OUMLAUT;
5115 case 0x15fb: // (green)
5116 element = EL_CHAR_UUMLAUT;
5119 case 0x15fc: // (green)
5120 element = EL_CHAR_0;
5123 case 0x15fd: // (green)
5124 element = EL_CHAR_1;
5127 case 0x15fe: // (green)
5128 element = EL_CHAR_2;
5131 case 0x15ff: // (green)
5132 element = EL_CHAR_3;
5135 case 0x1600: // (green)
5136 element = EL_CHAR_4;
5139 case 0x1601: // (green)
5140 element = EL_CHAR_5;
5143 case 0x1602: // (green)
5144 element = EL_CHAR_6;
5147 case 0x1603: // (green)
5148 element = EL_CHAR_7;
5151 case 0x1604: // (green)
5152 element = EL_CHAR_8;
5155 case 0x1605: // (green)
5156 element = EL_CHAR_9;
5159 case 0x1606: // (green)
5160 element = EL_CHAR_PERIOD;
5163 case 0x1607: // (green)
5164 element = EL_CHAR_EXCLAM;
5167 case 0x1608: // (green)
5168 element = EL_CHAR_COLON;
5171 case 0x1609: // (green)
5172 element = EL_CHAR_LESS;
5175 case 0x160a: // (green)
5176 element = EL_CHAR_GREATER;
5179 case 0x160b: // (green)
5180 element = EL_CHAR_QUESTION;
5183 case 0x160c: // (green)
5184 element = EL_CHAR_COPYRIGHT;
5187 case 0x160d: // (green)
5188 element = EL_CHAR_UP;
5191 case 0x160e: // (green)
5192 element = EL_CHAR_DOWN;
5195 case 0x160f: // (green)
5196 element = EL_CHAR_BUTTON;
5199 case 0x1610: // (green)
5200 element = EL_CHAR_PLUS;
5203 case 0x1611: // (green)
5204 element = EL_CHAR_MINUS;
5207 case 0x1612: // (green)
5208 element = EL_CHAR_APOSTROPHE;
5211 case 0x1613: // (green)
5212 element = EL_CHAR_PARENLEFT;
5215 case 0x1614: // (green)
5216 element = EL_CHAR_PARENRIGHT;
5219 case 0x1615: // (blue steel)
5220 element = EL_STEEL_CHAR_A;
5223 case 0x1616: // (blue steel)
5224 element = EL_STEEL_CHAR_B;
5227 case 0x1617: // (blue steel)
5228 element = EL_STEEL_CHAR_C;
5231 case 0x1618: // (blue steel)
5232 element = EL_STEEL_CHAR_D;
5235 case 0x1619: // (blue steel)
5236 element = EL_STEEL_CHAR_E;
5239 case 0x161a: // (blue steel)
5240 element = EL_STEEL_CHAR_F;
5243 case 0x161b: // (blue steel)
5244 element = EL_STEEL_CHAR_G;
5247 case 0x161c: // (blue steel)
5248 element = EL_STEEL_CHAR_H;
5251 case 0x161d: // (blue steel)
5252 element = EL_STEEL_CHAR_I;
5255 case 0x161e: // (blue steel)
5256 element = EL_STEEL_CHAR_J;
5259 case 0x161f: // (blue steel)
5260 element = EL_STEEL_CHAR_K;
5263 case 0x1620: // (blue steel)
5264 element = EL_STEEL_CHAR_L;
5267 case 0x1621: // (blue steel)
5268 element = EL_STEEL_CHAR_M;
5271 case 0x1622: // (blue steel)
5272 element = EL_STEEL_CHAR_N;
5275 case 0x1623: // (blue steel)
5276 element = EL_STEEL_CHAR_O;
5279 case 0x1624: // (blue steel)
5280 element = EL_STEEL_CHAR_P;
5283 case 0x1625: // (blue steel)
5284 element = EL_STEEL_CHAR_Q;
5287 case 0x1626: // (blue steel)
5288 element = EL_STEEL_CHAR_R;
5291 case 0x1627: // (blue steel)
5292 element = EL_STEEL_CHAR_S;
5295 case 0x1628: // (blue steel)
5296 element = EL_STEEL_CHAR_T;
5299 case 0x1629: // (blue steel)
5300 element = EL_STEEL_CHAR_U;
5303 case 0x162a: // (blue steel)
5304 element = EL_STEEL_CHAR_V;
5307 case 0x162b: // (blue steel)
5308 element = EL_STEEL_CHAR_W;
5311 case 0x162c: // (blue steel)
5312 element = EL_STEEL_CHAR_X;
5315 case 0x162d: // (blue steel)
5316 element = EL_STEEL_CHAR_Y;
5319 case 0x162e: // (blue steel)
5320 element = EL_STEEL_CHAR_Z;
5323 case 0x162f: // (blue steel)
5324 element = EL_STEEL_CHAR_AUMLAUT;
5327 case 0x1630: // (blue steel)
5328 element = EL_STEEL_CHAR_OUMLAUT;
5331 case 0x1631: // (blue steel)
5332 element = EL_STEEL_CHAR_UUMLAUT;
5335 case 0x1632: // (blue steel)
5336 element = EL_STEEL_CHAR_0;
5339 case 0x1633: // (blue steel)
5340 element = EL_STEEL_CHAR_1;
5343 case 0x1634: // (blue steel)
5344 element = EL_STEEL_CHAR_2;
5347 case 0x1635: // (blue steel)
5348 element = EL_STEEL_CHAR_3;
5351 case 0x1636: // (blue steel)
5352 element = EL_STEEL_CHAR_4;
5355 case 0x1637: // (blue steel)
5356 element = EL_STEEL_CHAR_5;
5359 case 0x1638: // (blue steel)
5360 element = EL_STEEL_CHAR_6;
5363 case 0x1639: // (blue steel)
5364 element = EL_STEEL_CHAR_7;
5367 case 0x163a: // (blue steel)
5368 element = EL_STEEL_CHAR_8;
5371 case 0x163b: // (blue steel)
5372 element = EL_STEEL_CHAR_9;
5375 case 0x163c: // (blue steel)
5376 element = EL_STEEL_CHAR_PERIOD;
5379 case 0x163d: // (blue steel)
5380 element = EL_STEEL_CHAR_EXCLAM;
5383 case 0x163e: // (blue steel)
5384 element = EL_STEEL_CHAR_COLON;
5387 case 0x163f: // (blue steel)
5388 element = EL_STEEL_CHAR_LESS;
5391 case 0x1640: // (blue steel)
5392 element = EL_STEEL_CHAR_GREATER;
5395 case 0x1641: // (blue steel)
5396 element = EL_STEEL_CHAR_QUESTION;
5399 case 0x1642: // (blue steel)
5400 element = EL_STEEL_CHAR_COPYRIGHT;
5403 case 0x1643: // (blue steel)
5404 element = EL_STEEL_CHAR_UP;
5407 case 0x1644: // (blue steel)
5408 element = EL_STEEL_CHAR_DOWN;
5411 case 0x1645: // (blue steel)
5412 element = EL_STEEL_CHAR_BUTTON;
5415 case 0x1646: // (blue steel)
5416 element = EL_STEEL_CHAR_PLUS;
5419 case 0x1647: // (blue steel)
5420 element = EL_STEEL_CHAR_MINUS;
5423 case 0x1648: // (blue steel)
5424 element = EL_STEEL_CHAR_APOSTROPHE;
5427 case 0x1649: // (blue steel)
5428 element = EL_STEEL_CHAR_PARENLEFT;
5431 case 0x164a: // (blue steel)
5432 element = EL_STEEL_CHAR_PARENRIGHT;
5435 case 0x164b: // (green steel)
5436 element = EL_STEEL_CHAR_A;
5439 case 0x164c: // (green steel)
5440 element = EL_STEEL_CHAR_B;
5443 case 0x164d: // (green steel)
5444 element = EL_STEEL_CHAR_C;
5447 case 0x164e: // (green steel)
5448 element = EL_STEEL_CHAR_D;
5451 case 0x164f: // (green steel)
5452 element = EL_STEEL_CHAR_E;
5455 case 0x1650: // (green steel)
5456 element = EL_STEEL_CHAR_F;
5459 case 0x1651: // (green steel)
5460 element = EL_STEEL_CHAR_G;
5463 case 0x1652: // (green steel)
5464 element = EL_STEEL_CHAR_H;
5467 case 0x1653: // (green steel)
5468 element = EL_STEEL_CHAR_I;
5471 case 0x1654: // (green steel)
5472 element = EL_STEEL_CHAR_J;
5475 case 0x1655: // (green steel)
5476 element = EL_STEEL_CHAR_K;
5479 case 0x1656: // (green steel)
5480 element = EL_STEEL_CHAR_L;
5483 case 0x1657: // (green steel)
5484 element = EL_STEEL_CHAR_M;
5487 case 0x1658: // (green steel)
5488 element = EL_STEEL_CHAR_N;
5491 case 0x1659: // (green steel)
5492 element = EL_STEEL_CHAR_O;
5495 case 0x165a: // (green steel)
5496 element = EL_STEEL_CHAR_P;
5499 case 0x165b: // (green steel)
5500 element = EL_STEEL_CHAR_Q;
5503 case 0x165c: // (green steel)
5504 element = EL_STEEL_CHAR_R;
5507 case 0x165d: // (green steel)
5508 element = EL_STEEL_CHAR_S;
5511 case 0x165e: // (green steel)
5512 element = EL_STEEL_CHAR_T;
5515 case 0x165f: // (green steel)
5516 element = EL_STEEL_CHAR_U;
5519 case 0x1660: // (green steel)
5520 element = EL_STEEL_CHAR_V;
5523 case 0x1661: // (green steel)
5524 element = EL_STEEL_CHAR_W;
5527 case 0x1662: // (green steel)
5528 element = EL_STEEL_CHAR_X;
5531 case 0x1663: // (green steel)
5532 element = EL_STEEL_CHAR_Y;
5535 case 0x1664: // (green steel)
5536 element = EL_STEEL_CHAR_Z;
5539 case 0x1665: // (green steel)
5540 element = EL_STEEL_CHAR_AUMLAUT;
5543 case 0x1666: // (green steel)
5544 element = EL_STEEL_CHAR_OUMLAUT;
5547 case 0x1667: // (green steel)
5548 element = EL_STEEL_CHAR_UUMLAUT;
5551 case 0x1668: // (green steel)
5552 element = EL_STEEL_CHAR_0;
5555 case 0x1669: // (green steel)
5556 element = EL_STEEL_CHAR_1;
5559 case 0x166a: // (green steel)
5560 element = EL_STEEL_CHAR_2;
5563 case 0x166b: // (green steel)
5564 element = EL_STEEL_CHAR_3;
5567 case 0x166c: // (green steel)
5568 element = EL_STEEL_CHAR_4;
5571 case 0x166d: // (green steel)
5572 element = EL_STEEL_CHAR_5;
5575 case 0x166e: // (green steel)
5576 element = EL_STEEL_CHAR_6;
5579 case 0x166f: // (green steel)
5580 element = EL_STEEL_CHAR_7;
5583 case 0x1670: // (green steel)
5584 element = EL_STEEL_CHAR_8;
5587 case 0x1671: // (green steel)
5588 element = EL_STEEL_CHAR_9;
5591 case 0x1672: // (green steel)
5592 element = EL_STEEL_CHAR_PERIOD;
5595 case 0x1673: // (green steel)
5596 element = EL_STEEL_CHAR_EXCLAM;
5599 case 0x1674: // (green steel)
5600 element = EL_STEEL_CHAR_COLON;
5603 case 0x1675: // (green steel)
5604 element = EL_STEEL_CHAR_LESS;
5607 case 0x1676: // (green steel)
5608 element = EL_STEEL_CHAR_GREATER;
5611 case 0x1677: // (green steel)
5612 element = EL_STEEL_CHAR_QUESTION;
5615 case 0x1678: // (green steel)
5616 element = EL_STEEL_CHAR_COPYRIGHT;
5619 case 0x1679: // (green steel)
5620 element = EL_STEEL_CHAR_UP;
5623 case 0x167a: // (green steel)
5624 element = EL_STEEL_CHAR_DOWN;
5627 case 0x167b: // (green steel)
5628 element = EL_STEEL_CHAR_BUTTON;
5631 case 0x167c: // (green steel)
5632 element = EL_STEEL_CHAR_PLUS;
5635 case 0x167d: // (green steel)
5636 element = EL_STEEL_CHAR_MINUS;
5639 case 0x167e: // (green steel)
5640 element = EL_STEEL_CHAR_APOSTROPHE;
5643 case 0x167f: // (green steel)
5644 element = EL_STEEL_CHAR_PARENLEFT;
5647 case 0x1680: // (green steel)
5648 element = EL_STEEL_CHAR_PARENRIGHT;
5651 case 0x1681: // gate (red)
5652 element = EL_EM_GATE_1;
5655 case 0x1682: // secret gate (red)
5656 element = EL_EM_GATE_1_GRAY;
5659 case 0x1683: // gate (yellow)
5660 element = EL_EM_GATE_2;
5663 case 0x1684: // secret gate (yellow)
5664 element = EL_EM_GATE_2_GRAY;
5667 case 0x1685: // gate (blue)
5668 element = EL_EM_GATE_4;
5671 case 0x1686: // secret gate (blue)
5672 element = EL_EM_GATE_4_GRAY;
5675 case 0x1687: // gate (green)
5676 element = EL_EM_GATE_3;
5679 case 0x1688: // secret gate (green)
5680 element = EL_EM_GATE_3_GRAY;
5683 case 0x1689: // gate (white)
5684 element = EL_DC_GATE_WHITE;
5687 case 0x168a: // secret gate (white)
5688 element = EL_DC_GATE_WHITE_GRAY;
5691 case 0x168b: // secret gate (no key)
5692 element = EL_DC_GATE_FAKE_GRAY;
5696 element = EL_ROBOT_WHEEL;
5700 element = EL_DC_TIMEGATE_SWITCH;
5704 element = EL_ACID_POOL_BOTTOM;
5708 element = EL_ACID_POOL_TOPLEFT;
5712 element = EL_ACID_POOL_TOPRIGHT;
5716 element = EL_ACID_POOL_BOTTOMLEFT;
5720 element = EL_ACID_POOL_BOTTOMRIGHT;
5724 element = EL_STEELWALL;
5728 element = EL_STEELWALL_SLIPPERY;
5731 case 0x1695: // steel wall (not round)
5732 element = EL_STEELWALL;
5735 case 0x1696: // steel wall (left)
5736 element = EL_DC_STEELWALL_1_LEFT;
5739 case 0x1697: // steel wall (bottom)
5740 element = EL_DC_STEELWALL_1_BOTTOM;
5743 case 0x1698: // steel wall (right)
5744 element = EL_DC_STEELWALL_1_RIGHT;
5747 case 0x1699: // steel wall (top)
5748 element = EL_DC_STEELWALL_1_TOP;
5751 case 0x169a: // steel wall (left/bottom)
5752 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5755 case 0x169b: // steel wall (right/bottom)
5756 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5759 case 0x169c: // steel wall (right/top)
5760 element = EL_DC_STEELWALL_1_TOPRIGHT;
5763 case 0x169d: // steel wall (left/top)
5764 element = EL_DC_STEELWALL_1_TOPLEFT;
5767 case 0x169e: // steel wall (right/bottom small)
5768 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5771 case 0x169f: // steel wall (left/bottom small)
5772 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5775 case 0x16a0: // steel wall (right/top small)
5776 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5779 case 0x16a1: // steel wall (left/top small)
5780 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5783 case 0x16a2: // steel wall (left/right)
5784 element = EL_DC_STEELWALL_1_VERTICAL;
5787 case 0x16a3: // steel wall (top/bottom)
5788 element = EL_DC_STEELWALL_1_HORIZONTAL;
5791 case 0x16a4: // steel wall 2 (left end)
5792 element = EL_DC_STEELWALL_2_LEFT;
5795 case 0x16a5: // steel wall 2 (right end)
5796 element = EL_DC_STEELWALL_2_RIGHT;
5799 case 0x16a6: // steel wall 2 (top end)
5800 element = EL_DC_STEELWALL_2_TOP;
5803 case 0x16a7: // steel wall 2 (bottom end)
5804 element = EL_DC_STEELWALL_2_BOTTOM;
5807 case 0x16a8: // steel wall 2 (left/right)
5808 element = EL_DC_STEELWALL_2_HORIZONTAL;
5811 case 0x16a9: // steel wall 2 (up/down)
5812 element = EL_DC_STEELWALL_2_VERTICAL;
5815 case 0x16aa: // steel wall 2 (mid)
5816 element = EL_DC_STEELWALL_2_MIDDLE;
5820 element = EL_SIGN_EXCLAMATION;
5824 element = EL_SIGN_RADIOACTIVITY;
5828 element = EL_SIGN_STOP;
5832 element = EL_SIGN_WHEELCHAIR;
5836 element = EL_SIGN_PARKING;
5840 element = EL_SIGN_NO_ENTRY;
5844 element = EL_SIGN_HEART;
5848 element = EL_SIGN_GIVE_WAY;
5852 element = EL_SIGN_ENTRY_FORBIDDEN;
5856 element = EL_SIGN_EMERGENCY_EXIT;
5860 element = EL_SIGN_YIN_YANG;
5864 element = EL_WALL_EMERALD;
5868 element = EL_WALL_DIAMOND;
5872 element = EL_WALL_PEARL;
5876 element = EL_WALL_CRYSTAL;
5880 element = EL_INVISIBLE_WALL;
5884 element = EL_INVISIBLE_STEELWALL;
5888 // EL_INVISIBLE_SAND
5891 element = EL_LIGHT_SWITCH;
5895 element = EL_ENVELOPE_1;
5899 if (element >= 0x0117 && element <= 0x036e) // (?)
5900 element = EL_DIAMOND;
5901 else if (element >= 0x042d && element <= 0x0684) // (?)
5902 element = EL_EMERALD;
5903 else if (element >= 0x157c && element <= 0x158b)
5905 else if (element >= 0x1590 && element <= 0x159f)
5906 element = EL_DC_LANDMINE;
5907 else if (element >= 0x16bc && element <= 0x16cb)
5908 element = EL_INVISIBLE_SAND;
5911 Warn("unknown Diamond Caves element 0x%04x", element);
5913 element = EL_UNKNOWN;
5918 return getMappedElement(element);
5921 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5923 byte header[DC_LEVEL_HEADER_SIZE];
5925 int envelope_header_pos = 62;
5926 int envelope_content_pos = 94;
5927 int level_name_pos = 251;
5928 int level_author_pos = 292;
5929 int envelope_header_len;
5930 int envelope_content_len;
5932 int level_author_len;
5934 int num_yamyam_contents;
5937 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5939 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5941 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5943 header[i * 2 + 0] = header_word >> 8;
5944 header[i * 2 + 1] = header_word & 0xff;
5947 // read some values from level header to check level decoding integrity
5948 fieldx = header[6] | (header[7] << 8);
5949 fieldy = header[8] | (header[9] << 8);
5950 num_yamyam_contents = header[60] | (header[61] << 8);
5952 // do some simple sanity checks to ensure that level was correctly decoded
5953 if (fieldx < 1 || fieldx > 256 ||
5954 fieldy < 1 || fieldy > 256 ||
5955 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5957 level->no_valid_file = TRUE;
5959 Warn("cannot decode level from stream -- using empty level");
5964 // maximum envelope header size is 31 bytes
5965 envelope_header_len = header[envelope_header_pos];
5966 // maximum envelope content size is 110 (156?) bytes
5967 envelope_content_len = header[envelope_content_pos];
5969 // maximum level title size is 40 bytes
5970 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5971 // maximum level author size is 30 (51?) bytes
5972 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5976 for (i = 0; i < envelope_header_len; i++)
5977 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5978 level->envelope[0].text[envelope_size++] =
5979 header[envelope_header_pos + 1 + i];
5981 if (envelope_header_len > 0 && envelope_content_len > 0)
5983 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5984 level->envelope[0].text[envelope_size++] = '\n';
5985 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5986 level->envelope[0].text[envelope_size++] = '\n';
5989 for (i = 0; i < envelope_content_len; i++)
5990 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5991 level->envelope[0].text[envelope_size++] =
5992 header[envelope_content_pos + 1 + i];
5994 level->envelope[0].text[envelope_size] = '\0';
5996 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5997 level->envelope[0].ysize = 10;
5998 level->envelope[0].autowrap = TRUE;
5999 level->envelope[0].centered = TRUE;
6001 for (i = 0; i < level_name_len; i++)
6002 level->name[i] = header[level_name_pos + 1 + i];
6003 level->name[level_name_len] = '\0';
6005 for (i = 0; i < level_author_len; i++)
6006 level->author[i] = header[level_author_pos + 1 + i];
6007 level->author[level_author_len] = '\0';
6009 num_yamyam_contents = header[60] | (header[61] << 8);
6010 level->num_yamyam_contents =
6011 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6013 for (i = 0; i < num_yamyam_contents; i++)
6015 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6017 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6018 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6020 if (i < MAX_ELEMENT_CONTENTS)
6021 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6025 fieldx = header[6] | (header[7] << 8);
6026 fieldy = header[8] | (header[9] << 8);
6027 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6028 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6030 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6032 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6033 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6035 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6036 level->field[x][y] = getMappedElement_DC(element_dc);
6039 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6040 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6041 level->field[x][y] = EL_PLAYER_1;
6043 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6044 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6045 level->field[x][y] = EL_PLAYER_2;
6047 level->gems_needed = header[18] | (header[19] << 8);
6049 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6050 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6051 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6052 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6053 level->score[SC_NUT] = header[28] | (header[29] << 8);
6054 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6055 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6056 level->score[SC_BUG] = header[34] | (header[35] << 8);
6057 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6058 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6059 level->score[SC_KEY] = header[40] | (header[41] << 8);
6060 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6062 level->time = header[44] | (header[45] << 8);
6064 level->amoeba_speed = header[46] | (header[47] << 8);
6065 level->time_light = header[48] | (header[49] << 8);
6066 level->time_timegate = header[50] | (header[51] << 8);
6067 level->time_wheel = header[52] | (header[53] << 8);
6068 level->time_magic_wall = header[54] | (header[55] << 8);
6069 level->extra_time = header[56] | (header[57] << 8);
6070 level->shield_normal_time = header[58] | (header[59] << 8);
6072 // shield and extra time elements do not have a score
6073 level->score[SC_SHIELD] = 0;
6074 level->extra_time_score = 0;
6076 // set time for normal and deadly shields to the same value
6077 level->shield_deadly_time = level->shield_normal_time;
6079 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6080 // can slip down from flat walls, like normal walls and steel walls
6081 level->em_slippery_gems = TRUE;
6083 // time score is counted for each 10 seconds left in Diamond Caves levels
6084 level->time_score_base = 10;
6087 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6088 struct LevelFileInfo *level_file_info,
6089 boolean level_info_only)
6091 char *filename = level_file_info->filename;
6093 int num_magic_bytes = 8;
6094 char magic_bytes[num_magic_bytes + 1];
6095 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6097 if (!(file = openFile(filename, MODE_READ)))
6099 level->no_valid_file = TRUE;
6101 if (!level_info_only)
6102 Warn("cannot read level '%s' -- using empty level", filename);
6107 // fseek(file, 0x0000, SEEK_SET);
6109 if (level_file_info->packed)
6111 // read "magic bytes" from start of file
6112 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6113 magic_bytes[0] = '\0';
6115 // check "magic bytes" for correct file format
6116 if (!strPrefix(magic_bytes, "DC2"))
6118 level->no_valid_file = TRUE;
6120 Warn("unknown DC level file '%s' -- using empty level", filename);
6125 if (strPrefix(magic_bytes, "DC2Win95") ||
6126 strPrefix(magic_bytes, "DC2Win98"))
6128 int position_first_level = 0x00fa;
6129 int extra_bytes = 4;
6132 // advance file stream to first level inside the level package
6133 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6135 // each block of level data is followed by block of non-level data
6136 num_levels_to_skip *= 2;
6138 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6139 while (num_levels_to_skip >= 0)
6141 // advance file stream to next level inside the level package
6142 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6144 level->no_valid_file = TRUE;
6146 Warn("cannot fseek in file '%s' -- using empty level", filename);
6151 // skip apparently unused extra bytes following each level
6152 ReadUnusedBytesFromFile(file, extra_bytes);
6154 // read size of next level in level package
6155 skip_bytes = getFile32BitLE(file);
6157 num_levels_to_skip--;
6162 level->no_valid_file = TRUE;
6164 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6170 LoadLevelFromFileStream_DC(file, level);
6176 // ----------------------------------------------------------------------------
6177 // functions for loading SB level
6178 // ----------------------------------------------------------------------------
6180 int getMappedElement_SB(int element_ascii, boolean use_ces)
6188 sb_element_mapping[] =
6190 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6191 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6192 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6193 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6194 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6195 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6196 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6197 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6204 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6205 if (element_ascii == sb_element_mapping[i].ascii)
6206 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6208 return EL_UNDEFINED;
6211 static void SetLevelSettings_SB(struct LevelInfo *level)
6215 level->use_step_counter = TRUE;
6218 level->score[SC_TIME_BONUS] = 0;
6219 level->time_score_base = 1;
6220 level->rate_time_over_score = TRUE;
6223 level->auto_exit_sokoban = TRUE;
6226 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6227 struct LevelFileInfo *level_file_info,
6228 boolean level_info_only)
6230 char *filename = level_file_info->filename;
6231 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6232 char last_comment[MAX_LINE_LEN];
6233 char level_name[MAX_LINE_LEN];
6236 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6237 boolean read_continued_line = FALSE;
6238 boolean reading_playfield = FALSE;
6239 boolean got_valid_playfield_line = FALSE;
6240 boolean invalid_playfield_char = FALSE;
6241 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6242 int file_level_nr = 0;
6243 int x = 0, y = 0; // initialized to make compilers happy
6245 last_comment[0] = '\0';
6246 level_name[0] = '\0';
6248 if (!(file = openFile(filename, MODE_READ)))
6250 level->no_valid_file = TRUE;
6252 if (!level_info_only)
6253 Warn("cannot read level '%s' -- using empty level", filename);
6258 while (!checkEndOfFile(file))
6260 // level successfully read, but next level may follow here
6261 if (!got_valid_playfield_line && reading_playfield)
6263 // read playfield from single level file -- skip remaining file
6264 if (!level_file_info->packed)
6267 if (file_level_nr >= num_levels_to_skip)
6272 last_comment[0] = '\0';
6273 level_name[0] = '\0';
6275 reading_playfield = FALSE;
6278 got_valid_playfield_line = FALSE;
6280 // read next line of input file
6281 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6284 // cut trailing line break (this can be newline and/or carriage return)
6285 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6286 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6289 // copy raw input line for later use (mainly debugging output)
6290 strcpy(line_raw, line);
6292 if (read_continued_line)
6294 // append new line to existing line, if there is enough space
6295 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6296 strcat(previous_line, line_ptr);
6298 strcpy(line, previous_line); // copy storage buffer to line
6300 read_continued_line = FALSE;
6303 // if the last character is '\', continue at next line
6304 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6306 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6307 strcpy(previous_line, line); // copy line to storage buffer
6309 read_continued_line = TRUE;
6315 if (line[0] == '\0')
6318 // extract comment text from comment line
6321 for (line_ptr = line; *line_ptr; line_ptr++)
6322 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6325 strcpy(last_comment, line_ptr);
6330 // extract level title text from line containing level title
6331 if (line[0] == '\'')
6333 strcpy(level_name, &line[1]);
6335 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6336 level_name[strlen(level_name) - 1] = '\0';
6341 // skip lines containing only spaces (or empty lines)
6342 for (line_ptr = line; *line_ptr; line_ptr++)
6343 if (*line_ptr != ' ')
6345 if (*line_ptr == '\0')
6348 // at this point, we have found a line containing part of a playfield
6350 got_valid_playfield_line = TRUE;
6352 if (!reading_playfield)
6354 reading_playfield = TRUE;
6355 invalid_playfield_char = FALSE;
6357 for (x = 0; x < MAX_LEV_FIELDX; x++)
6358 for (y = 0; y < MAX_LEV_FIELDY; y++)
6359 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6364 // start with topmost tile row
6368 // skip playfield line if larger row than allowed
6369 if (y >= MAX_LEV_FIELDY)
6372 // start with leftmost tile column
6375 // read playfield elements from line
6376 for (line_ptr = line; *line_ptr; line_ptr++)
6378 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6380 // stop parsing playfield line if larger column than allowed
6381 if (x >= MAX_LEV_FIELDX)
6384 if (mapped_sb_element == EL_UNDEFINED)
6386 invalid_playfield_char = TRUE;
6391 level->field[x][y] = mapped_sb_element;
6393 // continue with next tile column
6396 level->fieldx = MAX(x, level->fieldx);
6399 if (invalid_playfield_char)
6401 // if first playfield line, treat invalid lines as comment lines
6403 reading_playfield = FALSE;
6408 // continue with next tile row
6416 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6417 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6419 if (!reading_playfield)
6421 level->no_valid_file = TRUE;
6423 Warn("cannot read level '%s' -- using empty level", filename);
6428 if (*level_name != '\0')
6430 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6431 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6433 else if (*last_comment != '\0')
6435 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6436 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6440 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6443 // set all empty fields beyond the border walls to invisible steel wall
6444 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6446 if ((x == 0 || x == level->fieldx - 1 ||
6447 y == 0 || y == level->fieldy - 1) &&
6448 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6449 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6450 level->field, level->fieldx, level->fieldy);
6453 // set special level settings for Sokoban levels
6454 SetLevelSettings_SB(level);
6456 if (load_xsb_to_ces)
6458 // special global settings can now be set in level template
6459 level->use_custom_template = TRUE;
6464 // -------------------------------------------------------------------------
6465 // functions for handling native levels
6466 // -------------------------------------------------------------------------
6468 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6469 struct LevelFileInfo *level_file_info,
6470 boolean level_info_only)
6474 // determine position of requested level inside level package
6475 if (level_file_info->packed)
6476 pos = level_file_info->nr - leveldir_current->first_level;
6478 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6479 level->no_valid_file = TRUE;
6482 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6483 struct LevelFileInfo *level_file_info,
6484 boolean level_info_only)
6486 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6487 level->no_valid_file = TRUE;
6490 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6491 struct LevelFileInfo *level_file_info,
6492 boolean level_info_only)
6496 // determine position of requested level inside level package
6497 if (level_file_info->packed)
6498 pos = level_file_info->nr - leveldir_current->first_level;
6500 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6501 level->no_valid_file = TRUE;
6504 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6505 struct LevelFileInfo *level_file_info,
6506 boolean level_info_only)
6508 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6509 level->no_valid_file = TRUE;
6512 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6514 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6515 CopyNativeLevel_RND_to_BD(level);
6516 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6517 CopyNativeLevel_RND_to_EM(level);
6518 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6519 CopyNativeLevel_RND_to_SP(level);
6520 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6521 CopyNativeLevel_RND_to_MM(level);
6524 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6526 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6527 CopyNativeLevel_BD_to_RND(level);
6528 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6529 CopyNativeLevel_EM_to_RND(level);
6530 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6531 CopyNativeLevel_SP_to_RND(level);
6532 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6533 CopyNativeLevel_MM_to_RND(level);
6536 void SaveNativeLevel(struct LevelInfo *level)
6538 // saving native level files only supported for some game engines
6539 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6540 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6543 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6544 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6545 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6546 char *filename = getLevelFilenameFromBasename(basename);
6548 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6551 boolean success = FALSE;
6553 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6555 CopyNativeLevel_RND_to_BD(level);
6556 // CopyNativeTape_RND_to_BD(level);
6558 success = SaveNativeLevel_BD(filename);
6560 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6562 CopyNativeLevel_RND_to_SP(level);
6563 CopyNativeTape_RND_to_SP(level);
6565 success = SaveNativeLevel_SP(filename);
6569 Request("Native level file saved!", REQ_CONFIRM);
6571 Request("Failed to save native level file!", REQ_CONFIRM);
6575 // ----------------------------------------------------------------------------
6576 // functions for loading generic level
6577 // ----------------------------------------------------------------------------
6579 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6580 struct LevelFileInfo *level_file_info,
6581 boolean level_info_only)
6583 // always start with reliable default values
6584 setLevelInfoToDefaults(level, level_info_only, TRUE);
6586 switch (level_file_info->type)
6588 case LEVEL_FILE_TYPE_RND:
6589 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6592 case LEVEL_FILE_TYPE_BD:
6593 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6594 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6597 case LEVEL_FILE_TYPE_EM:
6598 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6599 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6602 case LEVEL_FILE_TYPE_SP:
6603 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6604 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6607 case LEVEL_FILE_TYPE_MM:
6608 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6609 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6612 case LEVEL_FILE_TYPE_DC:
6613 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6616 case LEVEL_FILE_TYPE_SB:
6617 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6621 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6625 // if level file is invalid, restore level structure to default values
6626 if (level->no_valid_file)
6627 setLevelInfoToDefaults(level, level_info_only, FALSE);
6629 if (check_special_flags("use_native_bd_game_engine"))
6630 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6632 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6633 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6635 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6636 CopyNativeLevel_Native_to_RND(level);
6639 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6641 static struct LevelFileInfo level_file_info;
6643 // always start with reliable default values
6644 setFileInfoToDefaults(&level_file_info);
6646 level_file_info.nr = 0; // unknown level number
6647 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6649 setString(&level_file_info.filename, filename);
6651 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6654 static void LoadLevel_InitVersion(struct LevelInfo *level)
6658 if (leveldir_current == NULL) // only when dumping level
6661 // all engine modifications also valid for levels which use latest engine
6662 if (level->game_version < VERSION_IDENT(3,2,0,5))
6664 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6665 level->time_score_base = 10;
6668 if (leveldir_current->latest_engine)
6670 // ---------- use latest game engine --------------------------------------
6672 /* For all levels which are forced to use the latest game engine version
6673 (normally all but user contributed, private and undefined levels), set
6674 the game engine version to the actual version; this allows for actual
6675 corrections in the game engine to take effect for existing, converted
6676 levels (from "classic" or other existing games) to make the emulation
6677 of the corresponding game more accurate, while (hopefully) not breaking
6678 existing levels created from other players. */
6680 level->game_version = GAME_VERSION_ACTUAL;
6682 /* Set special EM style gems behaviour: EM style gems slip down from
6683 normal, steel and growing wall. As this is a more fundamental change,
6684 it seems better to set the default behaviour to "off" (as it is more
6685 natural) and make it configurable in the level editor (as a property
6686 of gem style elements). Already existing converted levels (neither
6687 private nor contributed levels) are changed to the new behaviour. */
6689 if (level->file_version < FILE_VERSION_2_0)
6690 level->em_slippery_gems = TRUE;
6695 // ---------- use game engine the level was created with --------------------
6697 /* For all levels which are not forced to use the latest game engine
6698 version (normally user contributed, private and undefined levels),
6699 use the version of the game engine the levels were created for.
6701 Since 2.0.1, the game engine version is now directly stored
6702 in the level file (chunk "VERS"), so there is no need anymore
6703 to set the game version from the file version (except for old,
6704 pre-2.0 levels, where the game version is still taken from the
6705 file format version used to store the level -- see above). */
6707 // player was faster than enemies in 1.0.0 and before
6708 if (level->file_version == FILE_VERSION_1_0)
6709 for (i = 0; i < MAX_PLAYERS; i++)
6710 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6712 // default behaviour for EM style gems was "slippery" only in 2.0.1
6713 if (level->game_version == VERSION_IDENT(2,0,1,0))
6714 level->em_slippery_gems = TRUE;
6716 // springs could be pushed over pits before (pre-release version) 2.2.0
6717 if (level->game_version < VERSION_IDENT(2,2,0,0))
6718 level->use_spring_bug = TRUE;
6720 if (level->game_version < VERSION_IDENT(3,2,0,5))
6722 // time orb caused limited time in endless time levels before 3.2.0-5
6723 level->use_time_orb_bug = TRUE;
6725 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6726 level->block_snap_field = FALSE;
6728 // extra time score was same value as time left score before 3.2.0-5
6729 level->extra_time_score = level->score[SC_TIME_BONUS];
6732 if (level->game_version < VERSION_IDENT(3,2,0,7))
6734 // default behaviour for snapping was "not continuous" before 3.2.0-7
6735 level->continuous_snapping = FALSE;
6738 // only few elements were able to actively move into acid before 3.1.0
6739 // trigger settings did not exist before 3.1.0; set to default "any"
6740 if (level->game_version < VERSION_IDENT(3,1,0,0))
6742 // correct "can move into acid" settings (all zero in old levels)
6744 level->can_move_into_acid_bits = 0; // nothing can move into acid
6745 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6747 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6748 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6749 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6750 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6752 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6753 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6755 // correct trigger settings (stored as zero == "none" in old levels)
6757 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6759 int element = EL_CUSTOM_START + i;
6760 struct ElementInfo *ei = &element_info[element];
6762 for (j = 0; j < ei->num_change_pages; j++)
6764 struct ElementChangeInfo *change = &ei->change_page[j];
6766 change->trigger_player = CH_PLAYER_ANY;
6767 change->trigger_page = CH_PAGE_ANY;
6772 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6774 int element = EL_CUSTOM_256;
6775 struct ElementInfo *ei = &element_info[element];
6776 struct ElementChangeInfo *change = &ei->change_page[0];
6778 /* This is needed to fix a problem that was caused by a bugfix in function
6779 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6780 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6781 not replace walkable elements, but instead just placed the player on it,
6782 without placing the Sokoban field under the player). Unfortunately, this
6783 breaks "Snake Bite" style levels when the snake is halfway through a door
6784 that just closes (the snake head is still alive and can be moved in this
6785 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6786 player (without Sokoban element) which then gets killed as designed). */
6788 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6789 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6790 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6791 change->target_element = EL_PLAYER_1;
6794 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6795 if (level->game_version < VERSION_IDENT(3,2,5,0))
6797 /* This is needed to fix a problem that was caused by a bugfix in function
6798 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6799 corrects the behaviour when a custom element changes to another custom
6800 element with a higher element number that has change actions defined.
6801 Normally, only one change per frame is allowed for custom elements.
6802 Therefore, it is checked if a custom element already changed in the
6803 current frame; if it did, subsequent changes are suppressed.
6804 Unfortunately, this is only checked for element changes, but not for
6805 change actions, which are still executed. As the function above loops
6806 through all custom elements from lower to higher, an element change
6807 resulting in a lower CE number won't be checked again, while a target
6808 element with a higher number will also be checked, and potential change
6809 actions will get executed for this CE, too (which is wrong), while
6810 further changes are ignored (which is correct). As this bugfix breaks
6811 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6812 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6813 behaviour for existing levels and tapes that make use of this bug */
6815 level->use_action_after_change_bug = TRUE;
6818 // not centering level after relocating player was default only in 3.2.3
6819 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6820 level->shifted_relocation = TRUE;
6822 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6823 if (level->game_version < VERSION_IDENT(3,2,6,0))
6824 level->em_explodes_by_fire = TRUE;
6826 // levels were solved by the first player entering an exit up to 4.1.0.0
6827 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6828 level->solved_by_one_player = TRUE;
6830 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6831 if (level->game_version < VERSION_IDENT(4,1,1,1))
6832 level->use_life_bugs = TRUE;
6834 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6835 if (level->game_version < VERSION_IDENT(4,1,1,1))
6836 level->sb_objects_needed = FALSE;
6838 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6839 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6840 level->finish_dig_collect = FALSE;
6842 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6843 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6844 level->keep_walkable_ce = TRUE;
6847 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6849 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6852 // check if this level is (not) a Sokoban level
6853 for (y = 0; y < level->fieldy; y++)
6854 for (x = 0; x < level->fieldx; x++)
6855 if (!IS_SB_ELEMENT(Tile[x][y]))
6856 is_sokoban_level = FALSE;
6858 if (is_sokoban_level)
6860 // set special level settings for Sokoban levels
6861 SetLevelSettings_SB(level);
6865 static void LoadLevel_InitSettings(struct LevelInfo *level)
6867 // adjust level settings for (non-native) Sokoban-style levels
6868 LoadLevel_InitSettings_SB(level);
6870 // rename levels with title "nameless level" or if renaming is forced
6871 if (leveldir_current->empty_level_name != NULL &&
6872 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6873 leveldir_current->force_level_name))
6874 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6875 leveldir_current->empty_level_name, level_nr);
6878 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6882 // map elements that have changed in newer versions
6883 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6884 level->game_version);
6885 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6886 for (x = 0; x < 3; x++)
6887 for (y = 0; y < 3; y++)
6888 level->yamyam_content[i].e[x][y] =
6889 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6890 level->game_version);
6894 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6898 // map custom element change events that have changed in newer versions
6899 // (these following values were accidentally changed in version 3.0.1)
6900 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6901 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6903 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6905 int element = EL_CUSTOM_START + i;
6907 // order of checking and copying events to be mapped is important
6908 // (do not change the start and end value -- they are constant)
6909 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6911 if (HAS_CHANGE_EVENT(element, j - 2))
6913 SET_CHANGE_EVENT(element, j - 2, FALSE);
6914 SET_CHANGE_EVENT(element, j, TRUE);
6918 // order of checking and copying events to be mapped is important
6919 // (do not change the start and end value -- they are constant)
6920 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6922 if (HAS_CHANGE_EVENT(element, j - 1))
6924 SET_CHANGE_EVENT(element, j - 1, FALSE);
6925 SET_CHANGE_EVENT(element, j, TRUE);
6931 // initialize "can_change" field for old levels with only one change page
6932 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6934 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6936 int element = EL_CUSTOM_START + i;
6938 if (CAN_CHANGE(element))
6939 element_info[element].change->can_change = TRUE;
6943 // correct custom element values (for old levels without these options)
6944 if (level->game_version < VERSION_IDENT(3,1,1,0))
6946 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6948 int element = EL_CUSTOM_START + i;
6949 struct ElementInfo *ei = &element_info[element];
6951 if (ei->access_direction == MV_NO_DIRECTION)
6952 ei->access_direction = MV_ALL_DIRECTIONS;
6956 // correct custom element values (fix invalid values for all versions)
6959 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6961 int element = EL_CUSTOM_START + i;
6962 struct ElementInfo *ei = &element_info[element];
6964 for (j = 0; j < ei->num_change_pages; j++)
6966 struct ElementChangeInfo *change = &ei->change_page[j];
6968 if (change->trigger_player == CH_PLAYER_NONE)
6969 change->trigger_player = CH_PLAYER_ANY;
6971 if (change->trigger_side == CH_SIDE_NONE)
6972 change->trigger_side = CH_SIDE_ANY;
6977 // initialize "can_explode" field for old levels which did not store this
6978 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6979 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6981 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6983 int element = EL_CUSTOM_START + i;
6985 if (EXPLODES_1X1_OLD(element))
6986 element_info[element].explosion_type = EXPLODES_1X1;
6988 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6989 EXPLODES_SMASHED(element) ||
6990 EXPLODES_IMPACT(element)));
6994 // correct previously hard-coded move delay values for maze runner style
6995 if (level->game_version < VERSION_IDENT(3,1,1,0))
6997 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6999 int element = EL_CUSTOM_START + i;
7001 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7003 // previously hard-coded and therefore ignored
7004 element_info[element].move_delay_fixed = 9;
7005 element_info[element].move_delay_random = 0;
7010 // set some other uninitialized values of custom elements in older levels
7011 if (level->game_version < VERSION_IDENT(3,1,0,0))
7013 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7015 int element = EL_CUSTOM_START + i;
7017 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7019 element_info[element].explosion_delay = 17;
7020 element_info[element].ignition_delay = 8;
7024 // set mouse click change events to work for left/middle/right mouse button
7025 if (level->game_version < VERSION_IDENT(4,2,3,0))
7027 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7029 int element = EL_CUSTOM_START + i;
7030 struct ElementInfo *ei = &element_info[element];
7032 for (j = 0; j < ei->num_change_pages; j++)
7034 struct ElementChangeInfo *change = &ei->change_page[j];
7036 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7037 change->has_event[CE_PRESSED_BY_MOUSE] ||
7038 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7039 change->has_event[CE_MOUSE_PRESSED_ON_X])
7040 change->trigger_side = CH_SIDE_ANY;
7046 static void LoadLevel_InitElements(struct LevelInfo *level)
7048 LoadLevel_InitStandardElements(level);
7050 if (level->file_has_custom_elements)
7051 LoadLevel_InitCustomElements(level);
7053 // initialize element properties for level editor etc.
7054 InitElementPropertiesEngine(level->game_version);
7055 InitElementPropertiesGfxElement();
7058 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7062 // map elements that have changed in newer versions
7063 for (y = 0; y < level->fieldy; y++)
7064 for (x = 0; x < level->fieldx; x++)
7065 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7066 level->game_version);
7068 // clear unused playfield data (nicer if level gets resized in editor)
7069 for (x = 0; x < MAX_LEV_FIELDX; x++)
7070 for (y = 0; y < MAX_LEV_FIELDY; y++)
7071 if (x >= level->fieldx || y >= level->fieldy)
7072 level->field[x][y] = EL_EMPTY;
7074 // copy elements to runtime playfield array
7075 for (x = 0; x < MAX_LEV_FIELDX; x++)
7076 for (y = 0; y < MAX_LEV_FIELDY; y++)
7077 Tile[x][y] = level->field[x][y];
7079 // initialize level size variables for faster access
7080 lev_fieldx = level->fieldx;
7081 lev_fieldy = level->fieldy;
7083 // determine border element for this level
7084 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7085 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7090 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7092 struct LevelFileInfo *level_file_info = &level->file_info;
7094 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7095 CopyNativeLevel_RND_to_Native(level);
7098 static void LoadLevelTemplate_LoadAndInit(void)
7100 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7102 LoadLevel_InitVersion(&level_template);
7103 LoadLevel_InitElements(&level_template);
7104 LoadLevel_InitSettings(&level_template);
7106 ActivateLevelTemplate();
7109 void LoadLevelTemplate(int nr)
7111 if (!fileExists(getGlobalLevelTemplateFilename()))
7113 Warn("no level template found for this level");
7118 setLevelFileInfo(&level_template.file_info, nr);
7120 LoadLevelTemplate_LoadAndInit();
7123 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7125 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7127 LoadLevelTemplate_LoadAndInit();
7130 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7132 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7134 if (level.use_custom_template)
7136 if (network_level != NULL)
7137 LoadNetworkLevelTemplate(network_level);
7139 LoadLevelTemplate(-1);
7142 LoadLevel_InitVersion(&level);
7143 LoadLevel_InitElements(&level);
7144 LoadLevel_InitPlayfield(&level);
7145 LoadLevel_InitSettings(&level);
7147 LoadLevel_InitNativeEngines(&level);
7150 void LoadLevel(int nr)
7152 SetLevelSetInfo(leveldir_current->identifier, nr);
7154 setLevelFileInfo(&level.file_info, nr);
7156 LoadLevel_LoadAndInit(NULL);
7159 void LoadLevelInfoOnly(int nr)
7161 setLevelFileInfo(&level.file_info, nr);
7163 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7166 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7168 SetLevelSetInfo(network_level->leveldir_identifier,
7169 network_level->file_info.nr);
7171 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7173 LoadLevel_LoadAndInit(network_level);
7176 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7180 chunk_size += putFileVersion(file, level->file_version);
7181 chunk_size += putFileVersion(file, level->game_version);
7186 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7190 chunk_size += putFile16BitBE(file, level->creation_date.year);
7191 chunk_size += putFile8Bit(file, level->creation_date.month);
7192 chunk_size += putFile8Bit(file, level->creation_date.day);
7197 #if ENABLE_HISTORIC_CHUNKS
7198 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7202 putFile8Bit(file, level->fieldx);
7203 putFile8Bit(file, level->fieldy);
7205 putFile16BitBE(file, level->time);
7206 putFile16BitBE(file, level->gems_needed);
7208 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7209 putFile8Bit(file, level->name[i]);
7211 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7212 putFile8Bit(file, level->score[i]);
7214 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7215 for (y = 0; y < 3; y++)
7216 for (x = 0; x < 3; x++)
7217 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7218 level->yamyam_content[i].e[x][y]));
7219 putFile8Bit(file, level->amoeba_speed);
7220 putFile8Bit(file, level->time_magic_wall);
7221 putFile8Bit(file, level->time_wheel);
7222 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7223 level->amoeba_content));
7224 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7225 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7226 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7227 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7229 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7231 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7232 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7233 putFile32BitBE(file, level->can_move_into_acid_bits);
7234 putFile8Bit(file, level->dont_collide_with_bits);
7236 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7237 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7239 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7240 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7241 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7243 putFile8Bit(file, level->game_engine_type);
7245 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7249 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7254 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7255 chunk_size += putFile8Bit(file, level->name[i]);
7260 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7265 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7266 chunk_size += putFile8Bit(file, level->author[i]);
7271 #if ENABLE_HISTORIC_CHUNKS
7272 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7277 for (y = 0; y < level->fieldy; y++)
7278 for (x = 0; x < level->fieldx; x++)
7279 if (level->encoding_16bit_field)
7280 chunk_size += putFile16BitBE(file, level->field[x][y]);
7282 chunk_size += putFile8Bit(file, level->field[x][y]);
7288 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7293 for (y = 0; y < level->fieldy; y++)
7294 for (x = 0; x < level->fieldx; x++)
7295 chunk_size += putFile16BitBE(file, level->field[x][y]);
7300 #if ENABLE_HISTORIC_CHUNKS
7301 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7305 putFile8Bit(file, EL_YAMYAM);
7306 putFile8Bit(file, level->num_yamyam_contents);
7307 putFile8Bit(file, 0);
7308 putFile8Bit(file, 0);
7310 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7311 for (y = 0; y < 3; y++)
7312 for (x = 0; x < 3; x++)
7313 if (level->encoding_16bit_field)
7314 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7316 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7320 #if ENABLE_HISTORIC_CHUNKS
7321 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7324 int num_contents, content_xsize, content_ysize;
7325 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7327 if (element == EL_YAMYAM)
7329 num_contents = level->num_yamyam_contents;
7333 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7334 for (y = 0; y < 3; y++)
7335 for (x = 0; x < 3; x++)
7336 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7338 else if (element == EL_BD_AMOEBA)
7344 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7345 for (y = 0; y < 3; y++)
7346 for (x = 0; x < 3; x++)
7347 content_array[i][x][y] = EL_EMPTY;
7348 content_array[0][0][0] = level->amoeba_content;
7352 // chunk header already written -- write empty chunk data
7353 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7355 Warn("cannot save content for element '%d'", element);
7360 putFile16BitBE(file, element);
7361 putFile8Bit(file, num_contents);
7362 putFile8Bit(file, content_xsize);
7363 putFile8Bit(file, content_ysize);
7365 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7367 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7368 for (y = 0; y < 3; y++)
7369 for (x = 0; x < 3; x++)
7370 putFile16BitBE(file, content_array[i][x][y]);
7374 #if ENABLE_HISTORIC_CHUNKS
7375 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7377 int envelope_nr = element - EL_ENVELOPE_1;
7378 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7382 chunk_size += putFile16BitBE(file, element);
7383 chunk_size += putFile16BitBE(file, envelope_len);
7384 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7385 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7387 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7388 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7390 for (i = 0; i < envelope_len; i++)
7391 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7397 #if ENABLE_HISTORIC_CHUNKS
7398 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7399 int num_changed_custom_elements)
7403 putFile16BitBE(file, num_changed_custom_elements);
7405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7407 int element = EL_CUSTOM_START + i;
7409 struct ElementInfo *ei = &element_info[element];
7411 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7413 if (check < num_changed_custom_elements)
7415 putFile16BitBE(file, element);
7416 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7423 if (check != num_changed_custom_elements) // should not happen
7424 Warn("inconsistent number of custom element properties");
7428 #if ENABLE_HISTORIC_CHUNKS
7429 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7430 int num_changed_custom_elements)
7434 putFile16BitBE(file, num_changed_custom_elements);
7436 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7438 int element = EL_CUSTOM_START + i;
7440 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7442 if (check < num_changed_custom_elements)
7444 putFile16BitBE(file, element);
7445 putFile16BitBE(file, element_info[element].change->target_element);
7452 if (check != num_changed_custom_elements) // should not happen
7453 Warn("inconsistent number of custom target elements");
7457 #if ENABLE_HISTORIC_CHUNKS
7458 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7459 int num_changed_custom_elements)
7461 int i, j, x, y, check = 0;
7463 putFile16BitBE(file, num_changed_custom_elements);
7465 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7467 int element = EL_CUSTOM_START + i;
7468 struct ElementInfo *ei = &element_info[element];
7470 if (ei->modified_settings)
7472 if (check < num_changed_custom_elements)
7474 putFile16BitBE(file, element);
7476 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7477 putFile8Bit(file, ei->description[j]);
7479 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7481 // some free bytes for future properties and padding
7482 WriteUnusedBytesToFile(file, 7);
7484 putFile8Bit(file, ei->use_gfx_element);
7485 putFile16BitBE(file, ei->gfx_element_initial);
7487 putFile8Bit(file, ei->collect_score_initial);
7488 putFile8Bit(file, ei->collect_count_initial);
7490 putFile16BitBE(file, ei->push_delay_fixed);
7491 putFile16BitBE(file, ei->push_delay_random);
7492 putFile16BitBE(file, ei->move_delay_fixed);
7493 putFile16BitBE(file, ei->move_delay_random);
7495 putFile16BitBE(file, ei->move_pattern);
7496 putFile8Bit(file, ei->move_direction_initial);
7497 putFile8Bit(file, ei->move_stepsize);
7499 for (y = 0; y < 3; y++)
7500 for (x = 0; x < 3; x++)
7501 putFile16BitBE(file, ei->content.e[x][y]);
7503 putFile32BitBE(file, ei->change->events);
7505 putFile16BitBE(file, ei->change->target_element);
7507 putFile16BitBE(file, ei->change->delay_fixed);
7508 putFile16BitBE(file, ei->change->delay_random);
7509 putFile16BitBE(file, ei->change->delay_frames);
7511 putFile16BitBE(file, ei->change->initial_trigger_element);
7513 putFile8Bit(file, ei->change->explode);
7514 putFile8Bit(file, ei->change->use_target_content);
7515 putFile8Bit(file, ei->change->only_if_complete);
7516 putFile8Bit(file, ei->change->use_random_replace);
7518 putFile8Bit(file, ei->change->random_percentage);
7519 putFile8Bit(file, ei->change->replace_when);
7521 for (y = 0; y < 3; y++)
7522 for (x = 0; x < 3; x++)
7523 putFile16BitBE(file, ei->change->content.e[x][y]);
7525 putFile8Bit(file, ei->slippery_type);
7527 // some free bytes for future properties and padding
7528 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7535 if (check != num_changed_custom_elements) // should not happen
7536 Warn("inconsistent number of custom element properties");
7540 #if ENABLE_HISTORIC_CHUNKS
7541 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7543 struct ElementInfo *ei = &element_info[element];
7546 // ---------- custom element base property values (96 bytes) ----------------
7548 putFile16BitBE(file, element);
7550 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7551 putFile8Bit(file, ei->description[i]);
7553 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7555 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7557 putFile8Bit(file, ei->num_change_pages);
7559 putFile16BitBE(file, ei->ce_value_fixed_initial);
7560 putFile16BitBE(file, ei->ce_value_random_initial);
7561 putFile8Bit(file, ei->use_last_ce_value);
7563 putFile8Bit(file, ei->use_gfx_element);
7564 putFile16BitBE(file, ei->gfx_element_initial);
7566 putFile8Bit(file, ei->collect_score_initial);
7567 putFile8Bit(file, ei->collect_count_initial);
7569 putFile8Bit(file, ei->drop_delay_fixed);
7570 putFile8Bit(file, ei->push_delay_fixed);
7571 putFile8Bit(file, ei->drop_delay_random);
7572 putFile8Bit(file, ei->push_delay_random);
7573 putFile16BitBE(file, ei->move_delay_fixed);
7574 putFile16BitBE(file, ei->move_delay_random);
7576 // bits 0 - 15 of "move_pattern" ...
7577 putFile16BitBE(file, ei->move_pattern & 0xffff);
7578 putFile8Bit(file, ei->move_direction_initial);
7579 putFile8Bit(file, ei->move_stepsize);
7581 putFile8Bit(file, ei->slippery_type);
7583 for (y = 0; y < 3; y++)
7584 for (x = 0; x < 3; x++)
7585 putFile16BitBE(file, ei->content.e[x][y]);
7587 putFile16BitBE(file, ei->move_enter_element);
7588 putFile16BitBE(file, ei->move_leave_element);
7589 putFile8Bit(file, ei->move_leave_type);
7591 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7592 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7594 putFile8Bit(file, ei->access_direction);
7596 putFile8Bit(file, ei->explosion_delay);
7597 putFile8Bit(file, ei->ignition_delay);
7598 putFile8Bit(file, ei->explosion_type);
7600 // some free bytes for future custom property values and padding
7601 WriteUnusedBytesToFile(file, 1);
7603 // ---------- change page property values (48 bytes) ------------------------
7605 for (i = 0; i < ei->num_change_pages; i++)
7607 struct ElementChangeInfo *change = &ei->change_page[i];
7608 unsigned int event_bits;
7610 // bits 0 - 31 of "has_event[]" ...
7612 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7613 if (change->has_event[j])
7614 event_bits |= (1u << j);
7615 putFile32BitBE(file, event_bits);
7617 putFile16BitBE(file, change->target_element);
7619 putFile16BitBE(file, change->delay_fixed);
7620 putFile16BitBE(file, change->delay_random);
7621 putFile16BitBE(file, change->delay_frames);
7623 putFile16BitBE(file, change->initial_trigger_element);
7625 putFile8Bit(file, change->explode);
7626 putFile8Bit(file, change->use_target_content);
7627 putFile8Bit(file, change->only_if_complete);
7628 putFile8Bit(file, change->use_random_replace);
7630 putFile8Bit(file, change->random_percentage);
7631 putFile8Bit(file, change->replace_when);
7633 for (y = 0; y < 3; y++)
7634 for (x = 0; x < 3; x++)
7635 putFile16BitBE(file, change->target_content.e[x][y]);
7637 putFile8Bit(file, change->can_change);
7639 putFile8Bit(file, change->trigger_side);
7641 putFile8Bit(file, change->trigger_player);
7642 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7643 log_2(change->trigger_page)));
7645 putFile8Bit(file, change->has_action);
7646 putFile8Bit(file, change->action_type);
7647 putFile8Bit(file, change->action_mode);
7648 putFile16BitBE(file, change->action_arg);
7650 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7652 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7653 if (change->has_event[j])
7654 event_bits |= (1u << (j - 32));
7655 putFile8Bit(file, event_bits);
7660 #if ENABLE_HISTORIC_CHUNKS
7661 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7663 struct ElementInfo *ei = &element_info[element];
7664 struct ElementGroupInfo *group = ei->group;
7667 putFile16BitBE(file, element);
7669 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7670 putFile8Bit(file, ei->description[i]);
7672 putFile8Bit(file, group->num_elements);
7674 putFile8Bit(file, ei->use_gfx_element);
7675 putFile16BitBE(file, ei->gfx_element_initial);
7677 putFile8Bit(file, group->choice_mode);
7679 // some free bytes for future values and padding
7680 WriteUnusedBytesToFile(file, 3);
7682 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7683 putFile16BitBE(file, group->element[i]);
7687 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7688 boolean write_element)
7690 int save_type = entry->save_type;
7691 int data_type = entry->data_type;
7692 int conf_type = entry->conf_type;
7693 int byte_mask = conf_type & CONF_MASK_BYTES;
7694 int element = entry->element;
7695 int default_value = entry->default_value;
7697 boolean modified = FALSE;
7699 if (byte_mask != CONF_MASK_MULTI_BYTES)
7701 void *value_ptr = entry->value;
7702 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7705 // check if any settings have been modified before saving them
7706 if (value != default_value)
7709 // do not save if explicitly told or if unmodified default settings
7710 if ((save_type == SAVE_CONF_NEVER) ||
7711 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7715 num_bytes += putFile16BitBE(file, element);
7717 num_bytes += putFile8Bit(file, conf_type);
7718 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7719 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7720 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7723 else if (data_type == TYPE_STRING)
7725 char *default_string = entry->default_string;
7726 char *string = (char *)(entry->value);
7727 int string_length = strlen(string);
7730 // check if any settings have been modified before saving them
7731 if (!strEqual(string, default_string))
7734 // do not save if explicitly told or if unmodified default settings
7735 if ((save_type == SAVE_CONF_NEVER) ||
7736 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7740 num_bytes += putFile16BitBE(file, element);
7742 num_bytes += putFile8Bit(file, conf_type);
7743 num_bytes += putFile16BitBE(file, string_length);
7745 for (i = 0; i < string_length; i++)
7746 num_bytes += putFile8Bit(file, string[i]);
7748 else if (data_type == TYPE_ELEMENT_LIST)
7750 int *element_array = (int *)(entry->value);
7751 int num_elements = *(int *)(entry->num_entities);
7754 // check if any settings have been modified before saving them
7755 for (i = 0; i < num_elements; i++)
7756 if (element_array[i] != default_value)
7759 // do not save if explicitly told or if unmodified default settings
7760 if ((save_type == SAVE_CONF_NEVER) ||
7761 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7765 num_bytes += putFile16BitBE(file, element);
7767 num_bytes += putFile8Bit(file, conf_type);
7768 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7770 for (i = 0; i < num_elements; i++)
7771 num_bytes += putFile16BitBE(file, element_array[i]);
7773 else if (data_type == TYPE_CONTENT_LIST)
7775 struct Content *content = (struct Content *)(entry->value);
7776 int num_contents = *(int *)(entry->num_entities);
7779 // check if any settings have been modified before saving them
7780 for (i = 0; i < num_contents; i++)
7781 for (y = 0; y < 3; y++)
7782 for (x = 0; x < 3; x++)
7783 if (content[i].e[x][y] != default_value)
7786 // do not save if explicitly told or if unmodified default settings
7787 if ((save_type == SAVE_CONF_NEVER) ||
7788 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7792 num_bytes += putFile16BitBE(file, element);
7794 num_bytes += putFile8Bit(file, conf_type);
7795 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7797 for (i = 0; i < num_contents; i++)
7798 for (y = 0; y < 3; y++)
7799 for (x = 0; x < 3; x++)
7800 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7806 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7811 li = *level; // copy level data into temporary buffer
7813 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7814 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7819 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7824 li = *level; // copy level data into temporary buffer
7826 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7827 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7832 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7834 int envelope_nr = element - EL_ENVELOPE_1;
7838 chunk_size += putFile16BitBE(file, element);
7840 // copy envelope data into temporary buffer
7841 xx_envelope = level->envelope[envelope_nr];
7843 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7844 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7849 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7851 struct ElementInfo *ei = &element_info[element];
7855 chunk_size += putFile16BitBE(file, element);
7857 xx_ei = *ei; // copy element data into temporary buffer
7859 // set default description string for this specific element
7860 strcpy(xx_default_description, getDefaultElementDescription(ei));
7862 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7863 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7865 for (i = 0; i < ei->num_change_pages; i++)
7867 struct ElementChangeInfo *change = &ei->change_page[i];
7869 xx_current_change_page = i;
7871 xx_change = *change; // copy change data into temporary buffer
7874 setEventBitsFromEventFlags(change);
7876 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7877 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7884 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7886 struct ElementInfo *ei = &element_info[element];
7887 struct ElementGroupInfo *group = ei->group;
7891 chunk_size += putFile16BitBE(file, element);
7893 xx_ei = *ei; // copy element data into temporary buffer
7894 xx_group = *group; // copy group data into temporary buffer
7896 // set default description string for this specific element
7897 strcpy(xx_default_description, getDefaultElementDescription(ei));
7899 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7900 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7905 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7907 struct ElementInfo *ei = &element_info[element];
7911 chunk_size += putFile16BitBE(file, element);
7913 xx_ei = *ei; // copy element data into temporary buffer
7915 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7916 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7921 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7922 boolean save_as_template)
7928 if (!(file = fopen(filename, MODE_WRITE)))
7930 Warn("cannot save level file '%s'", filename);
7935 level->file_version = FILE_VERSION_ACTUAL;
7936 level->game_version = GAME_VERSION_ACTUAL;
7938 level->creation_date = getCurrentDate();
7940 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7941 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7943 chunk_size = SaveLevel_VERS(NULL, level);
7944 putFileChunkBE(file, "VERS", chunk_size);
7945 SaveLevel_VERS(file, level);
7947 chunk_size = SaveLevel_DATE(NULL, level);
7948 putFileChunkBE(file, "DATE", chunk_size);
7949 SaveLevel_DATE(file, level);
7951 chunk_size = SaveLevel_NAME(NULL, level);
7952 putFileChunkBE(file, "NAME", chunk_size);
7953 SaveLevel_NAME(file, level);
7955 chunk_size = SaveLevel_AUTH(NULL, level);
7956 putFileChunkBE(file, "AUTH", chunk_size);
7957 SaveLevel_AUTH(file, level);
7959 chunk_size = SaveLevel_INFO(NULL, level);
7960 putFileChunkBE(file, "INFO", chunk_size);
7961 SaveLevel_INFO(file, level);
7963 chunk_size = SaveLevel_BODY(NULL, level);
7964 putFileChunkBE(file, "BODY", chunk_size);
7965 SaveLevel_BODY(file, level);
7967 chunk_size = SaveLevel_ELEM(NULL, level);
7968 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7970 putFileChunkBE(file, "ELEM", chunk_size);
7971 SaveLevel_ELEM(file, level);
7974 for (i = 0; i < NUM_ENVELOPES; i++)
7976 int element = EL_ENVELOPE_1 + i;
7978 chunk_size = SaveLevel_NOTE(NULL, level, element);
7979 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7981 putFileChunkBE(file, "NOTE", chunk_size);
7982 SaveLevel_NOTE(file, level, element);
7986 // if not using template level, check for non-default custom/group elements
7987 if (!level->use_custom_template || save_as_template)
7989 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7991 int element = EL_CUSTOM_START + i;
7993 chunk_size = SaveLevel_CUSX(NULL, level, element);
7994 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7996 putFileChunkBE(file, "CUSX", chunk_size);
7997 SaveLevel_CUSX(file, level, element);
8001 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8003 int element = EL_GROUP_START + i;
8005 chunk_size = SaveLevel_GRPX(NULL, level, element);
8006 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8008 putFileChunkBE(file, "GRPX", chunk_size);
8009 SaveLevel_GRPX(file, level, element);
8013 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8015 int element = GET_EMPTY_ELEMENT(i);
8017 chunk_size = SaveLevel_EMPX(NULL, level, element);
8018 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8020 putFileChunkBE(file, "EMPX", chunk_size);
8021 SaveLevel_EMPX(file, level, element);
8028 SetFilePermissions(filename, PERMS_PRIVATE);
8031 void SaveLevel(int nr)
8033 char *filename = getDefaultLevelFilename(nr);
8035 SaveLevelFromFilename(&level, filename, FALSE);
8038 void SaveLevelTemplate(void)
8040 char *filename = getLocalLevelTemplateFilename();
8042 SaveLevelFromFilename(&level, filename, TRUE);
8045 boolean SaveLevelChecked(int nr)
8047 char *filename = getDefaultLevelFilename(nr);
8048 boolean new_level = !fileExists(filename);
8049 boolean level_saved = FALSE;
8051 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8056 Request("Level saved!", REQ_CONFIRM);
8064 void DumpLevel(struct LevelInfo *level)
8066 if (level->no_level_file || level->no_valid_file)
8068 Warn("cannot dump -- no valid level file found");
8074 Print("Level xxx (file version %08d, game version %08d)\n",
8075 level->file_version, level->game_version);
8078 Print("Level author: '%s'\n", level->author);
8079 Print("Level title: '%s'\n", level->name);
8081 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8083 Print("Level time: %d seconds\n", level->time);
8084 Print("Gems needed: %d\n", level->gems_needed);
8086 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8087 Print("Time for wheel: %d seconds\n", level->time_wheel);
8088 Print("Time for light: %d seconds\n", level->time_light);
8089 Print("Time for timegate: %d seconds\n", level->time_timegate);
8091 Print("Amoeba speed: %d\n", level->amoeba_speed);
8094 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8095 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8096 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8097 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8098 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8099 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8105 for (i = 0; i < NUM_ENVELOPES; i++)
8107 char *text = level->envelope[i].text;
8108 int text_len = strlen(text);
8109 boolean has_text = FALSE;
8111 for (j = 0; j < text_len; j++)
8112 if (text[j] != ' ' && text[j] != '\n')
8118 Print("Envelope %d:\n'%s'\n", i + 1, text);
8126 void DumpLevels(void)
8128 static LevelDirTree *dumplevel_leveldir = NULL;
8130 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8131 global.dumplevel_leveldir);
8133 if (dumplevel_leveldir == NULL)
8134 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8136 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8137 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8138 Fail("no such level number: %d", global.dumplevel_level_nr);
8140 leveldir_current = dumplevel_leveldir;
8142 LoadLevel(global.dumplevel_level_nr);
8149 // ============================================================================
8150 // tape file functions
8151 // ============================================================================
8153 static void setTapeInfoToDefaults(void)
8157 // always start with reliable default values (empty tape)
8160 // default values (also for pre-1.2 tapes) with only the first player
8161 tape.player_participates[0] = TRUE;
8162 for (i = 1; i < MAX_PLAYERS; i++)
8163 tape.player_participates[i] = FALSE;
8165 // at least one (default: the first) player participates in every tape
8166 tape.num_participating_players = 1;
8168 tape.property_bits = TAPE_PROPERTY_NONE;
8170 tape.level_nr = level_nr;
8172 tape.changed = FALSE;
8173 tape.solved = FALSE;
8175 tape.recording = FALSE;
8176 tape.playing = FALSE;
8177 tape.pausing = FALSE;
8179 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8180 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8182 tape.no_info_chunk = TRUE;
8183 tape.no_valid_file = FALSE;
8186 static int getTapePosSize(struct TapeInfo *tape)
8188 int tape_pos_size = 0;
8190 if (tape->use_key_actions)
8191 tape_pos_size += tape->num_participating_players;
8193 if (tape->use_mouse_actions)
8194 tape_pos_size += 3; // x and y position and mouse button mask
8196 tape_pos_size += 1; // tape action delay value
8198 return tape_pos_size;
8201 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8203 tape->use_key_actions = FALSE;
8204 tape->use_mouse_actions = FALSE;
8206 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8207 tape->use_key_actions = TRUE;
8209 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8210 tape->use_mouse_actions = TRUE;
8213 static int getTapeActionValue(struct TapeInfo *tape)
8215 return (tape->use_key_actions &&
8216 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8217 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8218 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8219 TAPE_ACTIONS_DEFAULT);
8222 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8224 tape->file_version = getFileVersion(file);
8225 tape->game_version = getFileVersion(file);
8230 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8234 tape->random_seed = getFile32BitBE(file);
8235 tape->date = getFile32BitBE(file);
8236 tape->length = getFile32BitBE(file);
8238 // read header fields that are new since version 1.2
8239 if (tape->file_version >= FILE_VERSION_1_2)
8241 byte store_participating_players = getFile8Bit(file);
8244 // since version 1.2, tapes store which players participate in the tape
8245 tape->num_participating_players = 0;
8246 for (i = 0; i < MAX_PLAYERS; i++)
8248 tape->player_participates[i] = FALSE;
8250 if (store_participating_players & (1 << i))
8252 tape->player_participates[i] = TRUE;
8253 tape->num_participating_players++;
8257 setTapeActionFlags(tape, getFile8Bit(file));
8259 tape->property_bits = getFile8Bit(file);
8260 tape->solved = getFile8Bit(file);
8262 engine_version = getFileVersion(file);
8263 if (engine_version > 0)
8264 tape->engine_version = engine_version;
8266 tape->engine_version = tape->game_version;
8272 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8274 tape->scr_fieldx = getFile8Bit(file);
8275 tape->scr_fieldy = getFile8Bit(file);
8280 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8282 char *level_identifier = NULL;
8283 int level_identifier_size;
8286 tape->no_info_chunk = FALSE;
8288 level_identifier_size = getFile16BitBE(file);
8290 level_identifier = checked_malloc(level_identifier_size);
8292 for (i = 0; i < level_identifier_size; i++)
8293 level_identifier[i] = getFile8Bit(file);
8295 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8296 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8298 checked_free(level_identifier);
8300 tape->level_nr = getFile16BitBE(file);
8302 chunk_size = 2 + level_identifier_size + 2;
8307 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8310 int tape_pos_size = getTapePosSize(tape);
8311 int chunk_size_expected = tape_pos_size * tape->length;
8313 if (chunk_size_expected != chunk_size)
8315 ReadUnusedBytesFromFile(file, chunk_size);
8316 return chunk_size_expected;
8319 for (i = 0; i < tape->length; i++)
8321 if (i >= MAX_TAPE_LEN)
8323 Warn("tape truncated -- size exceeds maximum tape size %d",
8326 // tape too large; read and ignore remaining tape data from this chunk
8327 for (;i < tape->length; i++)
8328 ReadUnusedBytesFromFile(file, tape_pos_size);
8333 if (tape->use_key_actions)
8335 for (j = 0; j < MAX_PLAYERS; j++)
8337 tape->pos[i].action[j] = MV_NONE;
8339 if (tape->player_participates[j])
8340 tape->pos[i].action[j] = getFile8Bit(file);
8344 if (tape->use_mouse_actions)
8346 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8347 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8348 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8351 tape->pos[i].delay = getFile8Bit(file);
8353 if (tape->file_version == FILE_VERSION_1_0)
8355 // eliminate possible diagonal moves in old tapes
8356 // this is only for backward compatibility
8358 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8359 byte action = tape->pos[i].action[0];
8360 int k, num_moves = 0;
8362 for (k = 0; k < 4; k++)
8364 if (action & joy_dir[k])
8366 tape->pos[i + num_moves].action[0] = joy_dir[k];
8368 tape->pos[i + num_moves].delay = 0;
8377 tape->length += num_moves;
8380 else if (tape->file_version < FILE_VERSION_2_0)
8382 // convert pre-2.0 tapes to new tape format
8384 if (tape->pos[i].delay > 1)
8387 tape->pos[i + 1] = tape->pos[i];
8388 tape->pos[i + 1].delay = 1;
8391 for (j = 0; j < MAX_PLAYERS; j++)
8392 tape->pos[i].action[j] = MV_NONE;
8393 tape->pos[i].delay--;
8400 if (checkEndOfFile(file))
8404 if (i != tape->length)
8405 chunk_size = tape_pos_size * i;
8410 static void LoadTape_SokobanSolution(char *filename)
8413 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8415 if (!(file = openFile(filename, MODE_READ)))
8417 tape.no_valid_file = TRUE;
8422 while (!checkEndOfFile(file))
8424 unsigned char c = getByteFromFile(file);
8426 if (checkEndOfFile(file))
8433 tape.pos[tape.length].action[0] = MV_UP;
8434 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8440 tape.pos[tape.length].action[0] = MV_DOWN;
8441 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8447 tape.pos[tape.length].action[0] = MV_LEFT;
8448 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8454 tape.pos[tape.length].action[0] = MV_RIGHT;
8455 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8463 // ignore white-space characters
8467 tape.no_valid_file = TRUE;
8469 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8477 if (tape.no_valid_file)
8480 tape.length_frames = GetTapeLengthFrames();
8481 tape.length_seconds = GetTapeLengthSeconds();
8484 void LoadTapeFromFilename(char *filename)
8486 char cookie[MAX_LINE_LEN];
8487 char chunk_name[CHUNK_ID_LEN + 1];
8491 // always start with reliable default values
8492 setTapeInfoToDefaults();
8494 if (strSuffix(filename, ".sln"))
8496 LoadTape_SokobanSolution(filename);
8501 if (!(file = openFile(filename, MODE_READ)))
8503 tape.no_valid_file = TRUE;
8508 getFileChunkBE(file, chunk_name, NULL);
8509 if (strEqual(chunk_name, "RND1"))
8511 getFile32BitBE(file); // not used
8513 getFileChunkBE(file, chunk_name, NULL);
8514 if (!strEqual(chunk_name, "TAPE"))
8516 tape.no_valid_file = TRUE;
8518 Warn("unknown format of tape file '%s'", filename);
8525 else // check for pre-2.0 file format with cookie string
8527 strcpy(cookie, chunk_name);
8528 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8530 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8531 cookie[strlen(cookie) - 1] = '\0';
8533 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8535 tape.no_valid_file = TRUE;
8537 Warn("unknown format of tape file '%s'", filename);
8544 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8546 tape.no_valid_file = TRUE;
8548 Warn("unsupported version of tape file '%s'", filename);
8555 // pre-2.0 tape files have no game version, so use file version here
8556 tape.game_version = tape.file_version;
8559 if (tape.file_version < FILE_VERSION_1_2)
8561 // tape files from versions before 1.2.0 without chunk structure
8562 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8563 LoadTape_BODY(file, 2 * tape.length, &tape);
8571 int (*loader)(File *, int, struct TapeInfo *);
8575 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8576 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8577 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8578 { "INFO", -1, LoadTape_INFO },
8579 { "BODY", -1, LoadTape_BODY },
8583 while (getFileChunkBE(file, chunk_name, &chunk_size))
8587 while (chunk_info[i].name != NULL &&
8588 !strEqual(chunk_name, chunk_info[i].name))
8591 if (chunk_info[i].name == NULL)
8593 Warn("unknown chunk '%s' in tape file '%s'",
8594 chunk_name, filename);
8596 ReadUnusedBytesFromFile(file, chunk_size);
8598 else if (chunk_info[i].size != -1 &&
8599 chunk_info[i].size != chunk_size)
8601 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8602 chunk_size, chunk_name, filename);
8604 ReadUnusedBytesFromFile(file, chunk_size);
8608 // call function to load this tape chunk
8609 int chunk_size_expected =
8610 (chunk_info[i].loader)(file, chunk_size, &tape);
8612 // the size of some chunks cannot be checked before reading other
8613 // chunks first (like "HEAD" and "BODY") that contain some header
8614 // information, so check them here
8615 if (chunk_size_expected != chunk_size)
8617 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8618 chunk_size, chunk_name, filename);
8626 tape.length_frames = GetTapeLengthFrames();
8627 tape.length_seconds = GetTapeLengthSeconds();
8630 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8632 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8634 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8635 tape.engine_version);
8639 void LoadTape(int nr)
8641 char *filename = getTapeFilename(nr);
8643 LoadTapeFromFilename(filename);
8646 void LoadSolutionTape(int nr)
8648 char *filename = getSolutionTapeFilename(nr);
8650 LoadTapeFromFilename(filename);
8652 if (TAPE_IS_EMPTY(tape))
8654 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8655 level.native_bd_level->replay != NULL)
8656 CopyNativeTape_BD_to_RND(&level);
8657 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8658 level.native_sp_level->demo.is_available)
8659 CopyNativeTape_SP_to_RND(&level);
8663 void LoadScoreTape(char *score_tape_basename, int nr)
8665 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8667 LoadTapeFromFilename(filename);
8670 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8672 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8674 LoadTapeFromFilename(filename);
8677 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8679 // chunk required for team mode tapes with non-default screen size
8680 return (tape->num_participating_players > 1 &&
8681 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8682 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8685 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8687 putFileVersion(file, tape->file_version);
8688 putFileVersion(file, tape->game_version);
8691 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8694 byte store_participating_players = 0;
8696 // set bits for participating players for compact storage
8697 for (i = 0; i < MAX_PLAYERS; i++)
8698 if (tape->player_participates[i])
8699 store_participating_players |= (1 << i);
8701 putFile32BitBE(file, tape->random_seed);
8702 putFile32BitBE(file, tape->date);
8703 putFile32BitBE(file, tape->length);
8705 putFile8Bit(file, store_participating_players);
8707 putFile8Bit(file, getTapeActionValue(tape));
8709 putFile8Bit(file, tape->property_bits);
8710 putFile8Bit(file, tape->solved);
8712 putFileVersion(file, tape->engine_version);
8715 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8717 putFile8Bit(file, tape->scr_fieldx);
8718 putFile8Bit(file, tape->scr_fieldy);
8721 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8723 int level_identifier_size = strlen(tape->level_identifier) + 1;
8726 putFile16BitBE(file, level_identifier_size);
8728 for (i = 0; i < level_identifier_size; i++)
8729 putFile8Bit(file, tape->level_identifier[i]);
8731 putFile16BitBE(file, tape->level_nr);
8734 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8738 for (i = 0; i < tape->length; i++)
8740 if (tape->use_key_actions)
8742 for (j = 0; j < MAX_PLAYERS; j++)
8743 if (tape->player_participates[j])
8744 putFile8Bit(file, tape->pos[i].action[j]);
8747 if (tape->use_mouse_actions)
8749 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8750 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8751 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8754 putFile8Bit(file, tape->pos[i].delay);
8758 void SaveTapeToFilename(char *filename)
8762 int info_chunk_size;
8763 int body_chunk_size;
8765 if (!(file = fopen(filename, MODE_WRITE)))
8767 Warn("cannot save level recording file '%s'", filename);
8772 tape_pos_size = getTapePosSize(&tape);
8774 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8775 body_chunk_size = tape_pos_size * tape.length;
8777 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8778 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8780 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8781 SaveTape_VERS(file, &tape);
8783 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8784 SaveTape_HEAD(file, &tape);
8786 if (checkSaveTape_SCRN(&tape))
8788 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8789 SaveTape_SCRN(file, &tape);
8792 putFileChunkBE(file, "INFO", info_chunk_size);
8793 SaveTape_INFO(file, &tape);
8795 putFileChunkBE(file, "BODY", body_chunk_size);
8796 SaveTape_BODY(file, &tape);
8800 SetFilePermissions(filename, PERMS_PRIVATE);
8803 static void SaveTapeExt(char *filename)
8807 tape.file_version = FILE_VERSION_ACTUAL;
8808 tape.game_version = GAME_VERSION_ACTUAL;
8810 tape.num_participating_players = 0;
8812 // count number of participating players
8813 for (i = 0; i < MAX_PLAYERS; i++)
8814 if (tape.player_participates[i])
8815 tape.num_participating_players++;
8817 SaveTapeToFilename(filename);
8819 tape.changed = FALSE;
8822 void SaveTape(int nr)
8824 char *filename = getTapeFilename(nr);
8826 InitTapeDirectory(leveldir_current->subdir);
8828 SaveTapeExt(filename);
8831 void SaveScoreTape(int nr)
8833 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8835 // used instead of "leveldir_current->subdir" (for network games)
8836 InitScoreTapeDirectory(levelset.identifier, nr);
8838 SaveTapeExt(filename);
8841 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8842 unsigned int req_state_added)
8844 char *filename = getTapeFilename(nr);
8845 boolean new_tape = !fileExists(filename);
8846 boolean tape_saved = FALSE;
8848 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8853 Request(msg_saved, REQ_CONFIRM | req_state_added);
8861 boolean SaveTapeChecked(int nr)
8863 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8866 boolean SaveTapeChecked_LevelSolved(int nr)
8868 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8869 "Level solved! Tape saved!", REQ_STAY_OPEN);
8872 void DumpTape(struct TapeInfo *tape)
8874 int tape_frame_counter;
8877 if (tape->no_valid_file)
8879 Warn("cannot dump -- no valid tape file found");
8886 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8887 tape->level_nr, tape->file_version, tape->game_version);
8888 Print(" (effective engine version %08d)\n",
8889 tape->engine_version);
8890 Print("Level series identifier: '%s'\n", tape->level_identifier);
8892 Print("Solution tape: %s\n",
8893 tape->solved ? "yes" :
8894 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8896 Print("Special tape properties: ");
8897 if (tape->property_bits == TAPE_PROPERTY_NONE)
8899 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8900 Print("[em_random_bug]");
8901 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8902 Print("[game_speed]");
8903 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8905 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8906 Print("[single_step]");
8907 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8908 Print("[snapshot]");
8909 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8910 Print("[replayed]");
8911 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8912 Print("[tas_keys]");
8913 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8914 Print("[small_graphics]");
8917 int year2 = tape->date / 10000;
8918 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8919 int month_index_raw = (tape->date / 100) % 100;
8920 int month_index = month_index_raw % 12; // prevent invalid index
8921 int month = month_index + 1;
8922 int day = tape->date % 100;
8924 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8928 tape_frame_counter = 0;
8930 for (i = 0; i < tape->length; i++)
8932 if (i >= MAX_TAPE_LEN)
8937 for (j = 0; j < MAX_PLAYERS; j++)
8939 if (tape->player_participates[j])
8941 int action = tape->pos[i].action[j];
8943 Print("%d:%02x ", j, action);
8944 Print("[%c%c%c%c|%c%c] - ",
8945 (action & JOY_LEFT ? '<' : ' '),
8946 (action & JOY_RIGHT ? '>' : ' '),
8947 (action & JOY_UP ? '^' : ' '),
8948 (action & JOY_DOWN ? 'v' : ' '),
8949 (action & JOY_BUTTON_1 ? '1' : ' '),
8950 (action & JOY_BUTTON_2 ? '2' : ' '));
8954 Print("(%03d) ", tape->pos[i].delay);
8955 Print("[%05d]\n", tape_frame_counter);
8957 tape_frame_counter += tape->pos[i].delay;
8963 void DumpTapes(void)
8965 static LevelDirTree *dumptape_leveldir = NULL;
8967 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8968 global.dumptape_leveldir);
8970 if (dumptape_leveldir == NULL)
8971 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8973 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8974 global.dumptape_level_nr > dumptape_leveldir->last_level)
8975 Fail("no such level number: %d", global.dumptape_level_nr);
8977 leveldir_current = dumptape_leveldir;
8979 if (options.mytapes)
8980 LoadTape(global.dumptape_level_nr);
8982 LoadSolutionTape(global.dumptape_level_nr);
8990 // ============================================================================
8991 // score file functions
8992 // ============================================================================
8994 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8998 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9000 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9001 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9002 scores->entry[i].score = 0;
9003 scores->entry[i].time = 0;
9005 scores->entry[i].id = -1;
9006 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9007 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9008 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9009 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9010 strcpy(scores->entry[i].country_code, "??");
9013 scores->num_entries = 0;
9014 scores->last_added = -1;
9015 scores->last_added_local = -1;
9017 scores->updated = FALSE;
9018 scores->uploaded = FALSE;
9019 scores->tape_downloaded = FALSE;
9020 scores->force_last_added = FALSE;
9022 // The following values are intentionally not reset here:
9026 // - continue_playing
9027 // - continue_on_return
9030 static void setScoreInfoToDefaults(void)
9032 setScoreInfoToDefaultsExt(&scores);
9035 static void setServerScoreInfoToDefaults(void)
9037 setScoreInfoToDefaultsExt(&server_scores);
9040 static void LoadScore_OLD(int nr)
9043 char *filename = getScoreFilename(nr);
9044 char cookie[MAX_LINE_LEN];
9045 char line[MAX_LINE_LEN];
9049 if (!(file = fopen(filename, MODE_READ)))
9052 // check file identifier
9053 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9055 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9056 cookie[strlen(cookie) - 1] = '\0';
9058 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9060 Warn("unknown format of score file '%s'", filename);
9067 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9069 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9070 Warn("fscanf() failed; %s", strerror(errno));
9072 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9075 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9076 line[strlen(line) - 1] = '\0';
9078 for (line_ptr = line; *line_ptr; line_ptr++)
9080 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9082 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9083 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9092 static void ConvertScore_OLD(void)
9094 // only convert score to time for levels that rate playing time over score
9095 if (!level.rate_time_over_score)
9098 // convert old score to playing time for score-less levels (like Supaplex)
9099 int time_final_max = 999;
9102 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9104 int score = scores.entry[i].score;
9106 if (score > 0 && score < time_final_max)
9107 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9111 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9113 scores->file_version = getFileVersion(file);
9114 scores->game_version = getFileVersion(file);
9119 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9121 char *level_identifier = NULL;
9122 int level_identifier_size;
9125 level_identifier_size = getFile16BitBE(file);
9127 level_identifier = checked_malloc(level_identifier_size);
9129 for (i = 0; i < level_identifier_size; i++)
9130 level_identifier[i] = getFile8Bit(file);
9132 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9133 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9135 checked_free(level_identifier);
9137 scores->level_nr = getFile16BitBE(file);
9138 scores->num_entries = getFile16BitBE(file);
9140 chunk_size = 2 + level_identifier_size + 2 + 2;
9145 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9149 for (i = 0; i < scores->num_entries; i++)
9151 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9152 scores->entry[i].name[j] = getFile8Bit(file);
9154 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9157 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9162 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9166 for (i = 0; i < scores->num_entries; i++)
9167 scores->entry[i].score = getFile16BitBE(file);
9169 chunk_size = scores->num_entries * 2;
9174 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9178 for (i = 0; i < scores->num_entries; i++)
9179 scores->entry[i].score = getFile32BitBE(file);
9181 chunk_size = scores->num_entries * 4;
9186 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9190 for (i = 0; i < scores->num_entries; i++)
9191 scores->entry[i].time = getFile32BitBE(file);
9193 chunk_size = scores->num_entries * 4;
9198 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9202 for (i = 0; i < scores->num_entries; i++)
9204 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9205 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9207 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9210 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9215 void LoadScore(int nr)
9217 char *filename = getScoreFilename(nr);
9218 char cookie[MAX_LINE_LEN];
9219 char chunk_name[CHUNK_ID_LEN + 1];
9221 boolean old_score_file_format = FALSE;
9224 // always start with reliable default values
9225 setScoreInfoToDefaults();
9227 if (!(file = openFile(filename, MODE_READ)))
9230 getFileChunkBE(file, chunk_name, NULL);
9231 if (strEqual(chunk_name, "RND1"))
9233 getFile32BitBE(file); // not used
9235 getFileChunkBE(file, chunk_name, NULL);
9236 if (!strEqual(chunk_name, "SCOR"))
9238 Warn("unknown format of score file '%s'", filename);
9245 else // check for old file format with cookie string
9247 strcpy(cookie, chunk_name);
9248 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9250 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9251 cookie[strlen(cookie) - 1] = '\0';
9253 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9255 Warn("unknown format of score file '%s'", filename);
9262 old_score_file_format = TRUE;
9265 if (old_score_file_format)
9267 // score files from versions before 4.2.4.0 without chunk structure
9270 // convert score to time, if possible (mainly for Supaplex levels)
9279 int (*loader)(File *, int, struct ScoreInfo *);
9283 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9284 { "INFO", -1, LoadScore_INFO },
9285 { "NAME", -1, LoadScore_NAME },
9286 { "SCOR", -1, LoadScore_SCOR },
9287 { "SC4R", -1, LoadScore_SC4R },
9288 { "TIME", -1, LoadScore_TIME },
9289 { "TAPE", -1, LoadScore_TAPE },
9294 while (getFileChunkBE(file, chunk_name, &chunk_size))
9298 while (chunk_info[i].name != NULL &&
9299 !strEqual(chunk_name, chunk_info[i].name))
9302 if (chunk_info[i].name == NULL)
9304 Warn("unknown chunk '%s' in score file '%s'",
9305 chunk_name, filename);
9307 ReadUnusedBytesFromFile(file, chunk_size);
9309 else if (chunk_info[i].size != -1 &&
9310 chunk_info[i].size != chunk_size)
9312 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9313 chunk_size, chunk_name, filename);
9315 ReadUnusedBytesFromFile(file, chunk_size);
9319 // call function to load this score chunk
9320 int chunk_size_expected =
9321 (chunk_info[i].loader)(file, chunk_size, &scores);
9323 // the size of some chunks cannot be checked before reading other
9324 // chunks first (like "HEAD" and "BODY") that contain some header
9325 // information, so check them here
9326 if (chunk_size_expected != chunk_size)
9328 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9329 chunk_size, chunk_name, filename);
9338 #if ENABLE_HISTORIC_CHUNKS
9339 void SaveScore_OLD(int nr)
9342 char *filename = getScoreFilename(nr);
9345 // used instead of "leveldir_current->subdir" (for network games)
9346 InitScoreDirectory(levelset.identifier);
9348 if (!(file = fopen(filename, MODE_WRITE)))
9350 Warn("cannot save score for level %d", nr);
9355 fprintf(file, "%s\n\n", SCORE_COOKIE);
9357 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9358 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9362 SetFilePermissions(filename, PERMS_PRIVATE);
9366 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9368 putFileVersion(file, scores->file_version);
9369 putFileVersion(file, scores->game_version);
9372 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9374 int level_identifier_size = strlen(scores->level_identifier) + 1;
9377 putFile16BitBE(file, level_identifier_size);
9379 for (i = 0; i < level_identifier_size; i++)
9380 putFile8Bit(file, scores->level_identifier[i]);
9382 putFile16BitBE(file, scores->level_nr);
9383 putFile16BitBE(file, scores->num_entries);
9386 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9390 for (i = 0; i < scores->num_entries; i++)
9392 int name_size = strlen(scores->entry[i].name);
9394 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9395 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9399 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9403 for (i = 0; i < scores->num_entries; i++)
9404 putFile16BitBE(file, scores->entry[i].score);
9407 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9411 for (i = 0; i < scores->num_entries; i++)
9412 putFile32BitBE(file, scores->entry[i].score);
9415 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9419 for (i = 0; i < scores->num_entries; i++)
9420 putFile32BitBE(file, scores->entry[i].time);
9423 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9427 for (i = 0; i < scores->num_entries; i++)
9429 int size = strlen(scores->entry[i].tape_basename);
9431 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9432 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9436 static void SaveScoreToFilename(char *filename)
9439 int info_chunk_size;
9440 int name_chunk_size;
9441 int scor_chunk_size;
9442 int sc4r_chunk_size;
9443 int time_chunk_size;
9444 int tape_chunk_size;
9445 boolean has_large_score_values;
9448 if (!(file = fopen(filename, MODE_WRITE)))
9450 Warn("cannot save score file '%s'", filename);
9455 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9456 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9457 scor_chunk_size = scores.num_entries * 2;
9458 sc4r_chunk_size = scores.num_entries * 4;
9459 time_chunk_size = scores.num_entries * 4;
9460 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9462 has_large_score_values = FALSE;
9463 for (i = 0; i < scores.num_entries; i++)
9464 if (scores.entry[i].score > 0xffff)
9465 has_large_score_values = TRUE;
9467 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9468 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9470 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9471 SaveScore_VERS(file, &scores);
9473 putFileChunkBE(file, "INFO", info_chunk_size);
9474 SaveScore_INFO(file, &scores);
9476 putFileChunkBE(file, "NAME", name_chunk_size);
9477 SaveScore_NAME(file, &scores);
9479 if (has_large_score_values)
9481 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9482 SaveScore_SC4R(file, &scores);
9486 putFileChunkBE(file, "SCOR", scor_chunk_size);
9487 SaveScore_SCOR(file, &scores);
9490 putFileChunkBE(file, "TIME", time_chunk_size);
9491 SaveScore_TIME(file, &scores);
9493 putFileChunkBE(file, "TAPE", tape_chunk_size);
9494 SaveScore_TAPE(file, &scores);
9498 SetFilePermissions(filename, PERMS_PRIVATE);
9501 void SaveScore(int nr)
9503 char *filename = getScoreFilename(nr);
9506 // used instead of "leveldir_current->subdir" (for network games)
9507 InitScoreDirectory(levelset.identifier);
9509 scores.file_version = FILE_VERSION_ACTUAL;
9510 scores.game_version = GAME_VERSION_ACTUAL;
9512 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9513 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9514 scores.level_nr = level_nr;
9516 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9517 if (scores.entry[i].score == 0 &&
9518 scores.entry[i].time == 0 &&
9519 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9522 scores.num_entries = i;
9524 if (scores.num_entries == 0)
9527 SaveScoreToFilename(filename);
9530 static void LoadServerScoreFromCache(int nr)
9532 struct ScoreEntry score_entry;
9541 { &score_entry.score, FALSE, 0 },
9542 { &score_entry.time, FALSE, 0 },
9543 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9544 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9545 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9546 { &score_entry.id, FALSE, 0 },
9547 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9548 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9549 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9550 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9554 char *filename = getScoreCacheFilename(nr);
9555 SetupFileHash *score_hash = loadSetupFileHash(filename);
9558 server_scores.num_entries = 0;
9560 if (score_hash == NULL)
9563 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9565 score_entry = server_scores.entry[i];
9567 for (j = 0; score_mapping[j].value != NULL; j++)
9571 sprintf(token, "%02d.%d", i, j);
9573 char *value = getHashEntry(score_hash, token);
9578 if (score_mapping[j].is_string)
9580 char *score_value = (char *)score_mapping[j].value;
9581 int value_size = score_mapping[j].string_size;
9583 strncpy(score_value, value, value_size);
9584 score_value[value_size] = '\0';
9588 int *score_value = (int *)score_mapping[j].value;
9590 *score_value = atoi(value);
9593 server_scores.num_entries = i + 1;
9596 server_scores.entry[i] = score_entry;
9599 freeSetupFileHash(score_hash);
9602 void LoadServerScore(int nr, boolean download_score)
9604 if (!setup.use_api_server)
9607 // always start with reliable default values
9608 setServerScoreInfoToDefaults();
9610 // 1st step: load server scores from cache file (which may not exist)
9611 // (this should prevent reading it while the thread is writing to it)
9612 LoadServerScoreFromCache(nr);
9614 if (download_score && runtime.use_api_server)
9616 // 2nd step: download server scores from score server to cache file
9617 // (as thread, as it might time out if the server is not reachable)
9618 ApiGetScoreAsThread(nr);
9622 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9624 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9626 // if score tape not uploaded, ask for uploading missing tapes later
9627 if (!setup.has_remaining_tapes)
9628 setup.ask_for_remaining_tapes = TRUE;
9630 setup.provide_uploading_tapes = TRUE;
9631 setup.has_remaining_tapes = TRUE;
9633 SaveSetup_ServerSetup();
9636 void SaveServerScore(int nr, boolean tape_saved)
9638 if (!runtime.use_api_server)
9640 PrepareScoreTapesForUpload(leveldir_current->subdir);
9645 ApiAddScoreAsThread(nr, tape_saved, NULL);
9648 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9649 char *score_tape_filename)
9651 if (!runtime.use_api_server)
9654 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9657 void LoadLocalAndServerScore(int nr, boolean download_score)
9659 int last_added_local = scores.last_added_local;
9660 boolean force_last_added = scores.force_last_added;
9662 // needed if only showing server scores
9663 setScoreInfoToDefaults();
9665 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9668 // restore last added local score entry (before merging server scores)
9669 scores.last_added = scores.last_added_local = last_added_local;
9671 if (setup.use_api_server &&
9672 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9674 // load server scores from cache file and trigger update from server
9675 LoadServerScore(nr, download_score);
9677 // merge local scores with scores from server
9681 if (force_last_added)
9682 scores.force_last_added = force_last_added;
9686 // ============================================================================
9687 // setup file functions
9688 // ============================================================================
9690 #define TOKEN_STR_PLAYER_PREFIX "player_"
9693 static struct TokenInfo global_setup_tokens[] =
9697 &setup.player_name, "player_name"
9701 &setup.multiple_users, "multiple_users"
9705 &setup.sound, "sound"
9709 &setup.sound_loops, "repeating_sound_loops"
9713 &setup.sound_music, "background_music"
9717 &setup.sound_simple, "simple_sound_effects"
9721 &setup.toons, "toons"
9725 &setup.global_animations, "global_animations"
9729 &setup.scroll_delay, "scroll_delay"
9733 &setup.forced_scroll_delay, "forced_scroll_delay"
9737 &setup.scroll_delay_value, "scroll_delay_value"
9741 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9745 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9749 &setup.fade_screens, "fade_screens"
9753 &setup.autorecord, "automatic_tape_recording"
9757 &setup.autorecord_after_replay, "autorecord_after_replay"
9761 &setup.auto_pause_on_start, "auto_pause_on_start"
9765 &setup.show_titlescreen, "show_titlescreen"
9769 &setup.quick_doors, "quick_doors"
9773 &setup.team_mode, "team_mode"
9777 &setup.handicap, "handicap"
9781 &setup.skip_levels, "skip_levels"
9785 &setup.increment_levels, "increment_levels"
9789 &setup.auto_play_next_level, "auto_play_next_level"
9793 &setup.count_score_after_game, "count_score_after_game"
9797 &setup.show_scores_after_game, "show_scores_after_game"
9801 &setup.time_limit, "time_limit"
9805 &setup.fullscreen, "fullscreen"
9809 &setup.window_scaling_percent, "window_scaling_percent"
9813 &setup.window_scaling_quality, "window_scaling_quality"
9817 &setup.screen_rendering_mode, "screen_rendering_mode"
9821 &setup.vsync_mode, "vsync_mode"
9825 &setup.ask_on_escape, "ask_on_escape"
9829 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9833 &setup.ask_on_game_over, "ask_on_game_over"
9837 &setup.ask_on_quit_game, "ask_on_quit_game"
9841 &setup.ask_on_quit_program, "ask_on_quit_program"
9845 &setup.quick_switch, "quick_player_switch"
9849 &setup.input_on_focus, "input_on_focus"
9853 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9857 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9861 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9865 &setup.game_speed_extended, "game_speed_extended"
9869 &setup.game_frame_delay, "game_frame_delay"
9873 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9877 &setup.bd_skip_hatching, "bd_skip_hatching"
9881 &setup.bd_scroll_delay, "bd_scroll_delay"
9885 &setup.bd_smooth_movements, "bd_smooth_movements"
9889 &setup.sp_show_border_elements, "sp_show_border_elements"
9893 &setup.small_game_graphics, "small_game_graphics"
9897 &setup.show_load_save_buttons, "show_load_save_buttons"
9901 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9905 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9909 &setup.graphics_set, "graphics_set"
9913 &setup.sounds_set, "sounds_set"
9917 &setup.music_set, "music_set"
9921 &setup.override_level_graphics, "override_level_graphics"
9925 &setup.override_level_sounds, "override_level_sounds"
9929 &setup.override_level_music, "override_level_music"
9933 &setup.volume_simple, "volume_simple"
9937 &setup.volume_loops, "volume_loops"
9941 &setup.volume_music, "volume_music"
9945 &setup.network_mode, "network_mode"
9949 &setup.network_player_nr, "network_player"
9953 &setup.network_server_hostname, "network_server_hostname"
9957 &setup.touch.control_type, "touch.control_type"
9961 &setup.touch.move_distance, "touch.move_distance"
9965 &setup.touch.drop_distance, "touch.drop_distance"
9969 &setup.touch.transparency, "touch.transparency"
9973 &setup.touch.draw_outlined, "touch.draw_outlined"
9977 &setup.touch.draw_pressed, "touch.draw_pressed"
9981 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9985 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9989 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9993 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9997 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10001 static struct TokenInfo auto_setup_tokens[] =
10005 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10009 static struct TokenInfo server_setup_tokens[] =
10013 &setup.player_uuid, "player_uuid"
10017 &setup.player_version, "player_version"
10021 &setup.use_api_server, TEST_PREFIX "use_api_server"
10025 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10029 &setup.api_server_password, TEST_PREFIX "api_server_password"
10033 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10037 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10041 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10045 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10049 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10053 static struct TokenInfo editor_setup_tokens[] =
10057 &setup.editor.el_classic, "editor.el_classic"
10061 &setup.editor.el_custom, "editor.el_custom"
10065 &setup.editor.el_user_defined, "editor.el_user_defined"
10069 &setup.editor.el_dynamic, "editor.el_dynamic"
10073 &setup.editor.el_headlines, "editor.el_headlines"
10077 &setup.editor.show_element_token, "editor.show_element_token"
10081 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10085 static struct TokenInfo editor_cascade_setup_tokens[] =
10089 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10093 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10097 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10101 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10105 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10109 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10113 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10117 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10121 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10125 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10129 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10133 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10137 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10141 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10145 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10149 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10153 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10157 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10161 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10165 static struct TokenInfo shortcut_setup_tokens[] =
10169 &setup.shortcut.save_game, "shortcut.save_game"
10173 &setup.shortcut.load_game, "shortcut.load_game"
10177 &setup.shortcut.restart_game, "shortcut.restart_game"
10181 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10185 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10189 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10193 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10197 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10201 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10205 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10209 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10213 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10217 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10221 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10225 &setup.shortcut.tape_record, "shortcut.tape_record"
10229 &setup.shortcut.tape_play, "shortcut.tape_play"
10233 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10237 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10241 &setup.shortcut.sound_music, "shortcut.sound_music"
10245 &setup.shortcut.snap_left, "shortcut.snap_left"
10249 &setup.shortcut.snap_right, "shortcut.snap_right"
10253 &setup.shortcut.snap_up, "shortcut.snap_up"
10257 &setup.shortcut.snap_down, "shortcut.snap_down"
10261 static struct SetupInputInfo setup_input;
10262 static struct TokenInfo player_setup_tokens[] =
10266 &setup_input.use_joystick, ".use_joystick"
10270 &setup_input.joy.device_name, ".joy.device_name"
10274 &setup_input.joy.xleft, ".joy.xleft"
10278 &setup_input.joy.xmiddle, ".joy.xmiddle"
10282 &setup_input.joy.xright, ".joy.xright"
10286 &setup_input.joy.yupper, ".joy.yupper"
10290 &setup_input.joy.ymiddle, ".joy.ymiddle"
10294 &setup_input.joy.ylower, ".joy.ylower"
10298 &setup_input.joy.snap, ".joy.snap_field"
10302 &setup_input.joy.drop, ".joy.place_bomb"
10306 &setup_input.key.left, ".key.move_left"
10310 &setup_input.key.right, ".key.move_right"
10314 &setup_input.key.up, ".key.move_up"
10318 &setup_input.key.down, ".key.move_down"
10322 &setup_input.key.snap, ".key.snap_field"
10326 &setup_input.key.drop, ".key.place_bomb"
10330 static struct TokenInfo system_setup_tokens[] =
10334 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10338 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10342 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10346 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10350 static struct TokenInfo internal_setup_tokens[] =
10354 &setup.internal.program_title, "program_title"
10358 &setup.internal.program_version, "program_version"
10362 &setup.internal.program_author, "program_author"
10366 &setup.internal.program_email, "program_email"
10370 &setup.internal.program_website, "program_website"
10374 &setup.internal.program_copyright, "program_copyright"
10378 &setup.internal.program_company, "program_company"
10382 &setup.internal.program_icon_file, "program_icon_file"
10386 &setup.internal.default_graphics_set, "default_graphics_set"
10390 &setup.internal.default_sounds_set, "default_sounds_set"
10394 &setup.internal.default_music_set, "default_music_set"
10398 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10402 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10406 &setup.internal.fallback_music_file, "fallback_music_file"
10410 &setup.internal.default_level_series, "default_level_series"
10414 &setup.internal.default_window_width, "default_window_width"
10418 &setup.internal.default_window_height, "default_window_height"
10422 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10426 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10430 &setup.internal.create_user_levelset, "create_user_levelset"
10434 &setup.internal.info_screens_from_main, "info_screens_from_main"
10438 &setup.internal.menu_game, "menu_game"
10442 &setup.internal.menu_engines, "menu_engines"
10446 &setup.internal.menu_editor, "menu_editor"
10450 &setup.internal.menu_graphics, "menu_graphics"
10454 &setup.internal.menu_sound, "menu_sound"
10458 &setup.internal.menu_artwork, "menu_artwork"
10462 &setup.internal.menu_input, "menu_input"
10466 &setup.internal.menu_touch, "menu_touch"
10470 &setup.internal.menu_shortcuts, "menu_shortcuts"
10474 &setup.internal.menu_exit, "menu_exit"
10478 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10482 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10486 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10490 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10494 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10498 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10502 &setup.internal.info_title, "info_title"
10506 &setup.internal.info_elements, "info_elements"
10510 &setup.internal.info_music, "info_music"
10514 &setup.internal.info_credits, "info_credits"
10518 &setup.internal.info_program, "info_program"
10522 &setup.internal.info_version, "info_version"
10526 &setup.internal.info_levelset, "info_levelset"
10530 &setup.internal.info_exit, "info_exit"
10534 static struct TokenInfo debug_setup_tokens[] =
10538 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10542 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10546 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10550 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10554 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10558 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10562 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10566 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10570 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10574 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10578 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10582 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10586 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10590 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10594 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10598 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10602 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10606 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10610 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10614 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10618 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10621 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10625 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10629 &setup.debug.xsn_mode, "debug.xsn_mode"
10633 &setup.debug.xsn_percent, "debug.xsn_percent"
10637 static struct TokenInfo options_setup_tokens[] =
10641 &setup.options.verbose, "options.verbose"
10645 &setup.options.debug, "options.debug"
10649 &setup.options.debug_mode, "options.debug_mode"
10653 static void setSetupInfoToDefaults(struct SetupInfo *si)
10657 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10659 si->multiple_users = TRUE;
10662 si->sound_loops = TRUE;
10663 si->sound_music = TRUE;
10664 si->sound_simple = TRUE;
10666 si->global_animations = TRUE;
10667 si->scroll_delay = TRUE;
10668 si->forced_scroll_delay = FALSE;
10669 si->scroll_delay_value = STD_SCROLL_DELAY;
10670 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10671 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10672 si->fade_screens = TRUE;
10673 si->autorecord = TRUE;
10674 si->autorecord_after_replay = TRUE;
10675 si->auto_pause_on_start = FALSE;
10676 si->show_titlescreen = TRUE;
10677 si->quick_doors = FALSE;
10678 si->team_mode = FALSE;
10679 si->handicap = TRUE;
10680 si->skip_levels = TRUE;
10681 si->increment_levels = TRUE;
10682 si->auto_play_next_level = TRUE;
10683 si->count_score_after_game = TRUE;
10684 si->show_scores_after_game = TRUE;
10685 si->time_limit = TRUE;
10686 si->fullscreen = FALSE;
10687 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10688 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10689 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10690 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10691 si->ask_on_escape = TRUE;
10692 si->ask_on_escape_editor = TRUE;
10693 si->ask_on_game_over = TRUE;
10694 si->ask_on_quit_game = TRUE;
10695 si->ask_on_quit_program = TRUE;
10696 si->quick_switch = FALSE;
10697 si->input_on_focus = FALSE;
10698 si->prefer_aga_graphics = TRUE;
10699 si->prefer_lowpass_sounds = FALSE;
10700 si->prefer_extra_panel_items = TRUE;
10701 si->game_speed_extended = FALSE;
10702 si->game_frame_delay = GAME_FRAME_DELAY;
10703 si->bd_skip_uncovering = FALSE;
10704 si->bd_skip_hatching = FALSE;
10705 si->bd_scroll_delay = TRUE;
10706 si->bd_smooth_movements = AUTO;
10707 si->sp_show_border_elements = FALSE;
10708 si->small_game_graphics = FALSE;
10709 si->show_load_save_buttons = FALSE;
10710 si->show_undo_redo_buttons = FALSE;
10711 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10713 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10714 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10715 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10717 si->override_level_graphics = FALSE;
10718 si->override_level_sounds = FALSE;
10719 si->override_level_music = FALSE;
10721 si->volume_simple = 100; // percent
10722 si->volume_loops = 100; // percent
10723 si->volume_music = 100; // percent
10725 si->network_mode = FALSE;
10726 si->network_player_nr = 0; // first player
10727 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10729 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10730 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10731 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10732 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10733 si->touch.draw_outlined = TRUE;
10734 si->touch.draw_pressed = TRUE;
10736 for (i = 0; i < 2; i++)
10738 char *default_grid_button[6][2] =
10744 { "111222", " vv " },
10745 { "111222", " vv " }
10747 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10748 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10749 int min_xsize = MIN(6, grid_xsize);
10750 int min_ysize = MIN(6, grid_ysize);
10751 int startx = grid_xsize - min_xsize;
10752 int starty = grid_ysize - min_ysize;
10755 // virtual buttons grid can only be set to defaults if video is initialized
10756 // (this will be repeated if virtual buttons are not loaded from setup file)
10757 if (video.initialized)
10759 si->touch.grid_xsize[i] = grid_xsize;
10760 si->touch.grid_ysize[i] = grid_ysize;
10764 si->touch.grid_xsize[i] = -1;
10765 si->touch.grid_ysize[i] = -1;
10768 for (x = 0; x < MAX_GRID_XSIZE; x++)
10769 for (y = 0; y < MAX_GRID_YSIZE; y++)
10770 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10772 for (x = 0; x < min_xsize; x++)
10773 for (y = 0; y < min_ysize; y++)
10774 si->touch.grid_button[i][x][starty + y] =
10775 default_grid_button[y][0][x];
10777 for (x = 0; x < min_xsize; x++)
10778 for (y = 0; y < min_ysize; y++)
10779 si->touch.grid_button[i][startx + x][starty + y] =
10780 default_grid_button[y][1][x];
10783 si->touch.grid_initialized = video.initialized;
10785 si->touch.overlay_buttons = FALSE;
10787 si->editor.el_boulderdash = TRUE;
10788 si->editor.el_boulderdash_native = TRUE;
10789 si->editor.el_emerald_mine = TRUE;
10790 si->editor.el_emerald_mine_club = TRUE;
10791 si->editor.el_more = TRUE;
10792 si->editor.el_sokoban = TRUE;
10793 si->editor.el_supaplex = TRUE;
10794 si->editor.el_diamond_caves = TRUE;
10795 si->editor.el_dx_boulderdash = TRUE;
10797 si->editor.el_mirror_magic = TRUE;
10798 si->editor.el_deflektor = TRUE;
10800 si->editor.el_chars = TRUE;
10801 si->editor.el_steel_chars = TRUE;
10803 si->editor.el_classic = TRUE;
10804 si->editor.el_custom = TRUE;
10806 si->editor.el_user_defined = FALSE;
10807 si->editor.el_dynamic = TRUE;
10809 si->editor.el_headlines = TRUE;
10811 si->editor.show_element_token = FALSE;
10813 si->editor.show_read_only_warning = TRUE;
10815 si->editor.use_template_for_new_levels = TRUE;
10817 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10818 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10819 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10820 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10821 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10823 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10824 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10825 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10826 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10827 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10829 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10830 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10831 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10832 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10833 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10834 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10836 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10837 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10838 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10840 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10841 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10842 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10843 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10845 for (i = 0; i < MAX_PLAYERS; i++)
10847 si->input[i].use_joystick = FALSE;
10848 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10849 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10850 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10851 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10852 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10853 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10854 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10855 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10856 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10857 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10858 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10859 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10860 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10861 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10862 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10865 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10866 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10867 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10868 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10870 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10871 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10872 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10873 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10874 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10875 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10876 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10878 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10880 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10881 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10882 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10884 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10885 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10886 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10888 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10889 si->internal.choose_from_top_leveldir = FALSE;
10890 si->internal.show_scaling_in_title = TRUE;
10891 si->internal.create_user_levelset = TRUE;
10892 si->internal.info_screens_from_main = FALSE;
10894 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10895 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10897 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10898 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10899 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10900 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10901 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10902 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10903 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10904 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10905 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10906 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10908 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10909 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10910 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10911 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10912 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10913 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10914 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10915 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10916 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10917 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10919 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10920 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10922 si->debug.show_frames_per_second = FALSE;
10924 si->debug.xsn_mode = AUTO;
10925 si->debug.xsn_percent = 0;
10927 si->options.verbose = FALSE;
10928 si->options.debug = FALSE;
10929 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10931 #if defined(PLATFORM_ANDROID)
10932 si->fullscreen = TRUE;
10933 si->touch.overlay_buttons = TRUE;
10936 setHideSetupEntry(&setup.debug.xsn_mode);
10939 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10941 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10944 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10946 si->player_uuid = NULL; // (will be set later)
10947 si->player_version = 1; // (will be set later)
10949 si->use_api_server = TRUE;
10950 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10951 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10952 si->ask_for_uploading_tapes = TRUE;
10953 si->ask_for_remaining_tapes = FALSE;
10954 si->provide_uploading_tapes = TRUE;
10955 si->ask_for_using_api_server = TRUE;
10956 si->has_remaining_tapes = FALSE;
10959 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10961 si->editor_cascade.el_bd = TRUE;
10962 si->editor_cascade.el_bd_native = TRUE;
10963 si->editor_cascade.el_em = TRUE;
10964 si->editor_cascade.el_emc = TRUE;
10965 si->editor_cascade.el_rnd = TRUE;
10966 si->editor_cascade.el_sb = TRUE;
10967 si->editor_cascade.el_sp = TRUE;
10968 si->editor_cascade.el_dc = TRUE;
10969 si->editor_cascade.el_dx = TRUE;
10971 si->editor_cascade.el_mm = TRUE;
10972 si->editor_cascade.el_df = TRUE;
10974 si->editor_cascade.el_chars = FALSE;
10975 si->editor_cascade.el_steel_chars = FALSE;
10976 si->editor_cascade.el_ce = FALSE;
10977 si->editor_cascade.el_ge = FALSE;
10978 si->editor_cascade.el_es = FALSE;
10979 si->editor_cascade.el_ref = FALSE;
10980 si->editor_cascade.el_user = FALSE;
10981 si->editor_cascade.el_dynamic = FALSE;
10984 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10986 static char *getHideSetupToken(void *setup_value)
10988 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10990 if (setup_value != NULL)
10991 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10993 return hide_setup_token;
10996 void setHideSetupEntry(void *setup_value)
10998 char *hide_setup_token = getHideSetupToken(setup_value);
11000 if (hide_setup_hash == NULL)
11001 hide_setup_hash = newSetupFileHash();
11003 if (setup_value != NULL)
11004 setHashEntry(hide_setup_hash, hide_setup_token, "");
11007 void removeHideSetupEntry(void *setup_value)
11009 char *hide_setup_token = getHideSetupToken(setup_value);
11011 if (setup_value != NULL)
11012 removeHashEntry(hide_setup_hash, hide_setup_token);
11015 boolean hideSetupEntry(void *setup_value)
11017 char *hide_setup_token = getHideSetupToken(setup_value);
11019 return (setup_value != NULL &&
11020 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11023 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11024 struct TokenInfo *token_info,
11025 int token_nr, char *token_text)
11027 char *token_hide_text = getStringCat2(token_text, ".hide");
11028 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11030 // set the value of this setup option in the setup option structure
11031 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11033 // check if this setup option should be hidden in the setup menu
11034 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11035 setHideSetupEntry(token_info[token_nr].value);
11037 free(token_hide_text);
11040 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11041 struct TokenInfo *token_info,
11044 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11045 token_info[token_nr].text);
11048 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11052 if (!setup_file_hash)
11055 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11056 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11058 setup.touch.grid_initialized = TRUE;
11059 for (i = 0; i < 2; i++)
11061 int grid_xsize = setup.touch.grid_xsize[i];
11062 int grid_ysize = setup.touch.grid_ysize[i];
11065 // if virtual buttons are not loaded from setup file, repeat initializing
11066 // virtual buttons grid with default values later when video is initialized
11067 if (grid_xsize == -1 ||
11070 setup.touch.grid_initialized = FALSE;
11075 for (y = 0; y < grid_ysize; y++)
11077 char token_string[MAX_LINE_LEN];
11079 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11081 char *value_string = getHashEntry(setup_file_hash, token_string);
11083 if (value_string == NULL)
11086 for (x = 0; x < grid_xsize; x++)
11088 char c = value_string[x];
11090 setup.touch.grid_button[i][x][y] =
11091 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11096 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11097 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11099 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11100 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11102 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11106 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11108 setup_input = setup.input[pnr];
11109 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11111 char full_token[100];
11113 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11114 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11117 setup.input[pnr] = setup_input;
11120 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11121 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11123 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11124 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11126 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11127 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11129 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11130 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11132 setHideRelatedSetupEntries();
11135 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11139 if (!setup_file_hash)
11142 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11143 setSetupInfo(auto_setup_tokens, i,
11144 getHashEntry(setup_file_hash,
11145 auto_setup_tokens[i].text));
11148 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11152 if (!setup_file_hash)
11155 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11156 setSetupInfo(server_setup_tokens, i,
11157 getHashEntry(setup_file_hash,
11158 server_setup_tokens[i].text));
11161 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11165 if (!setup_file_hash)
11168 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11169 setSetupInfo(editor_cascade_setup_tokens, i,
11170 getHashEntry(setup_file_hash,
11171 editor_cascade_setup_tokens[i].text));
11174 void LoadUserNames(void)
11176 int last_user_nr = user.nr;
11179 if (global.user_names != NULL)
11181 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11182 checked_free(global.user_names[i]);
11184 checked_free(global.user_names);
11187 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11189 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11193 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11195 if (setup_file_hash)
11197 char *player_name = getHashEntry(setup_file_hash, "player_name");
11199 global.user_names[i] = getFixedUserName(player_name);
11201 freeSetupFileHash(setup_file_hash);
11204 if (global.user_names[i] == NULL)
11205 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11208 user.nr = last_user_nr;
11211 void LoadSetupFromFilename(char *filename)
11213 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11215 if (setup_file_hash)
11217 decodeSetupFileHash_Default(setup_file_hash);
11219 freeSetupFileHash(setup_file_hash);
11223 Debug("setup", "using default setup values");
11227 static void LoadSetup_SpecialPostProcessing(void)
11229 char *player_name_new;
11231 // needed to work around problems with fixed length strings
11232 player_name_new = getFixedUserName(setup.player_name);
11233 free(setup.player_name);
11234 setup.player_name = player_name_new;
11236 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11237 if (setup.scroll_delay == FALSE)
11239 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11240 setup.scroll_delay = TRUE; // now always "on"
11243 // make sure that scroll delay value stays inside valid range
11244 setup.scroll_delay_value =
11245 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11248 void LoadSetup_Default(void)
11252 // always start with reliable default values
11253 setSetupInfoToDefaults(&setup);
11255 // try to load setup values from default setup file
11256 filename = getDefaultSetupFilename();
11258 if (fileExists(filename))
11259 LoadSetupFromFilename(filename);
11261 // try to load setup values from platform setup file
11262 filename = getPlatformSetupFilename();
11264 if (fileExists(filename))
11265 LoadSetupFromFilename(filename);
11267 // try to load setup values from user setup file
11268 filename = getSetupFilename();
11270 LoadSetupFromFilename(filename);
11272 LoadSetup_SpecialPostProcessing();
11275 void LoadSetup_AutoSetup(void)
11277 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11278 SetupFileHash *setup_file_hash = NULL;
11280 // always start with reliable default values
11281 setSetupInfoToDefaults_AutoSetup(&setup);
11283 setup_file_hash = loadSetupFileHash(filename);
11285 if (setup_file_hash)
11287 decodeSetupFileHash_AutoSetup(setup_file_hash);
11289 freeSetupFileHash(setup_file_hash);
11295 void LoadSetup_ServerSetup(void)
11297 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11298 SetupFileHash *setup_file_hash = NULL;
11300 // always start with reliable default values
11301 setSetupInfoToDefaults_ServerSetup(&setup);
11303 setup_file_hash = loadSetupFileHash(filename);
11305 if (setup_file_hash)
11307 decodeSetupFileHash_ServerSetup(setup_file_hash);
11309 freeSetupFileHash(setup_file_hash);
11314 if (setup.player_uuid == NULL)
11316 // player UUID does not yet exist in setup file
11317 setup.player_uuid = getStringCopy(getUUID());
11318 setup.player_version = 2;
11320 SaveSetup_ServerSetup();
11324 void LoadSetup_EditorCascade(void)
11326 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11327 SetupFileHash *setup_file_hash = NULL;
11329 // always start with reliable default values
11330 setSetupInfoToDefaults_EditorCascade(&setup);
11332 setup_file_hash = loadSetupFileHash(filename);
11334 if (setup_file_hash)
11336 decodeSetupFileHash_EditorCascade(setup_file_hash);
11338 freeSetupFileHash(setup_file_hash);
11344 void LoadSetup(void)
11346 LoadSetup_Default();
11347 LoadSetup_AutoSetup();
11348 LoadSetup_ServerSetup();
11349 LoadSetup_EditorCascade();
11352 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11353 char *mapping_line)
11355 char mapping_guid[MAX_LINE_LEN];
11356 char *mapping_start, *mapping_end;
11358 // get GUID from game controller mapping line: copy complete line
11359 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11360 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11362 // get GUID from game controller mapping line: cut after GUID part
11363 mapping_start = strchr(mapping_guid, ',');
11364 if (mapping_start != NULL)
11365 *mapping_start = '\0';
11367 // cut newline from game controller mapping line
11368 mapping_end = strchr(mapping_line, '\n');
11369 if (mapping_end != NULL)
11370 *mapping_end = '\0';
11372 // add mapping entry to game controller mappings hash
11373 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11376 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11381 if (!(file = fopen(filename, MODE_READ)))
11383 Warn("cannot read game controller mappings file '%s'", filename);
11388 while (!feof(file))
11390 char line[MAX_LINE_LEN];
11392 if (!fgets(line, MAX_LINE_LEN, file))
11395 addGameControllerMappingToHash(mappings_hash, line);
11401 void SaveSetup_Default(void)
11403 char *filename = getSetupFilename();
11407 InitUserDataDirectory();
11409 if (!(file = fopen(filename, MODE_WRITE)))
11411 Warn("cannot write setup file '%s'", filename);
11416 fprintFileHeader(file, SETUP_FILENAME);
11418 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11420 // just to make things nicer :)
11421 if (global_setup_tokens[i].value == &setup.multiple_users ||
11422 global_setup_tokens[i].value == &setup.sound ||
11423 global_setup_tokens[i].value == &setup.graphics_set ||
11424 global_setup_tokens[i].value == &setup.volume_simple ||
11425 global_setup_tokens[i].value == &setup.network_mode ||
11426 global_setup_tokens[i].value == &setup.touch.control_type ||
11427 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11428 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11429 fprintf(file, "\n");
11431 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11434 for (i = 0; i < 2; i++)
11436 int grid_xsize = setup.touch.grid_xsize[i];
11437 int grid_ysize = setup.touch.grid_ysize[i];
11440 fprintf(file, "\n");
11442 for (y = 0; y < grid_ysize; y++)
11444 char token_string[MAX_LINE_LEN];
11445 char value_string[MAX_LINE_LEN];
11447 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11449 for (x = 0; x < grid_xsize; x++)
11451 char c = setup.touch.grid_button[i][x][y];
11453 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11456 value_string[grid_xsize] = '\0';
11458 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11462 fprintf(file, "\n");
11463 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11464 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11466 fprintf(file, "\n");
11467 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11468 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11470 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11474 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11475 fprintf(file, "\n");
11477 setup_input = setup.input[pnr];
11478 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11479 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11482 fprintf(file, "\n");
11483 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11484 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11486 // (internal setup values not saved to user setup file)
11488 fprintf(file, "\n");
11489 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11490 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11491 setup.debug.xsn_mode != AUTO)
11492 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11494 fprintf(file, "\n");
11495 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11496 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11500 SetFilePermissions(filename, PERMS_PRIVATE);
11503 void SaveSetup_AutoSetup(void)
11505 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11509 InitUserDataDirectory();
11511 if (!(file = fopen(filename, MODE_WRITE)))
11513 Warn("cannot write auto setup file '%s'", filename);
11520 fprintFileHeader(file, AUTOSETUP_FILENAME);
11522 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11523 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11527 SetFilePermissions(filename, PERMS_PRIVATE);
11532 void SaveSetup_ServerSetup(void)
11534 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11538 InitUserDataDirectory();
11540 if (!(file = fopen(filename, MODE_WRITE)))
11542 Warn("cannot write server setup file '%s'", filename);
11549 fprintFileHeader(file, SERVERSETUP_FILENAME);
11551 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11553 // just to make things nicer :)
11554 if (server_setup_tokens[i].value == &setup.use_api_server)
11555 fprintf(file, "\n");
11557 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11562 SetFilePermissions(filename, PERMS_PRIVATE);
11567 void SaveSetup_EditorCascade(void)
11569 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11573 InitUserDataDirectory();
11575 if (!(file = fopen(filename, MODE_WRITE)))
11577 Warn("cannot write editor cascade state file '%s'", filename);
11584 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11586 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11587 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11591 SetFilePermissions(filename, PERMS_PRIVATE);
11596 void SaveSetup(void)
11598 SaveSetup_Default();
11599 SaveSetup_AutoSetup();
11600 SaveSetup_ServerSetup();
11601 SaveSetup_EditorCascade();
11604 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11609 if (!(file = fopen(filename, MODE_WRITE)))
11611 Warn("cannot write game controller mappings file '%s'", filename);
11616 BEGIN_HASH_ITERATION(mappings_hash, itr)
11618 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11620 END_HASH_ITERATION(mappings_hash, itr)
11625 void SaveSetup_AddGameControllerMapping(char *mapping)
11627 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11628 SetupFileHash *mappings_hash = newSetupFileHash();
11630 InitUserDataDirectory();
11632 // load existing personal game controller mappings
11633 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11635 // add new mapping to personal game controller mappings
11636 addGameControllerMappingToHash(mappings_hash, mapping);
11638 // save updated personal game controller mappings
11639 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11641 freeSetupFileHash(mappings_hash);
11645 void LoadCustomElementDescriptions(void)
11647 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11648 SetupFileHash *setup_file_hash;
11651 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11653 if (element_info[i].custom_description != NULL)
11655 free(element_info[i].custom_description);
11656 element_info[i].custom_description = NULL;
11660 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11663 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11665 char *token = getStringCat2(element_info[i].token_name, ".name");
11666 char *value = getHashEntry(setup_file_hash, token);
11669 element_info[i].custom_description = getStringCopy(value);
11674 freeSetupFileHash(setup_file_hash);
11677 static int getElementFromToken(char *token)
11679 char *value = getHashEntry(element_token_hash, token);
11682 return atoi(value);
11684 Warn("unknown element token '%s'", token);
11686 return EL_UNDEFINED;
11689 void FreeGlobalAnimEventInfo(void)
11691 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11693 if (gaei->event_list == NULL)
11698 for (i = 0; i < gaei->num_event_lists; i++)
11700 checked_free(gaei->event_list[i]->event_value);
11701 checked_free(gaei->event_list[i]);
11704 checked_free(gaei->event_list);
11706 gaei->event_list = NULL;
11707 gaei->num_event_lists = 0;
11710 static int AddGlobalAnimEventList(void)
11712 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11713 int list_pos = gaei->num_event_lists++;
11715 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11716 sizeof(struct GlobalAnimEventListInfo *));
11718 gaei->event_list[list_pos] =
11719 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11721 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11723 gaeli->event_value = NULL;
11724 gaeli->num_event_values = 0;
11729 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11731 // do not add empty global animation events
11732 if (event_value == ANIM_EVENT_NONE)
11735 // if list position is undefined, create new list
11736 if (list_pos == ANIM_EVENT_UNDEFINED)
11737 list_pos = AddGlobalAnimEventList();
11739 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11740 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11741 int value_pos = gaeli->num_event_values++;
11743 gaeli->event_value = checked_realloc(gaeli->event_value,
11744 gaeli->num_event_values * sizeof(int *));
11746 gaeli->event_value[value_pos] = event_value;
11751 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11753 if (list_pos == ANIM_EVENT_UNDEFINED)
11754 return ANIM_EVENT_NONE;
11756 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11757 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11759 return gaeli->event_value[value_pos];
11762 int GetGlobalAnimEventValueCount(int list_pos)
11764 if (list_pos == ANIM_EVENT_UNDEFINED)
11767 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11768 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11770 return gaeli->num_event_values;
11773 // This function checks if a string <s> of the format "string1, string2, ..."
11774 // exactly contains a string <s_contained>.
11776 static boolean string_has_parameter(char *s, char *s_contained)
11780 if (s == NULL || s_contained == NULL)
11783 if (strlen(s_contained) > strlen(s))
11786 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11788 char next_char = s[strlen(s_contained)];
11790 // check if next character is delimiter or whitespace
11791 if (next_char == ',' || next_char == '\0' ||
11792 next_char == ' ' || next_char == '\t')
11796 // check if string contains another parameter string after a comma
11797 substring = strchr(s, ',');
11798 if (substring == NULL) // string does not contain a comma
11801 // advance string pointer to next character after the comma
11804 // skip potential whitespaces after the comma
11805 while (*substring == ' ' || *substring == '\t')
11808 return string_has_parameter(substring, s_contained);
11811 static int get_anim_parameter_value_ce(char *s)
11814 char *pattern_1 = "ce_change:custom_";
11815 char *pattern_2 = ".page_";
11816 int pattern_1_len = strlen(pattern_1);
11817 char *matching_char = strstr(s_ptr, pattern_1);
11818 int result = ANIM_EVENT_NONE;
11820 if (matching_char == NULL)
11821 return ANIM_EVENT_NONE;
11823 result = ANIM_EVENT_CE_CHANGE;
11825 s_ptr = matching_char + pattern_1_len;
11827 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11828 if (*s_ptr >= '0' && *s_ptr <= '9')
11830 int gic_ce_nr = (*s_ptr++ - '0');
11832 if (*s_ptr >= '0' && *s_ptr <= '9')
11834 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11836 if (*s_ptr >= '0' && *s_ptr <= '9')
11837 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11840 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11841 return ANIM_EVENT_NONE;
11843 // custom element stored as 0 to 255
11846 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11850 // invalid custom element number specified
11852 return ANIM_EVENT_NONE;
11855 // check for change page number ("page_X" or "page_XX") (optional)
11856 if (strPrefix(s_ptr, pattern_2))
11858 s_ptr += strlen(pattern_2);
11860 if (*s_ptr >= '0' && *s_ptr <= '9')
11862 int gic_page_nr = (*s_ptr++ - '0');
11864 if (*s_ptr >= '0' && *s_ptr <= '9')
11865 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11867 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11868 return ANIM_EVENT_NONE;
11870 // change page stored as 1 to 32 (0 means "all change pages")
11872 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11876 // invalid animation part number specified
11878 return ANIM_EVENT_NONE;
11882 // discard result if next character is neither delimiter nor whitespace
11883 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11884 *s_ptr == ' ' || *s_ptr == '\t'))
11885 return ANIM_EVENT_NONE;
11890 static int get_anim_parameter_value(char *s)
11892 int event_value[] =
11900 char *pattern_1[] =
11908 char *pattern_2 = ".part_";
11909 char *matching_char = NULL;
11911 int pattern_1_len = 0;
11912 int result = ANIM_EVENT_NONE;
11915 result = get_anim_parameter_value_ce(s);
11917 if (result != ANIM_EVENT_NONE)
11920 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11922 matching_char = strstr(s_ptr, pattern_1[i]);
11923 pattern_1_len = strlen(pattern_1[i]);
11924 result = event_value[i];
11926 if (matching_char != NULL)
11930 if (matching_char == NULL)
11931 return ANIM_EVENT_NONE;
11933 s_ptr = matching_char + pattern_1_len;
11935 // check for main animation number ("anim_X" or "anim_XX")
11936 if (*s_ptr >= '0' && *s_ptr <= '9')
11938 int gic_anim_nr = (*s_ptr++ - '0');
11940 if (*s_ptr >= '0' && *s_ptr <= '9')
11941 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11943 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11944 return ANIM_EVENT_NONE;
11946 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11950 // invalid main animation number specified
11952 return ANIM_EVENT_NONE;
11955 // check for animation part number ("part_X" or "part_XX") (optional)
11956 if (strPrefix(s_ptr, pattern_2))
11958 s_ptr += strlen(pattern_2);
11960 if (*s_ptr >= '0' && *s_ptr <= '9')
11962 int gic_part_nr = (*s_ptr++ - '0');
11964 if (*s_ptr >= '0' && *s_ptr <= '9')
11965 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11967 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11968 return ANIM_EVENT_NONE;
11970 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11974 // invalid animation part number specified
11976 return ANIM_EVENT_NONE;
11980 // discard result if next character is neither delimiter nor whitespace
11981 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11982 *s_ptr == ' ' || *s_ptr == '\t'))
11983 return ANIM_EVENT_NONE;
11988 static int get_anim_parameter_values(char *s)
11990 int list_pos = ANIM_EVENT_UNDEFINED;
11991 int event_value = ANIM_EVENT_DEFAULT;
11993 if (string_has_parameter(s, "any"))
11994 event_value |= ANIM_EVENT_ANY;
11996 if (string_has_parameter(s, "click:self") ||
11997 string_has_parameter(s, "click") ||
11998 string_has_parameter(s, "self"))
11999 event_value |= ANIM_EVENT_SELF;
12001 if (string_has_parameter(s, "unclick:any"))
12002 event_value |= ANIM_EVENT_UNCLICK_ANY;
12004 // if animation event found, add it to global animation event list
12005 if (event_value != ANIM_EVENT_NONE)
12006 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12010 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12011 event_value = get_anim_parameter_value(s);
12013 // if animation event found, add it to global animation event list
12014 if (event_value != ANIM_EVENT_NONE)
12015 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12017 // continue with next part of the string, starting with next comma
12018 s = strchr(s + 1, ',');
12024 static int get_anim_action_parameter_value(char *token)
12026 // check most common default case first to massively speed things up
12027 if (strEqual(token, ARG_UNDEFINED))
12028 return ANIM_EVENT_ACTION_NONE;
12030 int result = getImageIDFromToken(token);
12034 char *gfx_token = getStringCat2("gfx.", token);
12036 result = getImageIDFromToken(gfx_token);
12038 checked_free(gfx_token);
12043 Key key = getKeyFromX11KeyName(token);
12045 if (key != KSYM_UNDEFINED)
12046 result = -(int)key;
12053 result = get_hash_from_string(token); // unsigned int => int
12054 result = ABS(result); // may be negative now
12055 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12057 setHashEntry(anim_url_hash, int2str(result, 0), token);
12062 result = ANIM_EVENT_ACTION_NONE;
12067 int get_parameter_value(char *value_raw, char *suffix, int type)
12069 char *value = getStringToLower(value_raw);
12070 int result = 0; // probably a save default value
12072 if (strEqual(suffix, ".direction"))
12074 result = (strEqual(value, "left") ? MV_LEFT :
12075 strEqual(value, "right") ? MV_RIGHT :
12076 strEqual(value, "up") ? MV_UP :
12077 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12079 else if (strEqual(suffix, ".position"))
12081 result = (strEqual(value, "left") ? POS_LEFT :
12082 strEqual(value, "right") ? POS_RIGHT :
12083 strEqual(value, "top") ? POS_TOP :
12084 strEqual(value, "upper") ? POS_UPPER :
12085 strEqual(value, "middle") ? POS_MIDDLE :
12086 strEqual(value, "lower") ? POS_LOWER :
12087 strEqual(value, "bottom") ? POS_BOTTOM :
12088 strEqual(value, "any") ? POS_ANY :
12089 strEqual(value, "ce") ? POS_CE :
12090 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12091 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12093 else if (strEqual(suffix, ".align"))
12095 result = (strEqual(value, "left") ? ALIGN_LEFT :
12096 strEqual(value, "right") ? ALIGN_RIGHT :
12097 strEqual(value, "center") ? ALIGN_CENTER :
12098 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12100 else if (strEqual(suffix, ".valign"))
12102 result = (strEqual(value, "top") ? VALIGN_TOP :
12103 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12104 strEqual(value, "middle") ? VALIGN_MIDDLE :
12105 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12107 else if (strEqual(suffix, ".anim_mode"))
12109 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12110 string_has_parameter(value, "loop") ? ANIM_LOOP :
12111 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12112 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12113 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12114 string_has_parameter(value, "random") ? ANIM_RANDOM :
12115 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12116 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12117 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12118 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12119 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12120 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12121 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12122 string_has_parameter(value, "all") ? ANIM_ALL :
12123 string_has_parameter(value, "tiled") ? ANIM_TILED :
12124 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12127 if (string_has_parameter(value, "once"))
12128 result |= ANIM_ONCE;
12130 if (string_has_parameter(value, "reverse"))
12131 result |= ANIM_REVERSE;
12133 if (string_has_parameter(value, "opaque_player"))
12134 result |= ANIM_OPAQUE_PLAYER;
12136 if (string_has_parameter(value, "static_panel"))
12137 result |= ANIM_STATIC_PANEL;
12139 else if (strEqual(suffix, ".init_event") ||
12140 strEqual(suffix, ".anim_event"))
12142 result = get_anim_parameter_values(value);
12144 else if (strEqual(suffix, ".init_delay_action") ||
12145 strEqual(suffix, ".anim_delay_action") ||
12146 strEqual(suffix, ".post_delay_action") ||
12147 strEqual(suffix, ".init_event_action") ||
12148 strEqual(suffix, ".anim_event_action"))
12150 result = get_anim_action_parameter_value(value_raw);
12152 else if (strEqual(suffix, ".class"))
12154 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12155 get_hash_from_string(value));
12157 else if (strEqual(suffix, ".style"))
12159 result = STYLE_DEFAULT;
12161 if (string_has_parameter(value, "accurate_borders"))
12162 result |= STYLE_ACCURATE_BORDERS;
12164 if (string_has_parameter(value, "inner_corners"))
12165 result |= STYLE_INNER_CORNERS;
12167 if (string_has_parameter(value, "reverse"))
12168 result |= STYLE_REVERSE;
12170 if (string_has_parameter(value, "leftmost_position"))
12171 result |= STYLE_LEFTMOST_POSITION;
12173 if (string_has_parameter(value, "block_clicks"))
12174 result |= STYLE_BLOCK;
12176 if (string_has_parameter(value, "passthrough_clicks"))
12177 result |= STYLE_PASSTHROUGH;
12179 if (string_has_parameter(value, "multiple_actions"))
12180 result |= STYLE_MULTIPLE_ACTIONS;
12182 if (string_has_parameter(value, "consume_ce_event"))
12183 result |= STYLE_CONSUME_CE_EVENT;
12185 else if (strEqual(suffix, ".fade_mode"))
12187 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12188 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12189 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12190 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12191 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12192 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12193 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12194 FADE_MODE_DEFAULT);
12196 else if (strEqual(suffix, ".auto_delay_unit"))
12198 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12199 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12200 AUTO_DELAY_UNIT_DEFAULT);
12202 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12204 result = gfx.get_font_from_token_function(value);
12206 else // generic parameter of type integer or boolean
12208 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12209 type == TYPE_INTEGER ? get_integer_from_string(value) :
12210 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12211 ARG_UNDEFINED_VALUE);
12219 static int get_token_parameter_value(char *token, char *value_raw)
12223 if (token == NULL || value_raw == NULL)
12224 return ARG_UNDEFINED_VALUE;
12226 suffix = strrchr(token, '.');
12227 if (suffix == NULL)
12230 if (strEqual(suffix, ".element"))
12231 return getElementFromToken(value_raw);
12233 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12234 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12237 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12238 boolean ignore_defaults)
12242 for (i = 0; image_config_vars[i].token != NULL; i++)
12244 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12246 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12247 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12251 *image_config_vars[i].value =
12252 get_token_parameter_value(image_config_vars[i].token, value);
12256 void InitMenuDesignSettings_Static(void)
12258 // always start with reliable default values from static default config
12259 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12262 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12266 // the following initializes hierarchical values from static configuration
12268 // special case: initialize "ARG_DEFAULT" values in static default config
12269 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12270 titlescreen_initial_first_default.fade_mode =
12271 title_initial_first_default.fade_mode;
12272 titlescreen_initial_first_default.fade_delay =
12273 title_initial_first_default.fade_delay;
12274 titlescreen_initial_first_default.post_delay =
12275 title_initial_first_default.post_delay;
12276 titlescreen_initial_first_default.auto_delay =
12277 title_initial_first_default.auto_delay;
12278 titlescreen_initial_first_default.auto_delay_unit =
12279 title_initial_first_default.auto_delay_unit;
12280 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12281 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12282 titlescreen_first_default.post_delay = title_first_default.post_delay;
12283 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12284 titlescreen_first_default.auto_delay_unit =
12285 title_first_default.auto_delay_unit;
12286 titlemessage_initial_first_default.fade_mode =
12287 title_initial_first_default.fade_mode;
12288 titlemessage_initial_first_default.fade_delay =
12289 title_initial_first_default.fade_delay;
12290 titlemessage_initial_first_default.post_delay =
12291 title_initial_first_default.post_delay;
12292 titlemessage_initial_first_default.auto_delay =
12293 title_initial_first_default.auto_delay;
12294 titlemessage_initial_first_default.auto_delay_unit =
12295 title_initial_first_default.auto_delay_unit;
12296 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12297 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12298 titlemessage_first_default.post_delay = title_first_default.post_delay;
12299 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12300 titlemessage_first_default.auto_delay_unit =
12301 title_first_default.auto_delay_unit;
12303 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12304 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12305 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12306 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12307 titlescreen_initial_default.auto_delay_unit =
12308 title_initial_default.auto_delay_unit;
12309 titlescreen_default.fade_mode = title_default.fade_mode;
12310 titlescreen_default.fade_delay = title_default.fade_delay;
12311 titlescreen_default.post_delay = title_default.post_delay;
12312 titlescreen_default.auto_delay = title_default.auto_delay;
12313 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12314 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12315 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12316 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12317 titlemessage_initial_default.auto_delay_unit =
12318 title_initial_default.auto_delay_unit;
12319 titlemessage_default.fade_mode = title_default.fade_mode;
12320 titlemessage_default.fade_delay = title_default.fade_delay;
12321 titlemessage_default.post_delay = title_default.post_delay;
12322 titlemessage_default.auto_delay = title_default.auto_delay;
12323 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12325 // special case: initialize "ARG_DEFAULT" values in static default config
12326 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12327 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12329 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12330 titlescreen_first[i] = titlescreen_first_default;
12331 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12332 titlemessage_first[i] = titlemessage_first_default;
12334 titlescreen_initial[i] = titlescreen_initial_default;
12335 titlescreen[i] = titlescreen_default;
12336 titlemessage_initial[i] = titlemessage_initial_default;
12337 titlemessage[i] = titlemessage_default;
12340 // special case: initialize "ARG_DEFAULT" values in static default config
12341 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12342 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12344 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12347 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12348 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12349 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12352 // special case: initialize "ARG_DEFAULT" values in static default config
12353 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12354 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12356 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12357 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12358 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12360 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12363 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12367 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12371 struct XY *dst, *src;
12373 game_buttons_xy[] =
12375 { &game.button.save, &game.button.stop },
12376 { &game.button.pause2, &game.button.pause },
12377 { &game.button.load, &game.button.play },
12378 { &game.button.undo, &game.button.stop },
12379 { &game.button.redo, &game.button.play },
12385 // special case: initialize later added SETUP list size from LEVELS value
12386 if (menu.list_size[GAME_MODE_SETUP] == -1)
12387 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12389 // set default position for snapshot buttons to stop/pause/play buttons
12390 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12391 if ((*game_buttons_xy[i].dst).x == -1 &&
12392 (*game_buttons_xy[i].dst).y == -1)
12393 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12395 // --------------------------------------------------------------------------
12396 // dynamic viewports (including playfield margins, borders and alignments)
12397 // --------------------------------------------------------------------------
12399 // dynamic viewports currently only supported for landscape mode
12400 int display_width = MAX(video.display_width, video.display_height);
12401 int display_height = MIN(video.display_width, video.display_height);
12403 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12405 struct RectWithBorder *vp_window = &viewport.window[i];
12406 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12407 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12408 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12409 boolean dynamic_window_width = (vp_window->min_width != -1);
12410 boolean dynamic_window_height = (vp_window->min_height != -1);
12411 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12412 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12414 // adjust window size if min/max width/height is specified
12416 if (vp_window->min_width != -1)
12418 int window_width = display_width;
12420 // when using static window height, use aspect ratio of display
12421 if (vp_window->min_height == -1)
12422 window_width = vp_window->height * display_width / display_height;
12424 vp_window->width = MAX(vp_window->min_width, window_width);
12427 if (vp_window->min_height != -1)
12429 int window_height = display_height;
12431 // when using static window width, use aspect ratio of display
12432 if (vp_window->min_width == -1)
12433 window_height = vp_window->width * display_height / display_width;
12435 vp_window->height = MAX(vp_window->min_height, window_height);
12438 if (vp_window->max_width != -1)
12439 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12441 if (vp_window->max_height != -1)
12442 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12444 int playfield_width = vp_window->width;
12445 int playfield_height = vp_window->height;
12447 // adjust playfield size and position according to specified margins
12449 playfield_width -= vp_playfield->margin_left;
12450 playfield_width -= vp_playfield->margin_right;
12452 playfield_height -= vp_playfield->margin_top;
12453 playfield_height -= vp_playfield->margin_bottom;
12455 // adjust playfield size if min/max width/height is specified
12457 if (vp_playfield->min_width != -1)
12458 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12460 if (vp_playfield->min_height != -1)
12461 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12463 if (vp_playfield->max_width != -1)
12464 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12466 if (vp_playfield->max_height != -1)
12467 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12469 // adjust playfield position according to specified alignment
12471 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12472 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12473 else if (vp_playfield->align == ALIGN_CENTER)
12474 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12475 else if (vp_playfield->align == ALIGN_RIGHT)
12476 vp_playfield->x += playfield_width - vp_playfield->width;
12478 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12479 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12480 else if (vp_playfield->valign == VALIGN_MIDDLE)
12481 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12482 else if (vp_playfield->valign == VALIGN_BOTTOM)
12483 vp_playfield->y += playfield_height - vp_playfield->height;
12485 vp_playfield->x += vp_playfield->margin_left;
12486 vp_playfield->y += vp_playfield->margin_top;
12488 // adjust individual playfield borders if only default border is specified
12490 if (vp_playfield->border_left == -1)
12491 vp_playfield->border_left = vp_playfield->border_size;
12492 if (vp_playfield->border_right == -1)
12493 vp_playfield->border_right = vp_playfield->border_size;
12494 if (vp_playfield->border_top == -1)
12495 vp_playfield->border_top = vp_playfield->border_size;
12496 if (vp_playfield->border_bottom == -1)
12497 vp_playfield->border_bottom = vp_playfield->border_size;
12499 // set dynamic playfield borders if borders are specified as undefined
12500 // (but only if window size was dynamic and playfield size was static)
12502 if (dynamic_window_width && !dynamic_playfield_width)
12504 if (vp_playfield->border_left == -1)
12506 vp_playfield->border_left = (vp_playfield->x -
12507 vp_playfield->margin_left);
12508 vp_playfield->x -= vp_playfield->border_left;
12509 vp_playfield->width += vp_playfield->border_left;
12512 if (vp_playfield->border_right == -1)
12514 vp_playfield->border_right = (vp_window->width -
12516 vp_playfield->width -
12517 vp_playfield->margin_right);
12518 vp_playfield->width += vp_playfield->border_right;
12522 if (dynamic_window_height && !dynamic_playfield_height)
12524 if (vp_playfield->border_top == -1)
12526 vp_playfield->border_top = (vp_playfield->y -
12527 vp_playfield->margin_top);
12528 vp_playfield->y -= vp_playfield->border_top;
12529 vp_playfield->height += vp_playfield->border_top;
12532 if (vp_playfield->border_bottom == -1)
12534 vp_playfield->border_bottom = (vp_window->height -
12536 vp_playfield->height -
12537 vp_playfield->margin_bottom);
12538 vp_playfield->height += vp_playfield->border_bottom;
12542 // adjust playfield size to be a multiple of a defined alignment tile size
12544 int align_size = vp_playfield->align_size;
12545 int playfield_xtiles = vp_playfield->width / align_size;
12546 int playfield_ytiles = vp_playfield->height / align_size;
12547 int playfield_width_corrected = playfield_xtiles * align_size;
12548 int playfield_height_corrected = playfield_ytiles * align_size;
12549 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12550 i == GFX_SPECIAL_ARG_EDITOR);
12552 if (is_playfield_mode &&
12553 dynamic_playfield_width &&
12554 vp_playfield->width != playfield_width_corrected)
12556 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12558 vp_playfield->width = playfield_width_corrected;
12560 if (vp_playfield->align == ALIGN_LEFT)
12562 vp_playfield->border_left += playfield_xdiff;
12564 else if (vp_playfield->align == ALIGN_RIGHT)
12566 vp_playfield->border_right += playfield_xdiff;
12568 else if (vp_playfield->align == ALIGN_CENTER)
12570 int border_left_diff = playfield_xdiff / 2;
12571 int border_right_diff = playfield_xdiff - border_left_diff;
12573 vp_playfield->border_left += border_left_diff;
12574 vp_playfield->border_right += border_right_diff;
12578 if (is_playfield_mode &&
12579 dynamic_playfield_height &&
12580 vp_playfield->height != playfield_height_corrected)
12582 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12584 vp_playfield->height = playfield_height_corrected;
12586 if (vp_playfield->valign == VALIGN_TOP)
12588 vp_playfield->border_top += playfield_ydiff;
12590 else if (vp_playfield->align == VALIGN_BOTTOM)
12592 vp_playfield->border_right += playfield_ydiff;
12594 else if (vp_playfield->align == VALIGN_MIDDLE)
12596 int border_top_diff = playfield_ydiff / 2;
12597 int border_bottom_diff = playfield_ydiff - border_top_diff;
12599 vp_playfield->border_top += border_top_diff;
12600 vp_playfield->border_bottom += border_bottom_diff;
12604 // adjust door positions according to specified alignment
12606 for (j = 0; j < 2; j++)
12608 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12610 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12611 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12612 else if (vp_door->align == ALIGN_CENTER)
12613 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12614 else if (vp_door->align == ALIGN_RIGHT)
12615 vp_door->x += vp_window->width - vp_door->width;
12617 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12618 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12619 else if (vp_door->valign == VALIGN_MIDDLE)
12620 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12621 else if (vp_door->valign == VALIGN_BOTTOM)
12622 vp_door->y += vp_window->height - vp_door->height;
12627 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12631 struct XYTileSize *dst, *src;
12634 editor_buttons_xy[] =
12637 &editor.button.element_left, &editor.palette.element_left,
12638 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12641 &editor.button.element_middle, &editor.palette.element_middle,
12642 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12645 &editor.button.element_right, &editor.palette.element_right,
12646 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12653 // set default position for element buttons to element graphics
12654 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12656 if ((*editor_buttons_xy[i].dst).x == -1 &&
12657 (*editor_buttons_xy[i].dst).y == -1)
12659 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12661 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12663 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12667 // adjust editor palette rows and columns if specified to be dynamic
12669 if (editor.palette.cols == -1)
12671 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12672 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12673 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12675 editor.palette.cols = (vp_width - sc_width) / bt_width;
12677 if (editor.palette.x == -1)
12679 int palette_width = editor.palette.cols * bt_width + sc_width;
12681 editor.palette.x = (vp_width - palette_width) / 2;
12685 if (editor.palette.rows == -1)
12687 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12688 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12689 int tx_height = getFontHeight(FONT_TEXT_2);
12691 editor.palette.rows = (vp_height - tx_height) / bt_height;
12693 if (editor.palette.y == -1)
12695 int palette_height = editor.palette.rows * bt_height + tx_height;
12697 editor.palette.y = (vp_height - palette_height) / 2;
12702 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12703 boolean initialize)
12705 // special case: check if network and preview player positions are redefined,
12706 // to compare this later against the main menu level preview being redefined
12707 struct TokenIntPtrInfo menu_config_players[] =
12709 { "main.network_players.x", &menu.main.network_players.redefined },
12710 { "main.network_players.y", &menu.main.network_players.redefined },
12711 { "main.preview_players.x", &menu.main.preview_players.redefined },
12712 { "main.preview_players.y", &menu.main.preview_players.redefined },
12713 { "preview.x", &preview.redefined },
12714 { "preview.y", &preview.redefined }
12720 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12721 *menu_config_players[i].value = FALSE;
12725 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12726 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12727 *menu_config_players[i].value = TRUE;
12731 static void InitMenuDesignSettings_PreviewPlayers(void)
12733 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12736 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12738 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12741 static void LoadMenuDesignSettingsFromFilename(char *filename)
12743 static struct TitleFadingInfo tfi;
12744 static struct TitleMessageInfo tmi;
12745 static struct TokenInfo title_tokens[] =
12747 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12748 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12749 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12750 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12751 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12755 static struct TokenInfo titlemessage_tokens[] =
12757 { TYPE_INTEGER, &tmi.x, ".x" },
12758 { TYPE_INTEGER, &tmi.y, ".y" },
12759 { TYPE_INTEGER, &tmi.width, ".width" },
12760 { TYPE_INTEGER, &tmi.height, ".height" },
12761 { TYPE_INTEGER, &tmi.chars, ".chars" },
12762 { TYPE_INTEGER, &tmi.lines, ".lines" },
12763 { TYPE_INTEGER, &tmi.align, ".align" },
12764 { TYPE_INTEGER, &tmi.valign, ".valign" },
12765 { TYPE_INTEGER, &tmi.font, ".font" },
12766 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12767 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12768 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12769 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12770 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12771 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12772 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12773 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12774 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12780 struct TitleFadingInfo *info;
12785 // initialize first titles from "enter screen" definitions, if defined
12786 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12787 { &title_first_default, "menu.enter_screen.TITLE" },
12789 // initialize title screens from "next screen" definitions, if defined
12790 { &title_initial_default, "menu.next_screen.TITLE" },
12791 { &title_default, "menu.next_screen.TITLE" },
12797 struct TitleMessageInfo *array;
12800 titlemessage_arrays[] =
12802 // initialize first titles from "enter screen" definitions, if defined
12803 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12804 { titlescreen_first, "menu.enter_screen.TITLE" },
12805 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12806 { titlemessage_first, "menu.enter_screen.TITLE" },
12808 // initialize titles from "next screen" definitions, if defined
12809 { titlescreen_initial, "menu.next_screen.TITLE" },
12810 { titlescreen, "menu.next_screen.TITLE" },
12811 { titlemessage_initial, "menu.next_screen.TITLE" },
12812 { titlemessage, "menu.next_screen.TITLE" },
12814 // overwrite titles with title definitions, if defined
12815 { titlescreen_initial_first, "[title_initial]" },
12816 { titlescreen_first, "[title]" },
12817 { titlemessage_initial_first, "[title_initial]" },
12818 { titlemessage_first, "[title]" },
12820 { titlescreen_initial, "[title_initial]" },
12821 { titlescreen, "[title]" },
12822 { titlemessage_initial, "[title_initial]" },
12823 { titlemessage, "[title]" },
12825 // overwrite titles with title screen/message definitions, if defined
12826 { titlescreen_initial_first, "[titlescreen_initial]" },
12827 { titlescreen_first, "[titlescreen]" },
12828 { titlemessage_initial_first, "[titlemessage_initial]" },
12829 { titlemessage_first, "[titlemessage]" },
12831 { titlescreen_initial, "[titlescreen_initial]" },
12832 { titlescreen, "[titlescreen]" },
12833 { titlemessage_initial, "[titlemessage_initial]" },
12834 { titlemessage, "[titlemessage]" },
12838 SetupFileHash *setup_file_hash;
12841 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12844 // the following initializes hierarchical values from dynamic configuration
12846 // special case: initialize with default values that may be overwritten
12847 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12848 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12850 struct TokenIntPtrInfo menu_config[] =
12852 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12853 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12854 { "menu.list_size", &menu.list_size[i] }
12857 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12859 char *token = menu_config[j].token;
12860 char *value = getHashEntry(setup_file_hash, token);
12863 *menu_config[j].value = get_integer_from_string(value);
12867 // special case: initialize with default values that may be overwritten
12868 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12869 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12871 struct TokenIntPtrInfo menu_config[] =
12873 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12874 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12875 { "menu.list_size.INFO", &menu.list_size_info[i] },
12876 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12877 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12880 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12882 char *token = menu_config[j].token;
12883 char *value = getHashEntry(setup_file_hash, token);
12886 *menu_config[j].value = get_integer_from_string(value);
12890 // special case: initialize with default values that may be overwritten
12891 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12892 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12894 struct TokenIntPtrInfo menu_config[] =
12896 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12897 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12900 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12902 char *token = menu_config[j].token;
12903 char *value = getHashEntry(setup_file_hash, token);
12906 *menu_config[j].value = get_integer_from_string(value);
12910 // special case: initialize with default values that may be overwritten
12911 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12912 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12914 struct TokenIntPtrInfo menu_config[] =
12916 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12917 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12918 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12919 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12920 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12921 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12922 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12923 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12924 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12925 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12928 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12930 char *token = menu_config[j].token;
12931 char *value = getHashEntry(setup_file_hash, token);
12934 *menu_config[j].value = get_integer_from_string(value);
12938 // special case: initialize with default values that may be overwritten
12939 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12940 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12942 struct TokenIntPtrInfo menu_config[] =
12944 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12945 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12946 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12947 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12948 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12949 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12950 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12951 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12952 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12955 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12957 char *token = menu_config[j].token;
12958 char *value = getHashEntry(setup_file_hash, token);
12961 *menu_config[j].value = get_token_parameter_value(token, value);
12965 // special case: initialize with default values that may be overwritten
12966 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12967 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12971 char *token_prefix;
12972 struct RectWithBorder *struct_ptr;
12976 { "viewport.window", &viewport.window[i] },
12977 { "viewport.playfield", &viewport.playfield[i] },
12978 { "viewport.door_1", &viewport.door_1[i] },
12979 { "viewport.door_2", &viewport.door_2[i] }
12982 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12984 struct TokenIntPtrInfo vp_config[] =
12986 { ".x", &vp_struct[j].struct_ptr->x },
12987 { ".y", &vp_struct[j].struct_ptr->y },
12988 { ".width", &vp_struct[j].struct_ptr->width },
12989 { ".height", &vp_struct[j].struct_ptr->height },
12990 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12991 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12992 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12993 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12994 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12995 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12996 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12997 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12998 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12999 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13000 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13001 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13002 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13003 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13004 { ".align", &vp_struct[j].struct_ptr->align },
13005 { ".valign", &vp_struct[j].struct_ptr->valign }
13008 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13010 char *token = getStringCat2(vp_struct[j].token_prefix,
13011 vp_config[k].token);
13012 char *value = getHashEntry(setup_file_hash, token);
13015 *vp_config[k].value = get_token_parameter_value(token, value);
13022 // special case: initialize with default values that may be overwritten
13023 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13024 for (i = 0; title_info[i].info != NULL; i++)
13026 struct TitleFadingInfo *info = title_info[i].info;
13027 char *base_token = title_info[i].text;
13029 for (j = 0; title_tokens[j].type != -1; j++)
13031 char *token = getStringCat2(base_token, title_tokens[j].text);
13032 char *value = getHashEntry(setup_file_hash, token);
13036 int parameter_value = get_token_parameter_value(token, value);
13040 *(int *)title_tokens[j].value = (int)parameter_value;
13049 // special case: initialize with default values that may be overwritten
13050 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13051 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13053 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13054 char *base_token = titlemessage_arrays[i].text;
13056 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13058 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13059 char *value = getHashEntry(setup_file_hash, token);
13063 int parameter_value = get_token_parameter_value(token, value);
13065 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13069 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13070 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13072 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13082 // read (and overwrite with) values that may be specified in config file
13083 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13085 // special case: check if network and preview player positions are redefined
13086 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13088 freeSetupFileHash(setup_file_hash);
13091 void LoadMenuDesignSettings(void)
13093 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13095 InitMenuDesignSettings_Static();
13096 InitMenuDesignSettings_SpecialPreProcessing();
13097 InitMenuDesignSettings_PreviewPlayers();
13099 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13101 // first look for special settings configured in level series config
13102 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13104 if (fileExists(filename_base))
13105 LoadMenuDesignSettingsFromFilename(filename_base);
13108 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13110 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13111 LoadMenuDesignSettingsFromFilename(filename_local);
13113 InitMenuDesignSettings_SpecialPostProcessing();
13116 void LoadMenuDesignSettings_AfterGraphics(void)
13118 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13121 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13122 boolean ignore_defaults)
13126 for (i = 0; sound_config_vars[i].token != NULL; i++)
13128 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13130 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13131 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13135 *sound_config_vars[i].value =
13136 get_token_parameter_value(sound_config_vars[i].token, value);
13140 void InitSoundSettings_Static(void)
13142 // always start with reliable default values from static default config
13143 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13146 static void LoadSoundSettingsFromFilename(char *filename)
13148 SetupFileHash *setup_file_hash;
13150 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13153 // read (and overwrite with) values that may be specified in config file
13154 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13156 freeSetupFileHash(setup_file_hash);
13159 void LoadSoundSettings(void)
13161 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13163 InitSoundSettings_Static();
13165 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13167 // first look for special settings configured in level series config
13168 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13170 if (fileExists(filename_base))
13171 LoadSoundSettingsFromFilename(filename_base);
13174 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13176 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13177 LoadSoundSettingsFromFilename(filename_local);
13180 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13182 char *filename = getEditorSetupFilename();
13183 SetupFileList *setup_file_list, *list;
13184 SetupFileHash *element_hash;
13185 int num_unknown_tokens = 0;
13188 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13191 element_hash = newSetupFileHash();
13193 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13194 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13196 // determined size may be larger than needed (due to unknown elements)
13198 for (list = setup_file_list; list != NULL; list = list->next)
13201 // add space for up to 3 more elements for padding that may be needed
13202 *num_elements += 3;
13204 // free memory for old list of elements, if needed
13205 checked_free(*elements);
13207 // allocate memory for new list of elements
13208 *elements = checked_malloc(*num_elements * sizeof(int));
13211 for (list = setup_file_list; list != NULL; list = list->next)
13213 char *value = getHashEntry(element_hash, list->token);
13215 if (value == NULL) // try to find obsolete token mapping
13217 char *mapped_token = get_mapped_token(list->token);
13219 if (mapped_token != NULL)
13221 value = getHashEntry(element_hash, mapped_token);
13223 free(mapped_token);
13229 (*elements)[(*num_elements)++] = atoi(value);
13233 if (num_unknown_tokens == 0)
13236 Warn("unknown token(s) found in config file:");
13237 Warn("- config file: '%s'", filename);
13239 num_unknown_tokens++;
13242 Warn("- token: '%s'", list->token);
13246 if (num_unknown_tokens > 0)
13249 while (*num_elements % 4) // pad with empty elements, if needed
13250 (*elements)[(*num_elements)++] = EL_EMPTY;
13252 freeSetupFileList(setup_file_list);
13253 freeSetupFileHash(element_hash);
13256 for (i = 0; i < *num_elements; i++)
13257 Debug("editor", "element '%s' [%d]\n",
13258 element_info[(*elements)[i]].token_name, (*elements)[i]);
13262 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13265 SetupFileHash *setup_file_hash = NULL;
13266 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13267 char *filename_music, *filename_prefix, *filename_info;
13273 token_to_value_ptr[] =
13275 { "title_header", &tmp_music_file_info.title_header },
13276 { "artist_header", &tmp_music_file_info.artist_header },
13277 { "album_header", &tmp_music_file_info.album_header },
13278 { "year_header", &tmp_music_file_info.year_header },
13279 { "played_header", &tmp_music_file_info.played_header },
13281 { "title", &tmp_music_file_info.title },
13282 { "artist", &tmp_music_file_info.artist },
13283 { "album", &tmp_music_file_info.album },
13284 { "year", &tmp_music_file_info.year },
13285 { "played", &tmp_music_file_info.played },
13291 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13292 getCustomMusicFilename(basename));
13294 if (filename_music == NULL)
13297 // ---------- try to replace file extension ----------
13299 filename_prefix = getStringCopy(filename_music);
13300 if (strrchr(filename_prefix, '.') != NULL)
13301 *strrchr(filename_prefix, '.') = '\0';
13302 filename_info = getStringCat2(filename_prefix, ".txt");
13304 if (fileExists(filename_info))
13305 setup_file_hash = loadSetupFileHash(filename_info);
13307 free(filename_prefix);
13308 free(filename_info);
13310 if (setup_file_hash == NULL)
13312 // ---------- try to add file extension ----------
13314 filename_prefix = getStringCopy(filename_music);
13315 filename_info = getStringCat2(filename_prefix, ".txt");
13317 if (fileExists(filename_info))
13318 setup_file_hash = loadSetupFileHash(filename_info);
13320 free(filename_prefix);
13321 free(filename_info);
13324 if (setup_file_hash == NULL)
13327 // ---------- music file info found ----------
13329 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13331 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13333 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13335 *token_to_value_ptr[i].value_ptr =
13336 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13339 tmp_music_file_info.basename = getStringCopy(basename);
13340 tmp_music_file_info.music = music;
13341 tmp_music_file_info.is_sound = is_sound;
13343 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13344 *new_music_file_info = tmp_music_file_info;
13346 return new_music_file_info;
13349 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13351 return get_music_file_info_ext(basename, music, FALSE);
13354 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13356 return get_music_file_info_ext(basename, sound, TRUE);
13359 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13360 char *basename, boolean is_sound)
13362 for (; list != NULL; list = list->next)
13363 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13369 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13371 return music_info_listed_ext(list, basename, FALSE);
13374 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13376 return music_info_listed_ext(list, basename, TRUE);
13379 void LoadMusicInfo(void)
13381 int num_music_noconf = getMusicListSize_NoConf();
13382 int num_music = getMusicListSize();
13383 int num_sounds = getSoundListSize();
13384 struct FileInfo *music, *sound;
13385 struct MusicFileInfo *next, **new;
13389 while (music_file_info != NULL)
13391 next = music_file_info->next;
13393 checked_free(music_file_info->basename);
13395 checked_free(music_file_info->title_header);
13396 checked_free(music_file_info->artist_header);
13397 checked_free(music_file_info->album_header);
13398 checked_free(music_file_info->year_header);
13399 checked_free(music_file_info->played_header);
13401 checked_free(music_file_info->title);
13402 checked_free(music_file_info->artist);
13403 checked_free(music_file_info->album);
13404 checked_free(music_file_info->year);
13405 checked_free(music_file_info->played);
13407 free(music_file_info);
13409 music_file_info = next;
13412 new = &music_file_info;
13414 // get (configured or unconfigured) music file info for all levels
13415 for (i = leveldir_current->first_level;
13416 i <= leveldir_current->last_level; i++)
13420 if (levelset.music[i] != MUS_UNDEFINED)
13422 // get music file info for configured level music
13423 music_nr = levelset.music[i];
13425 else if (num_music_noconf > 0)
13427 // get music file info for unconfigured level music
13428 int level_pos = i - leveldir_current->first_level;
13430 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13437 char *basename = getMusicInfoEntryFilename(music_nr);
13439 if (basename == NULL)
13442 if (!music_info_listed(music_file_info, basename))
13444 *new = get_music_file_info(basename, music_nr);
13447 new = &(*new)->next;
13451 // get music file info for all remaining configured music files
13452 for (i = 0; i < num_music; i++)
13454 music = getMusicListEntry(i);
13456 if (music->filename == NULL)
13459 if (strEqual(music->filename, UNDEFINED_FILENAME))
13462 // a configured file may be not recognized as music
13463 if (!FileIsMusic(music->filename))
13466 if (!music_info_listed(music_file_info, music->filename))
13468 *new = get_music_file_info(music->filename, i);
13471 new = &(*new)->next;
13475 // get sound file info for all configured sound files
13476 for (i = 0; i < num_sounds; i++)
13478 sound = getSoundListEntry(i);
13480 if (sound->filename == NULL)
13483 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13486 // a configured file may be not recognized as sound
13487 if (!FileIsSound(sound->filename))
13490 if (!sound_info_listed(music_file_info, sound->filename))
13492 *new = get_sound_file_info(sound->filename, i);
13494 new = &(*new)->next;
13498 // add pointers to previous list nodes
13500 struct MusicFileInfo *node = music_file_info;
13502 while (node != NULL)
13505 node->next->prev = node;
13511 static void add_helpanim_entry(int element, int action, int direction,
13512 int delay, int *num_list_entries)
13514 struct HelpAnimInfo *new_list_entry;
13515 (*num_list_entries)++;
13518 checked_realloc(helpanim_info,
13519 *num_list_entries * sizeof(struct HelpAnimInfo));
13520 new_list_entry = &helpanim_info[*num_list_entries - 1];
13522 new_list_entry->element = element;
13523 new_list_entry->action = action;
13524 new_list_entry->direction = direction;
13525 new_list_entry->delay = delay;
13528 static void print_unknown_token(char *filename, char *token, int token_nr)
13533 Warn("unknown token(s) found in config file:");
13534 Warn("- config file: '%s'", filename);
13537 Warn("- token: '%s'", token);
13540 static void print_unknown_token_end(int token_nr)
13546 void LoadHelpAnimInfo(void)
13548 char *filename = getHelpAnimFilename();
13549 SetupFileList *setup_file_list = NULL, *list;
13550 SetupFileHash *element_hash, *action_hash, *direction_hash;
13551 int num_list_entries = 0;
13552 int num_unknown_tokens = 0;
13555 if (fileExists(filename))
13556 setup_file_list = loadSetupFileList(filename);
13558 if (setup_file_list == NULL)
13560 // use reliable default values from static configuration
13561 SetupFileList *insert_ptr;
13563 insert_ptr = setup_file_list =
13564 newSetupFileList(helpanim_config[0].token,
13565 helpanim_config[0].value);
13567 for (i = 1; helpanim_config[i].token; i++)
13568 insert_ptr = addListEntry(insert_ptr,
13569 helpanim_config[i].token,
13570 helpanim_config[i].value);
13573 element_hash = newSetupFileHash();
13574 action_hash = newSetupFileHash();
13575 direction_hash = newSetupFileHash();
13577 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13578 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13580 for (i = 0; i < NUM_ACTIONS; i++)
13581 setHashEntry(action_hash, element_action_info[i].suffix,
13582 i_to_a(element_action_info[i].value));
13584 // do not store direction index (bit) here, but direction value!
13585 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13586 setHashEntry(direction_hash, element_direction_info[i].suffix,
13587 i_to_a(1 << element_direction_info[i].value));
13589 for (list = setup_file_list; list != NULL; list = list->next)
13591 char *element_token, *action_token, *direction_token;
13592 char *element_value, *action_value, *direction_value;
13593 int delay = atoi(list->value);
13595 if (strEqual(list->token, "end"))
13597 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13602 /* first try to break element into element/action/direction parts;
13603 if this does not work, also accept combined "element[.act][.dir]"
13604 elements (like "dynamite.active"), which are unique elements */
13606 if (strchr(list->token, '.') == NULL) // token contains no '.'
13608 element_value = getHashEntry(element_hash, list->token);
13609 if (element_value != NULL) // element found
13610 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13611 &num_list_entries);
13614 // no further suffixes found -- this is not an element
13615 print_unknown_token(filename, list->token, num_unknown_tokens++);
13621 // token has format "<prefix>.<something>"
13623 action_token = strchr(list->token, '.'); // suffix may be action ...
13624 direction_token = action_token; // ... or direction
13626 element_token = getStringCopy(list->token);
13627 *strchr(element_token, '.') = '\0';
13629 element_value = getHashEntry(element_hash, element_token);
13631 if (element_value == NULL) // this is no element
13633 element_value = getHashEntry(element_hash, list->token);
13634 if (element_value != NULL) // combined element found
13635 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13636 &num_list_entries);
13638 print_unknown_token(filename, list->token, num_unknown_tokens++);
13640 free(element_token);
13645 action_value = getHashEntry(action_hash, action_token);
13647 if (action_value != NULL) // action found
13649 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13650 &num_list_entries);
13652 free(element_token);
13657 direction_value = getHashEntry(direction_hash, direction_token);
13659 if (direction_value != NULL) // direction found
13661 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13662 &num_list_entries);
13664 free(element_token);
13669 if (strchr(action_token + 1, '.') == NULL)
13671 // no further suffixes found -- this is not an action nor direction
13673 element_value = getHashEntry(element_hash, list->token);
13674 if (element_value != NULL) // combined element found
13675 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13676 &num_list_entries);
13678 print_unknown_token(filename, list->token, num_unknown_tokens++);
13680 free(element_token);
13685 // token has format "<prefix>.<suffix>.<something>"
13687 direction_token = strchr(action_token + 1, '.');
13689 action_token = getStringCopy(action_token);
13690 *strchr(action_token + 1, '.') = '\0';
13692 action_value = getHashEntry(action_hash, action_token);
13694 if (action_value == NULL) // this is no action
13696 element_value = getHashEntry(element_hash, list->token);
13697 if (element_value != NULL) // combined element found
13698 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13699 &num_list_entries);
13701 print_unknown_token(filename, list->token, num_unknown_tokens++);
13703 free(element_token);
13704 free(action_token);
13709 direction_value = getHashEntry(direction_hash, direction_token);
13711 if (direction_value != NULL) // direction found
13713 add_helpanim_entry(atoi(element_value), atoi(action_value),
13714 atoi(direction_value), delay, &num_list_entries);
13716 free(element_token);
13717 free(action_token);
13722 // this is no direction
13724 element_value = getHashEntry(element_hash, list->token);
13725 if (element_value != NULL) // combined element found
13726 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13727 &num_list_entries);
13729 print_unknown_token(filename, list->token, num_unknown_tokens++);
13731 free(element_token);
13732 free(action_token);
13735 print_unknown_token_end(num_unknown_tokens);
13737 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13738 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13740 freeSetupFileList(setup_file_list);
13741 freeSetupFileHash(element_hash);
13742 freeSetupFileHash(action_hash);
13743 freeSetupFileHash(direction_hash);
13746 for (i = 0; i < num_list_entries; i++)
13747 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13748 EL_NAME(helpanim_info[i].element),
13749 helpanim_info[i].element,
13750 helpanim_info[i].action,
13751 helpanim_info[i].direction,
13752 helpanim_info[i].delay);
13756 void LoadHelpTextInfo(void)
13758 char *filename = getHelpTextFilename();
13761 if (helptext_info != NULL)
13763 freeSetupFileHash(helptext_info);
13764 helptext_info = NULL;
13767 if (fileExists(filename))
13768 helptext_info = loadSetupFileHash(filename);
13770 if (helptext_info == NULL)
13772 // use reliable default values from static configuration
13773 helptext_info = newSetupFileHash();
13775 for (i = 0; helptext_config[i].token; i++)
13776 setHashEntry(helptext_info,
13777 helptext_config[i].token,
13778 helptext_config[i].value);
13782 BEGIN_HASH_ITERATION(helptext_info, itr)
13784 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13785 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13787 END_HASH_ITERATION(hash, itr)
13792 // ----------------------------------------------------------------------------
13794 // ----------------------------------------------------------------------------
13796 #define MAX_NUM_CONVERT_LEVELS 1000
13798 void ConvertLevels(void)
13800 static LevelDirTree *convert_leveldir = NULL;
13801 static int convert_level_nr = -1;
13802 static int num_levels_handled = 0;
13803 static int num_levels_converted = 0;
13804 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13807 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13808 global.convert_leveldir);
13810 if (convert_leveldir == NULL)
13811 Fail("no such level identifier: '%s'", global.convert_leveldir);
13813 leveldir_current = convert_leveldir;
13815 if (global.convert_level_nr != -1)
13817 convert_leveldir->first_level = global.convert_level_nr;
13818 convert_leveldir->last_level = global.convert_level_nr;
13821 convert_level_nr = convert_leveldir->first_level;
13823 PrintLine("=", 79);
13824 Print("Converting levels\n");
13825 PrintLine("-", 79);
13826 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13827 Print("Level series name: '%s'\n", convert_leveldir->name);
13828 Print("Level series author: '%s'\n", convert_leveldir->author);
13829 Print("Number of levels: %d\n", convert_leveldir->levels);
13830 PrintLine("=", 79);
13833 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13834 levels_failed[i] = FALSE;
13836 while (convert_level_nr <= convert_leveldir->last_level)
13838 char *level_filename;
13841 level_nr = convert_level_nr++;
13843 Print("Level %03d: ", level_nr);
13845 LoadLevel(level_nr);
13846 if (level.no_level_file || level.no_valid_file)
13848 Print("(no level)\n");
13852 Print("converting level ... ");
13855 // special case: conversion of some EMC levels as requested by ACME
13856 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13859 level_filename = getDefaultLevelFilename(level_nr);
13860 new_level = !fileExists(level_filename);
13864 SaveLevel(level_nr);
13866 num_levels_converted++;
13868 Print("converted.\n");
13872 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13873 levels_failed[level_nr] = TRUE;
13875 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13878 num_levels_handled++;
13882 PrintLine("=", 79);
13883 Print("Number of levels handled: %d\n", num_levels_handled);
13884 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13885 (num_levels_handled ?
13886 num_levels_converted * 100 / num_levels_handled : 0));
13887 PrintLine("-", 79);
13888 Print("Summary (for automatic parsing by scripts):\n");
13889 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13890 convert_leveldir->identifier, num_levels_converted,
13891 num_levels_handled,
13892 (num_levels_handled ?
13893 num_levels_converted * 100 / num_levels_handled : 0));
13895 if (num_levels_handled != num_levels_converted)
13897 Print(", FAILED:");
13898 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13899 if (levels_failed[i])
13904 PrintLine("=", 79);
13906 CloseAllAndExit(0);
13910 // ----------------------------------------------------------------------------
13911 // create and save images for use in level sketches (raw BMP format)
13912 // ----------------------------------------------------------------------------
13914 void CreateLevelSketchImages(void)
13920 InitElementPropertiesGfxElement();
13922 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13923 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13925 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13927 int element = getMappedElement(i);
13928 char basename1[16];
13929 char basename2[16];
13933 sprintf(basename1, "%04d.bmp", i);
13934 sprintf(basename2, "%04ds.bmp", i);
13936 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13937 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13939 DrawSizedElement(0, 0, element, TILESIZE);
13940 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13942 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13943 Fail("cannot save level sketch image file '%s'", filename1);
13945 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13946 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13948 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13949 Fail("cannot save level sketch image file '%s'", filename2);
13954 // create corresponding SQL statements (for normal and small images)
13957 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13958 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13961 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13962 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13964 // optional: create content for forum level sketch demonstration post
13966 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13969 FreeBitmap(bitmap1);
13970 FreeBitmap(bitmap2);
13973 fprintf(stderr, "\n");
13975 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13977 CloseAllAndExit(0);
13981 // ----------------------------------------------------------------------------
13982 // create and save images for element collecting animations (raw BMP format)
13983 // ----------------------------------------------------------------------------
13985 static boolean createCollectImage(int element)
13987 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13990 void CreateCollectElementImages(void)
13994 int anim_frames = num_steps - 1;
13995 int tile_size = TILESIZE;
13996 int anim_width = tile_size * anim_frames;
13997 int anim_height = tile_size;
13998 int num_collect_images = 0;
13999 int pos_collect_images = 0;
14001 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14002 if (createCollectImage(i))
14003 num_collect_images++;
14005 Info("Creating %d element collecting animation images ...",
14006 num_collect_images);
14008 int dst_width = anim_width * 2;
14009 int dst_height = anim_height * num_collect_images / 2;
14010 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14011 char *basename_bmp = "RocksCollect.bmp";
14012 char *basename_png = "RocksCollect.png";
14013 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14014 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14015 int len_filename_bmp = strlen(filename_bmp);
14016 int len_filename_png = strlen(filename_png);
14017 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14018 char cmd_convert[max_command_len];
14020 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14024 // force using RGBA surface for destination bitmap
14025 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14026 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14028 dst_bitmap->surface =
14029 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14031 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14033 if (!createCollectImage(i))
14036 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14037 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14038 int graphic = el2img(i);
14039 char *token_name = element_info[i].token_name;
14040 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14041 Bitmap *src_bitmap;
14044 Info("- creating collecting image for '%s' ...", token_name);
14046 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14048 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14049 tile_size, tile_size, 0, 0);
14051 // force using RGBA surface for temporary bitmap (using transparent black)
14052 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14053 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14055 tmp_bitmap->surface =
14056 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14058 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14060 for (j = 0; j < anim_frames; j++)
14062 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14063 int frame_size = frame_size_final * num_steps;
14064 int offset = (tile_size - frame_size_final) / 2;
14065 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14067 while (frame_size > frame_size_final)
14071 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14073 FreeBitmap(frame_bitmap);
14075 frame_bitmap = half_bitmap;
14078 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14079 frame_size_final, frame_size_final,
14080 dst_x + j * tile_size + offset, dst_y + offset);
14082 FreeBitmap(frame_bitmap);
14085 tmp_bitmap->surface_masked = NULL;
14087 FreeBitmap(tmp_bitmap);
14089 pos_collect_images++;
14092 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14093 Fail("cannot save element collecting image file '%s'", filename_bmp);
14095 FreeBitmap(dst_bitmap);
14097 Info("Converting image file from BMP to PNG ...");
14099 if (system(cmd_convert) != 0)
14100 Fail("converting image file failed");
14102 unlink(filename_bmp);
14106 CloseAllAndExit(0);
14110 // ----------------------------------------------------------------------------
14111 // create and save images for custom and group elements (raw BMP format)
14112 // ----------------------------------------------------------------------------
14114 void CreateCustomElementImages(char *directory)
14116 char *src_basename = "RocksCE-template.ilbm";
14117 char *dst_basename = "RocksCE.bmp";
14118 char *src_filename = getPath2(directory, src_basename);
14119 char *dst_filename = getPath2(directory, dst_basename);
14120 Bitmap *src_bitmap;
14122 int yoffset_ce = 0;
14123 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14126 InitVideoDefaults();
14128 ReCreateBitmap(&backbuffer, video.width, video.height);
14130 src_bitmap = LoadImage(src_filename);
14132 bitmap = CreateBitmap(TILEX * 16 * 2,
14133 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14136 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14143 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14144 TILEX * x, TILEY * y + yoffset_ce);
14146 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14148 TILEX * x + TILEX * 16,
14149 TILEY * y + yoffset_ce);
14151 for (j = 2; j >= 0; j--)
14155 BlitBitmap(src_bitmap, bitmap,
14156 TILEX + c * 7, 0, 6, 10,
14157 TILEX * x + 6 + j * 7,
14158 TILEY * y + 11 + yoffset_ce);
14160 BlitBitmap(src_bitmap, bitmap,
14161 TILEX + c * 8, TILEY, 6, 10,
14162 TILEX * 16 + TILEX * x + 6 + j * 8,
14163 TILEY * y + 10 + yoffset_ce);
14169 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14176 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14177 TILEX * x, TILEY * y + yoffset_ge);
14179 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14181 TILEX * x + TILEX * 16,
14182 TILEY * y + yoffset_ge);
14184 for (j = 1; j >= 0; j--)
14188 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14189 TILEX * x + 6 + j * 10,
14190 TILEY * y + 11 + yoffset_ge);
14192 BlitBitmap(src_bitmap, bitmap,
14193 TILEX + c * 8, TILEY + 12, 6, 10,
14194 TILEX * 16 + TILEX * x + 10 + j * 8,
14195 TILEY * y + 10 + yoffset_ge);
14201 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14202 Fail("cannot save CE graphics file '%s'", dst_filename);
14204 FreeBitmap(bitmap);
14206 CloseAllAndExit(0);