1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
279 &li.bd_intermission, FALSE
284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
285 &li.bd_pal_timing, FALSE
295 static struct LevelFileConfigInfo chunk_config_ELEM[] =
297 // (these values are the same for each player)
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
301 &li.block_last_field, FALSE // default case for EM levels
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
306 &li.sp_block_last_field, TRUE // default case for SP levels
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
311 &li.instant_relocation, FALSE
315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
316 &li.can_pass_to_walkable, FALSE
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
321 &li.block_snap_field, TRUE
325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
326 &li.continuous_snapping, TRUE
330 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
331 &li.shifted_relocation, FALSE
335 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
336 &li.lazy_relocation, FALSE
340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
341 &li.finish_dig_collect, TRUE
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
346 &li.keep_walkable_ce, FALSE
349 // (these values are different for each player)
352 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
353 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
358 &li.initial_player_gravity[0], FALSE
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
363 &li.use_start_element[0], FALSE
367 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
368 &li.start_element[0], EL_PLAYER_1
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
373 &li.use_artwork_element[0], FALSE
377 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
378 &li.artwork_element[0], EL_PLAYER_1
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
383 &li.use_explosion_element[0], FALSE
387 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
388 &li.explosion_element[0], EL_PLAYER_1
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
393 &li.use_initial_inventory[0], FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
398 &li.initial_inventory_size[0], 1
402 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
403 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
404 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
409 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
410 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
414 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
415 &li.initial_player_gravity[1], FALSE
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
420 &li.use_start_element[1], FALSE
424 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
425 &li.start_element[1], EL_PLAYER_2
429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
430 &li.use_artwork_element[1], FALSE
434 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
435 &li.artwork_element[1], EL_PLAYER_2
439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
440 &li.use_explosion_element[1], FALSE
444 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
445 &li.explosion_element[1], EL_PLAYER_2
449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
450 &li.use_initial_inventory[1], FALSE
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
455 &li.initial_inventory_size[1], 1
459 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
460 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
461 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
466 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
467 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
471 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
472 &li.initial_player_gravity[2], FALSE
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
477 &li.use_start_element[2], FALSE
481 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
482 &li.start_element[2], EL_PLAYER_3
486 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
487 &li.use_artwork_element[2], FALSE
491 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
492 &li.artwork_element[2], EL_PLAYER_3
496 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
497 &li.use_explosion_element[2], FALSE
501 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
502 &li.explosion_element[2], EL_PLAYER_3
506 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
507 &li.use_initial_inventory[2], FALSE
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
512 &li.initial_inventory_size[2], 1
516 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
517 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
518 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
523 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
524 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
528 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
529 &li.initial_player_gravity[3], FALSE
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
534 &li.use_start_element[3], FALSE
538 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
539 &li.start_element[3], EL_PLAYER_4
543 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
544 &li.use_artwork_element[3], FALSE
548 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
549 &li.artwork_element[3], EL_PLAYER_4
553 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
554 &li.use_explosion_element[3], FALSE
558 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
559 &li.explosion_element[3], EL_PLAYER_4
563 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
564 &li.use_initial_inventory[3], FALSE
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
569 &li.initial_inventory_size[3], 1
573 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
574 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
575 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
578 // (these values are only valid for BD style levels)
581 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
582 &li.bd_diagonal_movements, FALSE
587 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
588 &li.score[SC_DIAMOND_EXTRA], 20
591 // (the following values are related to various game elements)
595 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
596 &li.score[SC_EMERALD], 10
601 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
602 &li.score[SC_DIAMOND], 10
607 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
608 &li.score[SC_BUG], 10
613 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
614 &li.score[SC_SPACESHIP], 10
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
620 &li.score[SC_PACMAN], 10
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_NUT], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_DYNAMITE], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_KEY], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_PEARL], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_CRYSTAL], 10
655 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
656 &li.amoeba_content, EL_DIAMOND
660 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
665 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
666 &li.grow_into_diggable, TRUE
671 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
672 &li.yamyam_content, EL_ROCK, NULL,
673 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
677 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
678 &li.score[SC_YAMYAM], 10
683 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
684 &li.score[SC_ROBOT], 10
688 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.time_magic_wall, 10
706 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
707 &li.game_of_life[0], 2
711 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
712 &li.game_of_life[1], 3
716 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
717 &li.game_of_life[2], 3
721 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
722 &li.game_of_life[3], 3
726 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
727 &li.use_life_bugs, FALSE
732 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
737 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
742 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
747 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
752 EL_TIMEGATE_SWITCH, -1,
753 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
754 &li.time_timegate, 10
758 EL_LIGHT_SWITCH_ACTIVE, -1,
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
764 EL_SHIELD_NORMAL, -1,
765 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 &li.shield_normal_time, 10
769 EL_SHIELD_NORMAL, -1,
770 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
771 &li.score[SC_SHIELD], 10
775 EL_SHIELD_DEADLY, -1,
776 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
777 &li.shield_deadly_time, 10
780 EL_SHIELD_DEADLY, -1,
781 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
782 &li.score[SC_SHIELD], 10
787 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
792 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
793 &li.extra_time_score, 10
797 EL_TIME_ORB_FULL, -1,
798 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
799 &li.time_orb_time, 10
802 EL_TIME_ORB_FULL, -1,
803 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
804 &li.use_time_orb_bug, FALSE
809 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
810 &li.use_spring_bug, FALSE
815 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 &li.android_move_time, 10
820 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 &li.android_clone_time, 10
824 EL_EMC_ANDROID, SAVE_CONF_NEVER,
825 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
826 &li.android_clone_element[0], EL_EMPTY, NULL,
827 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
831 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
832 &li.android_clone_element[0], EL_EMPTY, NULL,
833 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
838 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
843 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
848 EL_EMC_MAGNIFIER, -1,
849 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
850 &li.magnify_score, 10
853 EL_EMC_MAGNIFIER, -1,
854 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
859 EL_EMC_MAGIC_BALL, -1,
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
864 EL_EMC_MAGIC_BALL, -1,
865 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
866 &li.ball_random, FALSE
869 EL_EMC_MAGIC_BALL, -1,
870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
871 &li.ball_active_initial, FALSE
874 EL_EMC_MAGIC_BALL, -1,
875 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
876 &li.ball_content, EL_EMPTY, NULL,
877 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
881 EL_SOKOBAN_FIELD_EMPTY, -1,
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
883 &li.sb_fields_needed, TRUE
887 EL_SOKOBAN_OBJECT, -1,
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
889 &li.sb_objects_needed, TRUE
894 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
895 &li.mm_laser_red, FALSE
899 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
900 &li.mm_laser_green, FALSE
904 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
905 &li.mm_laser_blue, TRUE
910 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
911 &li.df_laser_red, TRUE
915 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
916 &li.df_laser_green, TRUE
920 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
921 &li.df_laser_blue, FALSE
925 EL_MM_FUSE_ACTIVE, -1,
926 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
931 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
942 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
943 &li.mm_ball_choice_mode, ANIM_RANDOM
947 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
948 &li.mm_ball_content, EL_EMPTY, NULL,
949 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
953 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
954 &li.rotate_mm_ball_content, TRUE
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
959 &li.explode_mm_ball, FALSE
963 EL_MM_STEEL_BLOCK, -1,
964 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
965 &li.mm_time_block, 75
969 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
970 &li.score[SC_ELEM_BONUS], 10
980 static struct LevelFileConfigInfo chunk_config_NOTE[] =
984 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
985 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
989 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
990 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
996 &xx_envelope.autowrap, FALSE
1000 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1001 &xx_envelope.centered, FALSE
1006 TYPE_STRING, CONF_VALUE_BYTES(1),
1007 &xx_envelope.text, -1, NULL,
1008 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1009 &xx_default_string_empty[0]
1019 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1023 TYPE_STRING, CONF_VALUE_BYTES(1),
1024 &xx_ei.description[0], -1,
1025 &yy_ei.description[0],
1026 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1027 &xx_default_description[0]
1032 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1033 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1034 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1036 #if ENABLE_RESERVED_CODE
1037 // (reserved for later use)
1040 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1041 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1042 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1048 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1049 &xx_ei.use_gfx_element, FALSE,
1050 &yy_ei.use_gfx_element
1054 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1055 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1056 &yy_ei.gfx_element_initial
1061 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1062 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1063 &yy_ei.access_direction
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1069 &xx_ei.collect_score_initial, 10,
1070 &yy_ei.collect_score_initial
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1075 &xx_ei.collect_count_initial, 1,
1076 &yy_ei.collect_count_initial
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1082 &xx_ei.ce_value_fixed_initial, 0,
1083 &yy_ei.ce_value_fixed_initial
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1088 &xx_ei.ce_value_random_initial, 0,
1089 &yy_ei.ce_value_random_initial
1093 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1094 &xx_ei.use_last_ce_value, FALSE,
1095 &yy_ei.use_last_ce_value
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1101 &xx_ei.push_delay_fixed, 8,
1102 &yy_ei.push_delay_fixed
1106 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1107 &xx_ei.push_delay_random, 8,
1108 &yy_ei.push_delay_random
1112 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1113 &xx_ei.drop_delay_fixed, 0,
1114 &yy_ei.drop_delay_fixed
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1119 &xx_ei.drop_delay_random, 0,
1120 &yy_ei.drop_delay_random
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1125 &xx_ei.move_delay_fixed, 0,
1126 &yy_ei.move_delay_fixed
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1131 &xx_ei.move_delay_random, 0,
1132 &yy_ei.move_delay_random
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1137 &xx_ei.step_delay_fixed, 0,
1138 &yy_ei.step_delay_fixed
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1143 &xx_ei.step_delay_random, 0,
1144 &yy_ei.step_delay_random
1149 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1150 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1155 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1156 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1157 &yy_ei.move_direction_initial
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1162 &xx_ei.move_stepsize, TILEX / 8,
1163 &yy_ei.move_stepsize
1168 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1169 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1170 &yy_ei.move_enter_element
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1175 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1176 &yy_ei.move_leave_element
1180 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1181 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1182 &yy_ei.move_leave_type
1187 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1188 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1189 &yy_ei.slippery_type
1194 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1195 &xx_ei.explosion_type, EXPLODES_3X3,
1196 &yy_ei.explosion_type
1200 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1201 &xx_ei.explosion_delay, 16,
1202 &yy_ei.explosion_delay
1206 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1207 &xx_ei.ignition_delay, 8,
1208 &yy_ei.ignition_delay
1213 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1214 &xx_ei.content, EL_EMPTY_SPACE,
1216 &xx_num_contents, 1, 1
1219 // ---------- "num_change_pages" must be the last entry ---------------------
1222 -1, SAVE_CONF_ALWAYS,
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1224 &xx_ei.num_change_pages, 1,
1225 &yy_ei.num_change_pages
1236 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1238 // ---------- "current_change_page" must be the first entry -----------------
1241 -1, SAVE_CONF_ALWAYS,
1242 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1243 &xx_current_change_page, -1
1246 // ---------- (the remaining entries can be in any order) -------------------
1250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1251 &xx_change.can_change, FALSE
1256 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1257 &xx_event_bits[0], 0
1261 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1262 &xx_event_bits[1], 0
1267 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1268 &xx_change.trigger_player, CH_PLAYER_ANY
1272 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1273 &xx_change.trigger_side, CH_SIDE_ANY
1277 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1278 &xx_change.trigger_page, CH_PAGE_ANY
1283 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1284 &xx_change.target_element, EL_EMPTY_SPACE
1289 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1290 &xx_change.delay_fixed, 0
1294 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1295 &xx_change.delay_random, 0
1299 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1300 &xx_change.delay_frames, FRAMES_PER_SECOND
1305 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1306 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1312 &xx_change.explode, FALSE
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1317 &xx_change.use_target_content, FALSE
1321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1322 &xx_change.only_if_complete, FALSE
1326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1327 &xx_change.use_random_replace, FALSE
1331 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1332 &xx_change.random_percentage, 100
1336 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1337 &xx_change.replace_when, CP_WHEN_EMPTY
1342 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1343 &xx_change.has_action, FALSE
1347 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1348 &xx_change.action_type, CA_NO_ACTION
1352 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1353 &xx_change.action_mode, CA_MODE_UNDEFINED
1357 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1358 &xx_change.action_arg, CA_ARG_UNDEFINED
1363 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1364 &xx_change.action_element, EL_EMPTY_SPACE
1369 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1370 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1371 &xx_num_contents, 1, 1
1381 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1385 TYPE_STRING, CONF_VALUE_BYTES(1),
1386 &xx_ei.description[0], -1, NULL,
1387 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1388 &xx_default_description[0]
1393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1394 &xx_ei.use_gfx_element, FALSE
1398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1399 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1404 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1405 &xx_group.choice_mode, ANIM_RANDOM
1410 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1411 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1412 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1422 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1427 &xx_ei.use_gfx_element, FALSE
1431 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1432 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1442 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1446 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1447 &li.block_snap_field, TRUE
1451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1452 &li.continuous_snapping, TRUE
1456 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1457 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1461 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1462 &li.use_start_element[0], FALSE
1466 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1467 &li.start_element[0], EL_PLAYER_1
1471 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1472 &li.use_artwork_element[0], FALSE
1476 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1477 &li.artwork_element[0], EL_PLAYER_1
1481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1482 &li.use_explosion_element[0], FALSE
1486 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1487 &li.explosion_element[0], EL_PLAYER_1
1502 filetype_id_list[] =
1504 { LEVEL_FILE_TYPE_RND, "RND" },
1505 { LEVEL_FILE_TYPE_BD, "BD" },
1506 { LEVEL_FILE_TYPE_EM, "EM" },
1507 { LEVEL_FILE_TYPE_SP, "SP" },
1508 { LEVEL_FILE_TYPE_DX, "DX" },
1509 { LEVEL_FILE_TYPE_SB, "SB" },
1510 { LEVEL_FILE_TYPE_DC, "DC" },
1511 { LEVEL_FILE_TYPE_MM, "MM" },
1512 { LEVEL_FILE_TYPE_MM, "DF" },
1517 // ============================================================================
1518 // level file functions
1519 // ============================================================================
1521 static boolean check_special_flags(char *flag)
1523 if (strEqual(options.special_flags, flag) ||
1524 strEqual(leveldir_current->special_flags, flag))
1530 static struct DateInfo getCurrentDate(void)
1532 time_t epoch_seconds = time(NULL);
1533 struct tm *now = localtime(&epoch_seconds);
1534 struct DateInfo date;
1536 date.year = now->tm_year + 1900;
1537 date.month = now->tm_mon + 1;
1538 date.day = now->tm_mday;
1540 date.src = DATE_SRC_CLOCK;
1545 static void resetEventFlags(struct ElementChangeInfo *change)
1549 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1550 change->has_event[i] = FALSE;
1553 static void resetEventBits(void)
1557 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1558 xx_event_bits[i] = 0;
1561 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1565 /* important: only change event flag if corresponding event bit is set
1566 (this is because all xx_event_bits[] values are loaded separately,
1567 and all xx_event_bits[] values are set back to zero before loading
1568 another value xx_event_bits[x] (each value representing 32 flags)) */
1570 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1571 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1572 change->has_event[i] = TRUE;
1575 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1579 /* in contrast to the above function setEventFlagsFromEventBits(), it
1580 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1581 depending on the corresponding change->has_event[i] values here, as
1582 all xx_event_bits[] values are reset in resetEventBits() before */
1584 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1585 if (change->has_event[i])
1586 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1589 static char *getDefaultElementDescription(struct ElementInfo *ei)
1591 static char description[MAX_ELEMENT_NAME_LEN + 1];
1592 char *default_description = (ei->custom_description != NULL ?
1593 ei->custom_description :
1594 ei->editor_description);
1597 // always start with reliable default values
1598 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1599 description[i] = '\0';
1601 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1602 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1604 return &description[0];
1607 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1609 char *default_description = getDefaultElementDescription(ei);
1612 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1613 ei->description[i] = default_description[i];
1616 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1620 for (i = 0; conf[i].data_type != -1; i++)
1622 int default_value = conf[i].default_value;
1623 int data_type = conf[i].data_type;
1624 int conf_type = conf[i].conf_type;
1625 int byte_mask = conf_type & CONF_MASK_BYTES;
1627 if (byte_mask == CONF_MASK_MULTI_BYTES)
1629 int default_num_entities = conf[i].default_num_entities;
1630 int max_num_entities = conf[i].max_num_entities;
1632 *(int *)(conf[i].num_entities) = default_num_entities;
1634 if (data_type == TYPE_STRING)
1636 char *default_string = conf[i].default_string;
1637 char *string = (char *)(conf[i].value);
1639 strncpy(string, default_string, max_num_entities);
1641 else if (data_type == TYPE_ELEMENT_LIST)
1643 int *element_array = (int *)(conf[i].value);
1646 for (j = 0; j < max_num_entities; j++)
1647 element_array[j] = default_value;
1649 else if (data_type == TYPE_CONTENT_LIST)
1651 struct Content *content = (struct Content *)(conf[i].value);
1654 for (c = 0; c < max_num_entities; c++)
1655 for (y = 0; y < 3; y++)
1656 for (x = 0; x < 3; x++)
1657 content[c].e[x][y] = default_value;
1660 else // constant size configuration data (1, 2 or 4 bytes)
1662 if (data_type == TYPE_BOOLEAN)
1663 *(boolean *)(conf[i].value) = default_value;
1665 *(int *) (conf[i].value) = default_value;
1670 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1674 for (i = 0; conf[i].data_type != -1; i++)
1676 int data_type = conf[i].data_type;
1677 int conf_type = conf[i].conf_type;
1678 int byte_mask = conf_type & CONF_MASK_BYTES;
1680 if (byte_mask == CONF_MASK_MULTI_BYTES)
1682 int max_num_entities = conf[i].max_num_entities;
1684 if (data_type == TYPE_STRING)
1686 char *string = (char *)(conf[i].value);
1687 char *string_copy = (char *)(conf[i].value_copy);
1689 strncpy(string_copy, string, max_num_entities);
1691 else if (data_type == TYPE_ELEMENT_LIST)
1693 int *element_array = (int *)(conf[i].value);
1694 int *element_array_copy = (int *)(conf[i].value_copy);
1697 for (j = 0; j < max_num_entities; j++)
1698 element_array_copy[j] = element_array[j];
1700 else if (data_type == TYPE_CONTENT_LIST)
1702 struct Content *content = (struct Content *)(conf[i].value);
1703 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1706 for (c = 0; c < max_num_entities; c++)
1707 for (y = 0; y < 3; y++)
1708 for (x = 0; x < 3; x++)
1709 content_copy[c].e[x][y] = content[c].e[x][y];
1712 else // constant size configuration data (1, 2 or 4 bytes)
1714 if (data_type == TYPE_BOOLEAN)
1715 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1717 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1722 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1726 xx_ei = *ei_from; // copy element data into temporary buffer
1727 yy_ei = *ei_to; // copy element data into temporary buffer
1729 copyConfigFromConfigList(chunk_config_CUSX_base);
1734 // ---------- reinitialize and copy change pages ----------
1736 ei_to->num_change_pages = ei_from->num_change_pages;
1737 ei_to->current_change_page = ei_from->current_change_page;
1739 setElementChangePages(ei_to, ei_to->num_change_pages);
1741 for (i = 0; i < ei_to->num_change_pages; i++)
1742 ei_to->change_page[i] = ei_from->change_page[i];
1744 // ---------- copy group element info ----------
1745 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1746 *ei_to->group = *ei_from->group;
1748 // mark this custom element as modified
1749 ei_to->modified_settings = TRUE;
1752 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1754 int change_page_size = sizeof(struct ElementChangeInfo);
1756 ei->num_change_pages = MAX(1, change_pages);
1759 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1761 if (ei->current_change_page >= ei->num_change_pages)
1762 ei->current_change_page = ei->num_change_pages - 1;
1764 ei->change = &ei->change_page[ei->current_change_page];
1767 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1769 xx_change = *change; // copy change data into temporary buffer
1771 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1773 *change = xx_change;
1775 resetEventFlags(change);
1777 change->direct_action = 0;
1778 change->other_action = 0;
1780 change->pre_change_function = NULL;
1781 change->change_function = NULL;
1782 change->post_change_function = NULL;
1785 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1789 li = *level; // copy level data into temporary buffer
1790 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1791 *level = li; // copy temporary buffer back to level data
1793 setLevelInfoToDefaults_BD();
1794 setLevelInfoToDefaults_EM();
1795 setLevelInfoToDefaults_SP();
1796 setLevelInfoToDefaults_MM();
1798 level->native_bd_level = &native_bd_level;
1799 level->native_em_level = &native_em_level;
1800 level->native_sp_level = &native_sp_level;
1801 level->native_mm_level = &native_mm_level;
1803 level->file_version = FILE_VERSION_ACTUAL;
1804 level->game_version = GAME_VERSION_ACTUAL;
1806 level->creation_date = getCurrentDate();
1808 level->encoding_16bit_field = TRUE;
1809 level->encoding_16bit_yamyam = TRUE;
1810 level->encoding_16bit_amoeba = TRUE;
1812 // clear level name and level author string buffers
1813 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1814 level->name[i] = '\0';
1815 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1816 level->author[i] = '\0';
1818 // set level name and level author to default values
1819 strcpy(level->name, NAMELESS_LEVEL_NAME);
1820 strcpy(level->author, ANONYMOUS_NAME);
1822 // set level playfield to playable default level with player and exit
1823 for (x = 0; x < MAX_LEV_FIELDX; x++)
1824 for (y = 0; y < MAX_LEV_FIELDY; y++)
1825 level->field[x][y] = EL_SAND;
1827 level->field[0][0] = EL_PLAYER_1;
1828 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1830 BorderElement = EL_STEELWALL;
1832 // detect custom elements when loading them
1833 level->file_has_custom_elements = FALSE;
1835 // set all bug compatibility flags to "false" => do not emulate this bug
1836 level->use_action_after_change_bug = FALSE;
1838 if (leveldir_current)
1840 // try to determine better author name than 'anonymous'
1841 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1843 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1844 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1848 switch (LEVELCLASS(leveldir_current))
1850 case LEVELCLASS_TUTORIAL:
1851 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1854 case LEVELCLASS_CONTRIB:
1855 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1856 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1859 case LEVELCLASS_PRIVATE:
1860 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1861 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1865 // keep default value
1872 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1874 static boolean clipboard_elements_initialized = FALSE;
1877 InitElementPropertiesStatic();
1879 li = *level; // copy level data into temporary buffer
1880 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1881 *level = li; // copy temporary buffer back to level data
1883 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1886 struct ElementInfo *ei = &element_info[element];
1888 if (element == EL_MM_GRAY_BALL)
1890 struct LevelInfo_MM *level_mm = level->native_mm_level;
1893 for (j = 0; j < level->num_mm_ball_contents; j++)
1894 level->mm_ball_content[j] =
1895 map_element_MM_to_RND(level_mm->ball_content[j]);
1898 // never initialize clipboard elements after the very first time
1899 // (to be able to use clipboard elements between several levels)
1900 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1903 if (IS_ENVELOPE(element))
1905 int envelope_nr = element - EL_ENVELOPE_1;
1907 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1909 level->envelope[envelope_nr] = xx_envelope;
1912 if (IS_CUSTOM_ELEMENT(element) ||
1913 IS_GROUP_ELEMENT(element) ||
1914 IS_INTERNAL_ELEMENT(element))
1916 xx_ei = *ei; // copy element data into temporary buffer
1918 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1923 setElementChangePages(ei, 1);
1924 setElementChangeInfoToDefaults(ei->change);
1926 if (IS_CUSTOM_ELEMENT(element) ||
1927 IS_GROUP_ELEMENT(element))
1929 setElementDescriptionToDefault(ei);
1931 ei->modified_settings = FALSE;
1934 if (IS_CUSTOM_ELEMENT(element) ||
1935 IS_INTERNAL_ELEMENT(element))
1937 // internal values used in level editor
1939 ei->access_type = 0;
1940 ei->access_layer = 0;
1941 ei->access_protected = 0;
1942 ei->walk_to_action = 0;
1943 ei->smash_targets = 0;
1946 ei->can_explode_by_fire = FALSE;
1947 ei->can_explode_smashed = FALSE;
1948 ei->can_explode_impact = FALSE;
1950 ei->current_change_page = 0;
1953 if (IS_GROUP_ELEMENT(element) ||
1954 IS_INTERNAL_ELEMENT(element))
1956 struct ElementGroupInfo *group;
1958 // initialize memory for list of elements in group
1959 if (ei->group == NULL)
1960 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1964 xx_group = *group; // copy group data into temporary buffer
1966 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1971 if (IS_EMPTY_ELEMENT(element) ||
1972 IS_INTERNAL_ELEMENT(element))
1974 xx_ei = *ei; // copy element data into temporary buffer
1976 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1982 clipboard_elements_initialized = TRUE;
1985 static void setLevelInfoToDefaults(struct LevelInfo *level,
1986 boolean level_info_only,
1987 boolean reset_file_status)
1989 setLevelInfoToDefaults_Level(level);
1991 if (!level_info_only)
1992 setLevelInfoToDefaults_Elements(level);
1994 if (reset_file_status)
1996 level->no_valid_file = FALSE;
1997 level->no_level_file = FALSE;
2000 level->changed = FALSE;
2003 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2005 level_file_info->nr = 0;
2006 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2007 level_file_info->packed = FALSE;
2009 setString(&level_file_info->basename, NULL);
2010 setString(&level_file_info->filename, NULL);
2013 int getMappedElement_SB(int, boolean);
2015 static void ActivateLevelTemplate(void)
2019 if (check_special_flags("load_xsb_to_ces"))
2021 // fill smaller playfields with padding "beyond border wall" elements
2022 if (level.fieldx < level_template.fieldx ||
2023 level.fieldy < level_template.fieldy)
2025 short field[level.fieldx][level.fieldy];
2026 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2027 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2028 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2029 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2031 // copy old playfield (which is smaller than the visible area)
2032 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2033 field[x][y] = level.field[x][y];
2035 // fill new, larger playfield with "beyond border wall" elements
2036 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2037 level.field[x][y] = getMappedElement_SB('_', TRUE);
2039 // copy the old playfield to the middle of the new playfield
2040 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2041 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2043 level.fieldx = new_fieldx;
2044 level.fieldy = new_fieldy;
2048 // Currently there is no special action needed to activate the template
2049 // data, because 'element_info' property settings overwrite the original
2050 // level data, while all other variables do not change.
2052 // Exception: 'from_level_template' elements in the original level playfield
2053 // are overwritten with the corresponding elements at the same position in
2054 // playfield from the level template.
2056 for (x = 0; x < level.fieldx; x++)
2057 for (y = 0; y < level.fieldy; y++)
2058 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2059 level.field[x][y] = level_template.field[x][y];
2061 if (check_special_flags("load_xsb_to_ces"))
2063 struct LevelInfo level_backup = level;
2065 // overwrite all individual level settings from template level settings
2066 level = level_template;
2068 // restore level file info
2069 level.file_info = level_backup.file_info;
2071 // restore playfield size
2072 level.fieldx = level_backup.fieldx;
2073 level.fieldy = level_backup.fieldy;
2075 // restore playfield content
2076 for (x = 0; x < level.fieldx; x++)
2077 for (y = 0; y < level.fieldy; y++)
2078 level.field[x][y] = level_backup.field[x][y];
2080 // restore name and author from individual level
2081 strcpy(level.name, level_backup.name);
2082 strcpy(level.author, level_backup.author);
2084 // restore flag "use_custom_template"
2085 level.use_custom_template = level_backup.use_custom_template;
2089 static boolean checkForPackageFromBasename_BD(char *basename)
2091 // check for native BD level file extensions
2092 if (!strSuffixLower(basename, ".bd") &&
2093 !strSuffixLower(basename, ".bdr") &&
2094 !strSuffixLower(basename, ".brc") &&
2095 !strSuffixLower(basename, ".gds"))
2098 // check for standard single-level BD files (like "001.bd")
2099 if (strSuffixLower(basename, ".bd") &&
2100 strlen(basename) == 6 &&
2101 basename[0] >= '0' && basename[0] <= '9' &&
2102 basename[1] >= '0' && basename[1] <= '9' &&
2103 basename[2] >= '0' && basename[2] <= '9')
2106 // this is a level package in native BD file format
2110 static char *getLevelFilenameFromBasename(char *basename)
2112 static char *filename = NULL;
2114 checked_free(filename);
2116 filename = getPath2(getCurrentLevelDir(), basename);
2121 static int getFileTypeFromBasename(char *basename)
2123 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2125 static char *filename = NULL;
2126 struct stat file_status;
2128 // ---------- try to determine file type from filename ----------
2130 // check for typical filename of a Supaplex level package file
2131 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2132 return LEVEL_FILE_TYPE_SP;
2134 // check for typical filename of a Diamond Caves II level package file
2135 if (strSuffixLower(basename, ".dc") ||
2136 strSuffixLower(basename, ".dc2"))
2137 return LEVEL_FILE_TYPE_DC;
2139 // check for typical filename of a Sokoban level package file
2140 if (strSuffixLower(basename, ".xsb") &&
2141 strchr(basename, '%') == NULL)
2142 return LEVEL_FILE_TYPE_SB;
2144 // check for typical filename of a Boulder Dash (GDash) level package file
2145 if (checkForPackageFromBasename_BD(basename))
2146 return LEVEL_FILE_TYPE_BD;
2148 // ---------- try to determine file type from filesize ----------
2150 checked_free(filename);
2151 filename = getPath2(getCurrentLevelDir(), basename);
2153 if (stat(filename, &file_status) == 0)
2155 // check for typical filesize of a Supaplex level package file
2156 if (file_status.st_size == 170496)
2157 return LEVEL_FILE_TYPE_SP;
2160 return LEVEL_FILE_TYPE_UNKNOWN;
2163 static int getFileTypeFromMagicBytes(char *filename, int type)
2167 if ((file = openFile(filename, MODE_READ)))
2169 char chunk_name[CHUNK_ID_LEN + 1];
2171 getFileChunkBE(file, chunk_name, NULL);
2173 if (strEqual(chunk_name, "MMII") ||
2174 strEqual(chunk_name, "MIRR"))
2175 type = LEVEL_FILE_TYPE_MM;
2183 static boolean checkForPackageFromBasename(char *basename)
2185 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2186 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2188 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2191 static char *getSingleLevelBasenameExt(int nr, char *extension)
2193 static char basename[MAX_FILENAME_LEN];
2196 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2198 sprintf(basename, "%03d.%s", nr, extension);
2203 static char *getSingleLevelBasename(int nr)
2205 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2208 static char *getPackedLevelBasename(int type)
2210 static char basename[MAX_FILENAME_LEN];
2211 char *directory = getCurrentLevelDir();
2213 DirectoryEntry *dir_entry;
2215 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2217 if ((dir = openDirectory(directory)) == NULL)
2219 Warn("cannot read current level directory '%s'", directory);
2224 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2226 char *entry_basename = dir_entry->basename;
2227 int entry_type = getFileTypeFromBasename(entry_basename);
2229 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2231 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2234 strcpy(basename, entry_basename);
2241 closeDirectory(dir);
2246 static char *getSingleLevelFilename(int nr)
2248 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2251 #if ENABLE_UNUSED_CODE
2252 static char *getPackedLevelFilename(int type)
2254 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2258 char *getDefaultLevelFilename(int nr)
2260 return getSingleLevelFilename(nr);
2263 #if ENABLE_UNUSED_CODE
2264 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2268 lfi->packed = FALSE;
2270 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2271 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2275 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2276 int type, char *format, ...)
2278 static char basename[MAX_FILENAME_LEN];
2281 va_start(ap, format);
2282 vsprintf(basename, format, ap);
2286 lfi->packed = FALSE;
2288 setString(&lfi->basename, basename);
2289 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2292 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2298 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2299 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2302 static int getFiletypeFromID(char *filetype_id)
2304 char *filetype_id_lower;
2305 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2308 if (filetype_id == NULL)
2309 return LEVEL_FILE_TYPE_UNKNOWN;
2311 filetype_id_lower = getStringToLower(filetype_id);
2313 for (i = 0; filetype_id_list[i].id != NULL; i++)
2315 char *id_lower = getStringToLower(filetype_id_list[i].id);
2317 if (strEqual(filetype_id_lower, id_lower))
2318 filetype = filetype_id_list[i].filetype;
2322 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2326 free(filetype_id_lower);
2331 char *getLocalLevelTemplateFilename(void)
2333 return getDefaultLevelFilename(-1);
2336 char *getGlobalLevelTemplateFilename(void)
2338 // global variable "leveldir_current" must be modified in the loop below
2339 LevelDirTree *leveldir_current_last = leveldir_current;
2340 char *filename = NULL;
2342 // check for template level in path from current to topmost tree node
2344 while (leveldir_current != NULL)
2346 filename = getDefaultLevelFilename(-1);
2348 if (fileExists(filename))
2351 leveldir_current = leveldir_current->node_parent;
2354 // restore global variable "leveldir_current" modified in above loop
2355 leveldir_current = leveldir_current_last;
2360 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2364 // special case: level number is negative => check for level template file
2367 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2368 getSingleLevelBasename(-1));
2370 // replace local level template filename with global template filename
2371 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2373 // no fallback if template file not existing
2377 // special case: check for file name/pattern specified in "levelinfo.conf"
2378 if (leveldir_current->level_filename != NULL)
2380 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2382 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2383 leveldir_current->level_filename, nr);
2385 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2387 if (fileExists(lfi->filename))
2390 else if (leveldir_current->level_filetype != NULL)
2392 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2394 // check for specified native level file with standard file name
2395 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2396 "%03d.%s", nr, LEVELFILE_EXTENSION);
2397 if (fileExists(lfi->filename))
2401 // check for native Rocks'n'Diamonds level file
2402 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2403 "%03d.%s", nr, LEVELFILE_EXTENSION);
2404 if (fileExists(lfi->filename))
2407 // check for native Boulder Dash level file
2408 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2409 if (fileExists(lfi->filename))
2412 // check for Emerald Mine level file (V1)
2413 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2414 'a' + (nr / 10) % 26, '0' + nr % 10);
2415 if (fileExists(lfi->filename))
2417 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2418 'A' + (nr / 10) % 26, '0' + nr % 10);
2419 if (fileExists(lfi->filename))
2422 // check for Emerald Mine level file (V2 to V5)
2423 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2424 if (fileExists(lfi->filename))
2427 // check for Emerald Mine level file (V6 / single mode)
2428 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2429 if (fileExists(lfi->filename))
2431 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2432 if (fileExists(lfi->filename))
2435 // check for Emerald Mine level file (V6 / teamwork mode)
2436 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2437 if (fileExists(lfi->filename))
2439 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2440 if (fileExists(lfi->filename))
2443 // check for various packed level file formats
2444 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2445 if (fileExists(lfi->filename))
2448 // no known level file found -- use default values (and fail later)
2449 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2450 "%03d.%s", nr, LEVELFILE_EXTENSION);
2453 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2455 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2456 lfi->type = getFileTypeFromBasename(lfi->basename);
2458 if (lfi->type == LEVEL_FILE_TYPE_RND)
2459 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2462 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2464 // always start with reliable default values
2465 setFileInfoToDefaults(level_file_info);
2467 level_file_info->nr = nr; // set requested level number
2469 determineLevelFileInfo_Filename(level_file_info);
2470 determineLevelFileInfo_Filetype(level_file_info);
2473 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2474 struct LevelFileInfo *lfi_to)
2476 lfi_to->nr = lfi_from->nr;
2477 lfi_to->type = lfi_from->type;
2478 lfi_to->packed = lfi_from->packed;
2480 setString(&lfi_to->basename, lfi_from->basename);
2481 setString(&lfi_to->filename, lfi_from->filename);
2484 // ----------------------------------------------------------------------------
2485 // functions for loading R'n'D level
2486 // ----------------------------------------------------------------------------
2488 int getMappedElement(int element)
2490 // remap some (historic, now obsolete) elements
2494 case EL_PLAYER_OBSOLETE:
2495 element = EL_PLAYER_1;
2498 case EL_KEY_OBSOLETE:
2502 case EL_EM_KEY_1_FILE_OBSOLETE:
2503 element = EL_EM_KEY_1;
2506 case EL_EM_KEY_2_FILE_OBSOLETE:
2507 element = EL_EM_KEY_2;
2510 case EL_EM_KEY_3_FILE_OBSOLETE:
2511 element = EL_EM_KEY_3;
2514 case EL_EM_KEY_4_FILE_OBSOLETE:
2515 element = EL_EM_KEY_4;
2518 case EL_ENVELOPE_OBSOLETE:
2519 element = EL_ENVELOPE_1;
2527 if (element >= NUM_FILE_ELEMENTS)
2529 Warn("invalid level element %d", element);
2531 element = EL_UNKNOWN;
2539 static int getMappedElementByVersion(int element, int game_version)
2541 // remap some elements due to certain game version
2543 if (game_version <= VERSION_IDENT(2,2,0,0))
2545 // map game font elements
2546 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2547 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2548 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2549 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2552 if (game_version < VERSION_IDENT(3,0,0,0))
2554 // map Supaplex gravity tube elements
2555 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2556 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2557 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2558 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2565 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2567 level->file_version = getFileVersion(file);
2568 level->game_version = getFileVersion(file);
2573 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2575 level->creation_date.year = getFile16BitBE(file);
2576 level->creation_date.month = getFile8Bit(file);
2577 level->creation_date.day = getFile8Bit(file);
2579 level->creation_date.src = DATE_SRC_LEVELFILE;
2584 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2586 int initial_player_stepsize;
2587 int initial_player_gravity;
2590 level->fieldx = getFile8Bit(file);
2591 level->fieldy = getFile8Bit(file);
2593 level->time = getFile16BitBE(file);
2594 level->gems_needed = getFile16BitBE(file);
2596 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2597 level->name[i] = getFile8Bit(file);
2598 level->name[MAX_LEVEL_NAME_LEN] = 0;
2600 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2601 level->score[i] = getFile8Bit(file);
2603 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2604 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2605 for (y = 0; y < 3; y++)
2606 for (x = 0; x < 3; x++)
2607 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2609 level->amoeba_speed = getFile8Bit(file);
2610 level->time_magic_wall = getFile8Bit(file);
2611 level->time_wheel = getFile8Bit(file);
2612 level->amoeba_content = getMappedElement(getFile8Bit(file));
2614 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2617 for (i = 0; i < MAX_PLAYERS; i++)
2618 level->initial_player_stepsize[i] = initial_player_stepsize;
2620 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2622 for (i = 0; i < MAX_PLAYERS; i++)
2623 level->initial_player_gravity[i] = initial_player_gravity;
2625 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2626 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2628 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2630 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2631 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2632 level->can_move_into_acid_bits = getFile32BitBE(file);
2633 level->dont_collide_with_bits = getFile8Bit(file);
2635 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2636 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2638 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2639 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2640 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2642 level->game_engine_type = getFile8Bit(file);
2644 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2649 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2653 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2654 level->name[i] = getFile8Bit(file);
2655 level->name[MAX_LEVEL_NAME_LEN] = 0;
2660 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2664 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2665 level->author[i] = getFile8Bit(file);
2666 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2671 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2674 int chunk_size_expected = level->fieldx * level->fieldy;
2676 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2677 stored with 16-bit encoding (and should be twice as big then).
2678 Even worse, playfield data was stored 16-bit when only yamyam content
2679 contained 16-bit elements and vice versa. */
2681 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2682 chunk_size_expected *= 2;
2684 if (chunk_size_expected != chunk_size)
2686 ReadUnusedBytesFromFile(file, chunk_size);
2687 return chunk_size_expected;
2690 for (y = 0; y < level->fieldy; y++)
2691 for (x = 0; x < level->fieldx; x++)
2692 level->field[x][y] =
2693 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2698 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2701 int header_size = 4;
2702 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2703 int chunk_size_expected = header_size + content_size;
2705 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2706 stored with 16-bit encoding (and should be twice as big then).
2707 Even worse, playfield data was stored 16-bit when only yamyam content
2708 contained 16-bit elements and vice versa. */
2710 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2711 chunk_size_expected += content_size;
2713 if (chunk_size_expected != chunk_size)
2715 ReadUnusedBytesFromFile(file, chunk_size);
2716 return chunk_size_expected;
2720 level->num_yamyam_contents = getFile8Bit(file);
2724 // correct invalid number of content fields -- should never happen
2725 if (level->num_yamyam_contents < 1 ||
2726 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2727 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2729 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2730 for (y = 0; y < 3; y++)
2731 for (x = 0; x < 3; x++)
2732 level->yamyam_content[i].e[x][y] =
2733 getMappedElement(level->encoding_16bit_field ?
2734 getFile16BitBE(file) : getFile8Bit(file));
2738 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2743 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2745 element = getMappedElement(getFile16BitBE(file));
2746 num_contents = getFile8Bit(file);
2748 getFile8Bit(file); // content x size (unused)
2749 getFile8Bit(file); // content y size (unused)
2751 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2753 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2754 for (y = 0; y < 3; y++)
2755 for (x = 0; x < 3; x++)
2756 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2758 // correct invalid number of content fields -- should never happen
2759 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2760 num_contents = STD_ELEMENT_CONTENTS;
2762 if (element == EL_YAMYAM)
2764 level->num_yamyam_contents = num_contents;
2766 for (i = 0; i < num_contents; i++)
2767 for (y = 0; y < 3; y++)
2768 for (x = 0; x < 3; x++)
2769 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2771 else if (element == EL_BD_AMOEBA)
2773 level->amoeba_content = content_array[0][0][0];
2777 Warn("cannot load content for element '%d'", element);
2783 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2789 int chunk_size_expected;
2791 element = getMappedElement(getFile16BitBE(file));
2792 if (!IS_ENVELOPE(element))
2793 element = EL_ENVELOPE_1;
2795 envelope_nr = element - EL_ENVELOPE_1;
2797 envelope_len = getFile16BitBE(file);
2799 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2800 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2802 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2804 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2805 if (chunk_size_expected != chunk_size)
2807 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2808 return chunk_size_expected;
2811 for (i = 0; i < envelope_len; i++)
2812 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2817 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2819 int num_changed_custom_elements = getFile16BitBE(file);
2820 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2823 if (chunk_size_expected != chunk_size)
2825 ReadUnusedBytesFromFile(file, chunk_size - 2);
2826 return chunk_size_expected;
2829 for (i = 0; i < num_changed_custom_elements; i++)
2831 int element = getMappedElement(getFile16BitBE(file));
2832 int properties = getFile32BitBE(file);
2834 if (IS_CUSTOM_ELEMENT(element))
2835 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2837 Warn("invalid custom element number %d", element);
2839 // older game versions that wrote level files with CUS1 chunks used
2840 // different default push delay values (not yet stored in level file)
2841 element_info[element].push_delay_fixed = 2;
2842 element_info[element].push_delay_random = 8;
2845 level->file_has_custom_elements = TRUE;
2850 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2852 int num_changed_custom_elements = getFile16BitBE(file);
2853 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2856 if (chunk_size_expected != chunk_size)
2858 ReadUnusedBytesFromFile(file, chunk_size - 2);
2859 return chunk_size_expected;
2862 for (i = 0; i < num_changed_custom_elements; i++)
2864 int element = getMappedElement(getFile16BitBE(file));
2865 int custom_target_element = getMappedElement(getFile16BitBE(file));
2867 if (IS_CUSTOM_ELEMENT(element))
2868 element_info[element].change->target_element = custom_target_element;
2870 Warn("invalid custom element number %d", element);
2873 level->file_has_custom_elements = TRUE;
2878 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2880 int num_changed_custom_elements = getFile16BitBE(file);
2881 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2884 if (chunk_size_expected != chunk_size)
2886 ReadUnusedBytesFromFile(file, chunk_size - 2);
2887 return chunk_size_expected;
2890 for (i = 0; i < num_changed_custom_elements; i++)
2892 int element = getMappedElement(getFile16BitBE(file));
2893 struct ElementInfo *ei = &element_info[element];
2894 unsigned int event_bits;
2896 if (!IS_CUSTOM_ELEMENT(element))
2898 Warn("invalid custom element number %d", element);
2900 element = EL_INTERNAL_DUMMY;
2903 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2904 ei->description[j] = getFile8Bit(file);
2905 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2907 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2909 // some free bytes for future properties and padding
2910 ReadUnusedBytesFromFile(file, 7);
2912 ei->use_gfx_element = getFile8Bit(file);
2913 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2915 ei->collect_score_initial = getFile8Bit(file);
2916 ei->collect_count_initial = getFile8Bit(file);
2918 ei->push_delay_fixed = getFile16BitBE(file);
2919 ei->push_delay_random = getFile16BitBE(file);
2920 ei->move_delay_fixed = getFile16BitBE(file);
2921 ei->move_delay_random = getFile16BitBE(file);
2923 ei->move_pattern = getFile16BitBE(file);
2924 ei->move_direction_initial = getFile8Bit(file);
2925 ei->move_stepsize = getFile8Bit(file);
2927 for (y = 0; y < 3; y++)
2928 for (x = 0; x < 3; x++)
2929 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2931 // bits 0 - 31 of "has_event[]"
2932 event_bits = getFile32BitBE(file);
2933 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2934 if (event_bits & (1u << j))
2935 ei->change->has_event[j] = TRUE;
2937 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2939 ei->change->delay_fixed = getFile16BitBE(file);
2940 ei->change->delay_random = getFile16BitBE(file);
2941 ei->change->delay_frames = getFile16BitBE(file);
2943 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2945 ei->change->explode = getFile8Bit(file);
2946 ei->change->use_target_content = getFile8Bit(file);
2947 ei->change->only_if_complete = getFile8Bit(file);
2948 ei->change->use_random_replace = getFile8Bit(file);
2950 ei->change->random_percentage = getFile8Bit(file);
2951 ei->change->replace_when = getFile8Bit(file);
2953 for (y = 0; y < 3; y++)
2954 for (x = 0; x < 3; x++)
2955 ei->change->target_content.e[x][y] =
2956 getMappedElement(getFile16BitBE(file));
2958 ei->slippery_type = getFile8Bit(file);
2960 // some free bytes for future properties and padding
2961 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2963 // mark that this custom element has been modified
2964 ei->modified_settings = TRUE;
2967 level->file_has_custom_elements = TRUE;
2972 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2974 struct ElementInfo *ei;
2975 int chunk_size_expected;
2979 // ---------- custom element base property values (96 bytes) ----------------
2981 element = getMappedElement(getFile16BitBE(file));
2983 if (!IS_CUSTOM_ELEMENT(element))
2985 Warn("invalid custom element number %d", element);
2987 ReadUnusedBytesFromFile(file, chunk_size - 2);
2992 ei = &element_info[element];
2994 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2995 ei->description[i] = getFile8Bit(file);
2996 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2998 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3000 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3002 ei->num_change_pages = getFile8Bit(file);
3004 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3005 if (chunk_size_expected != chunk_size)
3007 ReadUnusedBytesFromFile(file, chunk_size - 43);
3008 return chunk_size_expected;
3011 ei->ce_value_fixed_initial = getFile16BitBE(file);
3012 ei->ce_value_random_initial = getFile16BitBE(file);
3013 ei->use_last_ce_value = getFile8Bit(file);
3015 ei->use_gfx_element = getFile8Bit(file);
3016 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3018 ei->collect_score_initial = getFile8Bit(file);
3019 ei->collect_count_initial = getFile8Bit(file);
3021 ei->drop_delay_fixed = getFile8Bit(file);
3022 ei->push_delay_fixed = getFile8Bit(file);
3023 ei->drop_delay_random = getFile8Bit(file);
3024 ei->push_delay_random = getFile8Bit(file);
3025 ei->move_delay_fixed = getFile16BitBE(file);
3026 ei->move_delay_random = getFile16BitBE(file);
3028 // bits 0 - 15 of "move_pattern" ...
3029 ei->move_pattern = getFile16BitBE(file);
3030 ei->move_direction_initial = getFile8Bit(file);
3031 ei->move_stepsize = getFile8Bit(file);
3033 ei->slippery_type = getFile8Bit(file);
3035 for (y = 0; y < 3; y++)
3036 for (x = 0; x < 3; x++)
3037 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3039 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3040 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3041 ei->move_leave_type = getFile8Bit(file);
3043 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3044 ei->move_pattern |= (getFile16BitBE(file) << 16);
3046 ei->access_direction = getFile8Bit(file);
3048 ei->explosion_delay = getFile8Bit(file);
3049 ei->ignition_delay = getFile8Bit(file);
3050 ei->explosion_type = getFile8Bit(file);
3052 // some free bytes for future custom property values and padding
3053 ReadUnusedBytesFromFile(file, 1);
3055 // ---------- change page property values (48 bytes) ------------------------
3057 setElementChangePages(ei, ei->num_change_pages);
3059 for (i = 0; i < ei->num_change_pages; i++)
3061 struct ElementChangeInfo *change = &ei->change_page[i];
3062 unsigned int event_bits;
3064 // always start with reliable default values
3065 setElementChangeInfoToDefaults(change);
3067 // bits 0 - 31 of "has_event[]" ...
3068 event_bits = getFile32BitBE(file);
3069 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3070 if (event_bits & (1u << j))
3071 change->has_event[j] = TRUE;
3073 change->target_element = getMappedElement(getFile16BitBE(file));
3075 change->delay_fixed = getFile16BitBE(file);
3076 change->delay_random = getFile16BitBE(file);
3077 change->delay_frames = getFile16BitBE(file);
3079 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3081 change->explode = getFile8Bit(file);
3082 change->use_target_content = getFile8Bit(file);
3083 change->only_if_complete = getFile8Bit(file);
3084 change->use_random_replace = getFile8Bit(file);
3086 change->random_percentage = getFile8Bit(file);
3087 change->replace_when = getFile8Bit(file);
3089 for (y = 0; y < 3; y++)
3090 for (x = 0; x < 3; x++)
3091 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3093 change->can_change = getFile8Bit(file);
3095 change->trigger_side = getFile8Bit(file);
3097 change->trigger_player = getFile8Bit(file);
3098 change->trigger_page = getFile8Bit(file);
3100 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3101 CH_PAGE_ANY : (1 << change->trigger_page));
3103 change->has_action = getFile8Bit(file);
3104 change->action_type = getFile8Bit(file);
3105 change->action_mode = getFile8Bit(file);
3106 change->action_arg = getFile16BitBE(file);
3108 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3109 event_bits = getFile8Bit(file);
3110 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3111 if (event_bits & (1u << (j - 32)))
3112 change->has_event[j] = TRUE;
3115 // mark this custom element as modified
3116 ei->modified_settings = TRUE;
3118 level->file_has_custom_elements = TRUE;
3123 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3125 struct ElementInfo *ei;
3126 struct ElementGroupInfo *group;
3130 element = getMappedElement(getFile16BitBE(file));
3132 if (!IS_GROUP_ELEMENT(element))
3134 Warn("invalid group element number %d", element);
3136 ReadUnusedBytesFromFile(file, chunk_size - 2);
3141 ei = &element_info[element];
3143 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3144 ei->description[i] = getFile8Bit(file);
3145 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3147 group = element_info[element].group;
3149 group->num_elements = getFile8Bit(file);
3151 ei->use_gfx_element = getFile8Bit(file);
3152 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3154 group->choice_mode = getFile8Bit(file);
3156 // some free bytes for future values and padding
3157 ReadUnusedBytesFromFile(file, 3);
3159 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3160 group->element[i] = getMappedElement(getFile16BitBE(file));
3162 // mark this group element as modified
3163 element_info[element].modified_settings = TRUE;
3165 level->file_has_custom_elements = TRUE;
3170 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3171 int element, int real_element)
3173 int micro_chunk_size = 0;
3174 int conf_type = getFile8Bit(file);
3175 int byte_mask = conf_type & CONF_MASK_BYTES;
3176 boolean element_found = FALSE;
3179 micro_chunk_size += 1;
3181 if (byte_mask == CONF_MASK_MULTI_BYTES)
3183 int num_bytes = getFile16BitBE(file);
3184 byte *buffer = checked_malloc(num_bytes);
3186 ReadBytesFromFile(file, buffer, num_bytes);
3188 for (i = 0; conf[i].data_type != -1; i++)
3190 if (conf[i].element == element &&
3191 conf[i].conf_type == conf_type)
3193 int data_type = conf[i].data_type;
3194 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3195 int max_num_entities = conf[i].max_num_entities;
3197 if (num_entities > max_num_entities)
3199 Warn("truncating number of entities for element %d from %d to %d",
3200 element, num_entities, max_num_entities);
3202 num_entities = max_num_entities;
3205 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3206 data_type == TYPE_CONTENT_LIST))
3208 // for element and content lists, zero entities are not allowed
3209 Warn("found empty list of entities for element %d", element);
3211 // do not set "num_entities" here to prevent reading behind buffer
3213 *(int *)(conf[i].num_entities) = 1; // at least one is required
3217 *(int *)(conf[i].num_entities) = num_entities;
3220 element_found = TRUE;
3222 if (data_type == TYPE_STRING)
3224 char *string = (char *)(conf[i].value);
3227 for (j = 0; j < max_num_entities; j++)
3228 string[j] = (j < num_entities ? buffer[j] : '\0');
3230 else if (data_type == TYPE_ELEMENT_LIST)
3232 int *element_array = (int *)(conf[i].value);
3235 for (j = 0; j < num_entities; j++)
3237 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3239 else if (data_type == TYPE_CONTENT_LIST)
3241 struct Content *content= (struct Content *)(conf[i].value);
3244 for (c = 0; c < num_entities; c++)
3245 for (y = 0; y < 3; y++)
3246 for (x = 0; x < 3; x++)
3247 content[c].e[x][y] =
3248 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3251 element_found = FALSE;
3257 checked_free(buffer);
3259 micro_chunk_size += 2 + num_bytes;
3261 else // constant size configuration data (1, 2 or 4 bytes)
3263 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3264 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3265 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3267 for (i = 0; conf[i].data_type != -1; i++)
3269 if (conf[i].element == element &&
3270 conf[i].conf_type == conf_type)
3272 int data_type = conf[i].data_type;
3274 if (data_type == TYPE_ELEMENT)
3275 value = getMappedElement(value);
3277 if (data_type == TYPE_BOOLEAN)
3278 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3280 *(int *) (conf[i].value) = value;
3282 element_found = TRUE;
3288 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3293 char *error_conf_chunk_bytes =
3294 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3295 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3296 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3297 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3298 int error_element = real_element;
3300 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3301 error_conf_chunk_bytes, error_conf_chunk_token,
3302 error_element, EL_NAME(error_element));
3305 return micro_chunk_size;
3308 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3310 int real_chunk_size = 0;
3312 li = *level; // copy level data into temporary buffer
3314 while (!checkEndOfFile(file))
3316 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3318 if (real_chunk_size >= chunk_size)
3322 *level = li; // copy temporary buffer back to level data
3324 return real_chunk_size;
3327 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3329 int real_chunk_size = 0;
3331 li = *level; // copy level data into temporary buffer
3333 while (!checkEndOfFile(file))
3335 int element = getMappedElement(getFile16BitBE(file));
3337 real_chunk_size += 2;
3338 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3340 if (real_chunk_size >= chunk_size)
3344 *level = li; // copy temporary buffer back to level data
3346 return real_chunk_size;
3349 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3351 int real_chunk_size = 0;
3353 li = *level; // copy level data into temporary buffer
3355 while (!checkEndOfFile(file))
3357 int element = getMappedElement(getFile16BitBE(file));
3359 real_chunk_size += 2;
3360 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3362 if (real_chunk_size >= chunk_size)
3366 *level = li; // copy temporary buffer back to level data
3368 return real_chunk_size;
3371 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3373 int element = getMappedElement(getFile16BitBE(file));
3374 int envelope_nr = element - EL_ENVELOPE_1;
3375 int real_chunk_size = 2;
3377 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3379 while (!checkEndOfFile(file))
3381 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3384 if (real_chunk_size >= chunk_size)
3388 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3390 return real_chunk_size;
3393 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3395 int element = getMappedElement(getFile16BitBE(file));
3396 int real_chunk_size = 2;
3397 struct ElementInfo *ei = &element_info[element];
3400 xx_ei = *ei; // copy element data into temporary buffer
3402 xx_ei.num_change_pages = -1;
3404 while (!checkEndOfFile(file))
3406 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3408 if (xx_ei.num_change_pages != -1)
3411 if (real_chunk_size >= chunk_size)
3417 if (ei->num_change_pages == -1)
3419 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3422 ei->num_change_pages = 1;
3424 setElementChangePages(ei, 1);
3425 setElementChangeInfoToDefaults(ei->change);
3427 return real_chunk_size;
3430 // initialize number of change pages stored for this custom element
3431 setElementChangePages(ei, ei->num_change_pages);
3432 for (i = 0; i < ei->num_change_pages; i++)
3433 setElementChangeInfoToDefaults(&ei->change_page[i]);
3435 // start with reading properties for the first change page
3436 xx_current_change_page = 0;
3438 while (!checkEndOfFile(file))
3440 // level file might contain invalid change page number
3441 if (xx_current_change_page >= ei->num_change_pages)
3444 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3446 xx_change = *change; // copy change data into temporary buffer
3448 resetEventBits(); // reset bits; change page might have changed
3450 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3453 *change = xx_change;
3455 setEventFlagsFromEventBits(change);
3457 if (real_chunk_size >= chunk_size)
3461 level->file_has_custom_elements = TRUE;
3463 return real_chunk_size;
3466 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3468 int element = getMappedElement(getFile16BitBE(file));
3469 int real_chunk_size = 2;
3470 struct ElementInfo *ei = &element_info[element];
3471 struct ElementGroupInfo *group = ei->group;
3476 xx_ei = *ei; // copy element data into temporary buffer
3477 xx_group = *group; // copy group data into temporary buffer
3479 while (!checkEndOfFile(file))
3481 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3484 if (real_chunk_size >= chunk_size)
3491 level->file_has_custom_elements = TRUE;
3493 return real_chunk_size;
3496 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3498 int element = getMappedElement(getFile16BitBE(file));
3499 int real_chunk_size = 2;
3500 struct ElementInfo *ei = &element_info[element];
3502 xx_ei = *ei; // copy element data into temporary buffer
3504 while (!checkEndOfFile(file))
3506 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3509 if (real_chunk_size >= chunk_size)
3515 level->file_has_custom_elements = TRUE;
3517 return real_chunk_size;
3520 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3521 struct LevelFileInfo *level_file_info,
3522 boolean level_info_only)
3524 char *filename = level_file_info->filename;
3525 char cookie[MAX_LINE_LEN];
3526 char chunk_name[CHUNK_ID_LEN + 1];
3530 if (!(file = openFile(filename, MODE_READ)))
3532 level->no_valid_file = TRUE;
3533 level->no_level_file = TRUE;
3535 if (level_info_only)
3538 Warn("cannot read level '%s' -- using empty level", filename);
3540 if (!setup.editor.use_template_for_new_levels)
3543 // if level file not found, try to initialize level data from template
3544 filename = getGlobalLevelTemplateFilename();
3546 if (!(file = openFile(filename, MODE_READ)))
3549 // default: for empty levels, use level template for custom elements
3550 level->use_custom_template = TRUE;
3552 level->no_valid_file = FALSE;
3555 getFileChunkBE(file, chunk_name, NULL);
3556 if (strEqual(chunk_name, "RND1"))
3558 getFile32BitBE(file); // not used
3560 getFileChunkBE(file, chunk_name, NULL);
3561 if (!strEqual(chunk_name, "CAVE"))
3563 level->no_valid_file = TRUE;
3565 Warn("unknown format of level file '%s'", filename);
3572 else // check for pre-2.0 file format with cookie string
3574 strcpy(cookie, chunk_name);
3575 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3577 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3578 cookie[strlen(cookie) - 1] = '\0';
3580 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3582 level->no_valid_file = TRUE;
3584 Warn("unknown format of level file '%s'", filename);
3591 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3593 level->no_valid_file = TRUE;
3595 Warn("unsupported version of level file '%s'", filename);
3602 // pre-2.0 level files have no game version, so use file version here
3603 level->game_version = level->file_version;
3606 if (level->file_version < FILE_VERSION_1_2)
3608 // level files from versions before 1.2.0 without chunk structure
3609 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3610 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3618 int (*loader)(File *, int, struct LevelInfo *);
3622 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3623 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3624 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3625 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3626 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3627 { "INFO", -1, LoadLevel_INFO },
3628 { "BODY", -1, LoadLevel_BODY },
3629 { "CONT", -1, LoadLevel_CONT },
3630 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3631 { "CNT3", -1, LoadLevel_CNT3 },
3632 { "CUS1", -1, LoadLevel_CUS1 },
3633 { "CUS2", -1, LoadLevel_CUS2 },
3634 { "CUS3", -1, LoadLevel_CUS3 },
3635 { "CUS4", -1, LoadLevel_CUS4 },
3636 { "GRP1", -1, LoadLevel_GRP1 },
3637 { "CONF", -1, LoadLevel_CONF },
3638 { "ELEM", -1, LoadLevel_ELEM },
3639 { "NOTE", -1, LoadLevel_NOTE },
3640 { "CUSX", -1, LoadLevel_CUSX },
3641 { "GRPX", -1, LoadLevel_GRPX },
3642 { "EMPX", -1, LoadLevel_EMPX },
3647 while (getFileChunkBE(file, chunk_name, &chunk_size))
3651 while (chunk_info[i].name != NULL &&
3652 !strEqual(chunk_name, chunk_info[i].name))
3655 if (chunk_info[i].name == NULL)
3657 Warn("unknown chunk '%s' in level file '%s'",
3658 chunk_name, filename);
3660 ReadUnusedBytesFromFile(file, chunk_size);
3662 else if (chunk_info[i].size != -1 &&
3663 chunk_info[i].size != chunk_size)
3665 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3666 chunk_size, chunk_name, filename);
3668 ReadUnusedBytesFromFile(file, chunk_size);
3672 // call function to load this level chunk
3673 int chunk_size_expected =
3674 (chunk_info[i].loader)(file, chunk_size, level);
3676 if (chunk_size_expected < 0)
3678 Warn("error reading chunk '%s' in level file '%s'",
3679 chunk_name, filename);
3684 // the size of some chunks cannot be checked before reading other
3685 // chunks first (like "HEAD" and "BODY") that contain some header
3686 // information, so check them here
3687 if (chunk_size_expected != chunk_size)
3689 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3690 chunk_size, chunk_name, filename);
3702 // ----------------------------------------------------------------------------
3703 // functions for loading BD level
3704 // ----------------------------------------------------------------------------
3706 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3708 struct LevelInfo_BD *level_bd = level->native_bd_level;
3709 GdCave *cave = NULL; // will be changed below
3710 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3711 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3714 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3716 // cave and map newly allocated when set to defaults above
3717 cave = level_bd->cave;
3719 for (i = 0; i < 5; i++)
3721 cave->level_time[i] = level->time;
3722 cave->level_diamonds[i] = level->gems_needed;
3723 cave->level_magic_wall_time[i] = level->time_magic_wall;
3724 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3727 cave->diamond_value = level->score[SC_EMERALD];
3728 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3730 cave->level_speed[0] = 160; // set cave speed
3732 cave->pal_timing = level->bd_pal_timing;
3733 cave->intermission = level->bd_intermission;
3734 cave->diagonal_movements = level->bd_diagonal_movements;
3736 strncpy(cave->name, level->name, sizeof(GdString));
3737 cave->name[sizeof(GdString) - 1] = '\0';
3739 for (x = 0; x < cave->w; x++)
3740 for (y = 0; y < cave->h; y++)
3741 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3744 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3746 struct LevelInfo_BD *level_bd = level->native_bd_level;
3747 GdCave *cave = level_bd->cave;
3748 int bd_level_nr = level_bd->level_nr;
3751 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3752 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3754 level->time = cave->level_time[bd_level_nr];
3755 level->gems_needed = cave->level_diamonds[bd_level_nr];
3756 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3758 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3759 level->score[SC_EMERALD] = cave->diamond_value;
3760 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3762 level->bd_pal_timing = cave->pal_timing;
3763 level->bd_intermission = cave->intermission;
3764 level->bd_diagonal_movements = cave->diagonal_movements;
3766 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3767 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3769 for (x = 0; x < level->fieldx; x++)
3770 for (y = 0; y < level->fieldy; y++)
3771 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3774 static void setTapeInfoToDefaults(void);
3776 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3778 struct LevelInfo_BD *level_bd = level->native_bd_level;
3779 GdCave *cave = level_bd->cave;
3780 GdReplay *replay = level_bd->replay;
3786 // always start with reliable default values
3787 setTapeInfoToDefaults();
3789 tape.level_nr = level_nr; // (currently not used)
3790 tape.random_seed = replay->seed;
3792 TapeSetDateFromIsoDateString(replay->date);
3795 tape.pos[tape.counter].delay = 0;
3797 tape.bd_replay = TRUE;
3799 // all time calculations only used to display approximate tape time
3800 int cave_speed = cave->speed;
3801 int milliseconds_game = 0;
3802 int milliseconds_elapsed = 20;
3804 for (i = 0; i < replay->movements->len; i++)
3806 int replay_action = replay->movements->data[i];
3807 int tape_action = map_action_BD_to_RND(replay_action);
3808 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3809 boolean success = 0;
3813 success = TapeAddAction(action);
3815 milliseconds_game += milliseconds_elapsed;
3817 if (milliseconds_game >= cave_speed)
3819 milliseconds_game -= cave_speed;
3826 tape.pos[tape.counter].delay = 0;
3827 tape.pos[tape.counter].action[0] = 0;
3831 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3837 TapeHaltRecording();
3841 // ----------------------------------------------------------------------------
3842 // functions for loading EM level
3843 // ----------------------------------------------------------------------------
3845 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3847 static int ball_xy[8][2] =
3858 struct LevelInfo_EM *level_em = level->native_em_level;
3859 struct CAVE *cav = level_em->cav;
3862 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3863 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3865 cav->time_seconds = level->time;
3866 cav->gems_needed = level->gems_needed;
3868 cav->emerald_score = level->score[SC_EMERALD];
3869 cav->diamond_score = level->score[SC_DIAMOND];
3870 cav->alien_score = level->score[SC_ROBOT];
3871 cav->tank_score = level->score[SC_SPACESHIP];
3872 cav->bug_score = level->score[SC_BUG];
3873 cav->eater_score = level->score[SC_YAMYAM];
3874 cav->nut_score = level->score[SC_NUT];
3875 cav->dynamite_score = level->score[SC_DYNAMITE];
3876 cav->key_score = level->score[SC_KEY];
3877 cav->exit_score = level->score[SC_TIME_BONUS];
3879 cav->num_eater_arrays = level->num_yamyam_contents;
3881 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3882 for (y = 0; y < 3; y++)
3883 for (x = 0; x < 3; x++)
3884 cav->eater_array[i][y * 3 + x] =
3885 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3887 cav->amoeba_time = level->amoeba_speed;
3888 cav->wonderwall_time = level->time_magic_wall;
3889 cav->wheel_time = level->time_wheel;
3891 cav->android_move_time = level->android_move_time;
3892 cav->android_clone_time = level->android_clone_time;
3893 cav->ball_random = level->ball_random;
3894 cav->ball_active = level->ball_active_initial;
3895 cav->ball_time = level->ball_time;
3896 cav->num_ball_arrays = level->num_ball_contents;
3898 cav->lenses_score = level->lenses_score;
3899 cav->magnify_score = level->magnify_score;
3900 cav->slurp_score = level->slurp_score;
3902 cav->lenses_time = level->lenses_time;
3903 cav->magnify_time = level->magnify_time;
3905 cav->wind_time = 9999;
3906 cav->wind_direction =
3907 map_direction_RND_to_EM(level->wind_direction_initial);
3909 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3910 for (j = 0; j < 8; j++)
3911 cav->ball_array[i][j] =
3912 map_element_RND_to_EM_cave(level->ball_content[i].
3913 e[ball_xy[j][0]][ball_xy[j][1]]);
3915 map_android_clone_elements_RND_to_EM(level);
3917 // first fill the complete playfield with the empty space element
3918 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3919 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3920 cav->cave[x][y] = Cblank;
3922 // then copy the real level contents from level file into the playfield
3923 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3925 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3927 if (level->field[x][y] == EL_AMOEBA_DEAD)
3928 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3930 cav->cave[x][y] = new_element;
3933 for (i = 0; i < MAX_PLAYERS; i++)
3935 cav->player_x[i] = -1;
3936 cav->player_y[i] = -1;
3939 // initialize player positions and delete players from the playfield
3940 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3942 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3944 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3946 cav->player_x[player_nr] = x;
3947 cav->player_y[player_nr] = y;
3949 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3954 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3956 static int ball_xy[8][2] =
3967 struct LevelInfo_EM *level_em = level->native_em_level;
3968 struct CAVE *cav = level_em->cav;
3971 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3972 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3974 level->time = cav->time_seconds;
3975 level->gems_needed = cav->gems_needed;
3977 sprintf(level->name, "Level %d", level->file_info.nr);
3979 level->score[SC_EMERALD] = cav->emerald_score;
3980 level->score[SC_DIAMOND] = cav->diamond_score;
3981 level->score[SC_ROBOT] = cav->alien_score;
3982 level->score[SC_SPACESHIP] = cav->tank_score;
3983 level->score[SC_BUG] = cav->bug_score;
3984 level->score[SC_YAMYAM] = cav->eater_score;
3985 level->score[SC_NUT] = cav->nut_score;
3986 level->score[SC_DYNAMITE] = cav->dynamite_score;
3987 level->score[SC_KEY] = cav->key_score;
3988 level->score[SC_TIME_BONUS] = cav->exit_score;
3990 level->num_yamyam_contents = cav->num_eater_arrays;
3992 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3993 for (y = 0; y < 3; y++)
3994 for (x = 0; x < 3; x++)
3995 level->yamyam_content[i].e[x][y] =
3996 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3998 level->amoeba_speed = cav->amoeba_time;
3999 level->time_magic_wall = cav->wonderwall_time;
4000 level->time_wheel = cav->wheel_time;
4002 level->android_move_time = cav->android_move_time;
4003 level->android_clone_time = cav->android_clone_time;
4004 level->ball_random = cav->ball_random;
4005 level->ball_active_initial = cav->ball_active;
4006 level->ball_time = cav->ball_time;
4007 level->num_ball_contents = cav->num_ball_arrays;
4009 level->lenses_score = cav->lenses_score;
4010 level->magnify_score = cav->magnify_score;
4011 level->slurp_score = cav->slurp_score;
4013 level->lenses_time = cav->lenses_time;
4014 level->magnify_time = cav->magnify_time;
4016 level->wind_direction_initial =
4017 map_direction_EM_to_RND(cav->wind_direction);
4019 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4020 for (j = 0; j < 8; j++)
4021 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4022 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4024 map_android_clone_elements_EM_to_RND(level);
4026 // convert the playfield (some elements need special treatment)
4027 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4029 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4031 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4032 new_element = EL_AMOEBA_DEAD;
4034 level->field[x][y] = new_element;
4037 for (i = 0; i < MAX_PLAYERS; i++)
4039 // in case of all players set to the same field, use the first player
4040 int nr = MAX_PLAYERS - i - 1;
4041 int jx = cav->player_x[nr];
4042 int jy = cav->player_y[nr];
4044 if (jx != -1 && jy != -1)
4045 level->field[jx][jy] = EL_PLAYER_1 + nr;
4048 // time score is counted for each 10 seconds left in Emerald Mine levels
4049 level->time_score_base = 10;
4053 // ----------------------------------------------------------------------------
4054 // functions for loading SP level
4055 // ----------------------------------------------------------------------------
4057 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4059 struct LevelInfo_SP *level_sp = level->native_sp_level;
4060 LevelInfoType *header = &level_sp->header;
4063 level_sp->width = level->fieldx;
4064 level_sp->height = level->fieldy;
4066 for (x = 0; x < level->fieldx; x++)
4067 for (y = 0; y < level->fieldy; y++)
4068 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4070 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4072 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4073 header->LevelTitle[i] = level->name[i];
4074 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4076 header->InfotronsNeeded = level->gems_needed;
4078 header->SpecialPortCount = 0;
4080 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4082 boolean gravity_port_found = FALSE;
4083 boolean gravity_port_valid = FALSE;
4084 int gravity_port_flag;
4085 int gravity_port_base_element;
4086 int element = level->field[x][y];
4088 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4089 element <= EL_SP_GRAVITY_ON_PORT_UP)
4091 gravity_port_found = TRUE;
4092 gravity_port_valid = TRUE;
4093 gravity_port_flag = 1;
4094 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4096 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4097 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4099 gravity_port_found = TRUE;
4100 gravity_port_valid = TRUE;
4101 gravity_port_flag = 0;
4102 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4104 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4105 element <= EL_SP_GRAVITY_PORT_UP)
4107 // change R'n'D style gravity inverting special port to normal port
4108 // (there are no gravity inverting ports in native Supaplex engine)
4110 gravity_port_found = TRUE;
4111 gravity_port_valid = FALSE;
4112 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4115 if (gravity_port_found)
4117 if (gravity_port_valid &&
4118 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4120 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4122 port->PortLocation = (y * level->fieldx + x) * 2;
4123 port->Gravity = gravity_port_flag;
4125 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4127 header->SpecialPortCount++;
4131 // change special gravity port to normal port
4133 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4136 level_sp->playfield[x][y] = element - EL_SP_START;
4141 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4143 struct LevelInfo_SP *level_sp = level->native_sp_level;
4144 LevelInfoType *header = &level_sp->header;
4145 boolean num_invalid_elements = 0;
4148 level->fieldx = level_sp->width;
4149 level->fieldy = level_sp->height;
4151 for (x = 0; x < level->fieldx; x++)
4153 for (y = 0; y < level->fieldy; y++)
4155 int element_old = level_sp->playfield[x][y];
4156 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4158 if (element_new == EL_UNKNOWN)
4160 num_invalid_elements++;
4162 Debug("level:native:SP", "invalid element %d at position %d, %d",
4166 level->field[x][y] = element_new;
4170 if (num_invalid_elements > 0)
4171 Warn("found %d invalid elements%s", num_invalid_elements,
4172 (!options.debug ? " (use '--debug' for more details)" : ""));
4174 for (i = 0; i < MAX_PLAYERS; i++)
4175 level->initial_player_gravity[i] =
4176 (header->InitialGravity == 1 ? TRUE : FALSE);
4178 // skip leading spaces
4179 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4180 if (header->LevelTitle[i] != ' ')
4184 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4185 level->name[j] = header->LevelTitle[i];
4186 level->name[j] = '\0';
4188 // cut trailing spaces
4190 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4191 level->name[j - 1] = '\0';
4193 level->gems_needed = header->InfotronsNeeded;
4195 for (i = 0; i < header->SpecialPortCount; i++)
4197 SpecialPortType *port = &header->SpecialPort[i];
4198 int port_location = port->PortLocation;
4199 int gravity = port->Gravity;
4200 int port_x, port_y, port_element;
4202 port_x = (port_location / 2) % level->fieldx;
4203 port_y = (port_location / 2) / level->fieldx;
4205 if (port_x < 0 || port_x >= level->fieldx ||
4206 port_y < 0 || port_y >= level->fieldy)
4208 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4213 port_element = level->field[port_x][port_y];
4215 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4216 port_element > EL_SP_GRAVITY_PORT_UP)
4218 Warn("no special port at position (%d, %d)", port_x, port_y);
4223 // change previous (wrong) gravity inverting special port to either
4224 // gravity enabling special port or gravity disabling special port
4225 level->field[port_x][port_y] +=
4226 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4227 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4230 // change special gravity ports without database entries to normal ports
4231 for (x = 0; x < level->fieldx; x++)
4232 for (y = 0; y < level->fieldy; y++)
4233 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4234 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4235 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4237 level->time = 0; // no time limit
4238 level->amoeba_speed = 0;
4239 level->time_magic_wall = 0;
4240 level->time_wheel = 0;
4241 level->amoeba_content = EL_EMPTY;
4243 // original Supaplex does not use score values -- rate by playing time
4244 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4245 level->score[i] = 0;
4247 level->rate_time_over_score = TRUE;
4249 // there are no yamyams in supaplex levels
4250 for (i = 0; i < level->num_yamyam_contents; i++)
4251 for (x = 0; x < 3; x++)
4252 for (y = 0; y < 3; y++)
4253 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4256 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4258 struct LevelInfo_SP *level_sp = level->native_sp_level;
4259 struct DemoInfo_SP *demo = &level_sp->demo;
4262 // always start with reliable default values
4263 demo->is_available = FALSE;
4266 if (TAPE_IS_EMPTY(tape))
4269 demo->level_nr = tape.level_nr; // (currently not used)
4271 level_sp->header.DemoRandomSeed = tape.random_seed;
4275 for (i = 0; i < tape.length; i++)
4277 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4278 int demo_repeat = tape.pos[i].delay;
4279 int demo_entries = (demo_repeat + 15) / 16;
4281 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4283 Warn("tape truncated: size exceeds maximum SP demo size %d",
4289 for (j = 0; j < demo_repeat / 16; j++)
4290 demo->data[demo->length++] = 0xf0 | demo_action;
4292 if (demo_repeat % 16)
4293 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4296 demo->is_available = TRUE;
4299 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4301 struct LevelInfo_SP *level_sp = level->native_sp_level;
4302 struct DemoInfo_SP *demo = &level_sp->demo;
4303 char *filename = level->file_info.filename;
4306 // always start with reliable default values
4307 setTapeInfoToDefaults();
4309 if (!demo->is_available)
4312 tape.level_nr = demo->level_nr; // (currently not used)
4313 tape.random_seed = level_sp->header.DemoRandomSeed;
4315 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4318 tape.pos[tape.counter].delay = 0;
4320 for (i = 0; i < demo->length; i++)
4322 int demo_action = demo->data[i] & 0x0f;
4323 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4324 int tape_action = map_key_SP_to_RND(demo_action);
4325 int tape_repeat = demo_repeat + 1;
4326 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4327 boolean success = 0;
4330 for (j = 0; j < tape_repeat; j++)
4331 success = TapeAddAction(action);
4335 Warn("SP demo truncated: size exceeds maximum tape size %d",
4342 TapeHaltRecording();
4346 // ----------------------------------------------------------------------------
4347 // functions for loading MM level
4348 // ----------------------------------------------------------------------------
4350 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4352 struct LevelInfo_MM *level_mm = level->native_mm_level;
4355 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4356 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4358 level_mm->time = level->time;
4359 level_mm->kettles_needed = level->gems_needed;
4360 level_mm->auto_count_kettles = level->auto_count_gems;
4362 level_mm->mm_laser_red = level->mm_laser_red;
4363 level_mm->mm_laser_green = level->mm_laser_green;
4364 level_mm->mm_laser_blue = level->mm_laser_blue;
4366 level_mm->df_laser_red = level->df_laser_red;
4367 level_mm->df_laser_green = level->df_laser_green;
4368 level_mm->df_laser_blue = level->df_laser_blue;
4370 strcpy(level_mm->name, level->name);
4371 strcpy(level_mm->author, level->author);
4373 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4374 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4375 level_mm->score[SC_KEY] = level->score[SC_KEY];
4376 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4377 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4379 level_mm->amoeba_speed = level->amoeba_speed;
4380 level_mm->time_fuse = level->mm_time_fuse;
4381 level_mm->time_bomb = level->mm_time_bomb;
4382 level_mm->time_ball = level->mm_time_ball;
4383 level_mm->time_block = level->mm_time_block;
4385 level_mm->num_ball_contents = level->num_mm_ball_contents;
4386 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4387 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4388 level_mm->explode_ball = level->explode_mm_ball;
4390 for (i = 0; i < level->num_mm_ball_contents; i++)
4391 level_mm->ball_content[i] =
4392 map_element_RND_to_MM(level->mm_ball_content[i]);
4394 for (x = 0; x < level->fieldx; x++)
4395 for (y = 0; y < level->fieldy; y++)
4397 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4400 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4402 struct LevelInfo_MM *level_mm = level->native_mm_level;
4405 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4406 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4408 level->time = level_mm->time;
4409 level->gems_needed = level_mm->kettles_needed;
4410 level->auto_count_gems = level_mm->auto_count_kettles;
4412 level->mm_laser_red = level_mm->mm_laser_red;
4413 level->mm_laser_green = level_mm->mm_laser_green;
4414 level->mm_laser_blue = level_mm->mm_laser_blue;
4416 level->df_laser_red = level_mm->df_laser_red;
4417 level->df_laser_green = level_mm->df_laser_green;
4418 level->df_laser_blue = level_mm->df_laser_blue;
4420 strcpy(level->name, level_mm->name);
4422 // only overwrite author from 'levelinfo.conf' if author defined in level
4423 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4424 strcpy(level->author, level_mm->author);
4426 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4427 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4428 level->score[SC_KEY] = level_mm->score[SC_KEY];
4429 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4430 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4432 level->amoeba_speed = level_mm->amoeba_speed;
4433 level->mm_time_fuse = level_mm->time_fuse;
4434 level->mm_time_bomb = level_mm->time_bomb;
4435 level->mm_time_ball = level_mm->time_ball;
4436 level->mm_time_block = level_mm->time_block;
4438 level->num_mm_ball_contents = level_mm->num_ball_contents;
4439 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4440 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4441 level->explode_mm_ball = level_mm->explode_ball;
4443 for (i = 0; i < level->num_mm_ball_contents; i++)
4444 level->mm_ball_content[i] =
4445 map_element_MM_to_RND(level_mm->ball_content[i]);
4447 for (x = 0; x < level->fieldx; x++)
4448 for (y = 0; y < level->fieldy; y++)
4449 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4453 // ----------------------------------------------------------------------------
4454 // functions for loading DC level
4455 // ----------------------------------------------------------------------------
4457 #define DC_LEVEL_HEADER_SIZE 344
4459 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4462 static int last_data_encoded;
4466 int diff_hi, diff_lo;
4467 int data_hi, data_lo;
4468 unsigned short data_decoded;
4472 last_data_encoded = 0;
4479 diff = data_encoded - last_data_encoded;
4480 diff_hi = diff & ~0xff;
4481 diff_lo = diff & 0xff;
4485 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4486 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4487 data_hi = data_hi & 0xff00;
4489 data_decoded = data_hi | data_lo;
4491 last_data_encoded = data_encoded;
4493 offset1 = (offset1 + 1) % 31;
4494 offset2 = offset2 & 0xff;
4496 return data_decoded;
4499 static int getMappedElement_DC(int element)
4507 // 0x0117 - 0x036e: (?)
4510 // 0x042d - 0x0684: (?)
4526 element = EL_CRYSTAL;
4529 case 0x0e77: // quicksand (boulder)
4530 element = EL_QUICKSAND_FAST_FULL;
4533 case 0x0e99: // slow quicksand (boulder)
4534 element = EL_QUICKSAND_FULL;
4538 element = EL_EM_EXIT_OPEN;
4542 element = EL_EM_EXIT_CLOSED;
4546 element = EL_EM_STEEL_EXIT_OPEN;
4550 element = EL_EM_STEEL_EXIT_CLOSED;
4553 case 0x0f4f: // dynamite (lit 1)
4554 element = EL_EM_DYNAMITE_ACTIVE;
4557 case 0x0f57: // dynamite (lit 2)
4558 element = EL_EM_DYNAMITE_ACTIVE;
4561 case 0x0f5f: // dynamite (lit 3)
4562 element = EL_EM_DYNAMITE_ACTIVE;
4565 case 0x0f67: // dynamite (lit 4)
4566 element = EL_EM_DYNAMITE_ACTIVE;
4573 element = EL_AMOEBA_WET;
4577 element = EL_AMOEBA_DROP;
4581 element = EL_DC_MAGIC_WALL;
4585 element = EL_SPACESHIP_UP;
4589 element = EL_SPACESHIP_DOWN;
4593 element = EL_SPACESHIP_LEFT;
4597 element = EL_SPACESHIP_RIGHT;
4601 element = EL_BUG_UP;
4605 element = EL_BUG_DOWN;
4609 element = EL_BUG_LEFT;
4613 element = EL_BUG_RIGHT;
4617 element = EL_MOLE_UP;
4621 element = EL_MOLE_DOWN;
4625 element = EL_MOLE_LEFT;
4629 element = EL_MOLE_RIGHT;
4637 element = EL_YAMYAM_UP;
4641 element = EL_SWITCHGATE_OPEN;
4645 element = EL_SWITCHGATE_CLOSED;
4649 element = EL_DC_SWITCHGATE_SWITCH_UP;
4653 element = EL_TIMEGATE_CLOSED;
4656 case 0x144c: // conveyor belt switch (green)
4657 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4660 case 0x144f: // conveyor belt switch (red)
4661 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4664 case 0x1452: // conveyor belt switch (blue)
4665 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4669 element = EL_CONVEYOR_BELT_3_MIDDLE;
4673 element = EL_CONVEYOR_BELT_3_LEFT;
4677 element = EL_CONVEYOR_BELT_3_RIGHT;
4681 element = EL_CONVEYOR_BELT_1_MIDDLE;
4685 element = EL_CONVEYOR_BELT_1_LEFT;
4689 element = EL_CONVEYOR_BELT_1_RIGHT;
4693 element = EL_CONVEYOR_BELT_4_MIDDLE;
4697 element = EL_CONVEYOR_BELT_4_LEFT;
4701 element = EL_CONVEYOR_BELT_4_RIGHT;
4705 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4709 element = EL_EXPANDABLE_WALL_VERTICAL;
4713 element = EL_EXPANDABLE_WALL_ANY;
4716 case 0x14ce: // growing steel wall (left/right)
4717 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4720 case 0x14df: // growing steel wall (up/down)
4721 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4724 case 0x14e8: // growing steel wall (up/down/left/right)
4725 element = EL_EXPANDABLE_STEELWALL_ANY;
4729 element = EL_SHIELD_DEADLY;
4733 element = EL_EXTRA_TIME;
4741 element = EL_EMPTY_SPACE;
4744 case 0x1578: // quicksand (empty)
4745 element = EL_QUICKSAND_FAST_EMPTY;
4748 case 0x1579: // slow quicksand (empty)
4749 element = EL_QUICKSAND_EMPTY;
4759 element = EL_EM_DYNAMITE;
4762 case 0x15a1: // key (red)
4763 element = EL_EM_KEY_1;
4766 case 0x15a2: // key (yellow)
4767 element = EL_EM_KEY_2;
4770 case 0x15a3: // key (blue)
4771 element = EL_EM_KEY_4;
4774 case 0x15a4: // key (green)
4775 element = EL_EM_KEY_3;
4778 case 0x15a5: // key (white)
4779 element = EL_DC_KEY_WHITE;
4783 element = EL_WALL_SLIPPERY;
4790 case 0x15a8: // wall (not round)
4794 case 0x15a9: // (blue)
4795 element = EL_CHAR_A;
4798 case 0x15aa: // (blue)
4799 element = EL_CHAR_B;
4802 case 0x15ab: // (blue)
4803 element = EL_CHAR_C;
4806 case 0x15ac: // (blue)
4807 element = EL_CHAR_D;
4810 case 0x15ad: // (blue)
4811 element = EL_CHAR_E;
4814 case 0x15ae: // (blue)
4815 element = EL_CHAR_F;
4818 case 0x15af: // (blue)
4819 element = EL_CHAR_G;
4822 case 0x15b0: // (blue)
4823 element = EL_CHAR_H;
4826 case 0x15b1: // (blue)
4827 element = EL_CHAR_I;
4830 case 0x15b2: // (blue)
4831 element = EL_CHAR_J;
4834 case 0x15b3: // (blue)
4835 element = EL_CHAR_K;
4838 case 0x15b4: // (blue)
4839 element = EL_CHAR_L;
4842 case 0x15b5: // (blue)
4843 element = EL_CHAR_M;
4846 case 0x15b6: // (blue)
4847 element = EL_CHAR_N;
4850 case 0x15b7: // (blue)
4851 element = EL_CHAR_O;
4854 case 0x15b8: // (blue)
4855 element = EL_CHAR_P;
4858 case 0x15b9: // (blue)
4859 element = EL_CHAR_Q;
4862 case 0x15ba: // (blue)
4863 element = EL_CHAR_R;
4866 case 0x15bb: // (blue)
4867 element = EL_CHAR_S;
4870 case 0x15bc: // (blue)
4871 element = EL_CHAR_T;
4874 case 0x15bd: // (blue)
4875 element = EL_CHAR_U;
4878 case 0x15be: // (blue)
4879 element = EL_CHAR_V;
4882 case 0x15bf: // (blue)
4883 element = EL_CHAR_W;
4886 case 0x15c0: // (blue)
4887 element = EL_CHAR_X;
4890 case 0x15c1: // (blue)
4891 element = EL_CHAR_Y;
4894 case 0x15c2: // (blue)
4895 element = EL_CHAR_Z;
4898 case 0x15c3: // (blue)
4899 element = EL_CHAR_AUMLAUT;
4902 case 0x15c4: // (blue)
4903 element = EL_CHAR_OUMLAUT;
4906 case 0x15c5: // (blue)
4907 element = EL_CHAR_UUMLAUT;
4910 case 0x15c6: // (blue)
4911 element = EL_CHAR_0;
4914 case 0x15c7: // (blue)
4915 element = EL_CHAR_1;
4918 case 0x15c8: // (blue)
4919 element = EL_CHAR_2;
4922 case 0x15c9: // (blue)
4923 element = EL_CHAR_3;
4926 case 0x15ca: // (blue)
4927 element = EL_CHAR_4;
4930 case 0x15cb: // (blue)
4931 element = EL_CHAR_5;
4934 case 0x15cc: // (blue)
4935 element = EL_CHAR_6;
4938 case 0x15cd: // (blue)
4939 element = EL_CHAR_7;
4942 case 0x15ce: // (blue)
4943 element = EL_CHAR_8;
4946 case 0x15cf: // (blue)
4947 element = EL_CHAR_9;
4950 case 0x15d0: // (blue)
4951 element = EL_CHAR_PERIOD;
4954 case 0x15d1: // (blue)
4955 element = EL_CHAR_EXCLAM;
4958 case 0x15d2: // (blue)
4959 element = EL_CHAR_COLON;
4962 case 0x15d3: // (blue)
4963 element = EL_CHAR_LESS;
4966 case 0x15d4: // (blue)
4967 element = EL_CHAR_GREATER;
4970 case 0x15d5: // (blue)
4971 element = EL_CHAR_QUESTION;
4974 case 0x15d6: // (blue)
4975 element = EL_CHAR_COPYRIGHT;
4978 case 0x15d7: // (blue)
4979 element = EL_CHAR_UP;
4982 case 0x15d8: // (blue)
4983 element = EL_CHAR_DOWN;
4986 case 0x15d9: // (blue)
4987 element = EL_CHAR_BUTTON;
4990 case 0x15da: // (blue)
4991 element = EL_CHAR_PLUS;
4994 case 0x15db: // (blue)
4995 element = EL_CHAR_MINUS;
4998 case 0x15dc: // (blue)
4999 element = EL_CHAR_APOSTROPHE;
5002 case 0x15dd: // (blue)
5003 element = EL_CHAR_PARENLEFT;
5006 case 0x15de: // (blue)
5007 element = EL_CHAR_PARENRIGHT;
5010 case 0x15df: // (green)
5011 element = EL_CHAR_A;
5014 case 0x15e0: // (green)
5015 element = EL_CHAR_B;
5018 case 0x15e1: // (green)
5019 element = EL_CHAR_C;
5022 case 0x15e2: // (green)
5023 element = EL_CHAR_D;
5026 case 0x15e3: // (green)
5027 element = EL_CHAR_E;
5030 case 0x15e4: // (green)
5031 element = EL_CHAR_F;
5034 case 0x15e5: // (green)
5035 element = EL_CHAR_G;
5038 case 0x15e6: // (green)
5039 element = EL_CHAR_H;
5042 case 0x15e7: // (green)
5043 element = EL_CHAR_I;
5046 case 0x15e8: // (green)
5047 element = EL_CHAR_J;
5050 case 0x15e9: // (green)
5051 element = EL_CHAR_K;
5054 case 0x15ea: // (green)
5055 element = EL_CHAR_L;
5058 case 0x15eb: // (green)
5059 element = EL_CHAR_M;
5062 case 0x15ec: // (green)
5063 element = EL_CHAR_N;
5066 case 0x15ed: // (green)
5067 element = EL_CHAR_O;
5070 case 0x15ee: // (green)
5071 element = EL_CHAR_P;
5074 case 0x15ef: // (green)
5075 element = EL_CHAR_Q;
5078 case 0x15f0: // (green)
5079 element = EL_CHAR_R;
5082 case 0x15f1: // (green)
5083 element = EL_CHAR_S;
5086 case 0x15f2: // (green)
5087 element = EL_CHAR_T;
5090 case 0x15f3: // (green)
5091 element = EL_CHAR_U;
5094 case 0x15f4: // (green)
5095 element = EL_CHAR_V;
5098 case 0x15f5: // (green)
5099 element = EL_CHAR_W;
5102 case 0x15f6: // (green)
5103 element = EL_CHAR_X;
5106 case 0x15f7: // (green)
5107 element = EL_CHAR_Y;
5110 case 0x15f8: // (green)
5111 element = EL_CHAR_Z;
5114 case 0x15f9: // (green)
5115 element = EL_CHAR_AUMLAUT;
5118 case 0x15fa: // (green)
5119 element = EL_CHAR_OUMLAUT;
5122 case 0x15fb: // (green)
5123 element = EL_CHAR_UUMLAUT;
5126 case 0x15fc: // (green)
5127 element = EL_CHAR_0;
5130 case 0x15fd: // (green)
5131 element = EL_CHAR_1;
5134 case 0x15fe: // (green)
5135 element = EL_CHAR_2;
5138 case 0x15ff: // (green)
5139 element = EL_CHAR_3;
5142 case 0x1600: // (green)
5143 element = EL_CHAR_4;
5146 case 0x1601: // (green)
5147 element = EL_CHAR_5;
5150 case 0x1602: // (green)
5151 element = EL_CHAR_6;
5154 case 0x1603: // (green)
5155 element = EL_CHAR_7;
5158 case 0x1604: // (green)
5159 element = EL_CHAR_8;
5162 case 0x1605: // (green)
5163 element = EL_CHAR_9;
5166 case 0x1606: // (green)
5167 element = EL_CHAR_PERIOD;
5170 case 0x1607: // (green)
5171 element = EL_CHAR_EXCLAM;
5174 case 0x1608: // (green)
5175 element = EL_CHAR_COLON;
5178 case 0x1609: // (green)
5179 element = EL_CHAR_LESS;
5182 case 0x160a: // (green)
5183 element = EL_CHAR_GREATER;
5186 case 0x160b: // (green)
5187 element = EL_CHAR_QUESTION;
5190 case 0x160c: // (green)
5191 element = EL_CHAR_COPYRIGHT;
5194 case 0x160d: // (green)
5195 element = EL_CHAR_UP;
5198 case 0x160e: // (green)
5199 element = EL_CHAR_DOWN;
5202 case 0x160f: // (green)
5203 element = EL_CHAR_BUTTON;
5206 case 0x1610: // (green)
5207 element = EL_CHAR_PLUS;
5210 case 0x1611: // (green)
5211 element = EL_CHAR_MINUS;
5214 case 0x1612: // (green)
5215 element = EL_CHAR_APOSTROPHE;
5218 case 0x1613: // (green)
5219 element = EL_CHAR_PARENLEFT;
5222 case 0x1614: // (green)
5223 element = EL_CHAR_PARENRIGHT;
5226 case 0x1615: // (blue steel)
5227 element = EL_STEEL_CHAR_A;
5230 case 0x1616: // (blue steel)
5231 element = EL_STEEL_CHAR_B;
5234 case 0x1617: // (blue steel)
5235 element = EL_STEEL_CHAR_C;
5238 case 0x1618: // (blue steel)
5239 element = EL_STEEL_CHAR_D;
5242 case 0x1619: // (blue steel)
5243 element = EL_STEEL_CHAR_E;
5246 case 0x161a: // (blue steel)
5247 element = EL_STEEL_CHAR_F;
5250 case 0x161b: // (blue steel)
5251 element = EL_STEEL_CHAR_G;
5254 case 0x161c: // (blue steel)
5255 element = EL_STEEL_CHAR_H;
5258 case 0x161d: // (blue steel)
5259 element = EL_STEEL_CHAR_I;
5262 case 0x161e: // (blue steel)
5263 element = EL_STEEL_CHAR_J;
5266 case 0x161f: // (blue steel)
5267 element = EL_STEEL_CHAR_K;
5270 case 0x1620: // (blue steel)
5271 element = EL_STEEL_CHAR_L;
5274 case 0x1621: // (blue steel)
5275 element = EL_STEEL_CHAR_M;
5278 case 0x1622: // (blue steel)
5279 element = EL_STEEL_CHAR_N;
5282 case 0x1623: // (blue steel)
5283 element = EL_STEEL_CHAR_O;
5286 case 0x1624: // (blue steel)
5287 element = EL_STEEL_CHAR_P;
5290 case 0x1625: // (blue steel)
5291 element = EL_STEEL_CHAR_Q;
5294 case 0x1626: // (blue steel)
5295 element = EL_STEEL_CHAR_R;
5298 case 0x1627: // (blue steel)
5299 element = EL_STEEL_CHAR_S;
5302 case 0x1628: // (blue steel)
5303 element = EL_STEEL_CHAR_T;
5306 case 0x1629: // (blue steel)
5307 element = EL_STEEL_CHAR_U;
5310 case 0x162a: // (blue steel)
5311 element = EL_STEEL_CHAR_V;
5314 case 0x162b: // (blue steel)
5315 element = EL_STEEL_CHAR_W;
5318 case 0x162c: // (blue steel)
5319 element = EL_STEEL_CHAR_X;
5322 case 0x162d: // (blue steel)
5323 element = EL_STEEL_CHAR_Y;
5326 case 0x162e: // (blue steel)
5327 element = EL_STEEL_CHAR_Z;
5330 case 0x162f: // (blue steel)
5331 element = EL_STEEL_CHAR_AUMLAUT;
5334 case 0x1630: // (blue steel)
5335 element = EL_STEEL_CHAR_OUMLAUT;
5338 case 0x1631: // (blue steel)
5339 element = EL_STEEL_CHAR_UUMLAUT;
5342 case 0x1632: // (blue steel)
5343 element = EL_STEEL_CHAR_0;
5346 case 0x1633: // (blue steel)
5347 element = EL_STEEL_CHAR_1;
5350 case 0x1634: // (blue steel)
5351 element = EL_STEEL_CHAR_2;
5354 case 0x1635: // (blue steel)
5355 element = EL_STEEL_CHAR_3;
5358 case 0x1636: // (blue steel)
5359 element = EL_STEEL_CHAR_4;
5362 case 0x1637: // (blue steel)
5363 element = EL_STEEL_CHAR_5;
5366 case 0x1638: // (blue steel)
5367 element = EL_STEEL_CHAR_6;
5370 case 0x1639: // (blue steel)
5371 element = EL_STEEL_CHAR_7;
5374 case 0x163a: // (blue steel)
5375 element = EL_STEEL_CHAR_8;
5378 case 0x163b: // (blue steel)
5379 element = EL_STEEL_CHAR_9;
5382 case 0x163c: // (blue steel)
5383 element = EL_STEEL_CHAR_PERIOD;
5386 case 0x163d: // (blue steel)
5387 element = EL_STEEL_CHAR_EXCLAM;
5390 case 0x163e: // (blue steel)
5391 element = EL_STEEL_CHAR_COLON;
5394 case 0x163f: // (blue steel)
5395 element = EL_STEEL_CHAR_LESS;
5398 case 0x1640: // (blue steel)
5399 element = EL_STEEL_CHAR_GREATER;
5402 case 0x1641: // (blue steel)
5403 element = EL_STEEL_CHAR_QUESTION;
5406 case 0x1642: // (blue steel)
5407 element = EL_STEEL_CHAR_COPYRIGHT;
5410 case 0x1643: // (blue steel)
5411 element = EL_STEEL_CHAR_UP;
5414 case 0x1644: // (blue steel)
5415 element = EL_STEEL_CHAR_DOWN;
5418 case 0x1645: // (blue steel)
5419 element = EL_STEEL_CHAR_BUTTON;
5422 case 0x1646: // (blue steel)
5423 element = EL_STEEL_CHAR_PLUS;
5426 case 0x1647: // (blue steel)
5427 element = EL_STEEL_CHAR_MINUS;
5430 case 0x1648: // (blue steel)
5431 element = EL_STEEL_CHAR_APOSTROPHE;
5434 case 0x1649: // (blue steel)
5435 element = EL_STEEL_CHAR_PARENLEFT;
5438 case 0x164a: // (blue steel)
5439 element = EL_STEEL_CHAR_PARENRIGHT;
5442 case 0x164b: // (green steel)
5443 element = EL_STEEL_CHAR_A;
5446 case 0x164c: // (green steel)
5447 element = EL_STEEL_CHAR_B;
5450 case 0x164d: // (green steel)
5451 element = EL_STEEL_CHAR_C;
5454 case 0x164e: // (green steel)
5455 element = EL_STEEL_CHAR_D;
5458 case 0x164f: // (green steel)
5459 element = EL_STEEL_CHAR_E;
5462 case 0x1650: // (green steel)
5463 element = EL_STEEL_CHAR_F;
5466 case 0x1651: // (green steel)
5467 element = EL_STEEL_CHAR_G;
5470 case 0x1652: // (green steel)
5471 element = EL_STEEL_CHAR_H;
5474 case 0x1653: // (green steel)
5475 element = EL_STEEL_CHAR_I;
5478 case 0x1654: // (green steel)
5479 element = EL_STEEL_CHAR_J;
5482 case 0x1655: // (green steel)
5483 element = EL_STEEL_CHAR_K;
5486 case 0x1656: // (green steel)
5487 element = EL_STEEL_CHAR_L;
5490 case 0x1657: // (green steel)
5491 element = EL_STEEL_CHAR_M;
5494 case 0x1658: // (green steel)
5495 element = EL_STEEL_CHAR_N;
5498 case 0x1659: // (green steel)
5499 element = EL_STEEL_CHAR_O;
5502 case 0x165a: // (green steel)
5503 element = EL_STEEL_CHAR_P;
5506 case 0x165b: // (green steel)
5507 element = EL_STEEL_CHAR_Q;
5510 case 0x165c: // (green steel)
5511 element = EL_STEEL_CHAR_R;
5514 case 0x165d: // (green steel)
5515 element = EL_STEEL_CHAR_S;
5518 case 0x165e: // (green steel)
5519 element = EL_STEEL_CHAR_T;
5522 case 0x165f: // (green steel)
5523 element = EL_STEEL_CHAR_U;
5526 case 0x1660: // (green steel)
5527 element = EL_STEEL_CHAR_V;
5530 case 0x1661: // (green steel)
5531 element = EL_STEEL_CHAR_W;
5534 case 0x1662: // (green steel)
5535 element = EL_STEEL_CHAR_X;
5538 case 0x1663: // (green steel)
5539 element = EL_STEEL_CHAR_Y;
5542 case 0x1664: // (green steel)
5543 element = EL_STEEL_CHAR_Z;
5546 case 0x1665: // (green steel)
5547 element = EL_STEEL_CHAR_AUMLAUT;
5550 case 0x1666: // (green steel)
5551 element = EL_STEEL_CHAR_OUMLAUT;
5554 case 0x1667: // (green steel)
5555 element = EL_STEEL_CHAR_UUMLAUT;
5558 case 0x1668: // (green steel)
5559 element = EL_STEEL_CHAR_0;
5562 case 0x1669: // (green steel)
5563 element = EL_STEEL_CHAR_1;
5566 case 0x166a: // (green steel)
5567 element = EL_STEEL_CHAR_2;
5570 case 0x166b: // (green steel)
5571 element = EL_STEEL_CHAR_3;
5574 case 0x166c: // (green steel)
5575 element = EL_STEEL_CHAR_4;
5578 case 0x166d: // (green steel)
5579 element = EL_STEEL_CHAR_5;
5582 case 0x166e: // (green steel)
5583 element = EL_STEEL_CHAR_6;
5586 case 0x166f: // (green steel)
5587 element = EL_STEEL_CHAR_7;
5590 case 0x1670: // (green steel)
5591 element = EL_STEEL_CHAR_8;
5594 case 0x1671: // (green steel)
5595 element = EL_STEEL_CHAR_9;
5598 case 0x1672: // (green steel)
5599 element = EL_STEEL_CHAR_PERIOD;
5602 case 0x1673: // (green steel)
5603 element = EL_STEEL_CHAR_EXCLAM;
5606 case 0x1674: // (green steel)
5607 element = EL_STEEL_CHAR_COLON;
5610 case 0x1675: // (green steel)
5611 element = EL_STEEL_CHAR_LESS;
5614 case 0x1676: // (green steel)
5615 element = EL_STEEL_CHAR_GREATER;
5618 case 0x1677: // (green steel)
5619 element = EL_STEEL_CHAR_QUESTION;
5622 case 0x1678: // (green steel)
5623 element = EL_STEEL_CHAR_COPYRIGHT;
5626 case 0x1679: // (green steel)
5627 element = EL_STEEL_CHAR_UP;
5630 case 0x167a: // (green steel)
5631 element = EL_STEEL_CHAR_DOWN;
5634 case 0x167b: // (green steel)
5635 element = EL_STEEL_CHAR_BUTTON;
5638 case 0x167c: // (green steel)
5639 element = EL_STEEL_CHAR_PLUS;
5642 case 0x167d: // (green steel)
5643 element = EL_STEEL_CHAR_MINUS;
5646 case 0x167e: // (green steel)
5647 element = EL_STEEL_CHAR_APOSTROPHE;
5650 case 0x167f: // (green steel)
5651 element = EL_STEEL_CHAR_PARENLEFT;
5654 case 0x1680: // (green steel)
5655 element = EL_STEEL_CHAR_PARENRIGHT;
5658 case 0x1681: // gate (red)
5659 element = EL_EM_GATE_1;
5662 case 0x1682: // secret gate (red)
5663 element = EL_EM_GATE_1_GRAY;
5666 case 0x1683: // gate (yellow)
5667 element = EL_EM_GATE_2;
5670 case 0x1684: // secret gate (yellow)
5671 element = EL_EM_GATE_2_GRAY;
5674 case 0x1685: // gate (blue)
5675 element = EL_EM_GATE_4;
5678 case 0x1686: // secret gate (blue)
5679 element = EL_EM_GATE_4_GRAY;
5682 case 0x1687: // gate (green)
5683 element = EL_EM_GATE_3;
5686 case 0x1688: // secret gate (green)
5687 element = EL_EM_GATE_3_GRAY;
5690 case 0x1689: // gate (white)
5691 element = EL_DC_GATE_WHITE;
5694 case 0x168a: // secret gate (white)
5695 element = EL_DC_GATE_WHITE_GRAY;
5698 case 0x168b: // secret gate (no key)
5699 element = EL_DC_GATE_FAKE_GRAY;
5703 element = EL_ROBOT_WHEEL;
5707 element = EL_DC_TIMEGATE_SWITCH;
5711 element = EL_ACID_POOL_BOTTOM;
5715 element = EL_ACID_POOL_TOPLEFT;
5719 element = EL_ACID_POOL_TOPRIGHT;
5723 element = EL_ACID_POOL_BOTTOMLEFT;
5727 element = EL_ACID_POOL_BOTTOMRIGHT;
5731 element = EL_STEELWALL;
5735 element = EL_STEELWALL_SLIPPERY;
5738 case 0x1695: // steel wall (not round)
5739 element = EL_STEELWALL;
5742 case 0x1696: // steel wall (left)
5743 element = EL_DC_STEELWALL_1_LEFT;
5746 case 0x1697: // steel wall (bottom)
5747 element = EL_DC_STEELWALL_1_BOTTOM;
5750 case 0x1698: // steel wall (right)
5751 element = EL_DC_STEELWALL_1_RIGHT;
5754 case 0x1699: // steel wall (top)
5755 element = EL_DC_STEELWALL_1_TOP;
5758 case 0x169a: // steel wall (left/bottom)
5759 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5762 case 0x169b: // steel wall (right/bottom)
5763 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5766 case 0x169c: // steel wall (right/top)
5767 element = EL_DC_STEELWALL_1_TOPRIGHT;
5770 case 0x169d: // steel wall (left/top)
5771 element = EL_DC_STEELWALL_1_TOPLEFT;
5774 case 0x169e: // steel wall (right/bottom small)
5775 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5778 case 0x169f: // steel wall (left/bottom small)
5779 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5782 case 0x16a0: // steel wall (right/top small)
5783 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5786 case 0x16a1: // steel wall (left/top small)
5787 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5790 case 0x16a2: // steel wall (left/right)
5791 element = EL_DC_STEELWALL_1_VERTICAL;
5794 case 0x16a3: // steel wall (top/bottom)
5795 element = EL_DC_STEELWALL_1_HORIZONTAL;
5798 case 0x16a4: // steel wall 2 (left end)
5799 element = EL_DC_STEELWALL_2_LEFT;
5802 case 0x16a5: // steel wall 2 (right end)
5803 element = EL_DC_STEELWALL_2_RIGHT;
5806 case 0x16a6: // steel wall 2 (top end)
5807 element = EL_DC_STEELWALL_2_TOP;
5810 case 0x16a7: // steel wall 2 (bottom end)
5811 element = EL_DC_STEELWALL_2_BOTTOM;
5814 case 0x16a8: // steel wall 2 (left/right)
5815 element = EL_DC_STEELWALL_2_HORIZONTAL;
5818 case 0x16a9: // steel wall 2 (up/down)
5819 element = EL_DC_STEELWALL_2_VERTICAL;
5822 case 0x16aa: // steel wall 2 (mid)
5823 element = EL_DC_STEELWALL_2_MIDDLE;
5827 element = EL_SIGN_EXCLAMATION;
5831 element = EL_SIGN_RADIOACTIVITY;
5835 element = EL_SIGN_STOP;
5839 element = EL_SIGN_WHEELCHAIR;
5843 element = EL_SIGN_PARKING;
5847 element = EL_SIGN_NO_ENTRY;
5851 element = EL_SIGN_HEART;
5855 element = EL_SIGN_GIVE_WAY;
5859 element = EL_SIGN_ENTRY_FORBIDDEN;
5863 element = EL_SIGN_EMERGENCY_EXIT;
5867 element = EL_SIGN_YIN_YANG;
5871 element = EL_WALL_EMERALD;
5875 element = EL_WALL_DIAMOND;
5879 element = EL_WALL_PEARL;
5883 element = EL_WALL_CRYSTAL;
5887 element = EL_INVISIBLE_WALL;
5891 element = EL_INVISIBLE_STEELWALL;
5895 // EL_INVISIBLE_SAND
5898 element = EL_LIGHT_SWITCH;
5902 element = EL_ENVELOPE_1;
5906 if (element >= 0x0117 && element <= 0x036e) // (?)
5907 element = EL_DIAMOND;
5908 else if (element >= 0x042d && element <= 0x0684) // (?)
5909 element = EL_EMERALD;
5910 else if (element >= 0x157c && element <= 0x158b)
5912 else if (element >= 0x1590 && element <= 0x159f)
5913 element = EL_DC_LANDMINE;
5914 else if (element >= 0x16bc && element <= 0x16cb)
5915 element = EL_INVISIBLE_SAND;
5918 Warn("unknown Diamond Caves element 0x%04x", element);
5920 element = EL_UNKNOWN;
5925 return getMappedElement(element);
5928 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5930 byte header[DC_LEVEL_HEADER_SIZE];
5932 int envelope_header_pos = 62;
5933 int envelope_content_pos = 94;
5934 int level_name_pos = 251;
5935 int level_author_pos = 292;
5936 int envelope_header_len;
5937 int envelope_content_len;
5939 int level_author_len;
5941 int num_yamyam_contents;
5944 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5946 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5948 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5950 header[i * 2 + 0] = header_word >> 8;
5951 header[i * 2 + 1] = header_word & 0xff;
5954 // read some values from level header to check level decoding integrity
5955 fieldx = header[6] | (header[7] << 8);
5956 fieldy = header[8] | (header[9] << 8);
5957 num_yamyam_contents = header[60] | (header[61] << 8);
5959 // do some simple sanity checks to ensure that level was correctly decoded
5960 if (fieldx < 1 || fieldx > 256 ||
5961 fieldy < 1 || fieldy > 256 ||
5962 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5964 level->no_valid_file = TRUE;
5966 Warn("cannot decode level from stream -- using empty level");
5971 // maximum envelope header size is 31 bytes
5972 envelope_header_len = header[envelope_header_pos];
5973 // maximum envelope content size is 110 (156?) bytes
5974 envelope_content_len = header[envelope_content_pos];
5976 // maximum level title size is 40 bytes
5977 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5978 // maximum level author size is 30 (51?) bytes
5979 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5983 for (i = 0; i < envelope_header_len; i++)
5984 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5985 level->envelope[0].text[envelope_size++] =
5986 header[envelope_header_pos + 1 + i];
5988 if (envelope_header_len > 0 && envelope_content_len > 0)
5990 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5991 level->envelope[0].text[envelope_size++] = '\n';
5992 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5993 level->envelope[0].text[envelope_size++] = '\n';
5996 for (i = 0; i < envelope_content_len; i++)
5997 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5998 level->envelope[0].text[envelope_size++] =
5999 header[envelope_content_pos + 1 + i];
6001 level->envelope[0].text[envelope_size] = '\0';
6003 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6004 level->envelope[0].ysize = 10;
6005 level->envelope[0].autowrap = TRUE;
6006 level->envelope[0].centered = TRUE;
6008 for (i = 0; i < level_name_len; i++)
6009 level->name[i] = header[level_name_pos + 1 + i];
6010 level->name[level_name_len] = '\0';
6012 for (i = 0; i < level_author_len; i++)
6013 level->author[i] = header[level_author_pos + 1 + i];
6014 level->author[level_author_len] = '\0';
6016 num_yamyam_contents = header[60] | (header[61] << 8);
6017 level->num_yamyam_contents =
6018 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6020 for (i = 0; i < num_yamyam_contents; i++)
6022 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6024 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6025 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6027 if (i < MAX_ELEMENT_CONTENTS)
6028 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6032 fieldx = header[6] | (header[7] << 8);
6033 fieldy = header[8] | (header[9] << 8);
6034 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6035 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6037 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6039 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6040 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6042 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6043 level->field[x][y] = getMappedElement_DC(element_dc);
6046 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6047 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6048 level->field[x][y] = EL_PLAYER_1;
6050 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6051 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6052 level->field[x][y] = EL_PLAYER_2;
6054 level->gems_needed = header[18] | (header[19] << 8);
6056 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6057 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6058 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6059 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6060 level->score[SC_NUT] = header[28] | (header[29] << 8);
6061 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6062 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6063 level->score[SC_BUG] = header[34] | (header[35] << 8);
6064 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6065 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6066 level->score[SC_KEY] = header[40] | (header[41] << 8);
6067 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6069 level->time = header[44] | (header[45] << 8);
6071 level->amoeba_speed = header[46] | (header[47] << 8);
6072 level->time_light = header[48] | (header[49] << 8);
6073 level->time_timegate = header[50] | (header[51] << 8);
6074 level->time_wheel = header[52] | (header[53] << 8);
6075 level->time_magic_wall = header[54] | (header[55] << 8);
6076 level->extra_time = header[56] | (header[57] << 8);
6077 level->shield_normal_time = header[58] | (header[59] << 8);
6079 // shield and extra time elements do not have a score
6080 level->score[SC_SHIELD] = 0;
6081 level->extra_time_score = 0;
6083 // set time for normal and deadly shields to the same value
6084 level->shield_deadly_time = level->shield_normal_time;
6086 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6087 // can slip down from flat walls, like normal walls and steel walls
6088 level->em_slippery_gems = TRUE;
6090 // time score is counted for each 10 seconds left in Diamond Caves levels
6091 level->time_score_base = 10;
6094 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6095 struct LevelFileInfo *level_file_info,
6096 boolean level_info_only)
6098 char *filename = level_file_info->filename;
6100 int num_magic_bytes = 8;
6101 char magic_bytes[num_magic_bytes + 1];
6102 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6104 if (!(file = openFile(filename, MODE_READ)))
6106 level->no_valid_file = TRUE;
6108 if (!level_info_only)
6109 Warn("cannot read level '%s' -- using empty level", filename);
6114 // fseek(file, 0x0000, SEEK_SET);
6116 if (level_file_info->packed)
6118 // read "magic bytes" from start of file
6119 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6120 magic_bytes[0] = '\0';
6122 // check "magic bytes" for correct file format
6123 if (!strPrefix(magic_bytes, "DC2"))
6125 level->no_valid_file = TRUE;
6127 Warn("unknown DC level file '%s' -- using empty level", filename);
6132 if (strPrefix(magic_bytes, "DC2Win95") ||
6133 strPrefix(magic_bytes, "DC2Win98"))
6135 int position_first_level = 0x00fa;
6136 int extra_bytes = 4;
6139 // advance file stream to first level inside the level package
6140 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6142 // each block of level data is followed by block of non-level data
6143 num_levels_to_skip *= 2;
6145 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6146 while (num_levels_to_skip >= 0)
6148 // advance file stream to next level inside the level package
6149 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6151 level->no_valid_file = TRUE;
6153 Warn("cannot fseek in file '%s' -- using empty level", filename);
6158 // skip apparently unused extra bytes following each level
6159 ReadUnusedBytesFromFile(file, extra_bytes);
6161 // read size of next level in level package
6162 skip_bytes = getFile32BitLE(file);
6164 num_levels_to_skip--;
6169 level->no_valid_file = TRUE;
6171 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6177 LoadLevelFromFileStream_DC(file, level);
6183 // ----------------------------------------------------------------------------
6184 // functions for loading SB level
6185 // ----------------------------------------------------------------------------
6187 int getMappedElement_SB(int element_ascii, boolean use_ces)
6195 sb_element_mapping[] =
6197 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6198 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6199 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6200 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6201 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6202 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6203 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6204 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6211 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6212 if (element_ascii == sb_element_mapping[i].ascii)
6213 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6215 return EL_UNDEFINED;
6218 static void SetLevelSettings_SB(struct LevelInfo *level)
6222 level->use_step_counter = TRUE;
6225 level->score[SC_TIME_BONUS] = 0;
6226 level->time_score_base = 1;
6227 level->rate_time_over_score = TRUE;
6230 level->auto_exit_sokoban = TRUE;
6233 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6234 struct LevelFileInfo *level_file_info,
6235 boolean level_info_only)
6237 char *filename = level_file_info->filename;
6238 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6239 char last_comment[MAX_LINE_LEN];
6240 char level_name[MAX_LINE_LEN];
6243 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6244 boolean read_continued_line = FALSE;
6245 boolean reading_playfield = FALSE;
6246 boolean got_valid_playfield_line = FALSE;
6247 boolean invalid_playfield_char = FALSE;
6248 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6249 int file_level_nr = 0;
6250 int x = 0, y = 0; // initialized to make compilers happy
6252 last_comment[0] = '\0';
6253 level_name[0] = '\0';
6255 if (!(file = openFile(filename, MODE_READ)))
6257 level->no_valid_file = TRUE;
6259 if (!level_info_only)
6260 Warn("cannot read level '%s' -- using empty level", filename);
6265 while (!checkEndOfFile(file))
6267 // level successfully read, but next level may follow here
6268 if (!got_valid_playfield_line && reading_playfield)
6270 // read playfield from single level file -- skip remaining file
6271 if (!level_file_info->packed)
6274 if (file_level_nr >= num_levels_to_skip)
6279 last_comment[0] = '\0';
6280 level_name[0] = '\0';
6282 reading_playfield = FALSE;
6285 got_valid_playfield_line = FALSE;
6287 // read next line of input file
6288 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6291 // cut trailing line break (this can be newline and/or carriage return)
6292 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6293 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6296 // copy raw input line for later use (mainly debugging output)
6297 strcpy(line_raw, line);
6299 if (read_continued_line)
6301 // append new line to existing line, if there is enough space
6302 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6303 strcat(previous_line, line_ptr);
6305 strcpy(line, previous_line); // copy storage buffer to line
6307 read_continued_line = FALSE;
6310 // if the last character is '\', continue at next line
6311 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6313 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6314 strcpy(previous_line, line); // copy line to storage buffer
6316 read_continued_line = TRUE;
6322 if (line[0] == '\0')
6325 // extract comment text from comment line
6328 for (line_ptr = line; *line_ptr; line_ptr++)
6329 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6332 strcpy(last_comment, line_ptr);
6337 // extract level title text from line containing level title
6338 if (line[0] == '\'')
6340 strcpy(level_name, &line[1]);
6342 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6343 level_name[strlen(level_name) - 1] = '\0';
6348 // skip lines containing only spaces (or empty lines)
6349 for (line_ptr = line; *line_ptr; line_ptr++)
6350 if (*line_ptr != ' ')
6352 if (*line_ptr == '\0')
6355 // at this point, we have found a line containing part of a playfield
6357 got_valid_playfield_line = TRUE;
6359 if (!reading_playfield)
6361 reading_playfield = TRUE;
6362 invalid_playfield_char = FALSE;
6364 for (x = 0; x < MAX_LEV_FIELDX; x++)
6365 for (y = 0; y < MAX_LEV_FIELDY; y++)
6366 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6371 // start with topmost tile row
6375 // skip playfield line if larger row than allowed
6376 if (y >= MAX_LEV_FIELDY)
6379 // start with leftmost tile column
6382 // read playfield elements from line
6383 for (line_ptr = line; *line_ptr; line_ptr++)
6385 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6387 // stop parsing playfield line if larger column than allowed
6388 if (x >= MAX_LEV_FIELDX)
6391 if (mapped_sb_element == EL_UNDEFINED)
6393 invalid_playfield_char = TRUE;
6398 level->field[x][y] = mapped_sb_element;
6400 // continue with next tile column
6403 level->fieldx = MAX(x, level->fieldx);
6406 if (invalid_playfield_char)
6408 // if first playfield line, treat invalid lines as comment lines
6410 reading_playfield = FALSE;
6415 // continue with next tile row
6423 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6424 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6426 if (!reading_playfield)
6428 level->no_valid_file = TRUE;
6430 Warn("cannot read level '%s' -- using empty level", filename);
6435 if (*level_name != '\0')
6437 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6438 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6440 else if (*last_comment != '\0')
6442 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6443 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6447 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6450 // set all empty fields beyond the border walls to invisible steel wall
6451 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6453 if ((x == 0 || x == level->fieldx - 1 ||
6454 y == 0 || y == level->fieldy - 1) &&
6455 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6456 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6457 level->field, level->fieldx, level->fieldy);
6460 // set special level settings for Sokoban levels
6461 SetLevelSettings_SB(level);
6463 if (load_xsb_to_ces)
6465 // special global settings can now be set in level template
6466 level->use_custom_template = TRUE;
6471 // -------------------------------------------------------------------------
6472 // functions for handling native levels
6473 // -------------------------------------------------------------------------
6475 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6476 struct LevelFileInfo *level_file_info,
6477 boolean level_info_only)
6481 // determine position of requested level inside level package
6482 if (level_file_info->packed)
6483 pos = level_file_info->nr - leveldir_current->first_level;
6485 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6486 level->no_valid_file = TRUE;
6489 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6490 struct LevelFileInfo *level_file_info,
6491 boolean level_info_only)
6493 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6494 level->no_valid_file = TRUE;
6497 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6498 struct LevelFileInfo *level_file_info,
6499 boolean level_info_only)
6503 // determine position of requested level inside level package
6504 if (level_file_info->packed)
6505 pos = level_file_info->nr - leveldir_current->first_level;
6507 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6508 level->no_valid_file = TRUE;
6511 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6512 struct LevelFileInfo *level_file_info,
6513 boolean level_info_only)
6515 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6516 level->no_valid_file = TRUE;
6519 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6521 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6522 CopyNativeLevel_RND_to_BD(level);
6523 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6524 CopyNativeLevel_RND_to_EM(level);
6525 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6526 CopyNativeLevel_RND_to_SP(level);
6527 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6528 CopyNativeLevel_RND_to_MM(level);
6531 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6533 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6534 CopyNativeLevel_BD_to_RND(level);
6535 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6536 CopyNativeLevel_EM_to_RND(level);
6537 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6538 CopyNativeLevel_SP_to_RND(level);
6539 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6540 CopyNativeLevel_MM_to_RND(level);
6543 void SaveNativeLevel(struct LevelInfo *level)
6545 // saving native level files only supported for some game engines
6546 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6547 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6550 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6551 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6552 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6553 char *filename = getLevelFilenameFromBasename(basename);
6555 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6558 boolean success = FALSE;
6560 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6562 CopyNativeLevel_RND_to_BD(level);
6563 // CopyNativeTape_RND_to_BD(level);
6565 success = SaveNativeLevel_BD(filename);
6567 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6569 CopyNativeLevel_RND_to_SP(level);
6570 CopyNativeTape_RND_to_SP(level);
6572 success = SaveNativeLevel_SP(filename);
6576 Request("Native level file saved!", REQ_CONFIRM);
6578 Request("Failed to save native level file!", REQ_CONFIRM);
6582 // ----------------------------------------------------------------------------
6583 // functions for loading generic level
6584 // ----------------------------------------------------------------------------
6586 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6587 struct LevelFileInfo *level_file_info,
6588 boolean level_info_only)
6590 // always start with reliable default values
6591 setLevelInfoToDefaults(level, level_info_only, TRUE);
6593 switch (level_file_info->type)
6595 case LEVEL_FILE_TYPE_RND:
6596 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6599 case LEVEL_FILE_TYPE_BD:
6600 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6601 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6604 case LEVEL_FILE_TYPE_EM:
6605 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6606 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6609 case LEVEL_FILE_TYPE_SP:
6610 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6611 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6614 case LEVEL_FILE_TYPE_MM:
6615 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6616 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6619 case LEVEL_FILE_TYPE_DC:
6620 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6623 case LEVEL_FILE_TYPE_SB:
6624 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6628 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6632 // if level file is invalid, restore level structure to default values
6633 if (level->no_valid_file)
6634 setLevelInfoToDefaults(level, level_info_only, FALSE);
6636 if (check_special_flags("use_native_bd_game_engine"))
6637 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6639 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6640 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6642 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6643 CopyNativeLevel_Native_to_RND(level);
6646 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6648 static struct LevelFileInfo level_file_info;
6650 // always start with reliable default values
6651 setFileInfoToDefaults(&level_file_info);
6653 level_file_info.nr = 0; // unknown level number
6654 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6656 setString(&level_file_info.filename, filename);
6658 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6661 static void LoadLevel_InitVersion(struct LevelInfo *level)
6665 if (leveldir_current == NULL) // only when dumping level
6668 // all engine modifications also valid for levels which use latest engine
6669 if (level->game_version < VERSION_IDENT(3,2,0,5))
6671 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6672 level->time_score_base = 10;
6675 if (leveldir_current->latest_engine)
6677 // ---------- use latest game engine --------------------------------------
6679 /* For all levels which are forced to use the latest game engine version
6680 (normally all but user contributed, private and undefined levels), set
6681 the game engine version to the actual version; this allows for actual
6682 corrections in the game engine to take effect for existing, converted
6683 levels (from "classic" or other existing games) to make the emulation
6684 of the corresponding game more accurate, while (hopefully) not breaking
6685 existing levels created from other players. */
6687 level->game_version = GAME_VERSION_ACTUAL;
6689 /* Set special EM style gems behaviour: EM style gems slip down from
6690 normal, steel and growing wall. As this is a more fundamental change,
6691 it seems better to set the default behaviour to "off" (as it is more
6692 natural) and make it configurable in the level editor (as a property
6693 of gem style elements). Already existing converted levels (neither
6694 private nor contributed levels) are changed to the new behaviour. */
6696 if (level->file_version < FILE_VERSION_2_0)
6697 level->em_slippery_gems = TRUE;
6702 // ---------- use game engine the level was created with --------------------
6704 /* For all levels which are not forced to use the latest game engine
6705 version (normally user contributed, private and undefined levels),
6706 use the version of the game engine the levels were created for.
6708 Since 2.0.1, the game engine version is now directly stored
6709 in the level file (chunk "VERS"), so there is no need anymore
6710 to set the game version from the file version (except for old,
6711 pre-2.0 levels, where the game version is still taken from the
6712 file format version used to store the level -- see above). */
6714 // player was faster than enemies in 1.0.0 and before
6715 if (level->file_version == FILE_VERSION_1_0)
6716 for (i = 0; i < MAX_PLAYERS; i++)
6717 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6719 // default behaviour for EM style gems was "slippery" only in 2.0.1
6720 if (level->game_version == VERSION_IDENT(2,0,1,0))
6721 level->em_slippery_gems = TRUE;
6723 // springs could be pushed over pits before (pre-release version) 2.2.0
6724 if (level->game_version < VERSION_IDENT(2,2,0,0))
6725 level->use_spring_bug = TRUE;
6727 if (level->game_version < VERSION_IDENT(3,2,0,5))
6729 // time orb caused limited time in endless time levels before 3.2.0-5
6730 level->use_time_orb_bug = TRUE;
6732 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6733 level->block_snap_field = FALSE;
6735 // extra time score was same value as time left score before 3.2.0-5
6736 level->extra_time_score = level->score[SC_TIME_BONUS];
6739 if (level->game_version < VERSION_IDENT(3,2,0,7))
6741 // default behaviour for snapping was "not continuous" before 3.2.0-7
6742 level->continuous_snapping = FALSE;
6745 // only few elements were able to actively move into acid before 3.1.0
6746 // trigger settings did not exist before 3.1.0; set to default "any"
6747 if (level->game_version < VERSION_IDENT(3,1,0,0))
6749 // correct "can move into acid" settings (all zero in old levels)
6751 level->can_move_into_acid_bits = 0; // nothing can move into acid
6752 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6754 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6755 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6756 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6757 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6759 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6760 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6762 // correct trigger settings (stored as zero == "none" in old levels)
6764 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6766 int element = EL_CUSTOM_START + i;
6767 struct ElementInfo *ei = &element_info[element];
6769 for (j = 0; j < ei->num_change_pages; j++)
6771 struct ElementChangeInfo *change = &ei->change_page[j];
6773 change->trigger_player = CH_PLAYER_ANY;
6774 change->trigger_page = CH_PAGE_ANY;
6779 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6781 int element = EL_CUSTOM_256;
6782 struct ElementInfo *ei = &element_info[element];
6783 struct ElementChangeInfo *change = &ei->change_page[0];
6785 /* This is needed to fix a problem that was caused by a bugfix in function
6786 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6787 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6788 not replace walkable elements, but instead just placed the player on it,
6789 without placing the Sokoban field under the player). Unfortunately, this
6790 breaks "Snake Bite" style levels when the snake is halfway through a door
6791 that just closes (the snake head is still alive and can be moved in this
6792 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6793 player (without Sokoban element) which then gets killed as designed). */
6795 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6796 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6797 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6798 change->target_element = EL_PLAYER_1;
6801 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6802 if (level->game_version < VERSION_IDENT(3,2,5,0))
6804 /* This is needed to fix a problem that was caused by a bugfix in function
6805 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6806 corrects the behaviour when a custom element changes to another custom
6807 element with a higher element number that has change actions defined.
6808 Normally, only one change per frame is allowed for custom elements.
6809 Therefore, it is checked if a custom element already changed in the
6810 current frame; if it did, subsequent changes are suppressed.
6811 Unfortunately, this is only checked for element changes, but not for
6812 change actions, which are still executed. As the function above loops
6813 through all custom elements from lower to higher, an element change
6814 resulting in a lower CE number won't be checked again, while a target
6815 element with a higher number will also be checked, and potential change
6816 actions will get executed for this CE, too (which is wrong), while
6817 further changes are ignored (which is correct). As this bugfix breaks
6818 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6819 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6820 behaviour for existing levels and tapes that make use of this bug */
6822 level->use_action_after_change_bug = TRUE;
6825 // not centering level after relocating player was default only in 3.2.3
6826 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6827 level->shifted_relocation = TRUE;
6829 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6830 if (level->game_version < VERSION_IDENT(3,2,6,0))
6831 level->em_explodes_by_fire = TRUE;
6833 // levels were solved by the first player entering an exit up to 4.1.0.0
6834 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6835 level->solved_by_one_player = TRUE;
6837 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6838 if (level->game_version < VERSION_IDENT(4,1,1,1))
6839 level->use_life_bugs = TRUE;
6841 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6842 if (level->game_version < VERSION_IDENT(4,1,1,1))
6843 level->sb_objects_needed = FALSE;
6845 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6846 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6847 level->finish_dig_collect = FALSE;
6849 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6850 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6851 level->keep_walkable_ce = TRUE;
6854 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6856 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6859 // check if this level is (not) a Sokoban level
6860 for (y = 0; y < level->fieldy; y++)
6861 for (x = 0; x < level->fieldx; x++)
6862 if (!IS_SB_ELEMENT(Tile[x][y]))
6863 is_sokoban_level = FALSE;
6865 if (is_sokoban_level)
6867 // set special level settings for Sokoban levels
6868 SetLevelSettings_SB(level);
6872 static void LoadLevel_InitSettings(struct LevelInfo *level)
6874 // adjust level settings for (non-native) Sokoban-style levels
6875 LoadLevel_InitSettings_SB(level);
6877 // rename levels with title "nameless level" or if renaming is forced
6878 if (leveldir_current->empty_level_name != NULL &&
6879 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6880 leveldir_current->force_level_name))
6881 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6882 leveldir_current->empty_level_name, level_nr);
6885 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6889 // map elements that have changed in newer versions
6890 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6891 level->game_version);
6892 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6893 for (x = 0; x < 3; x++)
6894 for (y = 0; y < 3; y++)
6895 level->yamyam_content[i].e[x][y] =
6896 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6897 level->game_version);
6901 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6905 // map custom element change events that have changed in newer versions
6906 // (these following values were accidentally changed in version 3.0.1)
6907 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6908 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6910 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6912 int element = EL_CUSTOM_START + i;
6914 // order of checking and copying events to be mapped is important
6915 // (do not change the start and end value -- they are constant)
6916 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6918 if (HAS_CHANGE_EVENT(element, j - 2))
6920 SET_CHANGE_EVENT(element, j - 2, FALSE);
6921 SET_CHANGE_EVENT(element, j, TRUE);
6925 // order of checking and copying events to be mapped is important
6926 // (do not change the start and end value -- they are constant)
6927 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6929 if (HAS_CHANGE_EVENT(element, j - 1))
6931 SET_CHANGE_EVENT(element, j - 1, FALSE);
6932 SET_CHANGE_EVENT(element, j, TRUE);
6938 // initialize "can_change" field for old levels with only one change page
6939 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6941 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6943 int element = EL_CUSTOM_START + i;
6945 if (CAN_CHANGE(element))
6946 element_info[element].change->can_change = TRUE;
6950 // correct custom element values (for old levels without these options)
6951 if (level->game_version < VERSION_IDENT(3,1,1,0))
6953 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6955 int element = EL_CUSTOM_START + i;
6956 struct ElementInfo *ei = &element_info[element];
6958 if (ei->access_direction == MV_NO_DIRECTION)
6959 ei->access_direction = MV_ALL_DIRECTIONS;
6963 // correct custom element values (fix invalid values for all versions)
6966 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6968 int element = EL_CUSTOM_START + i;
6969 struct ElementInfo *ei = &element_info[element];
6971 for (j = 0; j < ei->num_change_pages; j++)
6973 struct ElementChangeInfo *change = &ei->change_page[j];
6975 if (change->trigger_player == CH_PLAYER_NONE)
6976 change->trigger_player = CH_PLAYER_ANY;
6978 if (change->trigger_side == CH_SIDE_NONE)
6979 change->trigger_side = CH_SIDE_ANY;
6984 // initialize "can_explode" field for old levels which did not store this
6985 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6986 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6988 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6990 int element = EL_CUSTOM_START + i;
6992 if (EXPLODES_1X1_OLD(element))
6993 element_info[element].explosion_type = EXPLODES_1X1;
6995 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6996 EXPLODES_SMASHED(element) ||
6997 EXPLODES_IMPACT(element)));
7001 // correct previously hard-coded move delay values for maze runner style
7002 if (level->game_version < VERSION_IDENT(3,1,1,0))
7004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7006 int element = EL_CUSTOM_START + i;
7008 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7010 // previously hard-coded and therefore ignored
7011 element_info[element].move_delay_fixed = 9;
7012 element_info[element].move_delay_random = 0;
7017 // set some other uninitialized values of custom elements in older levels
7018 if (level->game_version < VERSION_IDENT(3,1,0,0))
7020 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7022 int element = EL_CUSTOM_START + i;
7024 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7026 element_info[element].explosion_delay = 17;
7027 element_info[element].ignition_delay = 8;
7031 // set mouse click change events to work for left/middle/right mouse button
7032 if (level->game_version < VERSION_IDENT(4,2,3,0))
7034 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7036 int element = EL_CUSTOM_START + i;
7037 struct ElementInfo *ei = &element_info[element];
7039 for (j = 0; j < ei->num_change_pages; j++)
7041 struct ElementChangeInfo *change = &ei->change_page[j];
7043 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7044 change->has_event[CE_PRESSED_BY_MOUSE] ||
7045 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7046 change->has_event[CE_MOUSE_PRESSED_ON_X])
7047 change->trigger_side = CH_SIDE_ANY;
7053 static void LoadLevel_InitElements(struct LevelInfo *level)
7055 LoadLevel_InitStandardElements(level);
7057 if (level->file_has_custom_elements)
7058 LoadLevel_InitCustomElements(level);
7060 // initialize element properties for level editor etc.
7061 InitElementPropertiesEngine(level->game_version);
7062 InitElementPropertiesGfxElement();
7065 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7069 // map elements that have changed in newer versions
7070 for (y = 0; y < level->fieldy; y++)
7071 for (x = 0; x < level->fieldx; x++)
7072 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7073 level->game_version);
7075 // clear unused playfield data (nicer if level gets resized in editor)
7076 for (x = 0; x < MAX_LEV_FIELDX; x++)
7077 for (y = 0; y < MAX_LEV_FIELDY; y++)
7078 if (x >= level->fieldx || y >= level->fieldy)
7079 level->field[x][y] = EL_EMPTY;
7081 // copy elements to runtime playfield array
7082 for (x = 0; x < MAX_LEV_FIELDX; x++)
7083 for (y = 0; y < MAX_LEV_FIELDY; y++)
7084 Tile[x][y] = level->field[x][y];
7086 // initialize level size variables for faster access
7087 lev_fieldx = level->fieldx;
7088 lev_fieldy = level->fieldy;
7090 // determine border element for this level
7091 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7092 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7097 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7099 struct LevelFileInfo *level_file_info = &level->file_info;
7101 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7102 CopyNativeLevel_RND_to_Native(level);
7105 static void LoadLevelTemplate_LoadAndInit(void)
7107 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7109 LoadLevel_InitVersion(&level_template);
7110 LoadLevel_InitElements(&level_template);
7111 LoadLevel_InitSettings(&level_template);
7113 ActivateLevelTemplate();
7116 void LoadLevelTemplate(int nr)
7118 if (!fileExists(getGlobalLevelTemplateFilename()))
7120 Warn("no level template found for this level");
7125 setLevelFileInfo(&level_template.file_info, nr);
7127 LoadLevelTemplate_LoadAndInit();
7130 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7132 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7134 LoadLevelTemplate_LoadAndInit();
7137 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7139 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7141 if (level.use_custom_template)
7143 if (network_level != NULL)
7144 LoadNetworkLevelTemplate(network_level);
7146 LoadLevelTemplate(-1);
7149 LoadLevel_InitVersion(&level);
7150 LoadLevel_InitElements(&level);
7151 LoadLevel_InitPlayfield(&level);
7152 LoadLevel_InitSettings(&level);
7154 LoadLevel_InitNativeEngines(&level);
7157 void LoadLevel(int nr)
7159 SetLevelSetInfo(leveldir_current->identifier, nr);
7161 setLevelFileInfo(&level.file_info, nr);
7163 LoadLevel_LoadAndInit(NULL);
7166 void LoadLevelInfoOnly(int nr)
7168 setLevelFileInfo(&level.file_info, nr);
7170 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7173 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7175 SetLevelSetInfo(network_level->leveldir_identifier,
7176 network_level->file_info.nr);
7178 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7180 LoadLevel_LoadAndInit(network_level);
7183 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7187 chunk_size += putFileVersion(file, level->file_version);
7188 chunk_size += putFileVersion(file, level->game_version);
7193 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7197 chunk_size += putFile16BitBE(file, level->creation_date.year);
7198 chunk_size += putFile8Bit(file, level->creation_date.month);
7199 chunk_size += putFile8Bit(file, level->creation_date.day);
7204 #if ENABLE_HISTORIC_CHUNKS
7205 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7209 putFile8Bit(file, level->fieldx);
7210 putFile8Bit(file, level->fieldy);
7212 putFile16BitBE(file, level->time);
7213 putFile16BitBE(file, level->gems_needed);
7215 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7216 putFile8Bit(file, level->name[i]);
7218 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7219 putFile8Bit(file, level->score[i]);
7221 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7222 for (y = 0; y < 3; y++)
7223 for (x = 0; x < 3; x++)
7224 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7225 level->yamyam_content[i].e[x][y]));
7226 putFile8Bit(file, level->amoeba_speed);
7227 putFile8Bit(file, level->time_magic_wall);
7228 putFile8Bit(file, level->time_wheel);
7229 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7230 level->amoeba_content));
7231 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7232 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7233 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7234 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7236 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7238 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7239 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7240 putFile32BitBE(file, level->can_move_into_acid_bits);
7241 putFile8Bit(file, level->dont_collide_with_bits);
7243 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7244 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7246 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7247 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7248 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7250 putFile8Bit(file, level->game_engine_type);
7252 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7256 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7261 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7262 chunk_size += putFile8Bit(file, level->name[i]);
7267 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7272 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7273 chunk_size += putFile8Bit(file, level->author[i]);
7278 #if ENABLE_HISTORIC_CHUNKS
7279 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7284 for (y = 0; y < level->fieldy; y++)
7285 for (x = 0; x < level->fieldx; x++)
7286 if (level->encoding_16bit_field)
7287 chunk_size += putFile16BitBE(file, level->field[x][y]);
7289 chunk_size += putFile8Bit(file, level->field[x][y]);
7295 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7300 for (y = 0; y < level->fieldy; y++)
7301 for (x = 0; x < level->fieldx; x++)
7302 chunk_size += putFile16BitBE(file, level->field[x][y]);
7307 #if ENABLE_HISTORIC_CHUNKS
7308 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7312 putFile8Bit(file, EL_YAMYAM);
7313 putFile8Bit(file, level->num_yamyam_contents);
7314 putFile8Bit(file, 0);
7315 putFile8Bit(file, 0);
7317 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7318 for (y = 0; y < 3; y++)
7319 for (x = 0; x < 3; x++)
7320 if (level->encoding_16bit_field)
7321 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7323 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7327 #if ENABLE_HISTORIC_CHUNKS
7328 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7331 int num_contents, content_xsize, content_ysize;
7332 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7334 if (element == EL_YAMYAM)
7336 num_contents = level->num_yamyam_contents;
7340 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7341 for (y = 0; y < 3; y++)
7342 for (x = 0; x < 3; x++)
7343 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7345 else if (element == EL_BD_AMOEBA)
7351 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7352 for (y = 0; y < 3; y++)
7353 for (x = 0; x < 3; x++)
7354 content_array[i][x][y] = EL_EMPTY;
7355 content_array[0][0][0] = level->amoeba_content;
7359 // chunk header already written -- write empty chunk data
7360 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7362 Warn("cannot save content for element '%d'", element);
7367 putFile16BitBE(file, element);
7368 putFile8Bit(file, num_contents);
7369 putFile8Bit(file, content_xsize);
7370 putFile8Bit(file, content_ysize);
7372 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7374 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7375 for (y = 0; y < 3; y++)
7376 for (x = 0; x < 3; x++)
7377 putFile16BitBE(file, content_array[i][x][y]);
7381 #if ENABLE_HISTORIC_CHUNKS
7382 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7384 int envelope_nr = element - EL_ENVELOPE_1;
7385 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7389 chunk_size += putFile16BitBE(file, element);
7390 chunk_size += putFile16BitBE(file, envelope_len);
7391 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7392 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7394 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7395 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7397 for (i = 0; i < envelope_len; i++)
7398 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7404 #if ENABLE_HISTORIC_CHUNKS
7405 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7406 int num_changed_custom_elements)
7410 putFile16BitBE(file, num_changed_custom_elements);
7412 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7414 int element = EL_CUSTOM_START + i;
7416 struct ElementInfo *ei = &element_info[element];
7418 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7420 if (check < num_changed_custom_elements)
7422 putFile16BitBE(file, element);
7423 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7430 if (check != num_changed_custom_elements) // should not happen
7431 Warn("inconsistent number of custom element properties");
7435 #if ENABLE_HISTORIC_CHUNKS
7436 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7437 int num_changed_custom_elements)
7441 putFile16BitBE(file, num_changed_custom_elements);
7443 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7445 int element = EL_CUSTOM_START + i;
7447 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7449 if (check < num_changed_custom_elements)
7451 putFile16BitBE(file, element);
7452 putFile16BitBE(file, element_info[element].change->target_element);
7459 if (check != num_changed_custom_elements) // should not happen
7460 Warn("inconsistent number of custom target elements");
7464 #if ENABLE_HISTORIC_CHUNKS
7465 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7466 int num_changed_custom_elements)
7468 int i, j, x, y, check = 0;
7470 putFile16BitBE(file, num_changed_custom_elements);
7472 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7474 int element = EL_CUSTOM_START + i;
7475 struct ElementInfo *ei = &element_info[element];
7477 if (ei->modified_settings)
7479 if (check < num_changed_custom_elements)
7481 putFile16BitBE(file, element);
7483 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7484 putFile8Bit(file, ei->description[j]);
7486 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7488 // some free bytes for future properties and padding
7489 WriteUnusedBytesToFile(file, 7);
7491 putFile8Bit(file, ei->use_gfx_element);
7492 putFile16BitBE(file, ei->gfx_element_initial);
7494 putFile8Bit(file, ei->collect_score_initial);
7495 putFile8Bit(file, ei->collect_count_initial);
7497 putFile16BitBE(file, ei->push_delay_fixed);
7498 putFile16BitBE(file, ei->push_delay_random);
7499 putFile16BitBE(file, ei->move_delay_fixed);
7500 putFile16BitBE(file, ei->move_delay_random);
7502 putFile16BitBE(file, ei->move_pattern);
7503 putFile8Bit(file, ei->move_direction_initial);
7504 putFile8Bit(file, ei->move_stepsize);
7506 for (y = 0; y < 3; y++)
7507 for (x = 0; x < 3; x++)
7508 putFile16BitBE(file, ei->content.e[x][y]);
7510 putFile32BitBE(file, ei->change->events);
7512 putFile16BitBE(file, ei->change->target_element);
7514 putFile16BitBE(file, ei->change->delay_fixed);
7515 putFile16BitBE(file, ei->change->delay_random);
7516 putFile16BitBE(file, ei->change->delay_frames);
7518 putFile16BitBE(file, ei->change->initial_trigger_element);
7520 putFile8Bit(file, ei->change->explode);
7521 putFile8Bit(file, ei->change->use_target_content);
7522 putFile8Bit(file, ei->change->only_if_complete);
7523 putFile8Bit(file, ei->change->use_random_replace);
7525 putFile8Bit(file, ei->change->random_percentage);
7526 putFile8Bit(file, ei->change->replace_when);
7528 for (y = 0; y < 3; y++)
7529 for (x = 0; x < 3; x++)
7530 putFile16BitBE(file, ei->change->content.e[x][y]);
7532 putFile8Bit(file, ei->slippery_type);
7534 // some free bytes for future properties and padding
7535 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7542 if (check != num_changed_custom_elements) // should not happen
7543 Warn("inconsistent number of custom element properties");
7547 #if ENABLE_HISTORIC_CHUNKS
7548 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7550 struct ElementInfo *ei = &element_info[element];
7553 // ---------- custom element base property values (96 bytes) ----------------
7555 putFile16BitBE(file, element);
7557 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7558 putFile8Bit(file, ei->description[i]);
7560 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7562 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7564 putFile8Bit(file, ei->num_change_pages);
7566 putFile16BitBE(file, ei->ce_value_fixed_initial);
7567 putFile16BitBE(file, ei->ce_value_random_initial);
7568 putFile8Bit(file, ei->use_last_ce_value);
7570 putFile8Bit(file, ei->use_gfx_element);
7571 putFile16BitBE(file, ei->gfx_element_initial);
7573 putFile8Bit(file, ei->collect_score_initial);
7574 putFile8Bit(file, ei->collect_count_initial);
7576 putFile8Bit(file, ei->drop_delay_fixed);
7577 putFile8Bit(file, ei->push_delay_fixed);
7578 putFile8Bit(file, ei->drop_delay_random);
7579 putFile8Bit(file, ei->push_delay_random);
7580 putFile16BitBE(file, ei->move_delay_fixed);
7581 putFile16BitBE(file, ei->move_delay_random);
7583 // bits 0 - 15 of "move_pattern" ...
7584 putFile16BitBE(file, ei->move_pattern & 0xffff);
7585 putFile8Bit(file, ei->move_direction_initial);
7586 putFile8Bit(file, ei->move_stepsize);
7588 putFile8Bit(file, ei->slippery_type);
7590 for (y = 0; y < 3; y++)
7591 for (x = 0; x < 3; x++)
7592 putFile16BitBE(file, ei->content.e[x][y]);
7594 putFile16BitBE(file, ei->move_enter_element);
7595 putFile16BitBE(file, ei->move_leave_element);
7596 putFile8Bit(file, ei->move_leave_type);
7598 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7599 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7601 putFile8Bit(file, ei->access_direction);
7603 putFile8Bit(file, ei->explosion_delay);
7604 putFile8Bit(file, ei->ignition_delay);
7605 putFile8Bit(file, ei->explosion_type);
7607 // some free bytes for future custom property values and padding
7608 WriteUnusedBytesToFile(file, 1);
7610 // ---------- change page property values (48 bytes) ------------------------
7612 for (i = 0; i < ei->num_change_pages; i++)
7614 struct ElementChangeInfo *change = &ei->change_page[i];
7615 unsigned int event_bits;
7617 // bits 0 - 31 of "has_event[]" ...
7619 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7620 if (change->has_event[j])
7621 event_bits |= (1u << j);
7622 putFile32BitBE(file, event_bits);
7624 putFile16BitBE(file, change->target_element);
7626 putFile16BitBE(file, change->delay_fixed);
7627 putFile16BitBE(file, change->delay_random);
7628 putFile16BitBE(file, change->delay_frames);
7630 putFile16BitBE(file, change->initial_trigger_element);
7632 putFile8Bit(file, change->explode);
7633 putFile8Bit(file, change->use_target_content);
7634 putFile8Bit(file, change->only_if_complete);
7635 putFile8Bit(file, change->use_random_replace);
7637 putFile8Bit(file, change->random_percentage);
7638 putFile8Bit(file, change->replace_when);
7640 for (y = 0; y < 3; y++)
7641 for (x = 0; x < 3; x++)
7642 putFile16BitBE(file, change->target_content.e[x][y]);
7644 putFile8Bit(file, change->can_change);
7646 putFile8Bit(file, change->trigger_side);
7648 putFile8Bit(file, change->trigger_player);
7649 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7650 log_2(change->trigger_page)));
7652 putFile8Bit(file, change->has_action);
7653 putFile8Bit(file, change->action_type);
7654 putFile8Bit(file, change->action_mode);
7655 putFile16BitBE(file, change->action_arg);
7657 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7659 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7660 if (change->has_event[j])
7661 event_bits |= (1u << (j - 32));
7662 putFile8Bit(file, event_bits);
7667 #if ENABLE_HISTORIC_CHUNKS
7668 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7670 struct ElementInfo *ei = &element_info[element];
7671 struct ElementGroupInfo *group = ei->group;
7674 putFile16BitBE(file, element);
7676 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7677 putFile8Bit(file, ei->description[i]);
7679 putFile8Bit(file, group->num_elements);
7681 putFile8Bit(file, ei->use_gfx_element);
7682 putFile16BitBE(file, ei->gfx_element_initial);
7684 putFile8Bit(file, group->choice_mode);
7686 // some free bytes for future values and padding
7687 WriteUnusedBytesToFile(file, 3);
7689 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7690 putFile16BitBE(file, group->element[i]);
7694 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7695 boolean write_element)
7697 int save_type = entry->save_type;
7698 int data_type = entry->data_type;
7699 int conf_type = entry->conf_type;
7700 int byte_mask = conf_type & CONF_MASK_BYTES;
7701 int element = entry->element;
7702 int default_value = entry->default_value;
7704 boolean modified = FALSE;
7706 if (byte_mask != CONF_MASK_MULTI_BYTES)
7708 void *value_ptr = entry->value;
7709 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7712 // check if any settings have been modified before saving them
7713 if (value != default_value)
7716 // do not save if explicitly told or if unmodified default settings
7717 if ((save_type == SAVE_CONF_NEVER) ||
7718 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7722 num_bytes += putFile16BitBE(file, element);
7724 num_bytes += putFile8Bit(file, conf_type);
7725 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7726 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7727 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7730 else if (data_type == TYPE_STRING)
7732 char *default_string = entry->default_string;
7733 char *string = (char *)(entry->value);
7734 int string_length = strlen(string);
7737 // check if any settings have been modified before saving them
7738 if (!strEqual(string, default_string))
7741 // do not save if explicitly told or if unmodified default settings
7742 if ((save_type == SAVE_CONF_NEVER) ||
7743 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7747 num_bytes += putFile16BitBE(file, element);
7749 num_bytes += putFile8Bit(file, conf_type);
7750 num_bytes += putFile16BitBE(file, string_length);
7752 for (i = 0; i < string_length; i++)
7753 num_bytes += putFile8Bit(file, string[i]);
7755 else if (data_type == TYPE_ELEMENT_LIST)
7757 int *element_array = (int *)(entry->value);
7758 int num_elements = *(int *)(entry->num_entities);
7761 // check if any settings have been modified before saving them
7762 for (i = 0; i < num_elements; i++)
7763 if (element_array[i] != default_value)
7766 // do not save if explicitly told or if unmodified default settings
7767 if ((save_type == SAVE_CONF_NEVER) ||
7768 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7772 num_bytes += putFile16BitBE(file, element);
7774 num_bytes += putFile8Bit(file, conf_type);
7775 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7777 for (i = 0; i < num_elements; i++)
7778 num_bytes += putFile16BitBE(file, element_array[i]);
7780 else if (data_type == TYPE_CONTENT_LIST)
7782 struct Content *content = (struct Content *)(entry->value);
7783 int num_contents = *(int *)(entry->num_entities);
7786 // check if any settings have been modified before saving them
7787 for (i = 0; i < num_contents; i++)
7788 for (y = 0; y < 3; y++)
7789 for (x = 0; x < 3; x++)
7790 if (content[i].e[x][y] != default_value)
7793 // do not save if explicitly told or if unmodified default settings
7794 if ((save_type == SAVE_CONF_NEVER) ||
7795 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7799 num_bytes += putFile16BitBE(file, element);
7801 num_bytes += putFile8Bit(file, conf_type);
7802 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7804 for (i = 0; i < num_contents; i++)
7805 for (y = 0; y < 3; y++)
7806 for (x = 0; x < 3; x++)
7807 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7813 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7818 li = *level; // copy level data into temporary buffer
7820 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7821 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7826 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7831 li = *level; // copy level data into temporary buffer
7833 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7834 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7839 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7841 int envelope_nr = element - EL_ENVELOPE_1;
7845 chunk_size += putFile16BitBE(file, element);
7847 // copy envelope data into temporary buffer
7848 xx_envelope = level->envelope[envelope_nr];
7850 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7851 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7856 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7858 struct ElementInfo *ei = &element_info[element];
7862 chunk_size += putFile16BitBE(file, element);
7864 xx_ei = *ei; // copy element data into temporary buffer
7866 // set default description string for this specific element
7867 strcpy(xx_default_description, getDefaultElementDescription(ei));
7869 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7870 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7872 for (i = 0; i < ei->num_change_pages; i++)
7874 struct ElementChangeInfo *change = &ei->change_page[i];
7876 xx_current_change_page = i;
7878 xx_change = *change; // copy change data into temporary buffer
7881 setEventBitsFromEventFlags(change);
7883 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7884 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7891 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7893 struct ElementInfo *ei = &element_info[element];
7894 struct ElementGroupInfo *group = ei->group;
7898 chunk_size += putFile16BitBE(file, element);
7900 xx_ei = *ei; // copy element data into temporary buffer
7901 xx_group = *group; // copy group data into temporary buffer
7903 // set default description string for this specific element
7904 strcpy(xx_default_description, getDefaultElementDescription(ei));
7906 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7907 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7912 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7914 struct ElementInfo *ei = &element_info[element];
7918 chunk_size += putFile16BitBE(file, element);
7920 xx_ei = *ei; // copy element data into temporary buffer
7922 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7923 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7928 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7929 boolean save_as_template)
7935 if (!(file = fopen(filename, MODE_WRITE)))
7937 Warn("cannot save level file '%s'", filename);
7942 level->file_version = FILE_VERSION_ACTUAL;
7943 level->game_version = GAME_VERSION_ACTUAL;
7945 level->creation_date = getCurrentDate();
7947 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7948 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7950 chunk_size = SaveLevel_VERS(NULL, level);
7951 putFileChunkBE(file, "VERS", chunk_size);
7952 SaveLevel_VERS(file, level);
7954 chunk_size = SaveLevel_DATE(NULL, level);
7955 putFileChunkBE(file, "DATE", chunk_size);
7956 SaveLevel_DATE(file, level);
7958 chunk_size = SaveLevel_NAME(NULL, level);
7959 putFileChunkBE(file, "NAME", chunk_size);
7960 SaveLevel_NAME(file, level);
7962 chunk_size = SaveLevel_AUTH(NULL, level);
7963 putFileChunkBE(file, "AUTH", chunk_size);
7964 SaveLevel_AUTH(file, level);
7966 chunk_size = SaveLevel_INFO(NULL, level);
7967 putFileChunkBE(file, "INFO", chunk_size);
7968 SaveLevel_INFO(file, level);
7970 chunk_size = SaveLevel_BODY(NULL, level);
7971 putFileChunkBE(file, "BODY", chunk_size);
7972 SaveLevel_BODY(file, level);
7974 chunk_size = SaveLevel_ELEM(NULL, level);
7975 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7977 putFileChunkBE(file, "ELEM", chunk_size);
7978 SaveLevel_ELEM(file, level);
7981 for (i = 0; i < NUM_ENVELOPES; i++)
7983 int element = EL_ENVELOPE_1 + i;
7985 chunk_size = SaveLevel_NOTE(NULL, level, element);
7986 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7988 putFileChunkBE(file, "NOTE", chunk_size);
7989 SaveLevel_NOTE(file, level, element);
7993 // if not using template level, check for non-default custom/group elements
7994 if (!level->use_custom_template || save_as_template)
7996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7998 int element = EL_CUSTOM_START + i;
8000 chunk_size = SaveLevel_CUSX(NULL, level, element);
8001 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8003 putFileChunkBE(file, "CUSX", chunk_size);
8004 SaveLevel_CUSX(file, level, element);
8008 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8010 int element = EL_GROUP_START + i;
8012 chunk_size = SaveLevel_GRPX(NULL, level, element);
8013 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8015 putFileChunkBE(file, "GRPX", chunk_size);
8016 SaveLevel_GRPX(file, level, element);
8020 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8022 int element = GET_EMPTY_ELEMENT(i);
8024 chunk_size = SaveLevel_EMPX(NULL, level, element);
8025 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8027 putFileChunkBE(file, "EMPX", chunk_size);
8028 SaveLevel_EMPX(file, level, element);
8035 SetFilePermissions(filename, PERMS_PRIVATE);
8038 void SaveLevel(int nr)
8040 char *filename = getDefaultLevelFilename(nr);
8042 SaveLevelFromFilename(&level, filename, FALSE);
8045 void SaveLevelTemplate(void)
8047 char *filename = getLocalLevelTemplateFilename();
8049 SaveLevelFromFilename(&level, filename, TRUE);
8052 boolean SaveLevelChecked(int nr)
8054 char *filename = getDefaultLevelFilename(nr);
8055 boolean new_level = !fileExists(filename);
8056 boolean level_saved = FALSE;
8058 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8063 Request("Level saved!", REQ_CONFIRM);
8071 void DumpLevel(struct LevelInfo *level)
8073 if (level->no_level_file || level->no_valid_file)
8075 Warn("cannot dump -- no valid level file found");
8081 Print("Level xxx (file version %08d, game version %08d)\n",
8082 level->file_version, level->game_version);
8085 Print("Level author: '%s'\n", level->author);
8086 Print("Level title: '%s'\n", level->name);
8088 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8090 Print("Level time: %d seconds\n", level->time);
8091 Print("Gems needed: %d\n", level->gems_needed);
8093 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8094 Print("Time for wheel: %d seconds\n", level->time_wheel);
8095 Print("Time for light: %d seconds\n", level->time_light);
8096 Print("Time for timegate: %d seconds\n", level->time_timegate);
8098 Print("Amoeba speed: %d\n", level->amoeba_speed);
8101 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8102 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8103 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8104 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8105 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8106 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8112 for (i = 0; i < NUM_ENVELOPES; i++)
8114 char *text = level->envelope[i].text;
8115 int text_len = strlen(text);
8116 boolean has_text = FALSE;
8118 for (j = 0; j < text_len; j++)
8119 if (text[j] != ' ' && text[j] != '\n')
8125 Print("Envelope %d:\n'%s'\n", i + 1, text);
8133 void DumpLevels(void)
8135 static LevelDirTree *dumplevel_leveldir = NULL;
8137 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8138 global.dumplevel_leveldir);
8140 if (dumplevel_leveldir == NULL)
8141 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8143 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8144 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8145 Fail("no such level number: %d", global.dumplevel_level_nr);
8147 leveldir_current = dumplevel_leveldir;
8149 LoadLevel(global.dumplevel_level_nr);
8156 // ============================================================================
8157 // tape file functions
8158 // ============================================================================
8160 static void setTapeInfoToDefaults(void)
8164 // always start with reliable default values (empty tape)
8167 // default values (also for pre-1.2 tapes) with only the first player
8168 tape.player_participates[0] = TRUE;
8169 for (i = 1; i < MAX_PLAYERS; i++)
8170 tape.player_participates[i] = FALSE;
8172 // at least one (default: the first) player participates in every tape
8173 tape.num_participating_players = 1;
8175 tape.property_bits = TAPE_PROPERTY_NONE;
8177 tape.level_nr = level_nr;
8179 tape.changed = FALSE;
8180 tape.solved = FALSE;
8182 tape.recording = FALSE;
8183 tape.playing = FALSE;
8184 tape.pausing = FALSE;
8186 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8187 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8189 tape.no_info_chunk = TRUE;
8190 tape.no_valid_file = FALSE;
8193 static int getTapePosSize(struct TapeInfo *tape)
8195 int tape_pos_size = 0;
8197 if (tape->use_key_actions)
8198 tape_pos_size += tape->num_participating_players;
8200 if (tape->use_mouse_actions)
8201 tape_pos_size += 3; // x and y position and mouse button mask
8203 tape_pos_size += 1; // tape action delay value
8205 return tape_pos_size;
8208 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8210 tape->use_key_actions = FALSE;
8211 tape->use_mouse_actions = FALSE;
8213 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8214 tape->use_key_actions = TRUE;
8216 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8217 tape->use_mouse_actions = TRUE;
8220 static int getTapeActionValue(struct TapeInfo *tape)
8222 return (tape->use_key_actions &&
8223 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8224 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8225 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8226 TAPE_ACTIONS_DEFAULT);
8229 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8231 tape->file_version = getFileVersion(file);
8232 tape->game_version = getFileVersion(file);
8237 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8241 tape->random_seed = getFile32BitBE(file);
8242 tape->date = getFile32BitBE(file);
8243 tape->length = getFile32BitBE(file);
8245 // read header fields that are new since version 1.2
8246 if (tape->file_version >= FILE_VERSION_1_2)
8248 byte store_participating_players = getFile8Bit(file);
8251 // since version 1.2, tapes store which players participate in the tape
8252 tape->num_participating_players = 0;
8253 for (i = 0; i < MAX_PLAYERS; i++)
8255 tape->player_participates[i] = FALSE;
8257 if (store_participating_players & (1 << i))
8259 tape->player_participates[i] = TRUE;
8260 tape->num_participating_players++;
8264 setTapeActionFlags(tape, getFile8Bit(file));
8266 tape->property_bits = getFile8Bit(file);
8267 tape->solved = getFile8Bit(file);
8269 engine_version = getFileVersion(file);
8270 if (engine_version > 0)
8271 tape->engine_version = engine_version;
8273 tape->engine_version = tape->game_version;
8279 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8281 tape->scr_fieldx = getFile8Bit(file);
8282 tape->scr_fieldy = getFile8Bit(file);
8287 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8289 char *level_identifier = NULL;
8290 int level_identifier_size;
8293 tape->no_info_chunk = FALSE;
8295 level_identifier_size = getFile16BitBE(file);
8297 level_identifier = checked_malloc(level_identifier_size);
8299 for (i = 0; i < level_identifier_size; i++)
8300 level_identifier[i] = getFile8Bit(file);
8302 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8303 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8305 checked_free(level_identifier);
8307 tape->level_nr = getFile16BitBE(file);
8309 chunk_size = 2 + level_identifier_size + 2;
8314 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8317 int tape_pos_size = getTapePosSize(tape);
8318 int chunk_size_expected = tape_pos_size * tape->length;
8320 if (chunk_size_expected != chunk_size)
8322 ReadUnusedBytesFromFile(file, chunk_size);
8323 return chunk_size_expected;
8326 for (i = 0; i < tape->length; i++)
8328 if (i >= MAX_TAPE_LEN)
8330 Warn("tape truncated -- size exceeds maximum tape size %d",
8333 // tape too large; read and ignore remaining tape data from this chunk
8334 for (;i < tape->length; i++)
8335 ReadUnusedBytesFromFile(file, tape_pos_size);
8340 if (tape->use_key_actions)
8342 for (j = 0; j < MAX_PLAYERS; j++)
8344 tape->pos[i].action[j] = MV_NONE;
8346 if (tape->player_participates[j])
8347 tape->pos[i].action[j] = getFile8Bit(file);
8351 if (tape->use_mouse_actions)
8353 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8354 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8355 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8358 tape->pos[i].delay = getFile8Bit(file);
8360 if (tape->file_version == FILE_VERSION_1_0)
8362 // eliminate possible diagonal moves in old tapes
8363 // this is only for backward compatibility
8365 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8366 byte action = tape->pos[i].action[0];
8367 int k, num_moves = 0;
8369 for (k = 0; k < 4; k++)
8371 if (action & joy_dir[k])
8373 tape->pos[i + num_moves].action[0] = joy_dir[k];
8375 tape->pos[i + num_moves].delay = 0;
8384 tape->length += num_moves;
8387 else if (tape->file_version < FILE_VERSION_2_0)
8389 // convert pre-2.0 tapes to new tape format
8391 if (tape->pos[i].delay > 1)
8394 tape->pos[i + 1] = tape->pos[i];
8395 tape->pos[i + 1].delay = 1;
8398 for (j = 0; j < MAX_PLAYERS; j++)
8399 tape->pos[i].action[j] = MV_NONE;
8400 tape->pos[i].delay--;
8407 if (checkEndOfFile(file))
8411 if (i != tape->length)
8412 chunk_size = tape_pos_size * i;
8417 static void LoadTape_SokobanSolution(char *filename)
8420 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8422 if (!(file = openFile(filename, MODE_READ)))
8424 tape.no_valid_file = TRUE;
8429 while (!checkEndOfFile(file))
8431 unsigned char c = getByteFromFile(file);
8433 if (checkEndOfFile(file))
8440 tape.pos[tape.length].action[0] = MV_UP;
8441 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8447 tape.pos[tape.length].action[0] = MV_DOWN;
8448 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8454 tape.pos[tape.length].action[0] = MV_LEFT;
8455 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8461 tape.pos[tape.length].action[0] = MV_RIGHT;
8462 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8470 // ignore white-space characters
8474 tape.no_valid_file = TRUE;
8476 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8484 if (tape.no_valid_file)
8487 tape.length_frames = GetTapeLengthFrames();
8488 tape.length_seconds = GetTapeLengthSeconds();
8491 void LoadTapeFromFilename(char *filename)
8493 char cookie[MAX_LINE_LEN];
8494 char chunk_name[CHUNK_ID_LEN + 1];
8498 // always start with reliable default values
8499 setTapeInfoToDefaults();
8501 if (strSuffix(filename, ".sln"))
8503 LoadTape_SokobanSolution(filename);
8508 if (!(file = openFile(filename, MODE_READ)))
8510 tape.no_valid_file = TRUE;
8515 getFileChunkBE(file, chunk_name, NULL);
8516 if (strEqual(chunk_name, "RND1"))
8518 getFile32BitBE(file); // not used
8520 getFileChunkBE(file, chunk_name, NULL);
8521 if (!strEqual(chunk_name, "TAPE"))
8523 tape.no_valid_file = TRUE;
8525 Warn("unknown format of tape file '%s'", filename);
8532 else // check for pre-2.0 file format with cookie string
8534 strcpy(cookie, chunk_name);
8535 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8537 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8538 cookie[strlen(cookie) - 1] = '\0';
8540 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8542 tape.no_valid_file = TRUE;
8544 Warn("unknown format of tape file '%s'", filename);
8551 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8553 tape.no_valid_file = TRUE;
8555 Warn("unsupported version of tape file '%s'", filename);
8562 // pre-2.0 tape files have no game version, so use file version here
8563 tape.game_version = tape.file_version;
8566 if (tape.file_version < FILE_VERSION_1_2)
8568 // tape files from versions before 1.2.0 without chunk structure
8569 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8570 LoadTape_BODY(file, 2 * tape.length, &tape);
8578 int (*loader)(File *, int, struct TapeInfo *);
8582 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8583 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8584 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8585 { "INFO", -1, LoadTape_INFO },
8586 { "BODY", -1, LoadTape_BODY },
8590 while (getFileChunkBE(file, chunk_name, &chunk_size))
8594 while (chunk_info[i].name != NULL &&
8595 !strEqual(chunk_name, chunk_info[i].name))
8598 if (chunk_info[i].name == NULL)
8600 Warn("unknown chunk '%s' in tape file '%s'",
8601 chunk_name, filename);
8603 ReadUnusedBytesFromFile(file, chunk_size);
8605 else if (chunk_info[i].size != -1 &&
8606 chunk_info[i].size != chunk_size)
8608 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8609 chunk_size, chunk_name, filename);
8611 ReadUnusedBytesFromFile(file, chunk_size);
8615 // call function to load this tape chunk
8616 int chunk_size_expected =
8617 (chunk_info[i].loader)(file, chunk_size, &tape);
8619 // the size of some chunks cannot be checked before reading other
8620 // chunks first (like "HEAD" and "BODY") that contain some header
8621 // information, so check them here
8622 if (chunk_size_expected != chunk_size)
8624 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8625 chunk_size, chunk_name, filename);
8633 tape.length_frames = GetTapeLengthFrames();
8634 tape.length_seconds = GetTapeLengthSeconds();
8637 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8639 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8641 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8642 tape.engine_version);
8646 void LoadTape(int nr)
8648 char *filename = getTapeFilename(nr);
8650 LoadTapeFromFilename(filename);
8653 void LoadSolutionTape(int nr)
8655 char *filename = getSolutionTapeFilename(nr);
8657 LoadTapeFromFilename(filename);
8659 if (TAPE_IS_EMPTY(tape))
8661 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8662 level.native_bd_level->replay != NULL)
8663 CopyNativeTape_BD_to_RND(&level);
8664 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8665 level.native_sp_level->demo.is_available)
8666 CopyNativeTape_SP_to_RND(&level);
8670 void LoadScoreTape(char *score_tape_basename, int nr)
8672 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8674 LoadTapeFromFilename(filename);
8677 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8679 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8681 LoadTapeFromFilename(filename);
8684 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8686 // chunk required for team mode tapes with non-default screen size
8687 return (tape->num_participating_players > 1 &&
8688 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8689 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8692 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8694 putFileVersion(file, tape->file_version);
8695 putFileVersion(file, tape->game_version);
8698 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8701 byte store_participating_players = 0;
8703 // set bits for participating players for compact storage
8704 for (i = 0; i < MAX_PLAYERS; i++)
8705 if (tape->player_participates[i])
8706 store_participating_players |= (1 << i);
8708 putFile32BitBE(file, tape->random_seed);
8709 putFile32BitBE(file, tape->date);
8710 putFile32BitBE(file, tape->length);
8712 putFile8Bit(file, store_participating_players);
8714 putFile8Bit(file, getTapeActionValue(tape));
8716 putFile8Bit(file, tape->property_bits);
8717 putFile8Bit(file, tape->solved);
8719 putFileVersion(file, tape->engine_version);
8722 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8724 putFile8Bit(file, tape->scr_fieldx);
8725 putFile8Bit(file, tape->scr_fieldy);
8728 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8730 int level_identifier_size = strlen(tape->level_identifier) + 1;
8733 putFile16BitBE(file, level_identifier_size);
8735 for (i = 0; i < level_identifier_size; i++)
8736 putFile8Bit(file, tape->level_identifier[i]);
8738 putFile16BitBE(file, tape->level_nr);
8741 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8745 for (i = 0; i < tape->length; i++)
8747 if (tape->use_key_actions)
8749 for (j = 0; j < MAX_PLAYERS; j++)
8750 if (tape->player_participates[j])
8751 putFile8Bit(file, tape->pos[i].action[j]);
8754 if (tape->use_mouse_actions)
8756 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8757 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8758 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8761 putFile8Bit(file, tape->pos[i].delay);
8765 void SaveTapeToFilename(char *filename)
8769 int info_chunk_size;
8770 int body_chunk_size;
8772 if (!(file = fopen(filename, MODE_WRITE)))
8774 Warn("cannot save level recording file '%s'", filename);
8779 tape_pos_size = getTapePosSize(&tape);
8781 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8782 body_chunk_size = tape_pos_size * tape.length;
8784 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8785 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8787 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8788 SaveTape_VERS(file, &tape);
8790 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8791 SaveTape_HEAD(file, &tape);
8793 if (checkSaveTape_SCRN(&tape))
8795 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8796 SaveTape_SCRN(file, &tape);
8799 putFileChunkBE(file, "INFO", info_chunk_size);
8800 SaveTape_INFO(file, &tape);
8802 putFileChunkBE(file, "BODY", body_chunk_size);
8803 SaveTape_BODY(file, &tape);
8807 SetFilePermissions(filename, PERMS_PRIVATE);
8810 static void SaveTapeExt(char *filename)
8814 tape.file_version = FILE_VERSION_ACTUAL;
8815 tape.game_version = GAME_VERSION_ACTUAL;
8817 tape.num_participating_players = 0;
8819 // count number of participating players
8820 for (i = 0; i < MAX_PLAYERS; i++)
8821 if (tape.player_participates[i])
8822 tape.num_participating_players++;
8824 SaveTapeToFilename(filename);
8826 tape.changed = FALSE;
8829 void SaveTape(int nr)
8831 char *filename = getTapeFilename(nr);
8833 InitTapeDirectory(leveldir_current->subdir);
8835 SaveTapeExt(filename);
8838 void SaveScoreTape(int nr)
8840 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8842 // used instead of "leveldir_current->subdir" (for network games)
8843 InitScoreTapeDirectory(levelset.identifier, nr);
8845 SaveTapeExt(filename);
8848 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8849 unsigned int req_state_added)
8851 char *filename = getTapeFilename(nr);
8852 boolean new_tape = !fileExists(filename);
8853 boolean tape_saved = FALSE;
8855 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8860 Request(msg_saved, REQ_CONFIRM | req_state_added);
8868 boolean SaveTapeChecked(int nr)
8870 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8873 boolean SaveTapeChecked_LevelSolved(int nr)
8875 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8876 "Level solved! Tape saved!", REQ_STAY_OPEN);
8879 void DumpTape(struct TapeInfo *tape)
8881 int tape_frame_counter;
8884 if (tape->no_valid_file)
8886 Warn("cannot dump -- no valid tape file found");
8893 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8894 tape->level_nr, tape->file_version, tape->game_version);
8895 Print(" (effective engine version %08d)\n",
8896 tape->engine_version);
8897 Print("Level series identifier: '%s'\n", tape->level_identifier);
8899 Print("Solution tape: %s\n",
8900 tape->solved ? "yes" :
8901 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8903 Print("Special tape properties: ");
8904 if (tape->property_bits == TAPE_PROPERTY_NONE)
8906 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8907 Print("[em_random_bug]");
8908 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8909 Print("[game_speed]");
8910 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8912 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8913 Print("[single_step]");
8914 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8915 Print("[snapshot]");
8916 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8917 Print("[replayed]");
8918 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8919 Print("[tas_keys]");
8920 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8921 Print("[small_graphics]");
8924 int year2 = tape->date / 10000;
8925 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8926 int month_index_raw = (tape->date / 100) % 100;
8927 int month_index = month_index_raw % 12; // prevent invalid index
8928 int month = month_index + 1;
8929 int day = tape->date % 100;
8931 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8935 tape_frame_counter = 0;
8937 for (i = 0; i < tape->length; i++)
8939 if (i >= MAX_TAPE_LEN)
8944 for (j = 0; j < MAX_PLAYERS; j++)
8946 if (tape->player_participates[j])
8948 int action = tape->pos[i].action[j];
8950 Print("%d:%02x ", j, action);
8951 Print("[%c%c%c%c|%c%c] - ",
8952 (action & JOY_LEFT ? '<' : ' '),
8953 (action & JOY_RIGHT ? '>' : ' '),
8954 (action & JOY_UP ? '^' : ' '),
8955 (action & JOY_DOWN ? 'v' : ' '),
8956 (action & JOY_BUTTON_1 ? '1' : ' '),
8957 (action & JOY_BUTTON_2 ? '2' : ' '));
8961 Print("(%03d) ", tape->pos[i].delay);
8962 Print("[%05d]\n", tape_frame_counter);
8964 tape_frame_counter += tape->pos[i].delay;
8970 void DumpTapes(void)
8972 static LevelDirTree *dumptape_leveldir = NULL;
8974 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8975 global.dumptape_leveldir);
8977 if (dumptape_leveldir == NULL)
8978 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8980 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8981 global.dumptape_level_nr > dumptape_leveldir->last_level)
8982 Fail("no such level number: %d", global.dumptape_level_nr);
8984 leveldir_current = dumptape_leveldir;
8986 if (options.mytapes)
8987 LoadTape(global.dumptape_level_nr);
8989 LoadSolutionTape(global.dumptape_level_nr);
8997 // ============================================================================
8998 // score file functions
8999 // ============================================================================
9001 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9005 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9007 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9008 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9009 scores->entry[i].score = 0;
9010 scores->entry[i].time = 0;
9012 scores->entry[i].id = -1;
9013 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9014 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9015 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9016 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9017 strcpy(scores->entry[i].country_code, "??");
9020 scores->num_entries = 0;
9021 scores->last_added = -1;
9022 scores->last_added_local = -1;
9024 scores->updated = FALSE;
9025 scores->uploaded = FALSE;
9026 scores->tape_downloaded = FALSE;
9027 scores->force_last_added = FALSE;
9029 // The following values are intentionally not reset here:
9033 // - continue_playing
9034 // - continue_on_return
9037 static void setScoreInfoToDefaults(void)
9039 setScoreInfoToDefaultsExt(&scores);
9042 static void setServerScoreInfoToDefaults(void)
9044 setScoreInfoToDefaultsExt(&server_scores);
9047 static void LoadScore_OLD(int nr)
9050 char *filename = getScoreFilename(nr);
9051 char cookie[MAX_LINE_LEN];
9052 char line[MAX_LINE_LEN];
9056 if (!(file = fopen(filename, MODE_READ)))
9059 // check file identifier
9060 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9062 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9063 cookie[strlen(cookie) - 1] = '\0';
9065 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9067 Warn("unknown format of score file '%s'", filename);
9074 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9076 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9077 Warn("fscanf() failed; %s", strerror(errno));
9079 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9082 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9083 line[strlen(line) - 1] = '\0';
9085 for (line_ptr = line; *line_ptr; line_ptr++)
9087 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9089 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9090 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9099 static void ConvertScore_OLD(void)
9101 // only convert score to time for levels that rate playing time over score
9102 if (!level.rate_time_over_score)
9105 // convert old score to playing time for score-less levels (like Supaplex)
9106 int time_final_max = 999;
9109 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9111 int score = scores.entry[i].score;
9113 if (score > 0 && score < time_final_max)
9114 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9118 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9120 scores->file_version = getFileVersion(file);
9121 scores->game_version = getFileVersion(file);
9126 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9128 char *level_identifier = NULL;
9129 int level_identifier_size;
9132 level_identifier_size = getFile16BitBE(file);
9134 level_identifier = checked_malloc(level_identifier_size);
9136 for (i = 0; i < level_identifier_size; i++)
9137 level_identifier[i] = getFile8Bit(file);
9139 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9140 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9142 checked_free(level_identifier);
9144 scores->level_nr = getFile16BitBE(file);
9145 scores->num_entries = getFile16BitBE(file);
9147 chunk_size = 2 + level_identifier_size + 2 + 2;
9152 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9156 for (i = 0; i < scores->num_entries; i++)
9158 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9159 scores->entry[i].name[j] = getFile8Bit(file);
9161 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9164 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9169 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9173 for (i = 0; i < scores->num_entries; i++)
9174 scores->entry[i].score = getFile16BitBE(file);
9176 chunk_size = scores->num_entries * 2;
9181 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9185 for (i = 0; i < scores->num_entries; i++)
9186 scores->entry[i].score = getFile32BitBE(file);
9188 chunk_size = scores->num_entries * 4;
9193 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9197 for (i = 0; i < scores->num_entries; i++)
9198 scores->entry[i].time = getFile32BitBE(file);
9200 chunk_size = scores->num_entries * 4;
9205 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9209 for (i = 0; i < scores->num_entries; i++)
9211 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9212 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9214 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9217 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9222 void LoadScore(int nr)
9224 char *filename = getScoreFilename(nr);
9225 char cookie[MAX_LINE_LEN];
9226 char chunk_name[CHUNK_ID_LEN + 1];
9228 boolean old_score_file_format = FALSE;
9231 // always start with reliable default values
9232 setScoreInfoToDefaults();
9234 if (!(file = openFile(filename, MODE_READ)))
9237 getFileChunkBE(file, chunk_name, NULL);
9238 if (strEqual(chunk_name, "RND1"))
9240 getFile32BitBE(file); // not used
9242 getFileChunkBE(file, chunk_name, NULL);
9243 if (!strEqual(chunk_name, "SCOR"))
9245 Warn("unknown format of score file '%s'", filename);
9252 else // check for old file format with cookie string
9254 strcpy(cookie, chunk_name);
9255 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9257 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9258 cookie[strlen(cookie) - 1] = '\0';
9260 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9262 Warn("unknown format of score file '%s'", filename);
9269 old_score_file_format = TRUE;
9272 if (old_score_file_format)
9274 // score files from versions before 4.2.4.0 without chunk structure
9277 // convert score to time, if possible (mainly for Supaplex levels)
9286 int (*loader)(File *, int, struct ScoreInfo *);
9290 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9291 { "INFO", -1, LoadScore_INFO },
9292 { "NAME", -1, LoadScore_NAME },
9293 { "SCOR", -1, LoadScore_SCOR },
9294 { "SC4R", -1, LoadScore_SC4R },
9295 { "TIME", -1, LoadScore_TIME },
9296 { "TAPE", -1, LoadScore_TAPE },
9301 while (getFileChunkBE(file, chunk_name, &chunk_size))
9305 while (chunk_info[i].name != NULL &&
9306 !strEqual(chunk_name, chunk_info[i].name))
9309 if (chunk_info[i].name == NULL)
9311 Warn("unknown chunk '%s' in score file '%s'",
9312 chunk_name, filename);
9314 ReadUnusedBytesFromFile(file, chunk_size);
9316 else if (chunk_info[i].size != -1 &&
9317 chunk_info[i].size != chunk_size)
9319 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9320 chunk_size, chunk_name, filename);
9322 ReadUnusedBytesFromFile(file, chunk_size);
9326 // call function to load this score chunk
9327 int chunk_size_expected =
9328 (chunk_info[i].loader)(file, chunk_size, &scores);
9330 // the size of some chunks cannot be checked before reading other
9331 // chunks first (like "HEAD" and "BODY") that contain some header
9332 // information, so check them here
9333 if (chunk_size_expected != chunk_size)
9335 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9336 chunk_size, chunk_name, filename);
9345 #if ENABLE_HISTORIC_CHUNKS
9346 void SaveScore_OLD(int nr)
9349 char *filename = getScoreFilename(nr);
9352 // used instead of "leveldir_current->subdir" (for network games)
9353 InitScoreDirectory(levelset.identifier);
9355 if (!(file = fopen(filename, MODE_WRITE)))
9357 Warn("cannot save score for level %d", nr);
9362 fprintf(file, "%s\n\n", SCORE_COOKIE);
9364 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9365 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9369 SetFilePermissions(filename, PERMS_PRIVATE);
9373 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9375 putFileVersion(file, scores->file_version);
9376 putFileVersion(file, scores->game_version);
9379 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9381 int level_identifier_size = strlen(scores->level_identifier) + 1;
9384 putFile16BitBE(file, level_identifier_size);
9386 for (i = 0; i < level_identifier_size; i++)
9387 putFile8Bit(file, scores->level_identifier[i]);
9389 putFile16BitBE(file, scores->level_nr);
9390 putFile16BitBE(file, scores->num_entries);
9393 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9397 for (i = 0; i < scores->num_entries; i++)
9399 int name_size = strlen(scores->entry[i].name);
9401 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9402 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9406 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9410 for (i = 0; i < scores->num_entries; i++)
9411 putFile16BitBE(file, scores->entry[i].score);
9414 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9418 for (i = 0; i < scores->num_entries; i++)
9419 putFile32BitBE(file, scores->entry[i].score);
9422 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9426 for (i = 0; i < scores->num_entries; i++)
9427 putFile32BitBE(file, scores->entry[i].time);
9430 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9434 for (i = 0; i < scores->num_entries; i++)
9436 int size = strlen(scores->entry[i].tape_basename);
9438 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9439 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9443 static void SaveScoreToFilename(char *filename)
9446 int info_chunk_size;
9447 int name_chunk_size;
9448 int scor_chunk_size;
9449 int sc4r_chunk_size;
9450 int time_chunk_size;
9451 int tape_chunk_size;
9452 boolean has_large_score_values;
9455 if (!(file = fopen(filename, MODE_WRITE)))
9457 Warn("cannot save score file '%s'", filename);
9462 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9463 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9464 scor_chunk_size = scores.num_entries * 2;
9465 sc4r_chunk_size = scores.num_entries * 4;
9466 time_chunk_size = scores.num_entries * 4;
9467 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9469 has_large_score_values = FALSE;
9470 for (i = 0; i < scores.num_entries; i++)
9471 if (scores.entry[i].score > 0xffff)
9472 has_large_score_values = TRUE;
9474 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9475 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9477 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9478 SaveScore_VERS(file, &scores);
9480 putFileChunkBE(file, "INFO", info_chunk_size);
9481 SaveScore_INFO(file, &scores);
9483 putFileChunkBE(file, "NAME", name_chunk_size);
9484 SaveScore_NAME(file, &scores);
9486 if (has_large_score_values)
9488 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9489 SaveScore_SC4R(file, &scores);
9493 putFileChunkBE(file, "SCOR", scor_chunk_size);
9494 SaveScore_SCOR(file, &scores);
9497 putFileChunkBE(file, "TIME", time_chunk_size);
9498 SaveScore_TIME(file, &scores);
9500 putFileChunkBE(file, "TAPE", tape_chunk_size);
9501 SaveScore_TAPE(file, &scores);
9505 SetFilePermissions(filename, PERMS_PRIVATE);
9508 void SaveScore(int nr)
9510 char *filename = getScoreFilename(nr);
9513 // used instead of "leveldir_current->subdir" (for network games)
9514 InitScoreDirectory(levelset.identifier);
9516 scores.file_version = FILE_VERSION_ACTUAL;
9517 scores.game_version = GAME_VERSION_ACTUAL;
9519 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9520 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9521 scores.level_nr = level_nr;
9523 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9524 if (scores.entry[i].score == 0 &&
9525 scores.entry[i].time == 0 &&
9526 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9529 scores.num_entries = i;
9531 if (scores.num_entries == 0)
9534 SaveScoreToFilename(filename);
9537 static void LoadServerScoreFromCache(int nr)
9539 struct ScoreEntry score_entry;
9548 { &score_entry.score, FALSE, 0 },
9549 { &score_entry.time, FALSE, 0 },
9550 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9551 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9552 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9553 { &score_entry.id, FALSE, 0 },
9554 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9555 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9556 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9557 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9561 char *filename = getScoreCacheFilename(nr);
9562 SetupFileHash *score_hash = loadSetupFileHash(filename);
9565 server_scores.num_entries = 0;
9567 if (score_hash == NULL)
9570 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9572 score_entry = server_scores.entry[i];
9574 for (j = 0; score_mapping[j].value != NULL; j++)
9578 sprintf(token, "%02d.%d", i, j);
9580 char *value = getHashEntry(score_hash, token);
9585 if (score_mapping[j].is_string)
9587 char *score_value = (char *)score_mapping[j].value;
9588 int value_size = score_mapping[j].string_size;
9590 strncpy(score_value, value, value_size);
9591 score_value[value_size] = '\0';
9595 int *score_value = (int *)score_mapping[j].value;
9597 *score_value = atoi(value);
9600 server_scores.num_entries = i + 1;
9603 server_scores.entry[i] = score_entry;
9606 freeSetupFileHash(score_hash);
9609 void LoadServerScore(int nr, boolean download_score)
9611 if (!setup.use_api_server)
9614 // always start with reliable default values
9615 setServerScoreInfoToDefaults();
9617 // 1st step: load server scores from cache file (which may not exist)
9618 // (this should prevent reading it while the thread is writing to it)
9619 LoadServerScoreFromCache(nr);
9621 if (download_score && runtime.use_api_server)
9623 // 2nd step: download server scores from score server to cache file
9624 // (as thread, as it might time out if the server is not reachable)
9625 ApiGetScoreAsThread(nr);
9629 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9631 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9633 // if score tape not uploaded, ask for uploading missing tapes later
9634 if (!setup.has_remaining_tapes)
9635 setup.ask_for_remaining_tapes = TRUE;
9637 setup.provide_uploading_tapes = TRUE;
9638 setup.has_remaining_tapes = TRUE;
9640 SaveSetup_ServerSetup();
9643 void SaveServerScore(int nr, boolean tape_saved)
9645 if (!runtime.use_api_server)
9647 PrepareScoreTapesForUpload(leveldir_current->subdir);
9652 ApiAddScoreAsThread(nr, tape_saved, NULL);
9655 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9656 char *score_tape_filename)
9658 if (!runtime.use_api_server)
9661 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9664 void LoadLocalAndServerScore(int nr, boolean download_score)
9666 int last_added_local = scores.last_added_local;
9667 boolean force_last_added = scores.force_last_added;
9669 // needed if only showing server scores
9670 setScoreInfoToDefaults();
9672 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9675 // restore last added local score entry (before merging server scores)
9676 scores.last_added = scores.last_added_local = last_added_local;
9678 if (setup.use_api_server &&
9679 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9681 // load server scores from cache file and trigger update from server
9682 LoadServerScore(nr, download_score);
9684 // merge local scores with scores from server
9688 if (force_last_added)
9689 scores.force_last_added = force_last_added;
9693 // ============================================================================
9694 // setup file functions
9695 // ============================================================================
9697 #define TOKEN_STR_PLAYER_PREFIX "player_"
9700 static struct TokenInfo global_setup_tokens[] =
9704 &setup.player_name, "player_name"
9708 &setup.multiple_users, "multiple_users"
9712 &setup.sound, "sound"
9716 &setup.sound_loops, "repeating_sound_loops"
9720 &setup.sound_music, "background_music"
9724 &setup.sound_simple, "simple_sound_effects"
9728 &setup.toons, "toons"
9732 &setup.global_animations, "global_animations"
9736 &setup.scroll_delay, "scroll_delay"
9740 &setup.forced_scroll_delay, "forced_scroll_delay"
9744 &setup.scroll_delay_value, "scroll_delay_value"
9748 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9752 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9756 &setup.fade_screens, "fade_screens"
9760 &setup.autorecord, "automatic_tape_recording"
9764 &setup.autorecord_after_replay, "autorecord_after_replay"
9768 &setup.auto_pause_on_start, "auto_pause_on_start"
9772 &setup.show_titlescreen, "show_titlescreen"
9776 &setup.quick_doors, "quick_doors"
9780 &setup.team_mode, "team_mode"
9784 &setup.handicap, "handicap"
9788 &setup.skip_levels, "skip_levels"
9792 &setup.increment_levels, "increment_levels"
9796 &setup.auto_play_next_level, "auto_play_next_level"
9800 &setup.count_score_after_game, "count_score_after_game"
9804 &setup.show_scores_after_game, "show_scores_after_game"
9808 &setup.time_limit, "time_limit"
9812 &setup.fullscreen, "fullscreen"
9816 &setup.window_scaling_percent, "window_scaling_percent"
9820 &setup.window_scaling_quality, "window_scaling_quality"
9824 &setup.screen_rendering_mode, "screen_rendering_mode"
9828 &setup.vsync_mode, "vsync_mode"
9832 &setup.ask_on_escape, "ask_on_escape"
9836 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9840 &setup.ask_on_game_over, "ask_on_game_over"
9844 &setup.ask_on_quit_game, "ask_on_quit_game"
9848 &setup.ask_on_quit_program, "ask_on_quit_program"
9852 &setup.quick_switch, "quick_player_switch"
9856 &setup.input_on_focus, "input_on_focus"
9860 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9864 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9868 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9872 &setup.game_speed_extended, "game_speed_extended"
9876 &setup.game_frame_delay, "game_frame_delay"
9880 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9884 &setup.bd_skip_hatching, "bd_skip_hatching"
9888 &setup.bd_scroll_delay, "bd_scroll_delay"
9892 &setup.bd_smooth_movements, "bd_smooth_movements"
9896 &setup.sp_show_border_elements, "sp_show_border_elements"
9900 &setup.small_game_graphics, "small_game_graphics"
9904 &setup.show_load_save_buttons, "show_load_save_buttons"
9908 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9912 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9916 &setup.graphics_set, "graphics_set"
9920 &setup.sounds_set, "sounds_set"
9924 &setup.music_set, "music_set"
9928 &setup.override_level_graphics, "override_level_graphics"
9932 &setup.override_level_sounds, "override_level_sounds"
9936 &setup.override_level_music, "override_level_music"
9940 &setup.volume_simple, "volume_simple"
9944 &setup.volume_loops, "volume_loops"
9948 &setup.volume_music, "volume_music"
9952 &setup.network_mode, "network_mode"
9956 &setup.network_player_nr, "network_player"
9960 &setup.network_server_hostname, "network_server_hostname"
9964 &setup.touch.control_type, "touch.control_type"
9968 &setup.touch.move_distance, "touch.move_distance"
9972 &setup.touch.drop_distance, "touch.drop_distance"
9976 &setup.touch.transparency, "touch.transparency"
9980 &setup.touch.draw_outlined, "touch.draw_outlined"
9984 &setup.touch.draw_pressed, "touch.draw_pressed"
9988 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9992 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9996 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10000 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10004 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10008 static struct TokenInfo auto_setup_tokens[] =
10012 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10016 static struct TokenInfo server_setup_tokens[] =
10020 &setup.player_uuid, "player_uuid"
10024 &setup.player_version, "player_version"
10028 &setup.use_api_server, TEST_PREFIX "use_api_server"
10032 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10036 &setup.api_server_password, TEST_PREFIX "api_server_password"
10040 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10044 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10048 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10052 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10056 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10060 static struct TokenInfo editor_setup_tokens[] =
10064 &setup.editor.el_classic, "editor.el_classic"
10068 &setup.editor.el_custom, "editor.el_custom"
10072 &setup.editor.el_user_defined, "editor.el_user_defined"
10076 &setup.editor.el_dynamic, "editor.el_dynamic"
10080 &setup.editor.el_headlines, "editor.el_headlines"
10084 &setup.editor.show_element_token, "editor.show_element_token"
10088 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10092 static struct TokenInfo editor_cascade_setup_tokens[] =
10096 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10100 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10104 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10108 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10112 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10116 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10120 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10124 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10128 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10132 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10136 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10140 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10144 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10148 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10152 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10156 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10160 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10164 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10168 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10172 static struct TokenInfo shortcut_setup_tokens[] =
10176 &setup.shortcut.save_game, "shortcut.save_game"
10180 &setup.shortcut.load_game, "shortcut.load_game"
10184 &setup.shortcut.restart_game, "shortcut.restart_game"
10188 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10192 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10196 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10200 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10204 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10208 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10212 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10216 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10220 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10224 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10228 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10232 &setup.shortcut.tape_record, "shortcut.tape_record"
10236 &setup.shortcut.tape_play, "shortcut.tape_play"
10240 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10244 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10248 &setup.shortcut.sound_music, "shortcut.sound_music"
10252 &setup.shortcut.snap_left, "shortcut.snap_left"
10256 &setup.shortcut.snap_right, "shortcut.snap_right"
10260 &setup.shortcut.snap_up, "shortcut.snap_up"
10264 &setup.shortcut.snap_down, "shortcut.snap_down"
10268 static struct SetupInputInfo setup_input;
10269 static struct TokenInfo player_setup_tokens[] =
10273 &setup_input.use_joystick, ".use_joystick"
10277 &setup_input.joy.device_name, ".joy.device_name"
10281 &setup_input.joy.xleft, ".joy.xleft"
10285 &setup_input.joy.xmiddle, ".joy.xmiddle"
10289 &setup_input.joy.xright, ".joy.xright"
10293 &setup_input.joy.yupper, ".joy.yupper"
10297 &setup_input.joy.ymiddle, ".joy.ymiddle"
10301 &setup_input.joy.ylower, ".joy.ylower"
10305 &setup_input.joy.snap, ".joy.snap_field"
10309 &setup_input.joy.drop, ".joy.place_bomb"
10313 &setup_input.key.left, ".key.move_left"
10317 &setup_input.key.right, ".key.move_right"
10321 &setup_input.key.up, ".key.move_up"
10325 &setup_input.key.down, ".key.move_down"
10329 &setup_input.key.snap, ".key.snap_field"
10333 &setup_input.key.drop, ".key.place_bomb"
10337 static struct TokenInfo system_setup_tokens[] =
10341 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10345 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10349 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10353 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10357 static struct TokenInfo internal_setup_tokens[] =
10361 &setup.internal.program_title, "program_title"
10365 &setup.internal.program_version, "program_version"
10369 &setup.internal.program_author, "program_author"
10373 &setup.internal.program_email, "program_email"
10377 &setup.internal.program_website, "program_website"
10381 &setup.internal.program_copyright, "program_copyright"
10385 &setup.internal.program_company, "program_company"
10389 &setup.internal.program_icon_file, "program_icon_file"
10393 &setup.internal.default_graphics_set, "default_graphics_set"
10397 &setup.internal.default_sounds_set, "default_sounds_set"
10401 &setup.internal.default_music_set, "default_music_set"
10405 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10409 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10413 &setup.internal.fallback_music_file, "fallback_music_file"
10417 &setup.internal.default_level_series, "default_level_series"
10421 &setup.internal.default_window_width, "default_window_width"
10425 &setup.internal.default_window_height, "default_window_height"
10429 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10433 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10437 &setup.internal.create_user_levelset, "create_user_levelset"
10441 &setup.internal.info_screens_from_main, "info_screens_from_main"
10445 &setup.internal.menu_game, "menu_game"
10449 &setup.internal.menu_engines, "menu_engines"
10453 &setup.internal.menu_editor, "menu_editor"
10457 &setup.internal.menu_graphics, "menu_graphics"
10461 &setup.internal.menu_sound, "menu_sound"
10465 &setup.internal.menu_artwork, "menu_artwork"
10469 &setup.internal.menu_input, "menu_input"
10473 &setup.internal.menu_touch, "menu_touch"
10477 &setup.internal.menu_shortcuts, "menu_shortcuts"
10481 &setup.internal.menu_exit, "menu_exit"
10485 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10489 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10493 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10497 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10501 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10505 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10509 &setup.internal.info_title, "info_title"
10513 &setup.internal.info_elements, "info_elements"
10517 &setup.internal.info_music, "info_music"
10521 &setup.internal.info_credits, "info_credits"
10525 &setup.internal.info_program, "info_program"
10529 &setup.internal.info_version, "info_version"
10533 &setup.internal.info_levelset, "info_levelset"
10537 &setup.internal.info_exit, "info_exit"
10541 static struct TokenInfo debug_setup_tokens[] =
10545 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10549 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10553 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10557 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10561 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10565 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10569 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10573 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10577 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10581 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10585 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10589 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10593 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10597 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10601 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10605 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10609 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10613 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10617 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10621 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10625 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10628 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10632 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10636 &setup.debug.xsn_mode, "debug.xsn_mode"
10640 &setup.debug.xsn_percent, "debug.xsn_percent"
10644 static struct TokenInfo options_setup_tokens[] =
10648 &setup.options.verbose, "options.verbose"
10652 &setup.options.debug, "options.debug"
10656 &setup.options.debug_mode, "options.debug_mode"
10660 static void setSetupInfoToDefaults(struct SetupInfo *si)
10664 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10666 si->multiple_users = TRUE;
10669 si->sound_loops = TRUE;
10670 si->sound_music = TRUE;
10671 si->sound_simple = TRUE;
10673 si->global_animations = TRUE;
10674 si->scroll_delay = TRUE;
10675 si->forced_scroll_delay = FALSE;
10676 si->scroll_delay_value = STD_SCROLL_DELAY;
10677 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10678 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10679 si->fade_screens = TRUE;
10680 si->autorecord = TRUE;
10681 si->autorecord_after_replay = TRUE;
10682 si->auto_pause_on_start = FALSE;
10683 si->show_titlescreen = TRUE;
10684 si->quick_doors = FALSE;
10685 si->team_mode = FALSE;
10686 si->handicap = TRUE;
10687 si->skip_levels = TRUE;
10688 si->increment_levels = TRUE;
10689 si->auto_play_next_level = TRUE;
10690 si->count_score_after_game = TRUE;
10691 si->show_scores_after_game = TRUE;
10692 si->time_limit = TRUE;
10693 si->fullscreen = FALSE;
10694 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10695 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10696 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10697 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10698 si->ask_on_escape = TRUE;
10699 si->ask_on_escape_editor = TRUE;
10700 si->ask_on_game_over = TRUE;
10701 si->ask_on_quit_game = TRUE;
10702 si->ask_on_quit_program = TRUE;
10703 si->quick_switch = FALSE;
10704 si->input_on_focus = FALSE;
10705 si->prefer_aga_graphics = TRUE;
10706 si->prefer_lowpass_sounds = FALSE;
10707 si->prefer_extra_panel_items = TRUE;
10708 si->game_speed_extended = FALSE;
10709 si->game_frame_delay = GAME_FRAME_DELAY;
10710 si->bd_skip_uncovering = FALSE;
10711 si->bd_skip_hatching = FALSE;
10712 si->bd_scroll_delay = TRUE;
10713 si->bd_smooth_movements = AUTO;
10714 si->sp_show_border_elements = FALSE;
10715 si->small_game_graphics = FALSE;
10716 si->show_load_save_buttons = FALSE;
10717 si->show_undo_redo_buttons = FALSE;
10718 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10720 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10721 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10722 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10724 si->override_level_graphics = FALSE;
10725 si->override_level_sounds = FALSE;
10726 si->override_level_music = FALSE;
10728 si->volume_simple = 100; // percent
10729 si->volume_loops = 100; // percent
10730 si->volume_music = 100; // percent
10732 si->network_mode = FALSE;
10733 si->network_player_nr = 0; // first player
10734 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10736 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10737 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10738 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10739 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10740 si->touch.draw_outlined = TRUE;
10741 si->touch.draw_pressed = TRUE;
10743 for (i = 0; i < 2; i++)
10745 char *default_grid_button[6][2] =
10751 { "111222", " vv " },
10752 { "111222", " vv " }
10754 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10755 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10756 int min_xsize = MIN(6, grid_xsize);
10757 int min_ysize = MIN(6, grid_ysize);
10758 int startx = grid_xsize - min_xsize;
10759 int starty = grid_ysize - min_ysize;
10762 // virtual buttons grid can only be set to defaults if video is initialized
10763 // (this will be repeated if virtual buttons are not loaded from setup file)
10764 if (video.initialized)
10766 si->touch.grid_xsize[i] = grid_xsize;
10767 si->touch.grid_ysize[i] = grid_ysize;
10771 si->touch.grid_xsize[i] = -1;
10772 si->touch.grid_ysize[i] = -1;
10775 for (x = 0; x < MAX_GRID_XSIZE; x++)
10776 for (y = 0; y < MAX_GRID_YSIZE; y++)
10777 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10779 for (x = 0; x < min_xsize; x++)
10780 for (y = 0; y < min_ysize; y++)
10781 si->touch.grid_button[i][x][starty + y] =
10782 default_grid_button[y][0][x];
10784 for (x = 0; x < min_xsize; x++)
10785 for (y = 0; y < min_ysize; y++)
10786 si->touch.grid_button[i][startx + x][starty + y] =
10787 default_grid_button[y][1][x];
10790 si->touch.grid_initialized = video.initialized;
10792 si->touch.overlay_buttons = FALSE;
10794 si->editor.el_boulderdash = TRUE;
10795 si->editor.el_boulderdash_native = TRUE;
10796 si->editor.el_emerald_mine = TRUE;
10797 si->editor.el_emerald_mine_club = TRUE;
10798 si->editor.el_more = TRUE;
10799 si->editor.el_sokoban = TRUE;
10800 si->editor.el_supaplex = TRUE;
10801 si->editor.el_diamond_caves = TRUE;
10802 si->editor.el_dx_boulderdash = TRUE;
10804 si->editor.el_mirror_magic = TRUE;
10805 si->editor.el_deflektor = TRUE;
10807 si->editor.el_chars = TRUE;
10808 si->editor.el_steel_chars = TRUE;
10810 si->editor.el_classic = TRUE;
10811 si->editor.el_custom = TRUE;
10813 si->editor.el_user_defined = FALSE;
10814 si->editor.el_dynamic = TRUE;
10816 si->editor.el_headlines = TRUE;
10818 si->editor.show_element_token = FALSE;
10820 si->editor.show_read_only_warning = TRUE;
10822 si->editor.use_template_for_new_levels = TRUE;
10824 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10825 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10826 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10827 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10828 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10830 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10831 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10832 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10833 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10834 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10836 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10837 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10838 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10839 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10840 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10841 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10843 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10844 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10845 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10847 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10848 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10849 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10850 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10852 for (i = 0; i < MAX_PLAYERS; i++)
10854 si->input[i].use_joystick = FALSE;
10855 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10856 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10857 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10858 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10859 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10860 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10861 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10862 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10863 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10864 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10865 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10866 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10867 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10868 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10869 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10872 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10873 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10874 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10875 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10877 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10878 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10879 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10880 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10881 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10882 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10883 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10885 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10887 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10888 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10889 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10891 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10892 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10893 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10895 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10896 si->internal.choose_from_top_leveldir = FALSE;
10897 si->internal.show_scaling_in_title = TRUE;
10898 si->internal.create_user_levelset = TRUE;
10899 si->internal.info_screens_from_main = FALSE;
10901 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10902 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10904 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10905 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10906 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10907 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10908 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10909 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10910 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10911 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10912 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10913 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10915 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10916 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10917 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10918 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10919 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10920 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10921 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10922 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10923 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10924 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10926 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10927 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10929 si->debug.show_frames_per_second = FALSE;
10931 si->debug.xsn_mode = AUTO;
10932 si->debug.xsn_percent = 0;
10934 si->options.verbose = FALSE;
10935 si->options.debug = FALSE;
10936 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10938 #if defined(PLATFORM_ANDROID)
10939 si->fullscreen = TRUE;
10940 si->touch.overlay_buttons = TRUE;
10943 setHideSetupEntry(&setup.debug.xsn_mode);
10946 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10948 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10951 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10953 si->player_uuid = NULL; // (will be set later)
10954 si->player_version = 1; // (will be set later)
10956 si->use_api_server = TRUE;
10957 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10958 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10959 si->ask_for_uploading_tapes = TRUE;
10960 si->ask_for_remaining_tapes = FALSE;
10961 si->provide_uploading_tapes = TRUE;
10962 si->ask_for_using_api_server = TRUE;
10963 si->has_remaining_tapes = FALSE;
10966 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10968 si->editor_cascade.el_bd = TRUE;
10969 si->editor_cascade.el_bd_native = TRUE;
10970 si->editor_cascade.el_em = TRUE;
10971 si->editor_cascade.el_emc = TRUE;
10972 si->editor_cascade.el_rnd = TRUE;
10973 si->editor_cascade.el_sb = TRUE;
10974 si->editor_cascade.el_sp = TRUE;
10975 si->editor_cascade.el_dc = TRUE;
10976 si->editor_cascade.el_dx = TRUE;
10978 si->editor_cascade.el_mm = TRUE;
10979 si->editor_cascade.el_df = TRUE;
10981 si->editor_cascade.el_chars = FALSE;
10982 si->editor_cascade.el_steel_chars = FALSE;
10983 si->editor_cascade.el_ce = FALSE;
10984 si->editor_cascade.el_ge = FALSE;
10985 si->editor_cascade.el_es = FALSE;
10986 si->editor_cascade.el_ref = FALSE;
10987 si->editor_cascade.el_user = FALSE;
10988 si->editor_cascade.el_dynamic = FALSE;
10991 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10993 static char *getHideSetupToken(void *setup_value)
10995 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10997 if (setup_value != NULL)
10998 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11000 return hide_setup_token;
11003 void setHideSetupEntry(void *setup_value)
11005 char *hide_setup_token = getHideSetupToken(setup_value);
11007 if (hide_setup_hash == NULL)
11008 hide_setup_hash = newSetupFileHash();
11010 if (setup_value != NULL)
11011 setHashEntry(hide_setup_hash, hide_setup_token, "");
11014 void removeHideSetupEntry(void *setup_value)
11016 char *hide_setup_token = getHideSetupToken(setup_value);
11018 if (setup_value != NULL)
11019 removeHashEntry(hide_setup_hash, hide_setup_token);
11022 boolean hideSetupEntry(void *setup_value)
11024 char *hide_setup_token = getHideSetupToken(setup_value);
11026 return (setup_value != NULL &&
11027 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11030 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11031 struct TokenInfo *token_info,
11032 int token_nr, char *token_text)
11034 char *token_hide_text = getStringCat2(token_text, ".hide");
11035 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11037 // set the value of this setup option in the setup option structure
11038 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11040 // check if this setup option should be hidden in the setup menu
11041 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11042 setHideSetupEntry(token_info[token_nr].value);
11044 free(token_hide_text);
11047 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11048 struct TokenInfo *token_info,
11051 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11052 token_info[token_nr].text);
11055 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11059 if (!setup_file_hash)
11062 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11063 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11065 setup.touch.grid_initialized = TRUE;
11066 for (i = 0; i < 2; i++)
11068 int grid_xsize = setup.touch.grid_xsize[i];
11069 int grid_ysize = setup.touch.grid_ysize[i];
11072 // if virtual buttons are not loaded from setup file, repeat initializing
11073 // virtual buttons grid with default values later when video is initialized
11074 if (grid_xsize == -1 ||
11077 setup.touch.grid_initialized = FALSE;
11082 for (y = 0; y < grid_ysize; y++)
11084 char token_string[MAX_LINE_LEN];
11086 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11088 char *value_string = getHashEntry(setup_file_hash, token_string);
11090 if (value_string == NULL)
11093 for (x = 0; x < grid_xsize; x++)
11095 char c = value_string[x];
11097 setup.touch.grid_button[i][x][y] =
11098 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11103 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11104 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11106 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11107 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11109 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11113 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11115 setup_input = setup.input[pnr];
11116 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11118 char full_token[100];
11120 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11121 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11124 setup.input[pnr] = setup_input;
11127 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11128 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11130 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11131 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11133 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11134 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11136 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11137 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11139 setHideRelatedSetupEntries();
11142 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11146 if (!setup_file_hash)
11149 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11150 setSetupInfo(auto_setup_tokens, i,
11151 getHashEntry(setup_file_hash,
11152 auto_setup_tokens[i].text));
11155 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11159 if (!setup_file_hash)
11162 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11163 setSetupInfo(server_setup_tokens, i,
11164 getHashEntry(setup_file_hash,
11165 server_setup_tokens[i].text));
11168 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11172 if (!setup_file_hash)
11175 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11176 setSetupInfo(editor_cascade_setup_tokens, i,
11177 getHashEntry(setup_file_hash,
11178 editor_cascade_setup_tokens[i].text));
11181 void LoadUserNames(void)
11183 int last_user_nr = user.nr;
11186 if (global.user_names != NULL)
11188 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11189 checked_free(global.user_names[i]);
11191 checked_free(global.user_names);
11194 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11196 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11200 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11202 if (setup_file_hash)
11204 char *player_name = getHashEntry(setup_file_hash, "player_name");
11206 global.user_names[i] = getFixedUserName(player_name);
11208 freeSetupFileHash(setup_file_hash);
11211 if (global.user_names[i] == NULL)
11212 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11215 user.nr = last_user_nr;
11218 void LoadSetupFromFilename(char *filename)
11220 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11222 if (setup_file_hash)
11224 decodeSetupFileHash_Default(setup_file_hash);
11226 freeSetupFileHash(setup_file_hash);
11230 Debug("setup", "using default setup values");
11234 static void LoadSetup_SpecialPostProcessing(void)
11236 char *player_name_new;
11238 // needed to work around problems with fixed length strings
11239 player_name_new = getFixedUserName(setup.player_name);
11240 free(setup.player_name);
11241 setup.player_name = player_name_new;
11243 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11244 if (setup.scroll_delay == FALSE)
11246 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11247 setup.scroll_delay = TRUE; // now always "on"
11250 // make sure that scroll delay value stays inside valid range
11251 setup.scroll_delay_value =
11252 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11255 void LoadSetup_Default(void)
11259 // always start with reliable default values
11260 setSetupInfoToDefaults(&setup);
11262 // try to load setup values from default setup file
11263 filename = getDefaultSetupFilename();
11265 if (fileExists(filename))
11266 LoadSetupFromFilename(filename);
11268 // try to load setup values from platform setup file
11269 filename = getPlatformSetupFilename();
11271 if (fileExists(filename))
11272 LoadSetupFromFilename(filename);
11274 // try to load setup values from user setup file
11275 filename = getSetupFilename();
11277 LoadSetupFromFilename(filename);
11279 LoadSetup_SpecialPostProcessing();
11282 void LoadSetup_AutoSetup(void)
11284 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11285 SetupFileHash *setup_file_hash = NULL;
11287 // always start with reliable default values
11288 setSetupInfoToDefaults_AutoSetup(&setup);
11290 setup_file_hash = loadSetupFileHash(filename);
11292 if (setup_file_hash)
11294 decodeSetupFileHash_AutoSetup(setup_file_hash);
11296 freeSetupFileHash(setup_file_hash);
11302 void LoadSetup_ServerSetup(void)
11304 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11305 SetupFileHash *setup_file_hash = NULL;
11307 // always start with reliable default values
11308 setSetupInfoToDefaults_ServerSetup(&setup);
11310 setup_file_hash = loadSetupFileHash(filename);
11312 if (setup_file_hash)
11314 decodeSetupFileHash_ServerSetup(setup_file_hash);
11316 freeSetupFileHash(setup_file_hash);
11321 if (setup.player_uuid == NULL)
11323 // player UUID does not yet exist in setup file
11324 setup.player_uuid = getStringCopy(getUUID());
11325 setup.player_version = 2;
11327 SaveSetup_ServerSetup();
11331 void LoadSetup_EditorCascade(void)
11333 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11334 SetupFileHash *setup_file_hash = NULL;
11336 // always start with reliable default values
11337 setSetupInfoToDefaults_EditorCascade(&setup);
11339 setup_file_hash = loadSetupFileHash(filename);
11341 if (setup_file_hash)
11343 decodeSetupFileHash_EditorCascade(setup_file_hash);
11345 freeSetupFileHash(setup_file_hash);
11351 void LoadSetup(void)
11353 LoadSetup_Default();
11354 LoadSetup_AutoSetup();
11355 LoadSetup_ServerSetup();
11356 LoadSetup_EditorCascade();
11359 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11360 char *mapping_line)
11362 char mapping_guid[MAX_LINE_LEN];
11363 char *mapping_start, *mapping_end;
11365 // get GUID from game controller mapping line: copy complete line
11366 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11367 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11369 // get GUID from game controller mapping line: cut after GUID part
11370 mapping_start = strchr(mapping_guid, ',');
11371 if (mapping_start != NULL)
11372 *mapping_start = '\0';
11374 // cut newline from game controller mapping line
11375 mapping_end = strchr(mapping_line, '\n');
11376 if (mapping_end != NULL)
11377 *mapping_end = '\0';
11379 // add mapping entry to game controller mappings hash
11380 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11383 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11388 if (!(file = fopen(filename, MODE_READ)))
11390 Warn("cannot read game controller mappings file '%s'", filename);
11395 while (!feof(file))
11397 char line[MAX_LINE_LEN];
11399 if (!fgets(line, MAX_LINE_LEN, file))
11402 addGameControllerMappingToHash(mappings_hash, line);
11408 void SaveSetup_Default(void)
11410 char *filename = getSetupFilename();
11414 InitUserDataDirectory();
11416 if (!(file = fopen(filename, MODE_WRITE)))
11418 Warn("cannot write setup file '%s'", filename);
11423 fprintFileHeader(file, SETUP_FILENAME);
11425 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11427 // just to make things nicer :)
11428 if (global_setup_tokens[i].value == &setup.multiple_users ||
11429 global_setup_tokens[i].value == &setup.sound ||
11430 global_setup_tokens[i].value == &setup.graphics_set ||
11431 global_setup_tokens[i].value == &setup.volume_simple ||
11432 global_setup_tokens[i].value == &setup.network_mode ||
11433 global_setup_tokens[i].value == &setup.touch.control_type ||
11434 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11435 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11436 fprintf(file, "\n");
11438 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11441 for (i = 0; i < 2; i++)
11443 int grid_xsize = setup.touch.grid_xsize[i];
11444 int grid_ysize = setup.touch.grid_ysize[i];
11447 fprintf(file, "\n");
11449 for (y = 0; y < grid_ysize; y++)
11451 char token_string[MAX_LINE_LEN];
11452 char value_string[MAX_LINE_LEN];
11454 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11456 for (x = 0; x < grid_xsize; x++)
11458 char c = setup.touch.grid_button[i][x][y];
11460 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11463 value_string[grid_xsize] = '\0';
11465 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11469 fprintf(file, "\n");
11470 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11471 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11473 fprintf(file, "\n");
11474 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11475 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11477 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11481 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11482 fprintf(file, "\n");
11484 setup_input = setup.input[pnr];
11485 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11486 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11489 fprintf(file, "\n");
11490 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11491 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11493 // (internal setup values not saved to user setup file)
11495 fprintf(file, "\n");
11496 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11497 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11498 setup.debug.xsn_mode != AUTO)
11499 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11501 fprintf(file, "\n");
11502 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11503 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11507 SetFilePermissions(filename, PERMS_PRIVATE);
11510 void SaveSetup_AutoSetup(void)
11512 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11516 InitUserDataDirectory();
11518 if (!(file = fopen(filename, MODE_WRITE)))
11520 Warn("cannot write auto setup file '%s'", filename);
11527 fprintFileHeader(file, AUTOSETUP_FILENAME);
11529 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11530 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11534 SetFilePermissions(filename, PERMS_PRIVATE);
11539 void SaveSetup_ServerSetup(void)
11541 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11545 InitUserDataDirectory();
11547 if (!(file = fopen(filename, MODE_WRITE)))
11549 Warn("cannot write server setup file '%s'", filename);
11556 fprintFileHeader(file, SERVERSETUP_FILENAME);
11558 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11560 // just to make things nicer :)
11561 if (server_setup_tokens[i].value == &setup.use_api_server)
11562 fprintf(file, "\n");
11564 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11569 SetFilePermissions(filename, PERMS_PRIVATE);
11574 void SaveSetup_EditorCascade(void)
11576 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11580 InitUserDataDirectory();
11582 if (!(file = fopen(filename, MODE_WRITE)))
11584 Warn("cannot write editor cascade state file '%s'", filename);
11591 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11593 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11594 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11598 SetFilePermissions(filename, PERMS_PRIVATE);
11603 void SaveSetup(void)
11605 SaveSetup_Default();
11606 SaveSetup_AutoSetup();
11607 SaveSetup_ServerSetup();
11608 SaveSetup_EditorCascade();
11611 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11616 if (!(file = fopen(filename, MODE_WRITE)))
11618 Warn("cannot write game controller mappings file '%s'", filename);
11623 BEGIN_HASH_ITERATION(mappings_hash, itr)
11625 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11627 END_HASH_ITERATION(mappings_hash, itr)
11632 void SaveSetup_AddGameControllerMapping(char *mapping)
11634 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11635 SetupFileHash *mappings_hash = newSetupFileHash();
11637 InitUserDataDirectory();
11639 // load existing personal game controller mappings
11640 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11642 // add new mapping to personal game controller mappings
11643 addGameControllerMappingToHash(mappings_hash, mapping);
11645 // save updated personal game controller mappings
11646 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11648 freeSetupFileHash(mappings_hash);
11652 void LoadCustomElementDescriptions(void)
11654 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11655 SetupFileHash *setup_file_hash;
11658 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11660 if (element_info[i].custom_description != NULL)
11662 free(element_info[i].custom_description);
11663 element_info[i].custom_description = NULL;
11667 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11670 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11672 char *token = getStringCat2(element_info[i].token_name, ".name");
11673 char *value = getHashEntry(setup_file_hash, token);
11676 element_info[i].custom_description = getStringCopy(value);
11681 freeSetupFileHash(setup_file_hash);
11684 static int getElementFromToken(char *token)
11686 char *value = getHashEntry(element_token_hash, token);
11689 return atoi(value);
11691 Warn("unknown element token '%s'", token);
11693 return EL_UNDEFINED;
11696 void FreeGlobalAnimEventInfo(void)
11698 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11700 if (gaei->event_list == NULL)
11705 for (i = 0; i < gaei->num_event_lists; i++)
11707 checked_free(gaei->event_list[i]->event_value);
11708 checked_free(gaei->event_list[i]);
11711 checked_free(gaei->event_list);
11713 gaei->event_list = NULL;
11714 gaei->num_event_lists = 0;
11717 static int AddGlobalAnimEventList(void)
11719 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11720 int list_pos = gaei->num_event_lists++;
11722 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11723 sizeof(struct GlobalAnimEventListInfo *));
11725 gaei->event_list[list_pos] =
11726 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11728 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11730 gaeli->event_value = NULL;
11731 gaeli->num_event_values = 0;
11736 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11738 // do not add empty global animation events
11739 if (event_value == ANIM_EVENT_NONE)
11742 // if list position is undefined, create new list
11743 if (list_pos == ANIM_EVENT_UNDEFINED)
11744 list_pos = AddGlobalAnimEventList();
11746 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11747 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11748 int value_pos = gaeli->num_event_values++;
11750 gaeli->event_value = checked_realloc(gaeli->event_value,
11751 gaeli->num_event_values * sizeof(int *));
11753 gaeli->event_value[value_pos] = event_value;
11758 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11760 if (list_pos == ANIM_EVENT_UNDEFINED)
11761 return ANIM_EVENT_NONE;
11763 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11764 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11766 return gaeli->event_value[value_pos];
11769 int GetGlobalAnimEventValueCount(int list_pos)
11771 if (list_pos == ANIM_EVENT_UNDEFINED)
11774 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11775 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11777 return gaeli->num_event_values;
11780 // This function checks if a string <s> of the format "string1, string2, ..."
11781 // exactly contains a string <s_contained>.
11783 static boolean string_has_parameter(char *s, char *s_contained)
11787 if (s == NULL || s_contained == NULL)
11790 if (strlen(s_contained) > strlen(s))
11793 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11795 char next_char = s[strlen(s_contained)];
11797 // check if next character is delimiter or whitespace
11798 if (next_char == ',' || next_char == '\0' ||
11799 next_char == ' ' || next_char == '\t')
11803 // check if string contains another parameter string after a comma
11804 substring = strchr(s, ',');
11805 if (substring == NULL) // string does not contain a comma
11808 // advance string pointer to next character after the comma
11811 // skip potential whitespaces after the comma
11812 while (*substring == ' ' || *substring == '\t')
11815 return string_has_parameter(substring, s_contained);
11818 static int get_anim_parameter_value_ce(char *s)
11821 char *pattern_1 = "ce_change:custom_";
11822 char *pattern_2 = ".page_";
11823 int pattern_1_len = strlen(pattern_1);
11824 char *matching_char = strstr(s_ptr, pattern_1);
11825 int result = ANIM_EVENT_NONE;
11827 if (matching_char == NULL)
11828 return ANIM_EVENT_NONE;
11830 result = ANIM_EVENT_CE_CHANGE;
11832 s_ptr = matching_char + pattern_1_len;
11834 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11835 if (*s_ptr >= '0' && *s_ptr <= '9')
11837 int gic_ce_nr = (*s_ptr++ - '0');
11839 if (*s_ptr >= '0' && *s_ptr <= '9')
11841 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11843 if (*s_ptr >= '0' && *s_ptr <= '9')
11844 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11847 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11848 return ANIM_EVENT_NONE;
11850 // custom element stored as 0 to 255
11853 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11857 // invalid custom element number specified
11859 return ANIM_EVENT_NONE;
11862 // check for change page number ("page_X" or "page_XX") (optional)
11863 if (strPrefix(s_ptr, pattern_2))
11865 s_ptr += strlen(pattern_2);
11867 if (*s_ptr >= '0' && *s_ptr <= '9')
11869 int gic_page_nr = (*s_ptr++ - '0');
11871 if (*s_ptr >= '0' && *s_ptr <= '9')
11872 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11874 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11875 return ANIM_EVENT_NONE;
11877 // change page stored as 1 to 32 (0 means "all change pages")
11879 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11883 // invalid animation part number specified
11885 return ANIM_EVENT_NONE;
11889 // discard result if next character is neither delimiter nor whitespace
11890 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11891 *s_ptr == ' ' || *s_ptr == '\t'))
11892 return ANIM_EVENT_NONE;
11897 static int get_anim_parameter_value(char *s)
11899 int event_value[] =
11907 char *pattern_1[] =
11915 char *pattern_2 = ".part_";
11916 char *matching_char = NULL;
11918 int pattern_1_len = 0;
11919 int result = ANIM_EVENT_NONE;
11922 result = get_anim_parameter_value_ce(s);
11924 if (result != ANIM_EVENT_NONE)
11927 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11929 matching_char = strstr(s_ptr, pattern_1[i]);
11930 pattern_1_len = strlen(pattern_1[i]);
11931 result = event_value[i];
11933 if (matching_char != NULL)
11937 if (matching_char == NULL)
11938 return ANIM_EVENT_NONE;
11940 s_ptr = matching_char + pattern_1_len;
11942 // check for main animation number ("anim_X" or "anim_XX")
11943 if (*s_ptr >= '0' && *s_ptr <= '9')
11945 int gic_anim_nr = (*s_ptr++ - '0');
11947 if (*s_ptr >= '0' && *s_ptr <= '9')
11948 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11950 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11951 return ANIM_EVENT_NONE;
11953 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11957 // invalid main animation number specified
11959 return ANIM_EVENT_NONE;
11962 // check for animation part number ("part_X" or "part_XX") (optional)
11963 if (strPrefix(s_ptr, pattern_2))
11965 s_ptr += strlen(pattern_2);
11967 if (*s_ptr >= '0' && *s_ptr <= '9')
11969 int gic_part_nr = (*s_ptr++ - '0');
11971 if (*s_ptr >= '0' && *s_ptr <= '9')
11972 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11974 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11975 return ANIM_EVENT_NONE;
11977 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11981 // invalid animation part number specified
11983 return ANIM_EVENT_NONE;
11987 // discard result if next character is neither delimiter nor whitespace
11988 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11989 *s_ptr == ' ' || *s_ptr == '\t'))
11990 return ANIM_EVENT_NONE;
11995 static int get_anim_parameter_values(char *s)
11997 int list_pos = ANIM_EVENT_UNDEFINED;
11998 int event_value = ANIM_EVENT_DEFAULT;
12000 if (string_has_parameter(s, "any"))
12001 event_value |= ANIM_EVENT_ANY;
12003 if (string_has_parameter(s, "click:self") ||
12004 string_has_parameter(s, "click") ||
12005 string_has_parameter(s, "self"))
12006 event_value |= ANIM_EVENT_SELF;
12008 if (string_has_parameter(s, "unclick:any"))
12009 event_value |= ANIM_EVENT_UNCLICK_ANY;
12011 // if animation event found, add it to global animation event list
12012 if (event_value != ANIM_EVENT_NONE)
12013 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12017 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12018 event_value = get_anim_parameter_value(s);
12020 // if animation event found, add it to global animation event list
12021 if (event_value != ANIM_EVENT_NONE)
12022 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12024 // continue with next part of the string, starting with next comma
12025 s = strchr(s + 1, ',');
12031 static int get_anim_action_parameter_value(char *token)
12033 // check most common default case first to massively speed things up
12034 if (strEqual(token, ARG_UNDEFINED))
12035 return ANIM_EVENT_ACTION_NONE;
12037 int result = getImageIDFromToken(token);
12041 char *gfx_token = getStringCat2("gfx.", token);
12043 result = getImageIDFromToken(gfx_token);
12045 checked_free(gfx_token);
12050 Key key = getKeyFromX11KeyName(token);
12052 if (key != KSYM_UNDEFINED)
12053 result = -(int)key;
12060 result = get_hash_from_string(token); // unsigned int => int
12061 result = ABS(result); // may be negative now
12062 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12064 setHashEntry(anim_url_hash, int2str(result, 0), token);
12069 result = ANIM_EVENT_ACTION_NONE;
12074 int get_parameter_value(char *value_raw, char *suffix, int type)
12076 char *value = getStringToLower(value_raw);
12077 int result = 0; // probably a save default value
12079 if (strEqual(suffix, ".direction"))
12081 result = (strEqual(value, "left") ? MV_LEFT :
12082 strEqual(value, "right") ? MV_RIGHT :
12083 strEqual(value, "up") ? MV_UP :
12084 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12086 else if (strEqual(suffix, ".position"))
12088 result = (strEqual(value, "left") ? POS_LEFT :
12089 strEqual(value, "right") ? POS_RIGHT :
12090 strEqual(value, "top") ? POS_TOP :
12091 strEqual(value, "upper") ? POS_UPPER :
12092 strEqual(value, "middle") ? POS_MIDDLE :
12093 strEqual(value, "lower") ? POS_LOWER :
12094 strEqual(value, "bottom") ? POS_BOTTOM :
12095 strEqual(value, "any") ? POS_ANY :
12096 strEqual(value, "ce") ? POS_CE :
12097 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12098 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12100 else if (strEqual(suffix, ".align"))
12102 result = (strEqual(value, "left") ? ALIGN_LEFT :
12103 strEqual(value, "right") ? ALIGN_RIGHT :
12104 strEqual(value, "center") ? ALIGN_CENTER :
12105 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12107 else if (strEqual(suffix, ".valign"))
12109 result = (strEqual(value, "top") ? VALIGN_TOP :
12110 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12111 strEqual(value, "middle") ? VALIGN_MIDDLE :
12112 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12114 else if (strEqual(suffix, ".anim_mode"))
12116 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12117 string_has_parameter(value, "loop") ? ANIM_LOOP :
12118 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12119 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12120 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12121 string_has_parameter(value, "random") ? ANIM_RANDOM :
12122 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12123 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12124 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12125 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12126 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12127 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12128 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12129 string_has_parameter(value, "all") ? ANIM_ALL :
12130 string_has_parameter(value, "tiled") ? ANIM_TILED :
12131 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12134 if (string_has_parameter(value, "once"))
12135 result |= ANIM_ONCE;
12137 if (string_has_parameter(value, "reverse"))
12138 result |= ANIM_REVERSE;
12140 if (string_has_parameter(value, "opaque_player"))
12141 result |= ANIM_OPAQUE_PLAYER;
12143 if (string_has_parameter(value, "static_panel"))
12144 result |= ANIM_STATIC_PANEL;
12146 else if (strEqual(suffix, ".init_event") ||
12147 strEqual(suffix, ".anim_event"))
12149 result = get_anim_parameter_values(value);
12151 else if (strEqual(suffix, ".init_delay_action") ||
12152 strEqual(suffix, ".anim_delay_action") ||
12153 strEqual(suffix, ".post_delay_action") ||
12154 strEqual(suffix, ".init_event_action") ||
12155 strEqual(suffix, ".anim_event_action"))
12157 result = get_anim_action_parameter_value(value_raw);
12159 else if (strEqual(suffix, ".class"))
12161 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12162 get_hash_from_string(value));
12164 else if (strEqual(suffix, ".style"))
12166 result = STYLE_DEFAULT;
12168 if (string_has_parameter(value, "accurate_borders"))
12169 result |= STYLE_ACCURATE_BORDERS;
12171 if (string_has_parameter(value, "inner_corners"))
12172 result |= STYLE_INNER_CORNERS;
12174 if (string_has_parameter(value, "reverse"))
12175 result |= STYLE_REVERSE;
12177 if (string_has_parameter(value, "leftmost_position"))
12178 result |= STYLE_LEFTMOST_POSITION;
12180 if (string_has_parameter(value, "block_clicks"))
12181 result |= STYLE_BLOCK;
12183 if (string_has_parameter(value, "passthrough_clicks"))
12184 result |= STYLE_PASSTHROUGH;
12186 if (string_has_parameter(value, "multiple_actions"))
12187 result |= STYLE_MULTIPLE_ACTIONS;
12189 if (string_has_parameter(value, "consume_ce_event"))
12190 result |= STYLE_CONSUME_CE_EVENT;
12192 else if (strEqual(suffix, ".fade_mode"))
12194 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12195 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12196 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12197 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12198 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12199 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12200 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12201 FADE_MODE_DEFAULT);
12203 else if (strEqual(suffix, ".auto_delay_unit"))
12205 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12206 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12207 AUTO_DELAY_UNIT_DEFAULT);
12209 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12211 result = gfx.get_font_from_token_function(value);
12213 else // generic parameter of type integer or boolean
12215 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12216 type == TYPE_INTEGER ? get_integer_from_string(value) :
12217 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12218 ARG_UNDEFINED_VALUE);
12226 static int get_token_parameter_value(char *token, char *value_raw)
12230 if (token == NULL || value_raw == NULL)
12231 return ARG_UNDEFINED_VALUE;
12233 suffix = strrchr(token, '.');
12234 if (suffix == NULL)
12237 if (strEqual(suffix, ".element"))
12238 return getElementFromToken(value_raw);
12240 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12241 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12244 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12245 boolean ignore_defaults)
12249 for (i = 0; image_config_vars[i].token != NULL; i++)
12251 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12253 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12254 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12258 *image_config_vars[i].value =
12259 get_token_parameter_value(image_config_vars[i].token, value);
12263 void InitMenuDesignSettings_Static(void)
12265 // always start with reliable default values from static default config
12266 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12269 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12273 // the following initializes hierarchical values from static configuration
12275 // special case: initialize "ARG_DEFAULT" values in static default config
12276 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12277 titlescreen_initial_first_default.fade_mode =
12278 title_initial_first_default.fade_mode;
12279 titlescreen_initial_first_default.fade_delay =
12280 title_initial_first_default.fade_delay;
12281 titlescreen_initial_first_default.post_delay =
12282 title_initial_first_default.post_delay;
12283 titlescreen_initial_first_default.auto_delay =
12284 title_initial_first_default.auto_delay;
12285 titlescreen_initial_first_default.auto_delay_unit =
12286 title_initial_first_default.auto_delay_unit;
12287 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12288 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12289 titlescreen_first_default.post_delay = title_first_default.post_delay;
12290 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12291 titlescreen_first_default.auto_delay_unit =
12292 title_first_default.auto_delay_unit;
12293 titlemessage_initial_first_default.fade_mode =
12294 title_initial_first_default.fade_mode;
12295 titlemessage_initial_first_default.fade_delay =
12296 title_initial_first_default.fade_delay;
12297 titlemessage_initial_first_default.post_delay =
12298 title_initial_first_default.post_delay;
12299 titlemessage_initial_first_default.auto_delay =
12300 title_initial_first_default.auto_delay;
12301 titlemessage_initial_first_default.auto_delay_unit =
12302 title_initial_first_default.auto_delay_unit;
12303 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12304 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12305 titlemessage_first_default.post_delay = title_first_default.post_delay;
12306 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12307 titlemessage_first_default.auto_delay_unit =
12308 title_first_default.auto_delay_unit;
12310 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12311 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12312 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12313 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12314 titlescreen_initial_default.auto_delay_unit =
12315 title_initial_default.auto_delay_unit;
12316 titlescreen_default.fade_mode = title_default.fade_mode;
12317 titlescreen_default.fade_delay = title_default.fade_delay;
12318 titlescreen_default.post_delay = title_default.post_delay;
12319 titlescreen_default.auto_delay = title_default.auto_delay;
12320 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12321 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12322 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12323 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12324 titlemessage_initial_default.auto_delay_unit =
12325 title_initial_default.auto_delay_unit;
12326 titlemessage_default.fade_mode = title_default.fade_mode;
12327 titlemessage_default.fade_delay = title_default.fade_delay;
12328 titlemessage_default.post_delay = title_default.post_delay;
12329 titlemessage_default.auto_delay = title_default.auto_delay;
12330 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12332 // special case: initialize "ARG_DEFAULT" values in static default config
12333 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12334 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12336 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12337 titlescreen_first[i] = titlescreen_first_default;
12338 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12339 titlemessage_first[i] = titlemessage_first_default;
12341 titlescreen_initial[i] = titlescreen_initial_default;
12342 titlescreen[i] = titlescreen_default;
12343 titlemessage_initial[i] = titlemessage_initial_default;
12344 titlemessage[i] = titlemessage_default;
12347 // special case: initialize "ARG_DEFAULT" values in static default config
12348 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12349 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12351 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12354 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12355 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12356 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12359 // special case: initialize "ARG_DEFAULT" values in static default config
12360 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12361 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12363 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12364 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12365 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12367 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12370 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12374 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12378 struct XY *dst, *src;
12380 game_buttons_xy[] =
12382 { &game.button.save, &game.button.stop },
12383 { &game.button.pause2, &game.button.pause },
12384 { &game.button.load, &game.button.play },
12385 { &game.button.undo, &game.button.stop },
12386 { &game.button.redo, &game.button.play },
12392 // special case: initialize later added SETUP list size from LEVELS value
12393 if (menu.list_size[GAME_MODE_SETUP] == -1)
12394 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12396 // set default position for snapshot buttons to stop/pause/play buttons
12397 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12398 if ((*game_buttons_xy[i].dst).x == -1 &&
12399 (*game_buttons_xy[i].dst).y == -1)
12400 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12402 // --------------------------------------------------------------------------
12403 // dynamic viewports (including playfield margins, borders and alignments)
12404 // --------------------------------------------------------------------------
12406 // dynamic viewports currently only supported for landscape mode
12407 int display_width = MAX(video.display_width, video.display_height);
12408 int display_height = MIN(video.display_width, video.display_height);
12410 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12412 struct RectWithBorder *vp_window = &viewport.window[i];
12413 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12414 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12415 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12416 boolean dynamic_window_width = (vp_window->min_width != -1);
12417 boolean dynamic_window_height = (vp_window->min_height != -1);
12418 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12419 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12421 // adjust window size if min/max width/height is specified
12423 if (vp_window->min_width != -1)
12425 int window_width = display_width;
12427 // when using static window height, use aspect ratio of display
12428 if (vp_window->min_height == -1)
12429 window_width = vp_window->height * display_width / display_height;
12431 vp_window->width = MAX(vp_window->min_width, window_width);
12434 if (vp_window->min_height != -1)
12436 int window_height = display_height;
12438 // when using static window width, use aspect ratio of display
12439 if (vp_window->min_width == -1)
12440 window_height = vp_window->width * display_height / display_width;
12442 vp_window->height = MAX(vp_window->min_height, window_height);
12445 if (vp_window->max_width != -1)
12446 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12448 if (vp_window->max_height != -1)
12449 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12451 int playfield_width = vp_window->width;
12452 int playfield_height = vp_window->height;
12454 // adjust playfield size and position according to specified margins
12456 playfield_width -= vp_playfield->margin_left;
12457 playfield_width -= vp_playfield->margin_right;
12459 playfield_height -= vp_playfield->margin_top;
12460 playfield_height -= vp_playfield->margin_bottom;
12462 // adjust playfield size if min/max width/height is specified
12464 if (vp_playfield->min_width != -1)
12465 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12467 if (vp_playfield->min_height != -1)
12468 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12470 if (vp_playfield->max_width != -1)
12471 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12473 if (vp_playfield->max_height != -1)
12474 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12476 // adjust playfield position according to specified alignment
12478 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12479 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12480 else if (vp_playfield->align == ALIGN_CENTER)
12481 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12482 else if (vp_playfield->align == ALIGN_RIGHT)
12483 vp_playfield->x += playfield_width - vp_playfield->width;
12485 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12486 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12487 else if (vp_playfield->valign == VALIGN_MIDDLE)
12488 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12489 else if (vp_playfield->valign == VALIGN_BOTTOM)
12490 vp_playfield->y += playfield_height - vp_playfield->height;
12492 vp_playfield->x += vp_playfield->margin_left;
12493 vp_playfield->y += vp_playfield->margin_top;
12495 // adjust individual playfield borders if only default border is specified
12497 if (vp_playfield->border_left == -1)
12498 vp_playfield->border_left = vp_playfield->border_size;
12499 if (vp_playfield->border_right == -1)
12500 vp_playfield->border_right = vp_playfield->border_size;
12501 if (vp_playfield->border_top == -1)
12502 vp_playfield->border_top = vp_playfield->border_size;
12503 if (vp_playfield->border_bottom == -1)
12504 vp_playfield->border_bottom = vp_playfield->border_size;
12506 // set dynamic playfield borders if borders are specified as undefined
12507 // (but only if window size was dynamic and playfield size was static)
12509 if (dynamic_window_width && !dynamic_playfield_width)
12511 if (vp_playfield->border_left == -1)
12513 vp_playfield->border_left = (vp_playfield->x -
12514 vp_playfield->margin_left);
12515 vp_playfield->x -= vp_playfield->border_left;
12516 vp_playfield->width += vp_playfield->border_left;
12519 if (vp_playfield->border_right == -1)
12521 vp_playfield->border_right = (vp_window->width -
12523 vp_playfield->width -
12524 vp_playfield->margin_right);
12525 vp_playfield->width += vp_playfield->border_right;
12529 if (dynamic_window_height && !dynamic_playfield_height)
12531 if (vp_playfield->border_top == -1)
12533 vp_playfield->border_top = (vp_playfield->y -
12534 vp_playfield->margin_top);
12535 vp_playfield->y -= vp_playfield->border_top;
12536 vp_playfield->height += vp_playfield->border_top;
12539 if (vp_playfield->border_bottom == -1)
12541 vp_playfield->border_bottom = (vp_window->height -
12543 vp_playfield->height -
12544 vp_playfield->margin_bottom);
12545 vp_playfield->height += vp_playfield->border_bottom;
12549 // adjust playfield size to be a multiple of a defined alignment tile size
12551 int align_size = vp_playfield->align_size;
12552 int playfield_xtiles = vp_playfield->width / align_size;
12553 int playfield_ytiles = vp_playfield->height / align_size;
12554 int playfield_width_corrected = playfield_xtiles * align_size;
12555 int playfield_height_corrected = playfield_ytiles * align_size;
12556 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12557 i == GFX_SPECIAL_ARG_EDITOR);
12559 if (is_playfield_mode &&
12560 dynamic_playfield_width &&
12561 vp_playfield->width != playfield_width_corrected)
12563 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12565 vp_playfield->width = playfield_width_corrected;
12567 if (vp_playfield->align == ALIGN_LEFT)
12569 vp_playfield->border_left += playfield_xdiff;
12571 else if (vp_playfield->align == ALIGN_RIGHT)
12573 vp_playfield->border_right += playfield_xdiff;
12575 else if (vp_playfield->align == ALIGN_CENTER)
12577 int border_left_diff = playfield_xdiff / 2;
12578 int border_right_diff = playfield_xdiff - border_left_diff;
12580 vp_playfield->border_left += border_left_diff;
12581 vp_playfield->border_right += border_right_diff;
12585 if (is_playfield_mode &&
12586 dynamic_playfield_height &&
12587 vp_playfield->height != playfield_height_corrected)
12589 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12591 vp_playfield->height = playfield_height_corrected;
12593 if (vp_playfield->valign == VALIGN_TOP)
12595 vp_playfield->border_top += playfield_ydiff;
12597 else if (vp_playfield->align == VALIGN_BOTTOM)
12599 vp_playfield->border_right += playfield_ydiff;
12601 else if (vp_playfield->align == VALIGN_MIDDLE)
12603 int border_top_diff = playfield_ydiff / 2;
12604 int border_bottom_diff = playfield_ydiff - border_top_diff;
12606 vp_playfield->border_top += border_top_diff;
12607 vp_playfield->border_bottom += border_bottom_diff;
12611 // adjust door positions according to specified alignment
12613 for (j = 0; j < 2; j++)
12615 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12617 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12618 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12619 else if (vp_door->align == ALIGN_CENTER)
12620 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12621 else if (vp_door->align == ALIGN_RIGHT)
12622 vp_door->x += vp_window->width - vp_door->width;
12624 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12625 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12626 else if (vp_door->valign == VALIGN_MIDDLE)
12627 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12628 else if (vp_door->valign == VALIGN_BOTTOM)
12629 vp_door->y += vp_window->height - vp_door->height;
12634 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12638 struct XYTileSize *dst, *src;
12641 editor_buttons_xy[] =
12644 &editor.button.element_left, &editor.palette.element_left,
12645 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12648 &editor.button.element_middle, &editor.palette.element_middle,
12649 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12652 &editor.button.element_right, &editor.palette.element_right,
12653 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12660 // set default position for element buttons to element graphics
12661 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12663 if ((*editor_buttons_xy[i].dst).x == -1 &&
12664 (*editor_buttons_xy[i].dst).y == -1)
12666 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12668 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12670 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12674 // adjust editor palette rows and columns if specified to be dynamic
12676 if (editor.palette.cols == -1)
12678 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12679 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12680 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12682 editor.palette.cols = (vp_width - sc_width) / bt_width;
12684 if (editor.palette.x == -1)
12686 int palette_width = editor.palette.cols * bt_width + sc_width;
12688 editor.palette.x = (vp_width - palette_width) / 2;
12692 if (editor.palette.rows == -1)
12694 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12695 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12696 int tx_height = getFontHeight(FONT_TEXT_2);
12698 editor.palette.rows = (vp_height - tx_height) / bt_height;
12700 if (editor.palette.y == -1)
12702 int palette_height = editor.palette.rows * bt_height + tx_height;
12704 editor.palette.y = (vp_height - palette_height) / 2;
12709 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12710 boolean initialize)
12712 // special case: check if network and preview player positions are redefined,
12713 // to compare this later against the main menu level preview being redefined
12714 struct TokenIntPtrInfo menu_config_players[] =
12716 { "main.network_players.x", &menu.main.network_players.redefined },
12717 { "main.network_players.y", &menu.main.network_players.redefined },
12718 { "main.preview_players.x", &menu.main.preview_players.redefined },
12719 { "main.preview_players.y", &menu.main.preview_players.redefined },
12720 { "preview.x", &preview.redefined },
12721 { "preview.y", &preview.redefined }
12727 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12728 *menu_config_players[i].value = FALSE;
12732 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12733 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12734 *menu_config_players[i].value = TRUE;
12738 static void InitMenuDesignSettings_PreviewPlayers(void)
12740 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12743 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12745 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12748 static void LoadMenuDesignSettingsFromFilename(char *filename)
12750 static struct TitleFadingInfo tfi;
12751 static struct TitleMessageInfo tmi;
12752 static struct TokenInfo title_tokens[] =
12754 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12755 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12756 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12757 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12758 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12762 static struct TokenInfo titlemessage_tokens[] =
12764 { TYPE_INTEGER, &tmi.x, ".x" },
12765 { TYPE_INTEGER, &tmi.y, ".y" },
12766 { TYPE_INTEGER, &tmi.width, ".width" },
12767 { TYPE_INTEGER, &tmi.height, ".height" },
12768 { TYPE_INTEGER, &tmi.chars, ".chars" },
12769 { TYPE_INTEGER, &tmi.lines, ".lines" },
12770 { TYPE_INTEGER, &tmi.align, ".align" },
12771 { TYPE_INTEGER, &tmi.valign, ".valign" },
12772 { TYPE_INTEGER, &tmi.font, ".font" },
12773 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12774 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12775 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12776 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12777 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12778 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12779 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12780 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12781 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12787 struct TitleFadingInfo *info;
12792 // initialize first titles from "enter screen" definitions, if defined
12793 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12794 { &title_first_default, "menu.enter_screen.TITLE" },
12796 // initialize title screens from "next screen" definitions, if defined
12797 { &title_initial_default, "menu.next_screen.TITLE" },
12798 { &title_default, "menu.next_screen.TITLE" },
12804 struct TitleMessageInfo *array;
12807 titlemessage_arrays[] =
12809 // initialize first titles from "enter screen" definitions, if defined
12810 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12811 { titlescreen_first, "menu.enter_screen.TITLE" },
12812 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12813 { titlemessage_first, "menu.enter_screen.TITLE" },
12815 // initialize titles from "next screen" definitions, if defined
12816 { titlescreen_initial, "menu.next_screen.TITLE" },
12817 { titlescreen, "menu.next_screen.TITLE" },
12818 { titlemessage_initial, "menu.next_screen.TITLE" },
12819 { titlemessage, "menu.next_screen.TITLE" },
12821 // overwrite titles with title definitions, if defined
12822 { titlescreen_initial_first, "[title_initial]" },
12823 { titlescreen_first, "[title]" },
12824 { titlemessage_initial_first, "[title_initial]" },
12825 { titlemessage_first, "[title]" },
12827 { titlescreen_initial, "[title_initial]" },
12828 { titlescreen, "[title]" },
12829 { titlemessage_initial, "[title_initial]" },
12830 { titlemessage, "[title]" },
12832 // overwrite titles with title screen/message definitions, if defined
12833 { titlescreen_initial_first, "[titlescreen_initial]" },
12834 { titlescreen_first, "[titlescreen]" },
12835 { titlemessage_initial_first, "[titlemessage_initial]" },
12836 { titlemessage_first, "[titlemessage]" },
12838 { titlescreen_initial, "[titlescreen_initial]" },
12839 { titlescreen, "[titlescreen]" },
12840 { titlemessage_initial, "[titlemessage_initial]" },
12841 { titlemessage, "[titlemessage]" },
12845 SetupFileHash *setup_file_hash;
12848 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12851 // the following initializes hierarchical values from dynamic configuration
12853 // special case: initialize with default values that may be overwritten
12854 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12855 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12857 struct TokenIntPtrInfo menu_config[] =
12859 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12860 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12861 { "menu.list_size", &menu.list_size[i] }
12864 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12866 char *token = menu_config[j].token;
12867 char *value = getHashEntry(setup_file_hash, token);
12870 *menu_config[j].value = get_integer_from_string(value);
12874 // special case: initialize with default values that may be overwritten
12875 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12876 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12878 struct TokenIntPtrInfo menu_config[] =
12880 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12881 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12882 { "menu.list_size.INFO", &menu.list_size_info[i] },
12883 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12884 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12887 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12889 char *token = menu_config[j].token;
12890 char *value = getHashEntry(setup_file_hash, token);
12893 *menu_config[j].value = get_integer_from_string(value);
12897 // special case: initialize with default values that may be overwritten
12898 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12899 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12901 struct TokenIntPtrInfo menu_config[] =
12903 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12904 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12907 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12909 char *token = menu_config[j].token;
12910 char *value = getHashEntry(setup_file_hash, token);
12913 *menu_config[j].value = get_integer_from_string(value);
12917 // special case: initialize with default values that may be overwritten
12918 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12919 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12921 struct TokenIntPtrInfo menu_config[] =
12923 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12924 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12925 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12926 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12927 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12928 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12929 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12930 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12931 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12932 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12935 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12937 char *token = menu_config[j].token;
12938 char *value = getHashEntry(setup_file_hash, token);
12941 *menu_config[j].value = get_integer_from_string(value);
12945 // special case: initialize with default values that may be overwritten
12946 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12947 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12949 struct TokenIntPtrInfo menu_config[] =
12951 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12952 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12953 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12954 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12955 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12956 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12957 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12958 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12959 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12962 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12964 char *token = menu_config[j].token;
12965 char *value = getHashEntry(setup_file_hash, token);
12968 *menu_config[j].value = get_token_parameter_value(token, value);
12972 // special case: initialize with default values that may be overwritten
12973 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12974 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12978 char *token_prefix;
12979 struct RectWithBorder *struct_ptr;
12983 { "viewport.window", &viewport.window[i] },
12984 { "viewport.playfield", &viewport.playfield[i] },
12985 { "viewport.door_1", &viewport.door_1[i] },
12986 { "viewport.door_2", &viewport.door_2[i] }
12989 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12991 struct TokenIntPtrInfo vp_config[] =
12993 { ".x", &vp_struct[j].struct_ptr->x },
12994 { ".y", &vp_struct[j].struct_ptr->y },
12995 { ".width", &vp_struct[j].struct_ptr->width },
12996 { ".height", &vp_struct[j].struct_ptr->height },
12997 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12998 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12999 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13000 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13001 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13002 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13003 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13004 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13005 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13006 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13007 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13008 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13009 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13010 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13011 { ".align", &vp_struct[j].struct_ptr->align },
13012 { ".valign", &vp_struct[j].struct_ptr->valign }
13015 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13017 char *token = getStringCat2(vp_struct[j].token_prefix,
13018 vp_config[k].token);
13019 char *value = getHashEntry(setup_file_hash, token);
13022 *vp_config[k].value = get_token_parameter_value(token, value);
13029 // special case: initialize with default values that may be overwritten
13030 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13031 for (i = 0; title_info[i].info != NULL; i++)
13033 struct TitleFadingInfo *info = title_info[i].info;
13034 char *base_token = title_info[i].text;
13036 for (j = 0; title_tokens[j].type != -1; j++)
13038 char *token = getStringCat2(base_token, title_tokens[j].text);
13039 char *value = getHashEntry(setup_file_hash, token);
13043 int parameter_value = get_token_parameter_value(token, value);
13047 *(int *)title_tokens[j].value = (int)parameter_value;
13056 // special case: initialize with default values that may be overwritten
13057 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13058 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13060 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13061 char *base_token = titlemessage_arrays[i].text;
13063 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13065 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13066 char *value = getHashEntry(setup_file_hash, token);
13070 int parameter_value = get_token_parameter_value(token, value);
13072 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13076 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13077 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13079 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13089 // read (and overwrite with) values that may be specified in config file
13090 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13092 // special case: check if network and preview player positions are redefined
13093 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13095 freeSetupFileHash(setup_file_hash);
13098 void LoadMenuDesignSettings(void)
13100 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13102 InitMenuDesignSettings_Static();
13103 InitMenuDesignSettings_SpecialPreProcessing();
13104 InitMenuDesignSettings_PreviewPlayers();
13106 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13108 // first look for special settings configured in level series config
13109 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13111 if (fileExists(filename_base))
13112 LoadMenuDesignSettingsFromFilename(filename_base);
13115 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13117 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13118 LoadMenuDesignSettingsFromFilename(filename_local);
13120 InitMenuDesignSettings_SpecialPostProcessing();
13123 void LoadMenuDesignSettings_AfterGraphics(void)
13125 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13128 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13129 boolean ignore_defaults)
13133 for (i = 0; sound_config_vars[i].token != NULL; i++)
13135 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13137 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13138 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13142 *sound_config_vars[i].value =
13143 get_token_parameter_value(sound_config_vars[i].token, value);
13147 void InitSoundSettings_Static(void)
13149 // always start with reliable default values from static default config
13150 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13153 static void LoadSoundSettingsFromFilename(char *filename)
13155 SetupFileHash *setup_file_hash;
13157 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13160 // read (and overwrite with) values that may be specified in config file
13161 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13163 freeSetupFileHash(setup_file_hash);
13166 void LoadSoundSettings(void)
13168 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13170 InitSoundSettings_Static();
13172 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13174 // first look for special settings configured in level series config
13175 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13177 if (fileExists(filename_base))
13178 LoadSoundSettingsFromFilename(filename_base);
13181 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13183 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13184 LoadSoundSettingsFromFilename(filename_local);
13187 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13189 char *filename = getEditorSetupFilename();
13190 SetupFileList *setup_file_list, *list;
13191 SetupFileHash *element_hash;
13192 int num_unknown_tokens = 0;
13195 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13198 element_hash = newSetupFileHash();
13200 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13201 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13203 // determined size may be larger than needed (due to unknown elements)
13205 for (list = setup_file_list; list != NULL; list = list->next)
13208 // add space for up to 3 more elements for padding that may be needed
13209 *num_elements += 3;
13211 // free memory for old list of elements, if needed
13212 checked_free(*elements);
13214 // allocate memory for new list of elements
13215 *elements = checked_malloc(*num_elements * sizeof(int));
13218 for (list = setup_file_list; list != NULL; list = list->next)
13220 char *value = getHashEntry(element_hash, list->token);
13222 if (value == NULL) // try to find obsolete token mapping
13224 char *mapped_token = get_mapped_token(list->token);
13226 if (mapped_token != NULL)
13228 value = getHashEntry(element_hash, mapped_token);
13230 free(mapped_token);
13236 (*elements)[(*num_elements)++] = atoi(value);
13240 if (num_unknown_tokens == 0)
13243 Warn("unknown token(s) found in config file:");
13244 Warn("- config file: '%s'", filename);
13246 num_unknown_tokens++;
13249 Warn("- token: '%s'", list->token);
13253 if (num_unknown_tokens > 0)
13256 while (*num_elements % 4) // pad with empty elements, if needed
13257 (*elements)[(*num_elements)++] = EL_EMPTY;
13259 freeSetupFileList(setup_file_list);
13260 freeSetupFileHash(element_hash);
13263 for (i = 0; i < *num_elements; i++)
13264 Debug("editor", "element '%s' [%d]\n",
13265 element_info[(*elements)[i]].token_name, (*elements)[i]);
13269 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13272 SetupFileHash *setup_file_hash = NULL;
13273 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13274 char *filename_music, *filename_prefix, *filename_info;
13280 token_to_value_ptr[] =
13282 { "title_header", &tmp_music_file_info.title_header },
13283 { "artist_header", &tmp_music_file_info.artist_header },
13284 { "album_header", &tmp_music_file_info.album_header },
13285 { "year_header", &tmp_music_file_info.year_header },
13286 { "played_header", &tmp_music_file_info.played_header },
13288 { "title", &tmp_music_file_info.title },
13289 { "artist", &tmp_music_file_info.artist },
13290 { "album", &tmp_music_file_info.album },
13291 { "year", &tmp_music_file_info.year },
13292 { "played", &tmp_music_file_info.played },
13298 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13299 getCustomMusicFilename(basename));
13301 if (filename_music == NULL)
13304 // ---------- try to replace file extension ----------
13306 filename_prefix = getStringCopy(filename_music);
13307 if (strrchr(filename_prefix, '.') != NULL)
13308 *strrchr(filename_prefix, '.') = '\0';
13309 filename_info = getStringCat2(filename_prefix, ".txt");
13311 if (fileExists(filename_info))
13312 setup_file_hash = loadSetupFileHash(filename_info);
13314 free(filename_prefix);
13315 free(filename_info);
13317 if (setup_file_hash == NULL)
13319 // ---------- try to add file extension ----------
13321 filename_prefix = getStringCopy(filename_music);
13322 filename_info = getStringCat2(filename_prefix, ".txt");
13324 if (fileExists(filename_info))
13325 setup_file_hash = loadSetupFileHash(filename_info);
13327 free(filename_prefix);
13328 free(filename_info);
13331 if (setup_file_hash == NULL)
13334 // ---------- music file info found ----------
13336 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13338 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13340 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13342 *token_to_value_ptr[i].value_ptr =
13343 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13346 tmp_music_file_info.basename = getStringCopy(basename);
13347 tmp_music_file_info.music = music;
13348 tmp_music_file_info.is_sound = is_sound;
13350 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13351 *new_music_file_info = tmp_music_file_info;
13353 return new_music_file_info;
13356 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13358 return get_music_file_info_ext(basename, music, FALSE);
13361 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13363 return get_music_file_info_ext(basename, sound, TRUE);
13366 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13367 char *basename, boolean is_sound)
13369 for (; list != NULL; list = list->next)
13370 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13376 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13378 return music_info_listed_ext(list, basename, FALSE);
13381 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13383 return music_info_listed_ext(list, basename, TRUE);
13386 void LoadMusicInfo(void)
13388 int num_music_noconf = getMusicListSize_NoConf();
13389 int num_music = getMusicListSize();
13390 int num_sounds = getSoundListSize();
13391 struct FileInfo *music, *sound;
13392 struct MusicFileInfo *next, **new;
13396 while (music_file_info != NULL)
13398 next = music_file_info->next;
13400 checked_free(music_file_info->basename);
13402 checked_free(music_file_info->title_header);
13403 checked_free(music_file_info->artist_header);
13404 checked_free(music_file_info->album_header);
13405 checked_free(music_file_info->year_header);
13406 checked_free(music_file_info->played_header);
13408 checked_free(music_file_info->title);
13409 checked_free(music_file_info->artist);
13410 checked_free(music_file_info->album);
13411 checked_free(music_file_info->year);
13412 checked_free(music_file_info->played);
13414 free(music_file_info);
13416 music_file_info = next;
13419 new = &music_file_info;
13421 // get (configured or unconfigured) music file info for all levels
13422 for (i = leveldir_current->first_level;
13423 i <= leveldir_current->last_level; i++)
13427 if (levelset.music[i] != MUS_UNDEFINED)
13429 // get music file info for configured level music
13430 music_nr = levelset.music[i];
13432 else if (num_music_noconf > 0)
13434 // get music file info for unconfigured level music
13435 int level_pos = i - leveldir_current->first_level;
13437 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13444 char *basename = getMusicInfoEntryFilename(music_nr);
13446 if (basename == NULL)
13449 if (!music_info_listed(music_file_info, basename))
13451 *new = get_music_file_info(basename, music_nr);
13454 new = &(*new)->next;
13458 // get music file info for all remaining configured music files
13459 for (i = 0; i < num_music; i++)
13461 music = getMusicListEntry(i);
13463 if (music->filename == NULL)
13466 if (strEqual(music->filename, UNDEFINED_FILENAME))
13469 // a configured file may be not recognized as music
13470 if (!FileIsMusic(music->filename))
13473 if (!music_info_listed(music_file_info, music->filename))
13475 *new = get_music_file_info(music->filename, i);
13478 new = &(*new)->next;
13482 // get sound file info for all configured sound files
13483 for (i = 0; i < num_sounds; i++)
13485 sound = getSoundListEntry(i);
13487 if (sound->filename == NULL)
13490 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13493 // a configured file may be not recognized as sound
13494 if (!FileIsSound(sound->filename))
13497 if (!sound_info_listed(music_file_info, sound->filename))
13499 *new = get_sound_file_info(sound->filename, i);
13501 new = &(*new)->next;
13505 // add pointers to previous list nodes
13507 struct MusicFileInfo *node = music_file_info;
13509 while (node != NULL)
13512 node->next->prev = node;
13518 static void add_helpanim_entry(int element, int action, int direction,
13519 int delay, int *num_list_entries)
13521 struct HelpAnimInfo *new_list_entry;
13522 (*num_list_entries)++;
13525 checked_realloc(helpanim_info,
13526 *num_list_entries * sizeof(struct HelpAnimInfo));
13527 new_list_entry = &helpanim_info[*num_list_entries - 1];
13529 new_list_entry->element = element;
13530 new_list_entry->action = action;
13531 new_list_entry->direction = direction;
13532 new_list_entry->delay = delay;
13535 static void print_unknown_token(char *filename, char *token, int token_nr)
13540 Warn("unknown token(s) found in config file:");
13541 Warn("- config file: '%s'", filename);
13544 Warn("- token: '%s'", token);
13547 static void print_unknown_token_end(int token_nr)
13553 void LoadHelpAnimInfo(void)
13555 char *filename = getHelpAnimFilename();
13556 SetupFileList *setup_file_list = NULL, *list;
13557 SetupFileHash *element_hash, *action_hash, *direction_hash;
13558 int num_list_entries = 0;
13559 int num_unknown_tokens = 0;
13562 if (fileExists(filename))
13563 setup_file_list = loadSetupFileList(filename);
13565 if (setup_file_list == NULL)
13567 // use reliable default values from static configuration
13568 SetupFileList *insert_ptr;
13570 insert_ptr = setup_file_list =
13571 newSetupFileList(helpanim_config[0].token,
13572 helpanim_config[0].value);
13574 for (i = 1; helpanim_config[i].token; i++)
13575 insert_ptr = addListEntry(insert_ptr,
13576 helpanim_config[i].token,
13577 helpanim_config[i].value);
13580 element_hash = newSetupFileHash();
13581 action_hash = newSetupFileHash();
13582 direction_hash = newSetupFileHash();
13584 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13585 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13587 for (i = 0; i < NUM_ACTIONS; i++)
13588 setHashEntry(action_hash, element_action_info[i].suffix,
13589 i_to_a(element_action_info[i].value));
13591 // do not store direction index (bit) here, but direction value!
13592 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13593 setHashEntry(direction_hash, element_direction_info[i].suffix,
13594 i_to_a(1 << element_direction_info[i].value));
13596 for (list = setup_file_list; list != NULL; list = list->next)
13598 char *element_token, *action_token, *direction_token;
13599 char *element_value, *action_value, *direction_value;
13600 int delay = atoi(list->value);
13602 if (strEqual(list->token, "end"))
13604 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13609 /* first try to break element into element/action/direction parts;
13610 if this does not work, also accept combined "element[.act][.dir]"
13611 elements (like "dynamite.active"), which are unique elements */
13613 if (strchr(list->token, '.') == NULL) // token contains no '.'
13615 element_value = getHashEntry(element_hash, list->token);
13616 if (element_value != NULL) // element found
13617 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13618 &num_list_entries);
13621 // no further suffixes found -- this is not an element
13622 print_unknown_token(filename, list->token, num_unknown_tokens++);
13628 // token has format "<prefix>.<something>"
13630 action_token = strchr(list->token, '.'); // suffix may be action ...
13631 direction_token = action_token; // ... or direction
13633 element_token = getStringCopy(list->token);
13634 *strchr(element_token, '.') = '\0';
13636 element_value = getHashEntry(element_hash, element_token);
13638 if (element_value == NULL) // this is no element
13640 element_value = getHashEntry(element_hash, list->token);
13641 if (element_value != NULL) // combined element found
13642 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13643 &num_list_entries);
13645 print_unknown_token(filename, list->token, num_unknown_tokens++);
13647 free(element_token);
13652 action_value = getHashEntry(action_hash, action_token);
13654 if (action_value != NULL) // action found
13656 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13657 &num_list_entries);
13659 free(element_token);
13664 direction_value = getHashEntry(direction_hash, direction_token);
13666 if (direction_value != NULL) // direction found
13668 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13669 &num_list_entries);
13671 free(element_token);
13676 if (strchr(action_token + 1, '.') == NULL)
13678 // no further suffixes found -- this is not an action nor direction
13680 element_value = getHashEntry(element_hash, list->token);
13681 if (element_value != NULL) // combined element found
13682 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13683 &num_list_entries);
13685 print_unknown_token(filename, list->token, num_unknown_tokens++);
13687 free(element_token);
13692 // token has format "<prefix>.<suffix>.<something>"
13694 direction_token = strchr(action_token + 1, '.');
13696 action_token = getStringCopy(action_token);
13697 *strchr(action_token + 1, '.') = '\0';
13699 action_value = getHashEntry(action_hash, action_token);
13701 if (action_value == NULL) // this is no action
13703 element_value = getHashEntry(element_hash, list->token);
13704 if (element_value != NULL) // combined element found
13705 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13706 &num_list_entries);
13708 print_unknown_token(filename, list->token, num_unknown_tokens++);
13710 free(element_token);
13711 free(action_token);
13716 direction_value = getHashEntry(direction_hash, direction_token);
13718 if (direction_value != NULL) // direction found
13720 add_helpanim_entry(atoi(element_value), atoi(action_value),
13721 atoi(direction_value), delay, &num_list_entries);
13723 free(element_token);
13724 free(action_token);
13729 // this is no direction
13731 element_value = getHashEntry(element_hash, list->token);
13732 if (element_value != NULL) // combined element found
13733 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13734 &num_list_entries);
13736 print_unknown_token(filename, list->token, num_unknown_tokens++);
13738 free(element_token);
13739 free(action_token);
13742 print_unknown_token_end(num_unknown_tokens);
13744 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13745 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13747 freeSetupFileList(setup_file_list);
13748 freeSetupFileHash(element_hash);
13749 freeSetupFileHash(action_hash);
13750 freeSetupFileHash(direction_hash);
13753 for (i = 0; i < num_list_entries; i++)
13754 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13755 EL_NAME(helpanim_info[i].element),
13756 helpanim_info[i].element,
13757 helpanim_info[i].action,
13758 helpanim_info[i].direction,
13759 helpanim_info[i].delay);
13763 void LoadHelpTextInfo(void)
13765 char *filename = getHelpTextFilename();
13768 if (helptext_info != NULL)
13770 freeSetupFileHash(helptext_info);
13771 helptext_info = NULL;
13774 if (fileExists(filename))
13775 helptext_info = loadSetupFileHash(filename);
13777 if (helptext_info == NULL)
13779 // use reliable default values from static configuration
13780 helptext_info = newSetupFileHash();
13782 for (i = 0; helptext_config[i].token; i++)
13783 setHashEntry(helptext_info,
13784 helptext_config[i].token,
13785 helptext_config[i].value);
13789 BEGIN_HASH_ITERATION(helptext_info, itr)
13791 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13792 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13794 END_HASH_ITERATION(hash, itr)
13799 // ----------------------------------------------------------------------------
13801 // ----------------------------------------------------------------------------
13803 #define MAX_NUM_CONVERT_LEVELS 1000
13805 void ConvertLevels(void)
13807 static LevelDirTree *convert_leveldir = NULL;
13808 static int convert_level_nr = -1;
13809 static int num_levels_handled = 0;
13810 static int num_levels_converted = 0;
13811 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13814 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13815 global.convert_leveldir);
13817 if (convert_leveldir == NULL)
13818 Fail("no such level identifier: '%s'", global.convert_leveldir);
13820 leveldir_current = convert_leveldir;
13822 if (global.convert_level_nr != -1)
13824 convert_leveldir->first_level = global.convert_level_nr;
13825 convert_leveldir->last_level = global.convert_level_nr;
13828 convert_level_nr = convert_leveldir->first_level;
13830 PrintLine("=", 79);
13831 Print("Converting levels\n");
13832 PrintLine("-", 79);
13833 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13834 Print("Level series name: '%s'\n", convert_leveldir->name);
13835 Print("Level series author: '%s'\n", convert_leveldir->author);
13836 Print("Number of levels: %d\n", convert_leveldir->levels);
13837 PrintLine("=", 79);
13840 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13841 levels_failed[i] = FALSE;
13843 while (convert_level_nr <= convert_leveldir->last_level)
13845 char *level_filename;
13848 level_nr = convert_level_nr++;
13850 Print("Level %03d: ", level_nr);
13852 LoadLevel(level_nr);
13853 if (level.no_level_file || level.no_valid_file)
13855 Print("(no level)\n");
13859 Print("converting level ... ");
13862 // special case: conversion of some EMC levels as requested by ACME
13863 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13866 level_filename = getDefaultLevelFilename(level_nr);
13867 new_level = !fileExists(level_filename);
13871 SaveLevel(level_nr);
13873 num_levels_converted++;
13875 Print("converted.\n");
13879 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13880 levels_failed[level_nr] = TRUE;
13882 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13885 num_levels_handled++;
13889 PrintLine("=", 79);
13890 Print("Number of levels handled: %d\n", num_levels_handled);
13891 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13892 (num_levels_handled ?
13893 num_levels_converted * 100 / num_levels_handled : 0));
13894 PrintLine("-", 79);
13895 Print("Summary (for automatic parsing by scripts):\n");
13896 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13897 convert_leveldir->identifier, num_levels_converted,
13898 num_levels_handled,
13899 (num_levels_handled ?
13900 num_levels_converted * 100 / num_levels_handled : 0));
13902 if (num_levels_handled != num_levels_converted)
13904 Print(", FAILED:");
13905 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13906 if (levels_failed[i])
13911 PrintLine("=", 79);
13913 CloseAllAndExit(0);
13917 // ----------------------------------------------------------------------------
13918 // create and save images for use in level sketches (raw BMP format)
13919 // ----------------------------------------------------------------------------
13921 void CreateLevelSketchImages(void)
13927 InitElementPropertiesGfxElement();
13929 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13930 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13932 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13934 int element = getMappedElement(i);
13935 char basename1[16];
13936 char basename2[16];
13940 sprintf(basename1, "%04d.bmp", i);
13941 sprintf(basename2, "%04ds.bmp", i);
13943 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13944 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13946 DrawSizedElement(0, 0, element, TILESIZE);
13947 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13949 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13950 Fail("cannot save level sketch image file '%s'", filename1);
13952 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13953 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13955 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13956 Fail("cannot save level sketch image file '%s'", filename2);
13961 // create corresponding SQL statements (for normal and small images)
13964 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13965 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13968 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13969 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13971 // optional: create content for forum level sketch demonstration post
13973 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13976 FreeBitmap(bitmap1);
13977 FreeBitmap(bitmap2);
13980 fprintf(stderr, "\n");
13982 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13984 CloseAllAndExit(0);
13988 // ----------------------------------------------------------------------------
13989 // create and save images for element collecting animations (raw BMP format)
13990 // ----------------------------------------------------------------------------
13992 static boolean createCollectImage(int element)
13994 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13997 void CreateCollectElementImages(void)
14001 int anim_frames = num_steps - 1;
14002 int tile_size = TILESIZE;
14003 int anim_width = tile_size * anim_frames;
14004 int anim_height = tile_size;
14005 int num_collect_images = 0;
14006 int pos_collect_images = 0;
14008 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14009 if (createCollectImage(i))
14010 num_collect_images++;
14012 Info("Creating %d element collecting animation images ...",
14013 num_collect_images);
14015 int dst_width = anim_width * 2;
14016 int dst_height = anim_height * num_collect_images / 2;
14017 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14018 char *basename_bmp = "RocksCollect.bmp";
14019 char *basename_png = "RocksCollect.png";
14020 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14021 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14022 int len_filename_bmp = strlen(filename_bmp);
14023 int len_filename_png = strlen(filename_png);
14024 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14025 char cmd_convert[max_command_len];
14027 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14031 // force using RGBA surface for destination bitmap
14032 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14033 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14035 dst_bitmap->surface =
14036 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14038 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14040 if (!createCollectImage(i))
14043 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14044 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14045 int graphic = el2img(i);
14046 char *token_name = element_info[i].token_name;
14047 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14048 Bitmap *src_bitmap;
14051 Info("- creating collecting image for '%s' ...", token_name);
14053 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14055 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14056 tile_size, tile_size, 0, 0);
14058 // force using RGBA surface for temporary bitmap (using transparent black)
14059 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14060 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14062 tmp_bitmap->surface =
14063 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14065 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14067 for (j = 0; j < anim_frames; j++)
14069 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14070 int frame_size = frame_size_final * num_steps;
14071 int offset = (tile_size - frame_size_final) / 2;
14072 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14074 while (frame_size > frame_size_final)
14078 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14080 FreeBitmap(frame_bitmap);
14082 frame_bitmap = half_bitmap;
14085 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14086 frame_size_final, frame_size_final,
14087 dst_x + j * tile_size + offset, dst_y + offset);
14089 FreeBitmap(frame_bitmap);
14092 tmp_bitmap->surface_masked = NULL;
14094 FreeBitmap(tmp_bitmap);
14096 pos_collect_images++;
14099 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14100 Fail("cannot save element collecting image file '%s'", filename_bmp);
14102 FreeBitmap(dst_bitmap);
14104 Info("Converting image file from BMP to PNG ...");
14106 if (system(cmd_convert) != 0)
14107 Fail("converting image file failed");
14109 unlink(filename_bmp);
14113 CloseAllAndExit(0);
14117 // ----------------------------------------------------------------------------
14118 // create and save images for custom and group elements (raw BMP format)
14119 // ----------------------------------------------------------------------------
14121 void CreateCustomElementImages(char *directory)
14123 char *src_basename = "RocksCE-template.ilbm";
14124 char *dst_basename = "RocksCE.bmp";
14125 char *src_filename = getPath2(directory, src_basename);
14126 char *dst_filename = getPath2(directory, dst_basename);
14127 Bitmap *src_bitmap;
14129 int yoffset_ce = 0;
14130 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14133 InitVideoDefaults();
14135 ReCreateBitmap(&backbuffer, video.width, video.height);
14137 src_bitmap = LoadImage(src_filename);
14139 bitmap = CreateBitmap(TILEX * 16 * 2,
14140 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14143 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14150 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14151 TILEX * x, TILEY * y + yoffset_ce);
14153 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14155 TILEX * x + TILEX * 16,
14156 TILEY * y + yoffset_ce);
14158 for (j = 2; j >= 0; j--)
14162 BlitBitmap(src_bitmap, bitmap,
14163 TILEX + c * 7, 0, 6, 10,
14164 TILEX * x + 6 + j * 7,
14165 TILEY * y + 11 + yoffset_ce);
14167 BlitBitmap(src_bitmap, bitmap,
14168 TILEX + c * 8, TILEY, 6, 10,
14169 TILEX * 16 + TILEX * x + 6 + j * 8,
14170 TILEY * y + 10 + yoffset_ce);
14176 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14183 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14184 TILEX * x, TILEY * y + yoffset_ge);
14186 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14188 TILEX * x + TILEX * 16,
14189 TILEY * y + yoffset_ge);
14191 for (j = 1; j >= 0; j--)
14195 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14196 TILEX * x + 6 + j * 10,
14197 TILEY * y + 11 + yoffset_ge);
14199 BlitBitmap(src_bitmap, bitmap,
14200 TILEX + c * 8, TILEY + 12, 6, 10,
14201 TILEX * 16 + TILEX * x + 10 + j * 8,
14202 TILEY * y + 10 + yoffset_ge);
14208 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14209 Fail("cannot save CE graphics file '%s'", dst_filename);
14211 FreeBitmap(bitmap);
14213 CloseAllAndExit(0);